-
Notifications
You must be signed in to change notification settings - Fork 149
Fetch That Works in MCP Apps #425
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
MiguelsPizza
wants to merge
8
commits into
modelcontextprotocol:main
Choose a base branch
from
MiguelsPizza:feat/http-adapter
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Fetch That Works in MCP Apps #425
MiguelsPizza
wants to merge
8
commits into
modelcontextprotocol:main
from
MiguelsPizza:feat/http-adapter
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Introduce a standard http_request tool contract and the client/server adapter that makes HTTP a first-class, auditable transport for MCP Apps. Core additions: - Spec/types + generated schemas for McpHttpRequest/Response + body types. - Fetch and XHR wrappers that proxy matching requests through tools/call and reconstruct a Response/XHR surface on the way back. - Shared codecs for body serialization (json/text/formData/urlEncoded/base64), response parsing, and request normalization. - Server-side handler that enforces allowPaths/allowOrigins, strips forbidden headers, pins baseUrl, applies body size limits, and maps HTTP responses into structuredContent. - initMcpHttp entrypoint to wire fetch and XHR together with a single handle. This keeps auth and policy on the server side while letting UI code stay HTTP- native and portable across MCP and standalone environments.
Wire the adapter into the SDK build/exports and add end-to-end test coverage that exercises the exact host/app/server proxy path. Packaging + integration: - Export /http-adapter, /fetch-wrapper, and /xhr-wrapper bundles and build them in the SDK pipeline. - Add workspace linking so example packages resolve freshly built dist output. - Improve useApp so standalone UIs can initialize without a parent host. Test strategy (real browser + full flow): - Vitest browser harness with MSW service worker to capture real network requests and assert parity between direct and proxied calls. - Browser suites for fetch and XHR adapters covering body types, headers, status mapping, and error surfaces. - Basic-host app/host fixtures plus Playwright E2E that run a full loop: iframe → AppBridge → tools/call → http_request handler → HTTP response. Also expands gitignore for caches and browser screenshot output.
Show the adapter in practice with both a lightweight walkthrough and a full-stack reference app. - basic-server-vanillajs now uses http_request to serve /api/* in-process and demonstrates fetch/XHR interception without requiring a separate backend. - hono-react-server adds a complete dual-mode example (Hono backend + React UI) that runs against native HTTP in standalone dev and switches to MCP proxying in-host. - README calls out the new reference example for discovery.
- Rename http-adapter HTML files to .test.html convention - Update hono-react-server with improved MCP app UI - Add getBackendUrl helper to App class - Update E2E test to use new file naming Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Documentation: - Fix misleading @throws JSDoc in initMcpFetch - Combine split JSDoc comments (/** desc */ /** @internal */) - Add @internal tags to internal helper functions - Document maxBodySize <= 0 disables enforcement - Improve warnNativeFallback with actionable guidance Types: - Extract McpHttpHandleBase interface for common lifecycle methods - McpFetchHandle, McpXhrHandle, McpHttpHandle now extend base - Reduces duplication and improves discoverability Error context: - Add value preview to JSON parse warnings - Add stack trace to XHR debug error logging - Fix isInIframe() for cross-origin iframes and SSR Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove link-workspace-root.mjs (no longer needed) - Fix formatting in hono-react mcp-app.tsx Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add schema.js bundle to dist/src/generated/ so TypeScript can resolve types through declaration files with NodeNext module resolution - Re-export McpHttpRequestSchema and McpHttpResponseSchema from fetch-wrapper entry point for server-side tool registration - Update hono-react-server example to import schemas from fetch-wrapper Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
MCP-APPS-HTTP-PROXY.mp4
Fetch That Works in MCP Apps
Track: Extensions (proposal)
Status: Draft
Reference implementation:
src/http-adapter/Demo:
examples/hono-react-server/Tests:
tests/browser/*http-adapter*,tests/e2e/http-adapter.spec.tsAbstract
This proposal standardizes an app-only
http_requesttool plus a client-side HTTP adapter that transparently routesfetch()andXMLHttpRequestcalls through MCPtools/call. The result is a single, auditable HTTP transport primitive for MCP Apps that preserves MCP’s security model while dramatically improving developer experience, portability, and reuse of existing web code.Motivation
1) MCP App development is diverging from normal web development
Today, building MCP Apps often means rewriting the web app layer so it can speak
tools/call. That forces teams onto purpose-built frameworks or custom adapters just to reuse existing HTTP clients, SDKs, or libraries. If you’re not on a highly specialized stack, you end up writing and maintaining a parallel data layer.The HTTP adapter restores framework-agnostic development: you can keep
fetch, Axios, Honohc, GraphQL clients, OpenAPI SDKs (whatever your stack already uses) without replatforming your UI just to run inside an MCP host.2) UI-only tools overload MCP’s semantic tool model
MCP tools are semantic, model-facing interfaces that intentionally do not need to be versioned as the model calls them dynamically at runtime. But re-using MCP tools for UI interaction couples the MCP server to a programatic server client contract. UI-only interactions (button clicks, form submissions, hydration updates) force you to mint lots of tool wrappers that the model never sees and doesn’t need. That adds a semantic surface area you must now version and maintain, undermining the original “dynamic client” advantage of MCP.
The
http_requesttool collapses those UI-only calls into a single transport primitive, preserving MCP’s model-facing semantics while keeping UI traffic out of the tool namespace (except for the one tool that serves the UI resource itself).Goals
Non-Goals
tools/call.Quick Start (Server + App)
Server: one app-only
http_requesttoolThe MCP server can proxy upstream HTTP or handle routes directly (e.g., Hono). The tool contract is HTTP-shaped, but the handler can be any server logic.
App: fetch just works now
Architecture Overview
flowchart LR subgraph Host["MCP Host"] App["App iframe"] Adapter["HTTP Adapter (fetch/XHR wrapper)"] end subgraph Server["MCP Server"] Tool["http_request tool handler"] Routes["Local routes or upstream proxy"] end Backend["HTTP backend (optional)"] App --> Adapter Adapter -->|"tools/call"| Tool Tool --> Routes Routes --> Backend Backend --> Routes Routes --> Tool Tool -->|"structuredContent"| Adapter Adapter --> AppCurrent spec constraints (why direct HTTP is gated)
Per the MCP Apps spec (
specification/2026-01-26/apps.mdx), hosts enforce a restrictive CSP by default. Ifui.cspis omitted, the default includesconnect-src 'none', so embedded apps cannot make network requests. Hosts may opt in to network access viaui.csp.connectDomains, but the iframe still runs in a separate origin and does not share backend credentials. In practice, authenticated app→backend traffic must flow throughtools/call, which is already the audited, policy-enforced path in MCP.Specification
Tool Contract (from
src/spec.types.ts)Visibility:
_meta.ui.visibility: ["app"]Server Handler (reference implementation)
createHttpRequestToolHandler(options)enforces:allowPathsprefix allowlist.allowOriginsfor absolute URLs.forbiddenHeadersstripping for sensitive headers.baseUrlpinning for relative requests.maxBodySizeenforcement.credentials,timeoutMs, and customfetchimplementation.The handler maps request body types to HTTP body encodings and normalizes responses back into
McpHttpResponse.Client Adapter (fetch/XHR)
initMcpHttp(app, options)installs wrappers for:fetch()viainitMcpFetch.XMLHttpRequestviainitMcpXhr.Interception policy (default):
app.getHostCapabilities()?.serverTools).allowAbsoluteUrlsis enabled.interceptPathsprefixes.fallbackToNative: true.Request Lifecycle
sequenceDiagram participant App as App iframe participant Adapter as HTTP Adapter participant Host as MCP Host participant Server as MCP Server participant Backend as HTTP Backend App->>Adapter: fetch("/api/items", { method: "POST", body: ... }) Adapter->>Adapter: shouldIntercept? isMcpApp? path allowed? Adapter->>Host: tools/call http_request Host->>Server: tools/call http_request Server->>Server: validate allowPaths/headers/body size Server->>Backend: fetch(baseUrl + url, { method, headers, body }) Backend-->>Server: HTTP response Server-->>Host: CallToolResult (structuredContent) Host-->>Adapter: CallToolResult Adapter-->>App: Response(status, headers, body)Developer Experience and Portability
Why HTTP alignment matters
fetch()or client library code.hc, GraphQL clients, OpenAPI SDKs, and form upload flows.Dual-Mode Example
The app code remains identical in both modes. The adapter chooses the transport.
Rationale
Why a single generic tool?
Per-endpoint tools are ideal for model-facing actions. For app-only traffic, they introduce boilerplate without adding meaning. A single
http_requesttool preserves MCP auditability while removing the per-endpoint tool layer from UI code.Why intercept
fetch()and XHR?Intercepting
fetch()andXMLHttpRequestpreserves the web platform surface area and allows existing HTTP libraries to work without modification. The adapter is opt-in and path-scoped, so it does not change global network behavior unless explicitly configured.Why not host-level HTTP proxying?
Host-level proxies bypass MCP’s JSON-RPC transport. The HTTP adapter keeps every request within
tools/call, preserving audit logging, rate limiting, and policy enforcement.Security Considerations
visibility: ["app"]keeps the tool out of the model’s tool list.allowPathsblocks arbitrary URL access.baseUrlensures the backend destination is server-controlled.forbiddenHeadersprevents cookie and auth exfiltration.Backward Compatibility
app.callServerToolremains unchanged.http_requestis standardtools/call.Reference Implementation Notes
src/http-adapter/fetch-wrapper/fetch.tsimplements fetch interception, payload serialization, and response reconstruction.src/http-adapter/xhr-wrapper/xhr.tsmirrors XHR semantics and response types.src/http-adapter/shared/body.tshandles body type conversion and response extraction.examples/hono-react-server/demonstrates dual-mode operation against a real HTTP backend.tests/browser/*http-adapter*andtests/e2e/http-adapter.spec.tsvalidate direct vs proxied parity.Future Work
http_request.Testing
npm run test:browsernpm run test:e2e