{"_id":"@borodutch/modelcontextprotocol-sdk","name":"@borodutch/modelcontextprotocol-sdk","dist-tags":{"latest":"1.12.0"},"versions":{"1.12.0":{"name":"@borodutch/modelcontextprotocol-sdk","version":"1.12.0","description":"Model Context Protocol implementation for TypeScript","license":"MIT","author":{"name":"Anthropic, PBC","url":"https://anthropic.com"},"homepage":"https://modelcontextprotocol.io","bugs":{"url":"https://github.com/modelcontextprotocol/typescript-sdk/issues"},"type":"module","repository":{"type":"git","url":"git+https://github.com/modelcontextprotocol/typescript-sdk.git"},"engines":{"node":">=18"},"keywords":["modelcontextprotocol","mcp"],"exports":{"./*":{"import":"./dist/esm/*","require":"./dist/cjs/*"}},"typesVersions":{"*":{"*":["./dist/esm/*"]}},"scripts":{"build":"npm run build:esm && npm run build:cjs","build:esm":"tsc -p tsconfig.prod.json && echo '{\"type\": \"module\"}' > dist/esm/package.json","build:cjs":"tsc -p tsconfig.cjs.json && echo '{\"type\": \"commonjs\"}' > dist/cjs/package.json","prepack":"npm run build:esm && npm run build:cjs","lint":"eslint src/","test":"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","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","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":"@borodutch/modelcontextprotocol-sdk@1.12.0","gitHead":"df0d9c4ffc773b6414ba84bef3f016cf97e8cdd4","_nodeVersion":"20.11.0","_npmVersion":"10.2.4","dist":{"integrity":"sha512-S25hvXIErGJy7xT9kAV+rwzwRMXmR+1OxBQbOvEUcJsxiQP0X7DVHvCAD9YXNG3bFaFqa7251D1p0teB4n1xoA==","shasum":"cd07ffa2004bbda357a53aed276a3bddecd20c88","tarball":"https://registry.npmjs.org/@borodutch/modelcontextprotocol-sdk/-/modelcontextprotocol-sdk-1.12.0.tgz","fileCount":389,"unpackedSize":4867826,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDnc3lBvJ89c+Hd5Ej7JDzKH77j9oXHwSVVVh+z5QXVZwIhAOWD5DNMZ1Tt1WKogImphi7QDdcLhgcAHGhk5mehO8nk"}]},"_npmUser":{"name":"borodutch","email":"nikita@borodutch.com"},"directories":{},"maintainers":[{"name":"borodutch","email":"nikita@borodutch.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/modelcontextprotocol-sdk_1.12.0_1749659170841_0.026823357939709247"},"_hasShrinkwrap":false}},"time":{"created":"2025-06-11T16:26:10.722Z","1.12.0":"2025-06-11T16:26:11.092Z","modified":"2025-06-11T16:26:11.402Z"},"maintainers":[{"name":"borodutch","email":"nikita@borodutch.com"}],"description":"Model Context Protocol implementation for TypeScript","homepage":"https://modelcontextprotocol.io","keywords":["modelcontextprotocol","mcp"],"repository":{"type":"git","url":"git+https://github.com/modelcontextprotocol/typescript-sdk.git"},"author":{"name":"Anthropic, PBC","url":"https://anthropic.com"},"bugs":{"url":"https://github.com/modelcontextprotocol/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## Table of Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Quickstart](#quick-start)\n- [What is MCP?](#what-is-mcp)\n- [Core Concepts](#core-concepts)\n  - [Server](#server)\n  - [Resources](#resources)\n  - [Tools](#tools)\n  - [Prompts](#prompts)\n- [Running Your Server](#running-your-server)\n  - [stdio](#stdio)\n  - [Streamable HTTP](#streamable-http)\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  - [Low-Level Server](#low-level-server)\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## 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 the full MCP specification, making it easy to:\n\n- Build MCP clients that can connect to any MCP server\n- Create MCP servers that expose resources, prompts and tools\n- Use standard transports like stdio and Streamable HTTP\n- Handle all MCP protocol messages and lifecycle events\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:\n\n```typescript\nimport { McpServer, ResourceTemplate } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { z } from \"zod\";\n\n// Create an MCP server\nconst server = new McpServer({\n  name: \"Demo\",\n  version: \"1.0.0\"\n});\n\n// Add an addition tool\nserver.tool(\"add\",\n  { a: z.number(), b: z.number() },\n  async ({ a, b }) => ({\n    content: [{ type: \"text\", text: String(a + b) }]\n  })\n);\n\n// Add a dynamic greeting resource\nserver.resource(\n  \"greeting\",\n  new ResourceTemplate(\"greeting://{name}\", { list: undefined }),\n  async (uri, { name }) => ({\n    contents: [{\n      uri: uri.href,\n      text: `Hello, ${name}!`\n    }]\n  })\n);\n\n// Start receiving messages on stdin and sending messages on stdout\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\n```\n\n## What is MCP?\n\nThe [Model Context Protocol (MCP)](https://modelcontextprotocol.io) lets you build servers that expose data and functionality to LLM applications in a secure, standardized way. Think of it like a web API, but specifically designed for LLM interactions. MCP servers can:\n\n- Expose data through **Resources** (think of these sort of like GET endpoints; they are used to load information into the LLM's context)\n- Provide functionality through **Tools** (sort of like POST endpoints; they are used to execute code or otherwise produce a side effect)\n- Define interaction patterns through **Prompts** (reusable templates for LLM interactions)\n- And more!\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### Resources\n\nResources are how you expose data to LLMs. They're similar to GET endpoints in a REST API - they provide data but shouldn't perform significant computation or have side effects:\n\n```typescript\n// Static resource\nserver.resource(\n  \"config\",\n  \"config://app\",\n  async (uri) => ({\n    contents: [{\n      uri: uri.href,\n      text: \"App configuration here\"\n    }]\n  })\n);\n\n// Dynamic resource with parameters\nserver.resource(\n  \"user-profile\",\n  new ResourceTemplate(\"users://{userId}/profile\", { list: undefined }),\n  async (uri, { userId }) => ({\n    contents: [{\n      uri: uri.href,\n      text: `Profile data for user ${userId}`\n    }]\n  })\n);\n```\n\n### Tools\n\nTools let LLMs take actions through your server. Unlike resources, tools are expected to perform computation and have side effects:\n\n```typescript\n// Simple tool with parameters\nserver.tool(\n  \"calculate-bmi\",\n  {\n    weightKg: z.number(),\n    heightM: z.number()\n  },\n  async ({ weightKg, heightM }) => ({\n    content: [{\n      type: \"text\",\n      text: String(weightKg / (heightM * heightM))\n    }]\n  })\n);\n\n// Async tool with external API call\nserver.tool(\n  \"fetch-weather\",\n  { city: z.string() },\n  async ({ city }) => {\n    const response = await fetch(`https://api.weather.com/${city}`);\n    const data = await response.text();\n    return {\n      content: [{ type: \"text\", text: data }]\n    };\n  }\n);\n```\n\n### Prompts\n\nPrompts are reusable templates that help LLMs interact with your server effectively:\n\n```typescript\nserver.prompt(\n  \"review-code\",\n  { code: z.string() },\n  ({ code }) => ({\n    messages: [{\n      role: \"user\",\n      content: {\n        type: \"text\",\n        text: `Please review this code:\\n\\n${code}`\n      }\n    }]\n  })\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### stdio\n\nFor command-line tools and direct integrations:\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### Streamable HTTP\n\nFor remote servers, set up a Streamable HTTP transport that handles both client requests and server-to-client notifications.\n\n#### With Session Management\n\nIn some cases, servers need to be stateful. This is achieved by [session management](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#session-management).\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\n\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    });\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#### Without Session Management (Stateless)\n\nFor simpler use cases where session management isn't needed:\n\n```typescript\nconst app = express();\napp.use(express.json());\n\napp.post('/mcp', async (req: Request, res: Response) => {\n  // In stateless mode, create a new instance of transport and server for each request\n  // to ensure complete isolation. A single instance would cause request ID collisions\n  // when multiple clients connect concurrently.\n  \n  try {\n    const server = getServer(); \n    const transport: StreamableHTTPServerTransport = new StreamableHTTPServerTransport({\n      sessionIdGenerator: undefined,\n    });\n    res.on('close', () => {\n      console.log('Request closed');\n      transport.close();\n      server.close();\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\napp.get('/mcp', async (req: Request, res: Response) => {\n  console.log('Received GET MCP request');\n  res.writeHead(405).end(JSON.stringify({\n    jsonrpc: \"2.0\",\n    error: {\n      code: -32000,\n      message: \"Method not allowed.\"\n    },\n    id: null\n  }));\n});\n\napp.delete('/mcp', async (req: Request, res: Response) => {\n  console.log('Received DELETE MCP request');\n  res.writeHead(405).end(JSON.stringify({\n    jsonrpc: \"2.0\",\n    error: {\n      code: -32000,\n      message: \"Method not allowed.\"\n    },\n    id: null\n  }));\n});\n\n\n// Start the server\nconst PORT = 3000;\napp.listen(PORT, () => {\n  console.log(`MCP Stateless Streamable HTTP Server listening on port ${PORT}`);\n});\n\n```\n\nThis stateless approach is useful for:\n\n- Simple API wrappers\n- RESTful scenarios where each request is independent\n- Horizontally scaled deployments without shared session state\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\",\n  version: \"1.0.0\"\n});\n\nserver.resource(\n  \"echo\",\n  new ResourceTemplate(\"echo://{message}\", { list: undefined }),\n  async (uri, { message }) => ({\n    contents: [{\n      uri: uri.href,\n      text: `Resource echo: ${message}`\n    }]\n  })\n);\n\nserver.tool(\n  \"echo\",\n  { message: z.string() },\n  async ({ message }) => ({\n    content: [{ type: \"text\", text: `Tool echo: ${message}` }]\n  })\n);\n\nserver.prompt(\n  \"echo\",\n  { message: z.string() },\n  ({ message }) => ({\n    messages: [{\n      role: \"user\",\n      content: {\n        type: \"text\",\n        text: `Please process this message: ${message}`\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.resource(\n  \"schema\",\n  \"schema://main\",\n  async (uri) => {\n    const db = getDb();\n    try {\n      const tables = await db.all(\n        \"SELECT sql FROM sqlite_master WHERE type='table'\"\n      );\n      return {\n        contents: [{\n          uri: uri.href,\n          text: tables.map((t: {sql: string}) => t.sql).join(\"\\n\")\n        }]\n      };\n    } finally {\n      await db.close();\n    }\n  }\n);\n\nserver.tool(\n  \"query\",\n  { sql: z.string() },\n  async ({ sql }) => {\n    const db = getDb();\n    try {\n      const results = await db.all(sql);\n      return {\n        content: [{\n          type: \"text\",\n          text: JSON.stringify(results, null, 2)\n        }]\n      };\n    } catch (err: unknown) {\n      const error = err as Error;\n      return {\n        content: [{\n          type: \"text\",\n          text: `Error: ${error.message}`\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```ts\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\n\nconst server = new McpServer({\n  name: \"Dynamic Example\",\n  version: \"1.0.0\"\n});\n\nconst listMessageTool = server.tool(\n  \"listMessages\",\n  { channel: z.string() },\n  async ({ channel }) => ({\n    content: [{ type: \"text\", text: await listMessages(channel) }]\n  })\n);\n\nconst putMessageTool = server.tool(\n  \"putMessage\",\n  { channel: z.string(), message: z.string() },\n  async ({ channel, message }) => ({\n    content: [{ type: \"text\", text: await putMessage(channel, string) }]\n  })\n);\n// Until we upgrade auth, `putMessage` is disabled (won't show up in listTools)\nputMessageTool.disable()\n\nconst upgradeAuthTool = server.tool(\n  \"upgradeAuth\",\n  { permission: z.enum([\"write', admin\"])},\n  // Any mutations here will automatically emit `listChanged` notifications\n  async ({ permission }) => {\n    const { ok, err, previous } = await upgradeAuthAndStoreToken(permission)\n    if (!ok) return {content: [{ type: \"text\", text: `Error: ${err}` }]}\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        paramSchema: { 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)\n\n// Connect as normal\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\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 {\n  ListPromptsRequestSchema,\n  GetPromptRequestSchema\n} 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      name: \"example-prompt\",\n      description: \"An example prompt template\",\n      arguments: [{\n        name: \"arg1\",\n        description: \"Example argument\",\n        required: true\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      role: \"user\",\n      content: {\n        type: \"text\",\n        text: \"Example prompt text\"\n      }\n    }]\n  };\n});\n\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\n```\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  {\n    name: \"example-client\",\n    version: \"1.0.0\"\n  }\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(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\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 tranport 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(\n    new URL(baseUrl)\n  );\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-c0756d5ca4c30564a4b25414e5668f53"}