-
Notifications
You must be signed in to change notification settings - Fork 44
feat(examples): add SSE transport support and shared server utility #136
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
Conversation
Add backwards-compatible SSE transport support alongside the current Streamable HTTP transport. Each server now exposes: - /mcp (GET, POST, DELETE) - Streamable HTTP transport (current spec) - /sse (GET) - Legacy SSE transport stream endpoint - /messages (POST) - Legacy SSE transport message endpoint This enables older clients using the deprecated HTTP+SSE protocol (version 2024-11-05) to connect to the example servers. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Refactored the following example servers to use the shared startServer utility from examples/shared/server-utils.ts: - threejs-server - system-monitor-server - cohort-heatmap-server - budget-allocator-server - customer-segmentation-server - scenario-modeler-server Changes for each server: - Removed direct imports: SSEServerTransport, StdioServerTransport, StreamableHTTPServerTransport, cors, express - Removed PORT constant (now handled by shared utility) - Added import for startServer from ../shared/server-utils.js - Replaced entire async main() function with single startServer() call - Preserved all business logic (tool registration, resource registration, helper functions) This reduces code duplication by ~700 lines and ensures consistent transport handling across all example servers. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Complete the migration of example servers to use the shared server-utils.ts module for transport setup: - basic-server-react - basic-server-vanillajs - wiki-explorer-server - Add shared/server-utils.ts This centralizes all transport handling (stdio, Streamable HTTP, SSE) in one place, reducing code duplication across ~500 lines. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Address critical issues in the shared server utility: 1. Stateful sessions: Use sessionIdGenerator + onsessioninitialized to persist StreamableHTTPServerTransport across requests 2. Unified session store: Single Map for both transport types with proper type discrimination 3. Error handling: Try/catch on all endpoints with JSON-RPC errors 4. DNS rebinding protection: Use SDK's createMcpExpressApp helper 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Use host: "0.0.0.0" in createMcpExpressApp to disable DNS rebinding protection, allowing connections from Android emulators and other devices on the network. Also re-add CORS middleware. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
commit: |
- Add getPort() helper that returns undefined for --stdio, else port - Caller now explicitly passes port to startServer() - port: undefined → stdio mode, port: number → HTTP mode This makes the transport selection explicit at the call site. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
6a62304 to
46a1596
Compare
…isten
Use httpServer.on('listening') and httpServer.on('error') events
to properly resolve or reject the returned promise.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <[email protected]>
Exit with code 1 if server fails to start (e.g., port in use). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
…dio support Update all example server files to use the new main() pattern that supports both HTTP and stdio transports: - Add StdioServerTransport import - Remove getPort import (replaced with direct env var parsing) - Replace direct startServer call with async main() function - Add --stdio flag detection to choose between transports - Use PORT env var with default 3001 for HTTP mode Updated servers: - basic-server-vanillajs - wiki-explorer-server - threejs-server - system-monitor-server - cohort-heatmap-server - budget-allocator-server - customer-segmentation-server - scenario-modeler-server Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
690b5dc to
55d652b
Compare
Each server now defaults to its assigned port from run-all.ts: - basic-server-react: 3101 - basic-server-vanillajs: 3102 - budget-allocator-server: 3103 - cohort-heatmap-server: 3104 - customer-segmentation-server: 3105 - scenario-modeler-server: 3106 - system-monitor-server: 3107 - threejs-server: 3108 - wiki-explorer-server: 3109 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Without exposedHeaders in the CORS config, browsers block JavaScript from reading the mcp-session-id response header. This caused the SDK's StreamableHTTPClientTransport to never capture the session ID, breaking all subsequent requests with "Bad request: not initialized" errors. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need for the Kotlin SDK to connect to the basic examples? Or are the marketing examples sufficient?
Generally, I think we should keep basic-server-vanillajs and basic-server-react dead simple (and self-contained) as reference examples, so I would prefer that they only support a single transport.
@jonathanhefner I found bugs thanks to some of the examples already. If we adopt / keep the Kotlin & Swift examples it's good to be able to test them against something (native e2e tests will come in a while too), esp. since some users will only care about mobile apps hosts.
I kept the stdio part and their import inlined in each file, so users can trivially axe the http transports by removing two lines (or adopt it by bringing in that file, which imho should be in the TS MCP SDK in some form... but I digress). We do need some kind of remote for our own testing though, and I didn't want this to cause too much duplication. |
jonathanhefner
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I don't want to block anything. I will take a look into alternatives at a later date. 👍
Summary
server-utils.tsmodule for HTTP transport setupChanges
New:
examples/shared/server-utils.tsA shared utility for HTTP transports:
/mcp(GET/POST/DELETE): Streamable HTTP with stateful sessions/sse+/messages: Legacy SSE for older clientsFeatures
sessionIdGeneratorstartServer()returns Promise that resolves on listen, rejects on error0.0.0.0for Android/device accessServer Pattern
Each server now uses explicit transport selection:
Servers Updated
All 9 TypeScript example servers:
Test plan
npm run buildpassesnpm testpasses🤖 Generated with Claude Code