Architecture | Project Structure | Startup Process | Server Features | Extension Points | How It Works
- Usage
node dist/index.js [stdio|sse|streamableHttp] - Runs the specified transport manager to handle client connections.
- Specify transport type on command line (default
stdio)stdio→transports/stdio.jssse→transports/sse.js(deprecated, 2025-era clients only; prints a warning)streamableHttp→transports/streamableHttp.js
- Serves server instances built by
createServer()fromserver/index.ts, one per connection (or per request), each for the protocol era the client speaks. - Handles communication according to the MCP specs for the chosen transport.
- STDIO:
- One process‑bound connection, served by the SDK's
serveStdioentry. - The client's first message picks the era:
server/discover(or any request with the 2026-07-28_metaenvelope) for 2026-07-28,initializefor the 2025 era.serveStdiothen builds the server for that era withcreateServer({ era })and pins it to the connection (it may build and discard one more first, to answer a discover probe). - Runs an instance's
cleanup()when it closes, and onSIGINTcloses the connection, cleans up and exits.
- One process‑bound connection, served by the SDK's
- SSE (deprecated; 2025-era clients only):
- Supports multiple client connections, one server per
GET /ssestream. - Client transports are mapped to
sessionId. - Hooks server’s
oncloseto clean and remove session. - Exposes
/sseGET (SSE stream)/messagePOST (JSON‑RPC messages)
- Supports multiple client connections, one server per
- Streamable HTTP: one
/mcpendpoint for both eras.- 2026-07-28: a session-less POST carrying the per-request
_metaenvelope is served by the SDK'screateMcpHandler, which builds a fresh server per request withcreateServer({ era: "modern", publish }). There are no sessions. Change notifications are published on the handler's subscription bus (publish), which reaches every opensubscriptions/listenstream. - 2025 era: everything else, classified by the SDK's
isLegacyRequest, keeps the sessionful wiring: a server per session, created on theinitializePOST, with GET for the standalone SSE stream and DELETE to end the session. Transports are stored bysessionId, an event store makes streams resumable, and the session'scleanup()runs when it closes. Sessionful serving keeps the server-to-client channel the legacy shim needs for elicitation, sampling and roots. - A session-less POST's body is parsed once (up to 4 MiB) to classify it and serve it.
- 2026-07-28: a session-less POST carrying the per-request
- SSE and Streamable HTTP bind their port (
PORT, default3001) throughlistenOrExit()intransports/listen.ts.- The listening line is printed only once the port is bound.
- A bind failure, such as a port already in use, is reported on stderr and the process exits non-zero.
- STDIO:
- Invoke
createServer({ era, publish })fromserver/index.ts - Creates a new
McpServerinstance with- Capabilities:
tools: { listChanged: true }logging: {}prompts: { listChanged: true }resources: { subscribe: true, listChanged: true }extensions: { "io.modelcontextprotocol/tasks": {} }(2026-07-28 only)
- Server Instructions
- Loaded from the docs folder (
instructions.md).
- Loaded from the docs folder (
- Options
- Cache hints for the 2026-07-28 cacheable results (
cacheHints). - The
requestStateverifier for multi-round tools (roundStateCodec).
- Cache hints for the 2026-07-28 cacheable results (
- Registrations
- Builds a per-instance context (
server/context.ts): the era, where change notifications go, the 2025-era resource subscriptions, and a cleanup registry. - Registers tools via
registerTools(server, context), and then by era:registerModernToolsand the tasks extension (server/tasks.ts) for 2026-07-28;registerLegacyToolsnow andregisterConditionalToolsonce the client has initialized, for the 2025 era. - Registers resources via
registerResources(server, context). - Registers prompts via
registerPrompts(server).
- Builds a per-instance context (
- Other Request Handlers (2025 era)
- Sets up resource subscription handlers via
setSubscriptionHandlers(server, subscribed). - Syncs the client's roots after
initializeviasyncRoots(server), which also installs theroots/list_changedhandler.
- Sets up resource subscription handlers via
- Returns
- The
McpServerinstance - A
cleanup()callback that stops the instance's intervals and clears its state
- The
- Capabilities:
Every piece of per-connection state lives on the server instance that serves the connection: there are no maps keyed by session id. On stdio and on a 2025-era session one instance serves the whole connection; on the 2026-07-28 HTTP path each request gets its own instance, so what must outlive a request (a task, a minted resource) is stored process-wide under a handle the server mints.