Repository navigation
Investigate @orpc/mcp McpRegistrar (or Hono MCP adapter) in every-plugin to replace the hand-rolled MCP bridge #673
Copy link
Copy link
Open
Description
Context
host/src/services/mcp.ts hand-rolls MCP tool generation today:
- Generates an OpenAPI spec from the oRPC router (
@orpc/openapi), walkspaths, and hand-builds each tool'sinputSchemafrom operation parameters + request-body properties — the exact seam that broke in Host/api/mcpsilently fails to mount when any operation input has a$ref(e.g.z.json()) #670 (a$refin any input kills Ajv compilation; fixed stopgap in PR fix(host): self-contained MCP tool schemas; skip a failing tool instead of the whole mount #671). - Executes each tool call by constructing a
Requestand round-tripping through the OpenAPI handler, flattening every response body to a singletextcontent block. - Carries auth context via a module-level
AsyncLocalStoragebridge (mcpRequestStorage) into tool calls. - Skips any operation with an SSE response entirely — streaming procedures are invisible to MCP clients.
Proposed direction
@orpc/mcp ships McpRegistrar, which registers every router procedure directly as an MCP tool through the same standard handler as the RPC/OpenAPI handlers:
- Schema-derived tool inputs (no manual
$ref/$defssurgery — the Host/api/mcpsilently fails to mount when any operation input has a$ref(e.g.z.json()) #670 bug class disappears), with per-tool overrides viamcp.toolmetadata and procedurefilter. - Proper error mapping:
ORPCError→ MCP tool execution errors (model-readable JSON). - Structured content outputs; Smart Coercion plugin for
Date/BigIntinputs. - Per-request context via
register(server, { context: async ctx => ... })— likely replaces theAsyncLocalStoragebridge. - Interceptors/plugin reuse (timeout, logging; CORS-compression-style HTTP plugins are no-ops,
Rethrowunsupported).
Alternative to evaluate: a Hono MCP adapter for the transport side (transport mounting is currently the SDK's WebStandardStreamableHTTPServerTransport wired by hand).
Spike questions
- Placement — should this live in
every-plugin(plugin runtime consumes/hosts it) instead of host-internal code, so plugin RPC surfaces become MCP tools generically? - Which plugin surfaces — per-plugin MCP (
/api/rpc/{plugin}/...→ plugin MCP) vs. the composed base API router as today. - SSE/streaming procedures — currently forcibly skipped; does
McpRegistrarAsyncIteratorObject progress mapping give us these for free? - Auth flow — map the current
buildPluginContext+x-api-key/session middleware into the registrar'scontextfunction-of-request; confirm per-request context is evaluated per tool call, not per server. - Version/gate —
@orpc/mcp@beta, MCP TS SDK v2 (@modelcontextprotocol/server); check compatibility with the current@orpcpin and the Effect dev toolchain additions. - Teardown —
closeMcpServer()/mcpTransportlifecycle must survive the swap (host reload paths call it).
Notes
- PR fix(host): self-contained MCP tool schemas; skip a failing tool instead of the whole mount #671 merges as the stopgap (self-contained tool schemas + per-tool skip). This ticket is the systemic fix that should obsolete the hand-built schema machinery.
- Deferred from Host
/api/mcpsilently fails to mount when any operation input has a$ref(e.g.z.json()) #670: only advertise/api/mcpin/.well-known/mcp.jsonafter a successful mount — fold that decision in here (or as an immediate follow-up if the swap lands later).
Metadata
Metadata
Assignees
Labels
No labels
Type
Fields
Priority
None yet
Activity