Add example-mcp-app: Spiceflow + MCP Apps integration#46
Conversation
…map UI Demonstrates how a single Spiceflow app can serve both a website (RSC pages with Tailwind) and an MCP server endpoint at POST /mcp. Architecture: - src/main.tsx: Spiceflow app with / and /about pages, plus a /mcp endpoint that adapts Web Request/Response to the MCP SDK StreamableHTTPServerTransport - src/mcp-server.ts: McpServer factory with geocode (OpenStreetMap Nominatim) and show-map (interactive Leaflet map) tools - src/app/mcp-app.tsx: client-only React app for the MCP App iframe UI, uses @modelcontextprotocol/ext-apps SDK for host communication and registers a navigate-to tool back to the LLM - mcp-app.html: HTML shell bundled into a single file via vite-plugin-singlefile - Two Vite configs: one for the Spiceflow website, one for the MCP UI bundle Build pipeline: pnpm build:mcp-ui bundles the iframe HTML first, then pnpm build runs the Spiceflow production build. The MCP server reads the pre-built HTML from dist-mcp-ui/ at runtime and serves it as a ui:// resource. Session: ses_2462ab72dffelMbT6WxWAkDW24
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7eed5c464e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // Resolve the dist directory for the pre-built MCP App UI HTML. | ||
| // In dev we read from dist-mcp-ui/ (built by `pnpm build:mcp-ui`). | ||
| function getMcpUiDistDir(): string { | ||
| return path.join(import.meta.dirname, '..', 'dist-mcp-ui') |
There was a problem hiding this comment.
Resolve dist-mcp-ui relative to runtime entry
getMcpUiDistDir() is computed from import.meta.dirname, but after vite build this module runs from dist/rsc/..., so path.join(import.meta.dirname, '..', 'dist-mcp-ui') points inside dist/ instead of the project-level dist-mcp-ui folder produced by build:mcp-ui. In production (node dist/rsc/index.js), the show-map resource read will fail with ENOENT, so the MCP app UI cannot be served.
Useful? React with 👍 / 👎.
|
|
||
| try { | ||
| await server.connect(transport) | ||
| await transport.handleRequest(fakeReq as any, fakeRes as any, body) |
There was a problem hiding this comment.
Pass a real IncomingMessage to MCP transport
StreamableHTTPServerTransport.handleRequest() expects Node IncomingMessage/ServerResponse, but this code passes a plain object cast to any. The adapter layer used by the SDK relies on request stream/event APIs (for example request lifecycle listeners on POST bodies), which fakeReq does not implement, so /mcp requests can fail at runtime and return the generic 500 path instead of handling MCP messages.
Useful? React with 👍 / 👎.
Spiceflow website + MCP server in a single app. Demonstrates how one Spiceflow process can serve RSC pages at
/and speak the Model Context Protocol atPOST /mcp.src/main.tsx— Spiceflow app with website pages +/mcpendpoint that adapts Web Request/Response to the MCP SDK'sStreamableHTTPServerTransportsrc/mcp-server.ts—McpServerfactory withgeocode(OpenStreetMap Nominatim) andshow-map(interactive Leaflet map) toolssrc/app/mcp-app.tsx— client-only React app for the MCP App iframe, uses@modelcontextprotocol/ext-appsSDK, registers anavigate-totool back to the LLMvite-plugin-singlefileBuild:
pnpm build:mcp-uibundles the iframe HTML, thenpnpm buildruns the full Spiceflow production build. The MCP server reads the pre-built HTML fromdist-mcp-ui/at runtime.