Skip to content

feat(devframe): route-based MCP server on the dev server#85

Merged
antfu merged 1 commit into
mainfrom
feat/mcp-http-route
Jul 10, 2026
Merged

feat(devframe): route-based MCP server on the dev server#85
antfu merged 1 commit into
mainfrom
feat/mcp-http-route

Conversation

@antfubot

Copy link
Copy Markdown
Collaborator

What

Exposes a devframe's agent surface over the MCP Streamable-HTTP transport at /__mcp (relative to the base path), so an MCP client connects to the running dev/cli server and sees live tool/resource changes — rather than spawning a fresh stdio child that boots a throwaway context.

It's the same ctx.agent tools + shared-state resources the stdio mcp command serves, against the live server.

How

  • Opt-in. New cli.mcp?: boolean | McpRouteOptions on the definition, with --mcp / --no-mcp overriding per run; default off. McpRouteOptions carries path? and allowedOrigins?.
  • Mount. createDevServer mounts an h3 route at joinURL(basePath, '__mcp')/__mcp standalone, /__<id>/__mcp hosted — before the SPA static catch-all, and folds MCP session teardown into StartedServer.close().
  • Transport. mountMcpHttp() (new adapters/mcp/http.ts) drives the SDK's web-standard WebStandardStreamableHTTPServerTransport, whose handleRequest(Request): Response fits h3 v2's web event model. It reuses the transport-agnostic buildMcpServerFromContext. The response is copied onto event.res + body (not returned as a Response) so a legit MCP 404 isn't swallowed by h3's Response-404 fall-through into the static handler.
  • Stateful sessions. One transport + MCP server per Mcp-Session-Id, so tools/list_changed / resources/list_changed notifications reach connected clients; sessions are torn down on DELETE / disconnect.
  • Perimeter. Reuses the WS transport's isAllowedOrigin gate as the endpoint's DNS-rebinding protection (the external-middleware approach the SDK now recommends over its deprecated built-in host/origin flags), with the same loopback default; widen via allowedOrigins.
  • Discovery. __connection.json gains an optional mcp: { path } block when enabled.
  • Optional peer dep. @modelcontextprotocol/sdk is imported lazily — only when the route is enabled — so the core dev path never pulls it in.

Design

Shaped through a grilling session; key decisions: live agent surface (not throwaway ctx), Streamable-HTTP only, stateful sessions, cli-dev + vite bridge side-car coverage, basePath-relative path, WS-parity perimeter (no bearer token), opt-in default-off.

Tests / docs

  • New HTTP integration test (mcp-http.test.ts): initialize → session id, tools/list, tools/call, DELETE teardown + stale-id 404, cross-origin 403, and the __connection.json advertise/omit cases. Existing in-memory buildMcpServerFromContext test retained.
  • Updated docs/adapters/mcp.md, docs/errors/DF0017.md, and the tsnapi public-API snapshots.
  • pnpm lint && pnpm test && pnpm typecheck && pnpm build all green (642 tests).

This PR was created with the help of an agent.

Expose the agent surface over the MCP Streamable-HTTP transport at
`/__mcp` (relative to the base path) so an MCP client connects to the
running dev/cli server and sees live tool/resource changes, rather than
spawning a fresh stdio child.

Opt-in via `cli.mcp` (`boolean | McpRouteOptions`) with `--mcp` /
`--no-mcp` overriding per run; default off. Each client session gets its
own MCP server built from the live context, correlated by the
`Mcp-Session-Id` header, so listChanged notifications reach clients. The
endpoint reuses the WS transport's loopback origin gate as its
DNS-rebinding protection and is advertised in `__connection.json` for
in-browser discovery. The `@modelcontextprotocol/sdk` peer dependency is
loaded lazily, only when the route is enabled.
@netlify

netlify Bot commented Jul 10, 2026

Copy link
Copy Markdown

Deploy Preview for devfra ready!

Name Link
🔨 Latest commit feae887
🔍 Latest deploy log https://app.netlify.com/projects/devfra/deploys/6a504e9e9d9cc500081fea5b
😎 Deploy Preview https://deploy-preview-85--devfra.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@antfu antfu merged commit caf6c5a into main Jul 10, 2026
7 of 12 checks passed
@antfu antfu deleted the feat/mcp-http-route branch July 10, 2026 01:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants