{"_id":"@ampersend_ai/modelcontextprotocol-sdk","name":"@ampersend_ai/modelcontextprotocol-sdk","dist-tags":{"latest":"1.20.1"},"versions":{"1.20.1":{"name":"@ampersend_ai/modelcontextprotocol-sdk","version":"1.20.1","description":"Model Context Protocol implementation for TypeScript","license":"MIT","author":{"name":"Anthropic, PBC","url":"https://anthropic.com"},"homepage":"https://github.com/edgeandnode/mcp-typescript-sdk","bugs":{"url":"https://github.com/edgeandnode/mcp-typescript-sdk/issues"},"type":"module","repository":{"type":"git","url":"git+https://github.com/edgeandnode/mcp-typescript-sdk.git"},"engines":{"node":">=18"},"keywords":["modelcontextprotocol","mcp"],"exports":{".":{"import":"./dist/esm/index.js","require":"./dist/cjs/index.js"},"./client":{"import":"./dist/esm/client/index.js","require":"./dist/cjs/client/index.js"},"./server":{"import":"./dist/esm/server/index.js","require":"./dist/cjs/server/index.js"},"./*":{"import":"./dist/esm/*","require":"./dist/cjs/*"}},"typesVersions":{"*":{"*":["./dist/esm/*"]}},"scripts":{"fetch:spec-types":"curl -o spec.types.ts https://raw.githubusercontent.com/modelcontextprotocol/modelcontextprotocol/refs/heads/main/schema/draft/schema.ts","build":"npm run build:esm && npm run build:cjs","build:esm":"mkdir -p dist/esm && echo '{\"type\": \"module\"}' > dist/esm/package.json && tsc -p tsconfig.prod.json","build:esm:w":"npm run build:esm -- -w","build:cjs":"mkdir -p dist/cjs && echo '{\"type\": \"commonjs\"}' > dist/cjs/package.json && tsc -p tsconfig.cjs.json","build:cjs:w":"npm run build:cjs -- -w","examples:simple-server:w":"tsx --watch src/examples/server/simpleStreamableHttp.ts --oauth","prepack":"npm run build:esm && npm run build:cjs","lint":"eslint src/ && prettier --check .","lint:fix":"eslint src/ --fix && prettier --write .","test":"npm run fetch:spec-types && jest","start":"npm run server","server":"tsx watch --clear-screen=false src/cli.ts server","client":"tsx src/cli.ts client"},"dependencies":{"ajv":"^6.12.6","content-type":"^1.0.5","cors":"^2.8.5","cross-spawn":"^7.0.5","eventsource":"^3.0.2","eventsource-parser":"^3.0.0","express":"^5.0.1","express-rate-limit":"^7.5.0","pkce-challenge":"^5.0.0","raw-body":"^3.0.0","zod":"^3.23.8","zod-to-json-schema":"^3.24.1"},"devDependencies":{"@eslint/js":"^9.8.0","@jest-mock/express":"^3.0.0","@types/content-type":"^1.1.8","@types/cors":"^2.8.17","@types/cross-spawn":"^6.0.6","@types/eslint__js":"^8.42.3","@types/eventsource":"^1.1.15","@types/express":"^5.0.0","@types/jest":"^29.5.12","@types/node":"^22.0.2","@types/supertest":"^6.0.2","@types/ws":"^8.5.12","eslint":"^9.8.0","eslint-config-prettier":"^10.1.8","prettier":"3.6.2","jest":"^29.7.0","supertest":"^7.0.0","ts-jest":"^29.2.4","tsx":"^4.16.5","typescript":"^5.5.4","typescript-eslint":"^8.0.0","ws":"^8.18.0"},"resolutions":{"strip-ansi":"6.0.1"},"_id":"@ampersend_ai/modelcontextprotocol-sdk@1.20.1","gitHead":"21b9f3dfc77ff12400dbd07aae9d392c105d863b","_nodeVersion":"22.15.1","_npmVersion":"10.9.2","dist":{"integrity":"sha512-u28xfWkgMyYsFW9jyJWuU8BndOFGJVPGRXu6g7t1iuOOTDA76lBAB5a6cbWzL4Y852gHYNyit3qTSL4I3EHCuw==","shasum":"e29b1a98d531b7dade83db260dff0a7738aee441","tarball":"https://registry.npmjs.org/@ampersend_ai/modelcontextprotocol-sdk/-/modelcontextprotocol-sdk-1.20.1.tgz","fileCount":421,"unpackedSize":9040309,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIB7PkgIfsddGoFFE5lX1qy0DOWferU4ABkA7ptOrSthlAiBYFjT960dsizsRa5JKZHGA2Jvw2opAf/h7oXnaoUXOOQ=="}]},"_npmUser":{"name":"matiasedgeandnode","email":"matias@edgeandnode.com"},"directories":{},"maintainers":[{"name":"matiasedgeandnode","email":"matias@edgeandnode.com"},{"name":"pcarranzav_edgeandnode","email":"pablo@edgeandnode.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/modelcontextprotocol-sdk_1.20.1_1771955087282_0.9620499312424595"},"_hasShrinkwrap":false}},"time":{"created":"2026-02-24T17:44:47.208Z","1.20.1":"2026-02-24T17:44:47.465Z","modified":"2026-02-24T17:44:47.643Z"},"maintainers":[{"name":"matiasedgeandnode","email":"matias@edgeandnode.com"},{"name":"pcarranzav_edgeandnode","email":"pablo@edgeandnode.com"}],"description":"Model Context Protocol implementation for TypeScript","homepage":"https://github.com/edgeandnode/mcp-typescript-sdk","keywords":["modelcontextprotocol","mcp"],"repository":{"type":"git","url":"git+https://github.com/edgeandnode/mcp-typescript-sdk.git"},"author":{"name":"Anthropic, PBC","url":"https://anthropic.com"},"bugs":{"url":"https://github.com/edgeandnode/mcp-typescript-sdk/issues"},"license":"MIT","readme":"# MCP TypeScript SDK ![NPM Version](https://img.shields.io/npm/v/%40modelcontextprotocol%2Fsdk) ![MIT licensed](https://img.shields.io/npm/l/%40modelcontextprotocol%2Fsdk)\n\n<details>\n<summary>Table of Contents</summary>\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Quick Start](#quick-start)\n- [Core Concepts](#core-concepts)\n    - [Server](#server)\n    - [Tools](#tools)\n    - [Resources](#resources)\n    - [Prompts](#prompts)\n    - [Completions](#completions)\n    - [Display Names and Metadata](#display-names-and-metadata)\n    - [Sampling](#sampling)\n- [Running Your Server](#running-your-server)\n    - [Streamable HTTP](#streamable-http)\n    - [stdio](#stdio)\n    - [Testing and Debugging](#testing-and-debugging)\n- [Examples](#examples)\n    - [Echo Server](#echo-server)\n    - [SQLite Explorer](#sqlite-explorer)\n- [Advanced Usage](#advanced-usage)\n    - [Dynamic Servers](#dynamic-servers)\n    - [Improving Network Efficiency with Notification Debouncing](#improving-network-efficiency-with-notification-debouncing)\n    - [Low-Level Server](#low-level-server)\n    - [Eliciting User Input](#eliciting-user-input)\n    - [Writing MCP Clients](#writing-mcp-clients)\n    - [Proxy Authorization Requests Upstream](#proxy-authorization-requests-upstream)\n    - [Backwards Compatibility](#backwards-compatibility)\n- [Documentation](#documentation)\n- [Contributing](#contributing)\n- [License](#license)\n\n</details>\n\n## Overview\n\nThe Model Context Protocol allows applications to provide context for LLMs in a standardized way, separating the concerns of providing context from the actual LLM interaction. This TypeScript SDK implements\n[the full MCP specification](https://modelcontextprotocol.io/specification/latest), making it easy to:\n\n- Create MCP servers that expose resources, prompts and tools\n- Build MCP clients that can connect to any MCP server\n- Use standard transports like stdio and Streamable HTTP\n\n## Installation\n\n```bash\nnpm install @modelcontextprotocol/sdk\n```\n\n## Quick Start\n\nLet's create a simple MCP server that exposes a calculator tool and some data. Save the following as `server.ts`:\n\n```typescript\nimport { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';\nimport express from 'express';\nimport { z } from 'zod';\n\n// Create an MCP server\nconst server = new McpServer({\n    name: 'demo-server',\n    version: '1.0.0'\n});\n\n// Add an addition tool\nserver.registerTool(\n    'add',\n    {\n        title: 'Addition Tool',\n        description: 'Add two numbers',\n        inputSchema: { a: z.number(), b: z.number() },\n        outputSchema: { result: z.number() }\n    },\n    async ({ a, b }) => {\n        const output = { result: a + b };\n        return {\n            content: [{ type: 'text', text: JSON.stringify(output) }],\n            structuredContent: output\n        };\n    }\n);\n\n// Add a dynamic greeting resource\nserver.registerResource(\n    'greeting',\n    new ResourceTemplate('greeting://{name}', { list: undefined }),\n    {\n        title: 'Greeting Resource', // Display name for UI\n        description: 'Dynamic greeting generator'\n    },\n    async (uri, { name }) => ({\n        contents: [\n            {\n                uri: uri.href,\n                text: `Hello, ${name}!`\n            }\n        ]\n    })\n);\n\n// Set up Express and HTTP transport\nconst app = express();\napp.use(express.json());\n\napp.post('/mcp', async (req, res) => {\n    // Create a new transport for each request to prevent request ID collisions\n    const transport = new StreamableHTTPServerTransport({\n        sessionIdGenerator: undefined,\n        enableJsonResponse: true\n    });\n\n    res.on('close', () => {\n        transport.close();\n    });\n\n    await server.connect(transport);\n    await transport.handleRequest(req, res, req.body);\n});\n\nconst port = parseInt(process.env.PORT || '3000');\napp.listen(port, () => {\n    console.log(`Demo MCP Server running on http://localhost:${port}/mcp`);\n}).on('error', error => {\n    console.error('Server error:', error);\n    process.exit(1);\n});\n```\n\nInstall the deps with `npm install @modelcontextprotocol/sdk express zod@3`, and run with `npx -y tsx server.ts`.\n\nYou can connect to it using any MCP client that supports streamable http, such as:\n\n- [MCP Inspector](https://modelcontextprotocol.io/docs/tools/inspector): `npx @modelcontextprotocol/inspector` and connect to the streamable HTTP URL `http://localhost:3000/mcp`\n- [Claude Code](https://docs.claude.com/en/docs/claude-code/mcp): `claude mcp add --transport http my-server http://localhost:3000/mcp`\n- [VS Code](https://code.visualstudio.com/docs/copilot/customization/mcp-servers): `code --add-mcp \"{\\\"name\\\":\\\"my-server\\\",\\\"type\\\":\\\"http\\\",\\\"url\\\":\\\"http://localhost:3000/mcp\\\"}\"`\n- [Cursor](https://cursor.com/docs/context/mcp): Click [this deeplink](cursor://anysphere.cursor-deeplink/mcp/install?name=my-server&config=eyJ1cmwiOiJodHRwOi8vbG9jYWxob3N0OjMwMDAvbWNwIn0%3D)\n\nThen try asking your agent to add two numbers using its new tool!\n\n## Core Concepts\n\n### Server\n\nThe McpServer is your core interface to the MCP protocol. It handles connection management, protocol compliance, and message routing:\n\n```typescript\nconst server = new McpServer({\n    name: 'my-app',\n    version: '1.0.0'\n});\n```\n\n### Tools\n\n[Tools](https://modelcontextprotocol.io/specification/latest/server/tools) let LLMs take actions through your server. Tools can perform computation, fetch data and have side effects. Tools should be designed to be model-controlled - i.e. AI models will decide which tools to call,\nand the arguments.\n\n```typescript\n// Simple tool with parameters\nserver.registerTool(\n    'calculate-bmi',\n    {\n        title: 'BMI Calculator',\n        description: 'Calculate Body Mass Index',\n        inputSchema: {\n            weightKg: z.number(),\n            heightM: z.number()\n        },\n        outputSchema: { bmi: z.number() }\n    },\n    async ({ weightKg, heightM }) => {\n        const output = { bmi: weightKg / (heightM * heightM) };\n        return {\n            content: [\n                {\n                    type: 'text',\n                    text: JSON.stringify(output)\n                }\n            ],\n            structuredContent: output\n        };\n    }\n);\n\n// Async tool with external API call\nserver.registerTool(\n    'fetch-weather',\n    {\n        title: 'Weather Fetcher',\n        description: 'Get weather data for a city',\n        inputSchema: { city: z.string() },\n        outputSchema: { temperature: z.number(), conditions: z.string() }\n    },\n    async ({ city }) => {\n        const response = await fetch(`https://api.weather.com/${city}`);\n        const data = await response.json();\n        const output = { temperature: data.temp, conditions: data.conditions };\n        return {\n            content: [{ type: 'text', text: JSON.stringify(output) }],\n            structuredContent: output\n        };\n    }\n);\n\n// Tool that returns ResourceLinks\nserver.registerTool(\n    'list-files',\n    {\n        title: 'List Files',\n        description: 'List project files',\n        inputSchema: { pattern: z.string() },\n        outputSchema: {\n            count: z.number(),\n            files: z.array(z.object({ name: z.string(), uri: z.string() }))\n        }\n    },\n    async ({ pattern }) => {\n        const output = {\n            count: 2,\n            files: [\n                { name: 'README.md', uri: 'file:///project/README.md' },\n                { name: 'index.ts', uri: 'file:///project/src/index.ts' }\n            ]\n        };\n        return {\n            content: [\n                { type: 'text', text: JSON.stringify(output) },\n                // ResourceLinks let tools return references without file content\n                {\n                    type: 'resource_link',\n                    uri: 'file:///project/README.md',\n                    name: 'README.md',\n                    mimeType: 'text/markdown',\n                    description: 'A README file'\n                },\n                {\n                    type: 'resource_link',\n                    uri: 'file:///project/src/index.ts',\n                    name: 'index.ts',\n                    mimeType: 'text/typescript',\n                    description: 'An index file'\n                }\n            ],\n            structuredContent: output\n        };\n    }\n);\n```\n\n#### ResourceLinks\n\nTools can return `ResourceLink` objects to reference resources without embedding their full content. This can be helpful for performance when dealing with large files or many resources - clients can then selectively read only the resources they need using the provided URIs.\n\n### Resources\n\n[Resources](https://modelcontextprotocol.io/specification/latest/server/resources) can also expose data to LLMs, but unlike tools shouldn't perform significant computation or have side effects.\n\nResources are designed to be used in an application-driven way, meaning MCP client applications can decide how to expose them. For example, a client could expose a resource picker to the human, or could expose them to the model directly.\n\n```typescript\n// Static resource\nserver.registerResource(\n    'config',\n    'config://app',\n    {\n        title: 'Application Config',\n        description: 'Application configuration data',\n        mimeType: 'text/plain'\n    },\n    async uri => ({\n        contents: [\n            {\n                uri: uri.href,\n                text: 'App configuration here'\n            }\n        ]\n    })\n);\n\n// Dynamic resource with parameters\nserver.registerResource(\n    'user-profile',\n    new ResourceTemplate('users://{userId}/profile', { list: undefined }),\n    {\n        title: 'User Profile',\n        description: 'User profile information'\n    },\n    async (uri, { userId }) => ({\n        contents: [\n            {\n                uri: uri.href,\n                text: `Profile data for user ${userId}`\n            }\n        ]\n    })\n);\n\n// Resource with context-aware completion\nserver.registerResource(\n    'repository',\n    new ResourceTemplate('github://repos/{owner}/{repo}', {\n        list: undefined,\n        complete: {\n            // Provide intelligent completions based on previously resolved parameters\n            repo: (value, context) => {\n                if (context?.arguments?.['owner'] === 'org1') {\n                    return ['project1', 'project2', 'project3'].filter(r => r.startsWith(value));\n                }\n                return ['default-repo'].filter(r => r.startsWith(value));\n            }\n        }\n    }),\n    {\n        title: 'GitHub Repository',\n        description: 'Repository information'\n    },\n    async (uri, { owner, repo }) => ({\n        contents: [\n            {\n                uri: uri.href,\n                text: `Repository: ${owner}/${repo}`\n            }\n        ]\n    })\n);\n```\n\n### Prompts\n\n[Prompts](https://modelcontextprotocol.io/specification/latest/server/prompts) are reusable templates that help humans prompt models to interact with your server. They're designed to be user-driven, and might appear as slash commands in a chat interface.\n\n```typescript\nimport { completable } from '@modelcontextprotocol/sdk/server/completable.js';\n\nserver.registerPrompt(\n    'review-code',\n    {\n        title: 'Code Review',\n        description: 'Review code for best practices and potential issues',\n        argsSchema: { code: z.string() }\n    },\n    ({ code }) => ({\n        messages: [\n            {\n                role: 'user',\n                content: {\n                    type: 'text',\n                    text: `Please review this code:\\n\\n${code}`\n                }\n            }\n        ]\n    })\n);\n\n// Prompt with context-aware completion\nserver.registerPrompt(\n    'team-greeting',\n    {\n        title: 'Team Greeting',\n        description: 'Generate a greeting for team members',\n        argsSchema: {\n            department: completable(z.string(), value => {\n                // Department suggestions\n                return ['engineering', 'sales', 'marketing', 'support'].filter(d => d.startsWith(value));\n            }),\n            name: completable(z.string(), (value, context) => {\n                // Name suggestions based on selected department\n                const department = context?.arguments?.['department'];\n                if (department === 'engineering') {\n                    return ['Alice', 'Bob', 'Charlie'].filter(n => n.startsWith(value));\n                } else if (department === 'sales') {\n                    return ['David', 'Eve', 'Frank'].filter(n => n.startsWith(value));\n                } else if (department === 'marketing') {\n                    return ['Grace', 'Henry', 'Iris'].filter(n => n.startsWith(value));\n                }\n                return ['Guest'].filter(n => n.startsWith(value));\n            })\n        }\n    },\n    ({ department, name }) => ({\n        messages: [\n            {\n                role: 'assistant',\n                content: {\n                    type: 'text',\n                    text: `Hello ${name}, welcome to the ${department} team!`\n                }\n            }\n        ]\n    })\n);\n```\n\n### Completions\n\nMCP supports argument completions to help users fill in prompt arguments and resource template parameters. See the examples above for [resource completions](#resources) and [prompt completions](#prompts).\n\n#### Client Usage\n\n```typescript\n// Request completions for any argument\nconst result = await client.complete({\n    ref: {\n        type: 'ref/prompt', // or \"ref/resource\"\n        name: 'example' // or uri: \"template://...\"\n    },\n    argument: {\n        name: 'argumentName',\n        value: 'partial' // What the user has typed so far\n    },\n    context: {\n        // Optional: Include previously resolved arguments\n        arguments: {\n            previousArg: 'value'\n        }\n    }\n});\n```\n\n### Display Names and Metadata\n\nAll resources, tools, and prompts support an optional `title` field for better UI presentation. The `title` is used as a display name (e.g. 'Create a new issue'), while `name` remains the unique identifier (e.g. `create_issue`).\n\n**Note:** The `register*` methods (`registerTool`, `registerPrompt`, `registerResource`) are the recommended approach for new code. The older methods (`tool`, `prompt`, `resource`) remain available for backwards compatibility.\n\n#### Title Precedence for Tools\n\nFor tools specifically, there are two ways to specify a title:\n\n- `title` field in the tool configuration\n- `annotations.title` field (when using the older `tool()` method with annotations)\n\nThe precedence order is: `title` → `annotations.title` → `name`\n\n```typescript\n// Using registerTool (recommended)\nserver.registerTool(\n    'my_tool',\n    {\n        title: 'My Tool', // This title takes precedence\n        annotations: {\n            title: 'Annotation Title' // This is ignored if title is set\n        }\n    },\n    handler\n);\n\n// Using tool with annotations (older API)\nserver.tool(\n    'my_tool',\n    'description',\n    {\n        title: 'Annotation Title' // This is used as title\n    },\n    handler\n);\n```\n\nWhen building clients, use the provided utility to get the appropriate display name:\n\n```typescript\nimport { getDisplayName } from '@modelcontextprotocol/sdk/shared/metadataUtils.js';\n\n// Automatically handles the precedence: title → annotations.title → name\nconst displayName = getDisplayName(tool);\n```\n\n### Sampling\n\nMCP servers can request LLM completions from connected clients that support sampling.\n\n```typescript\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';\nimport express from 'express';\nimport { z } from 'zod';\n\nconst mcpServer = new McpServer({\n    name: 'tools-with-sample-server',\n    version: '1.0.0'\n});\n\n// Tool that uses LLM sampling to summarize any text\nmcpServer.registerTool(\n    'summarize',\n    {\n        title: 'Text Summarizer',\n        description: 'Summarize any text using an LLM',\n        inputSchema: {\n            text: z.string().describe('Text to summarize')\n        },\n        outputSchema: { summary: z.string() }\n    },\n    async ({ text }) => {\n        // Call the LLM through MCP sampling\n        const response = await mcpServer.server.createMessage({\n            messages: [\n                {\n                    role: 'user',\n                    content: {\n                        type: 'text',\n                        text: `Please summarize the following text concisely:\\n\\n${text}`\n                    }\n                }\n            ],\n            maxTokens: 500\n        });\n\n        const summary = response.content.type === 'text' ? response.content.text : 'Unable to generate summary';\n        const output = { summary };\n        return {\n            content: [{ type: 'text', text: JSON.stringify(output) }],\n            structuredContent: output\n        };\n    }\n);\n\nconst app = express();\napp.use(express.json());\n\napp.post('/mcp', async (req, res) => {\n    const transport = new StreamableHTTPServerTransport({\n        sessionIdGenerator: undefined,\n        enableJsonResponse: true\n    });\n\n    res.on('close', () => {\n        transport.close();\n    });\n\n    await mcpServer.connect(transport);\n    await transport.handleRequest(req, res, req.body);\n});\n\nconst port = parseInt(process.env.PORT || '3000');\napp.listen(port, () => {\n    console.log(`MCP Server running on http://localhost:${port}/mcp`);\n}).on('error', error => {\n    console.error('Server error:', error);\n    process.exit(1);\n});\n```\n\n## Running Your Server\n\nMCP servers in TypeScript need to be connected to a transport to communicate with clients. How you start the server depends on the choice of transport:\n\n### Streamable HTTP\n\nFor remote servers, use the Streamable HTTP transport.\n\n#### Without Session Management (Recommended)\n\nFor most use cases where session management isn't needed:\n\n```typescript\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';\nimport express from 'express';\nimport { z } from 'zod';\n\nconst app = express();\napp.use(express.json());\n\n// Create the MCP server once (can be reused across requests)\nconst server = new McpServer({\n    name: 'example-server',\n    version: '1.0.0'\n});\n\n// Set up your tools, resources, and prompts\nserver.registerTool(\n    'echo',\n    {\n        title: 'Echo Tool',\n        description: 'Echoes back the provided message',\n        inputSchema: { message: z.string() },\n        outputSchema: { echo: z.string() }\n    },\n    async ({ message }) => {\n        const output = { echo: `Tool echo: ${message}` };\n        return {\n            content: [{ type: 'text', text: JSON.stringify(output) }],\n            structuredContent: output\n        };\n    }\n);\n\napp.post('/mcp', async (req, res) => {\n    // In stateless mode, create a new transport for each request to prevent\n    // request ID collisions. Different clients may use the same JSON-RPC request IDs,\n    // which would cause responses to be routed to the wrong HTTP connections if\n    // the transport state is shared.\n\n    try {\n        const transport = new StreamableHTTPServerTransport({\n            sessionIdGenerator: undefined,\n            enableJsonResponse: true\n        });\n\n        res.on('close', () => {\n            transport.close();\n        });\n\n        await server.connect(transport);\n        await transport.handleRequest(req, res, req.body);\n    } catch (error) {\n        console.error('Error handling MCP request:', error);\n        if (!res.headersSent) {\n            res.status(500).json({\n                jsonrpc: '2.0',\n                error: {\n                    code: -32603,\n                    message: 'Internal server error'\n                },\n                id: null\n            });\n        }\n    }\n});\n\nconst port = parseInt(process.env.PORT || '3000');\napp.listen(port, () => {\n    console.log(`MCP Server running on http://localhost:${port}/mcp`);\n}).on('error', error => {\n    console.error('Server error:', error);\n    process.exit(1);\n});\n```\n\n#### With Session Management\n\nIn some cases, servers need stateful sessions. This can be achieved by [session management](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#session-management) in the MCP protocol.\n\n```typescript\nimport express from 'express';\nimport { randomUUID } from 'node:crypto';\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';\nimport { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';\n\nconst app = express();\napp.use(express.json());\n\n// Map to store transports by session ID\nconst transports: { [sessionId: string]: StreamableHTTPServerTransport } = {};\n\n// Handle POST requests for client-to-server communication\napp.post('/mcp', async (req, res) => {\n    // Check for existing session ID\n    const sessionId = req.headers['mcp-session-id'] as string | undefined;\n    let transport: StreamableHTTPServerTransport;\n\n    if (sessionId && transports[sessionId]) {\n        // Reuse existing transport\n        transport = transports[sessionId];\n    } else if (!sessionId && isInitializeRequest(req.body)) {\n        // New initialization request\n        transport = new StreamableHTTPServerTransport({\n            sessionIdGenerator: () => randomUUID(),\n            onsessioninitialized: sessionId => {\n                // Store the transport by session ID\n                transports[sessionId] = transport;\n            }\n            // DNS rebinding protection is disabled by default for backwards compatibility. If you are running this server\n            // locally, make sure to set:\n            // enableDnsRebindingProtection: true,\n            // allowedHosts: ['127.0.0.1'],\n        });\n\n        // Clean up transport when closed\n        transport.onclose = () => {\n            if (transport.sessionId) {\n                delete transports[transport.sessionId];\n            }\n        };\n        const server = new McpServer({\n            name: 'example-server',\n            version: '1.0.0'\n        });\n\n        // ... set up server resources, tools, and prompts ...\n\n        // Connect to the MCP server\n        await server.connect(transport);\n    } else {\n        // Invalid request\n        res.status(400).json({\n            jsonrpc: '2.0',\n            error: {\n                code: -32000,\n                message: 'Bad Request: No valid session ID provided'\n            },\n            id: null\n        });\n        return;\n    }\n\n    // Handle the request\n    await transport.handleRequest(req, res, req.body);\n});\n\n// Reusable handler for GET and DELETE requests\nconst handleSessionRequest = async (req: express.Request, res: express.Response) => {\n    const sessionId = req.headers['mcp-session-id'] as string | undefined;\n    if (!sessionId || !transports[sessionId]) {\n        res.status(400).send('Invalid or missing session ID');\n        return;\n    }\n\n    const transport = transports[sessionId];\n    await transport.handleRequest(req, res);\n};\n\n// Handle GET requests for server-to-client notifications via SSE\napp.get('/mcp', handleSessionRequest);\n\n// Handle DELETE requests for session termination\napp.delete('/mcp', handleSessionRequest);\n\napp.listen(3000);\n```\n\n#### CORS Configuration for Browser-Based Clients\n\nIf you'd like your server to be accessible by browser-based MCP clients, you'll need to configure CORS headers. The `Mcp-Session-Id` header must be exposed for browser clients to access it:\n\n```typescript\nimport cors from 'cors';\n\n// Add CORS middleware before your MCP routes\napp.use(\n    cors({\n        origin: '*', // Configure appropriately for production, for example:\n        // origin: ['https://your-remote-domain.com', 'https://your-other-remote-domain.com'],\n        exposedHeaders: ['Mcp-Session-Id'],\n        allowedHeaders: ['Content-Type', 'mcp-session-id']\n    })\n);\n```\n\nThis configuration is necessary because:\n\n- The MCP streamable HTTP transport uses the `Mcp-Session-Id` header for session management\n- Browsers restrict access to response headers unless explicitly exposed via CORS\n- Without this configuration, browser-based clients won't be able to read the session ID from initialization responses\n\n#### DNS Rebinding Protection\n\nThe Streamable HTTP transport includes DNS rebinding protection to prevent security vulnerabilities. By default, this protection is **disabled** for backwards compatibility.\n\n**Important**: If you are running this server locally, enable DNS rebinding protection:\n\n```typescript\nconst transport = new StreamableHTTPServerTransport({\n  sessionIdGenerator: () => randomUUID(),\n  enableDnsRebindingProtection: true,\n\n  allowedHosts: ['127.0.0.1', ...],\n  allowedOrigins: ['https://yourdomain.com', 'https://www.yourdomain.com']\n});\n```\n\n### stdio\n\nFor local integrations spawned by another process, you can use the stdio transport:\n\n```typescript\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\n\nconst server = new McpServer({\n    name: 'example-server',\n    version: '1.0.0'\n});\n\n// ... set up server resources, tools, and prompts ...\n\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\n```\n\n### Testing and Debugging\n\nTo test your server, you can use the [MCP Inspector](https://github.com/modelcontextprotocol/inspector). See its README for more information.\n\n## Examples\n\n### Echo Server\n\nA simple server demonstrating resources, tools, and prompts:\n\n```typescript\nimport { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\n\nconst server = new McpServer({\n    name: 'echo-server',\n    version: '1.0.0'\n});\n\nserver.registerTool(\n    'echo',\n    {\n        title: 'Echo Tool',\n        description: 'Echoes back the provided message',\n        inputSchema: { message: z.string() },\n        outputSchema: { echo: z.string() }\n    },\n    async ({ message }) => {\n        const output = { echo: `Tool echo: ${message}` };\n        return {\n            content: [{ type: 'text', text: JSON.stringify(output) }],\n            structuredContent: output\n        };\n    }\n);\n\nserver.registerResource(\n    'echo',\n    new ResourceTemplate('echo://{message}', { list: undefined }),\n    {\n        title: 'Echo Resource',\n        description: 'Echoes back messages as resources'\n    },\n    async (uri, { message }) => ({\n        contents: [\n            {\n                uri: uri.href,\n                text: `Resource echo: ${message}`\n            }\n        ]\n    })\n);\n\nserver.registerPrompt(\n    'echo',\n    {\n        title: 'Echo Prompt',\n        description: 'Creates a prompt to process a message',\n        argsSchema: { message: z.string() }\n    },\n    ({ message }) => ({\n        messages: [\n            {\n                role: 'user',\n                content: {\n                    type: 'text',\n                    text: `Please process this message: ${message}`\n                }\n            }\n        ]\n    })\n);\n```\n\n### SQLite Explorer\n\nA more complex example showing database integration:\n\n```typescript\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport sqlite3 from 'sqlite3';\nimport { promisify } from 'util';\nimport { z } from 'zod';\n\nconst server = new McpServer({\n    name: 'sqlite-explorer',\n    version: '1.0.0'\n});\n\n// Helper to create DB connection\nconst getDb = () => {\n    const db = new sqlite3.Database('database.db');\n    return {\n        all: promisify<string, any[]>(db.all.bind(db)),\n        close: promisify(db.close.bind(db))\n    };\n};\n\nserver.registerResource(\n    'schema',\n    'schema://main',\n    {\n        title: 'Database Schema',\n        description: 'SQLite database schema',\n        mimeType: 'text/plain'\n    },\n    async uri => {\n        const db = getDb();\n        try {\n            const tables = await db.all(\"SELECT sql FROM sqlite_master WHERE type='table'\");\n            return {\n                contents: [\n                    {\n                        uri: uri.href,\n                        text: tables.map((t: { sql: string }) => t.sql).join('\\n')\n                    }\n                ]\n            };\n        } finally {\n            await db.close();\n        }\n    }\n);\n\nserver.registerTool(\n    'query',\n    {\n        title: 'SQL Query',\n        description: 'Execute SQL queries on the database',\n        inputSchema: { sql: z.string() },\n        outputSchema: {\n            rows: z.array(z.record(z.any())),\n            rowCount: z.number()\n        }\n    },\n    async ({ sql }) => {\n        const db = getDb();\n        try {\n            const results = await db.all(sql);\n            const output = { rows: results, rowCount: results.length };\n            return {\n                content: [\n                    {\n                        type: 'text',\n                        text: JSON.stringify(output, null, 2)\n                    }\n                ],\n                structuredContent: output\n            };\n        } catch (err: unknown) {\n            const error = err as Error;\n            return {\n                content: [\n                    {\n                        type: 'text',\n                        text: `Error: ${error.message}`\n                    }\n                ],\n                isError: true\n            };\n        } finally {\n            await db.close();\n        }\n    }\n);\n```\n\n## Advanced Usage\n\n### Dynamic Servers\n\nIf you want to offer an initial set of tools/prompts/resources, but later add additional ones based on user action or external state change, you can add/update/remove them _after_ the Server is connected. This will automatically emit the corresponding `listChanged` notifications:\n\n```typescript\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';\nimport express from 'express';\nimport { z } from 'zod';\n\nconst server = new McpServer({\n    name: 'Dynamic Example',\n    version: '1.0.0'\n});\n\nconst listMessageTool = server.registerTool(\n    'listMessages',\n    {\n        title: 'List Messages',\n        description: 'List messages in a channel',\n        inputSchema: { channel: z.string() },\n        outputSchema: { messages: z.array(z.string()) }\n    },\n    async ({ channel }) => {\n        const messages = await listMessages(channel);\n        const output = { messages };\n        return {\n            content: [{ type: 'text', text: JSON.stringify(output) }],\n            structuredContent: output\n        };\n    }\n);\n\nconst putMessageTool = server.registerTool(\n    'putMessage',\n    {\n        title: 'Put Message',\n        description: 'Send a message to a channel',\n        inputSchema: { channel: z.string(), message: z.string() },\n        outputSchema: { success: z.boolean() }\n    },\n    async ({ channel, message }) => {\n        await putMessage(channel, message);\n        const output = { success: true };\n        return {\n            content: [{ type: 'text', text: JSON.stringify(output) }],\n            structuredContent: output\n        };\n    }\n);\n// Until we upgrade auth, `putMessage` is disabled (won't show up in listTools)\nputMessageTool.disable();\n\nconst upgradeAuthTool = server.registerTool(\n    'upgradeAuth',\n    {\n        title: 'Upgrade Authorization',\n        description: 'Upgrade user authorization level',\n        inputSchema: { permission: z.enum(['write', 'admin']) },\n        outputSchema: {\n            success: z.boolean(),\n            newPermission: z.string()\n        }\n    },\n    // Any mutations here will automatically emit `listChanged` notifications\n    async ({ permission }) => {\n        const { ok, err, previous } = await upgradeAuthAndStoreToken(permission);\n        if (!ok) {\n            return {\n                content: [{ type: 'text', text: `Error: ${err}` }],\n                isError: true\n            };\n        }\n\n        // If we previously had read-only access, 'putMessage' is now available\n        if (previous === 'read') {\n            putMessageTool.enable();\n        }\n\n        if (permission === 'write') {\n            // If we've just upgraded to 'write' permissions, we can still call 'upgradeAuth'\n            // but can only upgrade to 'admin'.\n            upgradeAuthTool.update({\n                paramsSchema: { permission: z.enum(['admin']) } // change validation rules\n            });\n        } else {\n            // If we're now an admin, we no longer have anywhere to upgrade to, so fully remove that tool\n            upgradeAuthTool.remove();\n        }\n\n        const output = { success: true, newPermission: permission };\n        return {\n            content: [{ type: 'text', text: JSON.stringify(output) }],\n            structuredContent: output\n        };\n    }\n);\n\n// Connect with HTTP transport\nconst app = express();\napp.use(express.json());\n\napp.post('/mcp', async (req, res) => {\n    const transport = new StreamableHTTPServerTransport({\n        sessionIdGenerator: undefined,\n        enableJsonResponse: true\n    });\n\n    res.on('close', () => {\n        transport.close();\n    });\n\n    await server.connect(transport);\n    await transport.handleRequest(req, res, req.body);\n});\n\nconst port = parseInt(process.env.PORT || '3000');\napp.listen(port, () => {\n    console.log(`MCP Server running on http://localhost:${port}/mcp`);\n});\n```\n\n### Improving Network Efficiency with Notification Debouncing\n\nWhen performing bulk updates that trigger notifications (e.g., enabling or disabling multiple tools in a loop), the SDK can send a large number of messages in a short period. To improve performance and reduce network traffic, you can enable notification debouncing.\n\nThis feature coalesces multiple, rapid calls for the same notification type into a single message. For example, if you disable five tools in a row, only one `notifications/tools/list_changed` message will be sent instead of five.\n\n> [!IMPORTANT] This feature is designed for \"simple\" notifications that do not carry unique data in their parameters. To prevent silent data loss, debouncing is **automatically bypassed** for any notification that contains a `params` object or a `relatedRequestId`. Such\n> notifications will always be sent immediately.\n\nThis is an opt-in feature configured during server initialization.\n\n```typescript\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\n\nconst server = new McpServer(\n  {\n    name: \"efficient-server\",\n    version: \"1.0.0\"\n  },\n  {\n    // Enable notification debouncing for specific methods\n    debouncedNotificationMethods: [\n      'notifications/tools/list_changed',\n      'notifications/resources/list_changed',\n      'notifications/prompts/list_changed'\n    ]\n  }\n);\n\n// Now, any rapid changes to tools, resources, or prompts will result\n// in a single, consolidated notification for each type.\nserver.registerTool(\"tool1\", ...).disable();\nserver.registerTool(\"tool2\", ...).disable();\nserver.registerTool(\"tool3\", ...).disable();\n// Only one 'notifications/tools/list_changed' is sent.\n```\n\n### Low-Level Server\n\nFor more control, you can use the low-level Server class directly:\n\n```typescript\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { ListPromptsRequestSchema, GetPromptRequestSchema } from '@modelcontextprotocol/sdk/types.js';\n\nconst server = new Server(\n    {\n        name: 'example-server',\n        version: '1.0.0'\n    },\n    {\n        capabilities: {\n            prompts: {}\n        }\n    }\n);\n\nserver.setRequestHandler(ListPromptsRequestSchema, async () => {\n    return {\n        prompts: [\n            {\n                name: 'example-prompt',\n                description: 'An example prompt template',\n                arguments: [\n                    {\n                        name: 'arg1',\n                        description: 'Example argument',\n                        required: true\n                    }\n                ]\n            }\n        ]\n    };\n});\n\nserver.setRequestHandler(GetPromptRequestSchema, async request => {\n    if (request.params.name !== 'example-prompt') {\n        throw new Error('Unknown prompt');\n    }\n    return {\n        description: 'Example prompt',\n        messages: [\n            {\n                role: 'user',\n                content: {\n                    type: 'text',\n                    text: 'Example prompt text'\n                }\n            }\n        ]\n    };\n});\n\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\n```\n\n### Eliciting User Input\n\nMCP servers can request additional information from users through the elicitation feature. This is useful for interactive workflows where the server needs user input or confirmation:\n\n```typescript\n// Server-side: Restaurant booking tool that asks for alternatives\nserver.registerTool(\n    'book-restaurant',\n    {\n        title: 'Book Restaurant',\n        description: 'Book a table at a restaurant',\n        inputSchema: {\n            restaurant: z.string(),\n            date: z.string(),\n            partySize: z.number()\n        },\n        outputSchema: {\n            success: z.boolean(),\n            booking: z\n                .object({\n                    restaurant: z.string(),\n                    date: z.string(),\n                    partySize: z.number()\n                })\n                .optional(),\n            alternatives: z.array(z.string()).optional()\n        }\n    },\n    async ({ restaurant, date, partySize }) => {\n        // Check availability\n        const available = await checkAvailability(restaurant, date, partySize);\n\n        if (!available) {\n            // Ask user if they want to try alternative dates\n            const result = await server.server.elicitInput({\n                message: `No tables available at ${restaurant} on ${date}. Would you like to check alternative dates?`,\n                requestedSchema: {\n                    type: 'object',\n                    properties: {\n                        checkAlternatives: {\n                            type: 'boolean',\n                            title: 'Check alternative dates',\n                            description: 'Would you like me to check other dates?'\n                        },\n                        flexibleDates: {\n                            type: 'string',\n                            title: 'Date flexibility',\n                            description: 'How flexible are your dates?',\n                            enum: ['next_day', 'same_week', 'next_week'],\n                            enumNames: ['Next day', 'Same week', 'Next week']\n                        }\n                    },\n                    required: ['checkAlternatives']\n                }\n            });\n\n            if (result.action === 'accept' && result.content?.checkAlternatives) {\n                const alternatives = await findAlternatives(restaurant, date, partySize, result.content.flexibleDates as string);\n                const output = { success: false, alternatives };\n                return {\n                    content: [\n                        {\n                            type: 'text',\n                            text: JSON.stringify(output)\n                        }\n                    ],\n                    structuredContent: output\n                };\n            }\n\n            const output = { success: false };\n            return {\n                content: [\n                    {\n                        type: 'text',\n                        text: JSON.stringify(output)\n                    }\n                ],\n                structuredContent: output\n            };\n        }\n\n        // Book the table\n        await makeBooking(restaurant, date, partySize);\n        const output = {\n            success: true,\n            booking: { restaurant, date, partySize }\n        };\n        return {\n            content: [\n                {\n                    type: 'text',\n                    text: JSON.stringify(output)\n                }\n            ],\n            structuredContent: output\n        };\n    }\n);\n```\n\nClient-side: Handle elicitation requests\n\n```typescript\n// This is a placeholder - implement based on your UI framework\nasync function getInputFromUser(\n    message: string,\n    schema: any\n): Promise<{\n    action: 'accept' | 'decline' | 'cancel';\n    data?: Record<string, any>;\n}> {\n    // This should be implemented depending on the app\n    throw new Error('getInputFromUser must be implemented for your platform');\n}\n\nclient.setRequestHandler(ElicitRequestSchema, async request => {\n    const userResponse = await getInputFromUser(request.params.message, request.params.requestedSchema);\n\n    return {\n        action: userResponse.action,\n        content: userResponse.action === 'accept' ? userResponse.data : undefined\n    };\n});\n```\n\n**Note**: Elicitation requires client support. Clients must declare the `elicitation` capability during initialization.\n\n### Writing MCP Clients\n\nThe SDK provides a high-level client interface:\n\n```typescript\nimport { Client } from '@modelcontextprotocol/sdk/client/index.js';\nimport { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';\n\nconst transport = new StdioClientTransport({\n    command: 'node',\n    args: ['server.js']\n});\n\nconst client = new Client({\n    name: 'example-client',\n    version: '1.0.0'\n});\n\nawait client.connect(transport);\n\n// List prompts\nconst prompts = await client.listPrompts();\n\n// Get a prompt\nconst prompt = await client.getPrompt({\n    name: 'example-prompt',\n    arguments: {\n        arg1: 'value'\n    }\n});\n\n// List resources\nconst resources = await client.listResources();\n\n// Read a resource\nconst resource = await client.readResource({\n    uri: 'file:///example.txt'\n});\n\n// Call a tool\nconst result = await client.callTool({\n    name: 'example-tool',\n    arguments: {\n        arg1: 'value'\n    }\n});\n```\n\n### Proxy Authorization Requests Upstream\n\nYou can proxy OAuth requests to an external authorization provider:\n\n```typescript\nimport express from 'express';\nimport { ProxyOAuthServerProvider } from '@modelcontextprotocol/sdk/server/auth/providers/proxyProvider.js';\nimport { mcpAuthRouter } from '@modelcontextprotocol/sdk/server/auth/router.js';\n\nconst app = express();\n\nconst proxyProvider = new ProxyOAuthServerProvider({\n    endpoints: {\n        authorizationUrl: 'https://auth.external.com/oauth2/v1/authorize',\n        tokenUrl: 'https://auth.external.com/oauth2/v1/token',\n        revocationUrl: 'https://auth.external.com/oauth2/v1/revoke'\n    },\n    verifyAccessToken: async token => {\n        return {\n            token,\n            clientId: '123',\n            scopes: ['openid', 'email', 'profile']\n        };\n    },\n    getClient: async client_id => {\n        return {\n            client_id,\n            redirect_uris: ['http://localhost:3000/callback']\n        };\n    }\n});\n\napp.use(\n    mcpAuthRouter({\n        provider: proxyProvider,\n        issuerUrl: new URL('http://auth.external.com'),\n        baseUrl: new URL('http://mcp.example.com'),\n        serviceDocumentationUrl: new URL('https://docs.example.com/')\n    })\n);\n```\n\nThis setup allows you to:\n\n- Forward OAuth requests to an external provider\n- Add custom token validation logic\n- Manage client registrations\n- Provide custom documentation URLs\n- Maintain control over the OAuth flow while delegating to an external provider\n\n### Backwards Compatibility\n\nClients and servers with StreamableHttp transport can maintain [backwards compatibility](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#backwards-compatibility) with the deprecated HTTP+SSE transport (from protocol version 2024-11-05) as follows\n\n#### Client-Side Compatibility\n\nFor clients that need to work with both Streamable HTTP and older SSE servers:\n\n```typescript\nimport { Client } from '@modelcontextprotocol/sdk/client/index.js';\nimport { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';\nimport { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';\nlet client: Client | undefined = undefined;\nconst baseUrl = new URL(url);\ntry {\n    client = new Client({\n        name: 'streamable-http-client',\n        version: '1.0.0'\n    });\n    const transport = new StreamableHTTPClientTransport(new URL(baseUrl));\n    await client.connect(transport);\n    console.log('Connected using Streamable HTTP transport');\n} catch (error) {\n    // If that fails with a 4xx error, try the older SSE transport\n    console.log('Streamable HTTP connection failed, falling back to SSE transport');\n    client = new Client({\n        name: 'sse-client',\n        version: '1.0.0'\n    });\n    const sseTransport = new SSEClientTransport(baseUrl);\n    await client.connect(sseTransport);\n    console.log('Connected using SSE transport');\n}\n```\n\n#### Server-Side Compatibility\n\nFor servers that need to support both Streamable HTTP and older clients:\n\n```typescript\nimport express from 'express';\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';\nimport { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';\n\nconst server = new McpServer({\n    name: 'backwards-compatible-server',\n    version: '1.0.0'\n});\n\n// ... set up server resources, tools, and prompts ...\n\nconst app = express();\napp.use(express.json());\n\n// Store transports for each session type\nconst transports = {\n    streamable: {} as Record<string, StreamableHTTPServerTransport>,\n    sse: {} as Record<string, SSEServerTransport>\n};\n\n// Modern Streamable HTTP endpoint\napp.all('/mcp', async (req, res) => {\n    // Handle Streamable HTTP transport for modern clients\n    // Implementation as shown in the \"With Session Management\" example\n    // ...\n});\n\n// Legacy SSE endpoint for older clients\napp.get('/sse', async (req, res) => {\n    // Create SSE transport for legacy clients\n    const transport = new SSEServerTransport('/messages', res);\n    transports.sse[transport.sessionId] = transport;\n\n    res.on('close', () => {\n        delete transports.sse[transport.sessionId];\n    });\n\n    await server.connect(transport);\n});\n\n// Legacy message endpoint for older clients\napp.post('/messages', async (req, res) => {\n    const sessionId = req.query.sessionId as string;\n    const transport = transports.sse[sessionId];\n    if (transport) {\n        await transport.handlePostMessage(req, res, req.body);\n    } else {\n        res.status(400).send('No transport found for sessionId');\n    }\n});\n\napp.listen(3000);\n```\n\n**Note**: The SSE transport is now deprecated in favor of Streamable HTTP. New implementations should use Streamable HTTP, and existing SSE implementations should plan to migrate.\n\n## Documentation\n\n- [Model Context Protocol documentation](https://modelcontextprotocol.io)\n- [MCP Specification](https://spec.modelcontextprotocol.io)\n- [Example Servers](https://github.com/modelcontextprotocol/servers)\n\n## Contributing\n\nIssues and pull requests are welcome on GitHub at <https://github.com/modelcontextprotocol/typescript-sdk>.\n\n## License\n\nThis project is licensed under the MIT License—see the [LICENSE](LICENSE) file for details.\n","readmeFilename":"README.md","_rev":"1-33e786a3cad3a9e3d3c41d345e73d7ca"}