AgentBack — System Architecture

Everything is a binding in one DI Context · servers discover by tag · one Zod schema feeds every contract

by tag by tag @inject Application — one DI Context · everything below is a binding Shared Zod schemas one artifact → validator · z.infer type · OpenAPI · MCP · docs Services Providers · Config bound by key singleton / transient resolved on demand Controllers @api · @get / @post / @put / @del carry Zod path / query / body / response [ tag: restController ] MCP tool classes @mcpServer · @tool / @resource / @prompt carry Zod input / output [ extensionFor: MCP_SERVERS ] RestServer servers.RestServer → HTTP · /openapi.json → /explorer (Swagger UI) discovers restController ◂ MCPServer servers.MCPServer → stdio transport → /mcp-inspector discovers MCP_SERVERS ◂ One Zod schema · five coherent views z.object({ … }) single source of truth runtime validator rejects bad input (422/400) z.infer<> TS type compile-time handler type OpenAPI 3.1 z.toJSONSchema() MCP input/output tool contract for the SDK Swagger UI / Inspector rendered docs

Everything is a binding

  • • The Application is a DI Context.
  • • Controllers, tools, services, config, and the servers are all bindings.
  • • New capability = new binding; you never edit a central registry.
  • • Swap any dependency for tests with one app.bind(...).

Servers discover by tag

  • • RestServer finds restController-tagged bindings.
  • • MCPServer finds MCP_SERVERS extensions.
  • • Discovery happens at app.start() — no router file.
  • • Run REST, MCP, or both from one process.

One schema, five views

  • • A Zod schema is the validator and the z.infer type.
  • • …the OpenAPI 3.1 parameter/response.
  • • …the MCP tool input/output.
  • • …and the rendered docs. Change it once; all move together.