{"_id":"@alcyone-labs/modelcontextprotocol-sdk","name":"@alcyone-labs/modelcontextprotocol-sdk","dist-tags":{"latest":"1.16.0"},"versions":{"1.16.0":{"name":"@alcyone-labs/modelcontextprotocol-sdk","version":"1.16.0","description":"Upgrade to Zod V4 for 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/Alcyone-Labs/mcp-typescript-sdk/issues"},"type":"module","repository":{"type":"git","url":"git+https://github.com/Alcyone-Labs/mcp-typescript-sdk.git"},"maintainers":[{"name":"nembleton","email":"nicolas.embleton@gmail.com"}],"engines":{"node":">=18"},"keywords":["modelcontextprotocol","mcp"],"exports":{"./*":{"import":"./dist/esm/*","require":"./dist/cjs/*"}},"typesVersions":{"*":{"*":["./dist/esm/*"]}},"dependencies":{"ajv":"^6.12.6","content-type":"^1.0.5","cors":"^2.8.5","cross-spawn":"^7.0.6","eventsource":"^3.0.7","eventsource-parser":"^3.0.3","express":"^5.1.0","express-rate-limit":"^7.5.1","pkce-challenge":"^5.0.0","raw-body":"^3.0.0","zod":"^4.0.8"},"devDependencies":{"@eslint/js":"^9.31.0","@jest-mock/express":"^3.0.0","@types/content-type":"^1.1.9","@types/cors":"^2.8.19","@types/cross-spawn":"^6.0.6","@types/eslint__js":"^8.42.3","@types/eventsource":"^1.1.15","@types/express":"^5.0.3","@types/jest":"^29.5.14","@types/node":"^22.16.5","@types/supertest":"^6.0.3","@types/ws":"^8.18.1","eslint":"^9.31.0","jest":"^29.7.0","supertest":"^7.1.4","ts-jest":"^29.4.0","tsx":"^4.20.3","typescript":"^5.8.3","typescript-eslint":"^8.38.0","ws":"^8.18.3"},"resolutions":{"strip-ansi":"6.0.1"},"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","lint":"eslint src/","test":"npm run fetch:spec-types && NODE_OPTIONS=\"--experimental-vm-modules\" jest","start":"npm run server","server":"tsx watch --clear-screen=false src/cli.ts server","client":"tsx src/cli.ts client"},"_id":"@alcyone-labs/modelcontextprotocol-sdk@1.16.0","_integrity":"sha512-6YHdBjqY4whY6qZhjRiCs7ZeB0z1g47RHXOQJ5ByIeKyOV+MbXWgRs3Sv01MZjwTJqN8tYkA7fwZY1tTD7lFww==","_resolved":"/private/var/folders/27/xlh5p6rd54vgnqk_t2kq551c0000gn/T/68e9d0ab47d57d692ed5b84ae973c748/alcyone-labs-modelcontextprotocol-sdk-1.16.0.tgz","_from":"file:alcyone-labs-modelcontextprotocol-sdk-1.16.0.tgz","_nodeVersion":"22.15.1","_npmVersion":"10.9.2","dist":{"integrity":"sha512-6YHdBjqY4whY6qZhjRiCs7ZeB0z1g47RHXOQJ5ByIeKyOV+MbXWgRs3Sv01MZjwTJqN8tYkA7fwZY1tTD7lFww==","shasum":"f6e5350e50e72ea00830f2f84d83b6ffa0f7f873","tarball":"https://registry.npmjs.org/@alcyone-labs/modelcontextprotocol-sdk/-/modelcontextprotocol-sdk-1.16.0.tgz","fileCount":413,"unpackedSize":2122270,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIBDA5kfJU6yQjoxMlEl+2bL1osuygzoZAD4Bk4jvn0qxAiEA+8BcJ7r3priG8pWcJo5jappOMyeEhL3XJWDc7io8NwU="}]},"_npmUser":{"name":"nembleton","email":"nicolas.embleton@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/modelcontextprotocol-sdk_1.16.0_1754037868495_0.5322214567784429"},"_hasShrinkwrap":false}},"time":{"created":"2025-08-01T08:44:28.413Z","1.16.0":"2025-08-01T08:44:28.726Z","modified":"2025-08-01T08:44:29.012Z"},"maintainers":[{"name":"nembleton","email":"nicolas.embleton@gmail.com"}],"description":"Upgrade to Zod V4 for Model Context Protocol implementation for TypeScript","homepage":"https://modelcontextprotocol.io","keywords":["modelcontextprotocol","mcp"],"repository":{"type":"git","url":"git+https://github.com/Alcyone-Labs/mcp-typescript-sdk.git"},"author":{"name":"Anthropic, PBC","url":"https://anthropic.com"},"bugs":{"url":"https://github.com/Alcyone-Labs/mcp-typescript-sdk/issues"},"license":"MIT","readme":"# MCP TypeScript SDK\n\n**Important Note** This is an unofficial fork upgraded to Zod V4. You can check the changes directly on the github repository\n\n## ORIGINAL version\n\n![NPM Version](https://img.shields.io/npm/v/%40modelcontextprotocol%2Fsdk) ![MIT licensed](https://img.shields.io/npm/l/%40modelcontextprotocol%2Fsdk)\nORIGINAL version\n\n## This fork\n\n![NPM Version](https://img.shields.io/npm/v/%40alcyone-labs%2Fmodelcontextprotocol-sdk) ![MIT licensed](https://img.shields.io/npm/l/%40alcyone-labs%2Fmodelcontextprotocol-sdk)\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  - [Completions](#completions)\n  - [Sampling](#sampling)\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> ⚠️ MCP requires Node.js v18.x or higher to work fine.\n\n## Quick Start\n\nLet's create a simple MCP server that exposes a calculator tool and some data:\n\n```typescript\nimport {\n  McpServer,\n  ResourceTemplate,\n} 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-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  },\n  async ({ a, b }) => ({\n    content: [{ type: \"text\", text: String(a + b) }],\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// 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.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) =>\n            r.startsWith(value),\n          );\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### 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.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  },\n  async ({ weightKg, heightM }) => ({\n    content: [\n      {\n        type: \"text\",\n        text: String(weightKg / (heightM * heightM)),\n      },\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  },\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// 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  },\n  async ({ pattern }) => ({\n    content: [\n      { type: \"text\", text: `Found files matching \"${pattern}\":` },\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  }),\n);\n```\n\n#### ResourceLinks\n\nTools can return `ResourceLink` objects to reference resources without embedding their full content. This is essential 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### Prompts\n\nPrompts are reusable templates that help LLMs interact with your server effectively:\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) =>\n          d.startsWith(value),\n        );\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, while `name` remains the unique identifier.\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 { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\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    description: \"Summarize any text using an LLM\",\n    inputSchema: {\n      text: z.string().describe(\"Text to summarize\"),\n    },\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    return {\n      content: [\n        {\n          type: \"text\",\n          text:\n            response.content.type === \"text\"\n              ? response.content.text\n              : \"Unable to generate summary\",\n        },\n      ],\n    };\n  },\n);\n\nasync function main() {\n  const transport = new StdioServerTransport();\n  await mcpServer.connect(transport);\n  console.log(\"MCP server is running...\");\n}\n\nmain().catch((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### 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\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 (\n  req: express.Request,\n  res: express.Response,\n) => {\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> [!TIP]\n> When using this in a remote environment, make sure to allow the header parameter `mcp-session-id` in CORS. Otherwise, it may result in a `Bad Request: No valid session ID provided` error. Read the following section for examples.\n>\n> ```\n>\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#### 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 =\n      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\n// SSE notifications not supported in stateless mode\napp.get(\"/mcp\", async (req: Request, res: Response) => {\n  console.log(\"Received GET MCP request\");\n  res.writeHead(405).end(\n    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// Session termination not needed in stateless mode\napp.delete(\"/mcp\", async (req: Request, res: Response) => {\n  console.log(\"Received DELETE MCP request\");\n  res.writeHead(405).end(\n    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;\nsetupServer()\n  .then(() => {\n    app.listen(PORT, (error) => {\n      if (error) {\n        console.error(\"Failed to start server:\", error);\n        process.exit(1);\n      }\n      console.log(\n        `MCP Stateless Streamable HTTP Server listening on port ${PORT}`,\n      );\n    });\n  })\n  .catch((error) => {\n    console.error(\"Failed to set up the server:\", error);\n    process.exit(1);\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#### 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### 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 {\n  McpServer,\n  ResourceTemplate,\n} 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.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.registerTool(\n  \"echo\",\n  {\n    title: \"Echo Tool\",\n    description: \"Echoes back the provided message\",\n    inputSchema: { message: z.string() },\n  },\n  async ({ message }) => ({\n    content: [{ type: \"text\", text: `Tool echo: ${message}` }],\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(\n        \"SELECT sql FROM sqlite_master WHERE type='table'\",\n      );\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  },\n  async ({ sql }) => {\n    const db = getDb();\n    try {\n      const results = await db.all(sql);\n      return {\n        content: [\n          {\n            type: \"text\",\n            text: JSON.stringify(results, null, 2),\n          },\n        ],\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```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, message) }],\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### 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]\n> 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 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 {\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      {\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.tool(\n  \"book-restaurant\",\n  {\n    restaurant: z.string(),\n    date: z.string(),\n    partySize: z.number(),\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(\n          restaurant,\n          date,\n          partySize,\n          result.content.flexibleDates as string,\n        );\n        return {\n          content: [\n            {\n              type: \"text\",\n              text: `Found these alternatives: ${alternatives.join(\", \")}`,\n            },\n          ],\n        };\n      }\n\n      return {\n        content: [\n          {\n            type: \"text\",\n            text: \"No booking made. Original date not available.\",\n          },\n        ],\n      };\n    }\n\n    // Book the table\n    await makeBooking(restaurant, date, partySize);\n    return {\n      content: [\n        {\n          type: \"text\",\n          text: `Booked table for ${partySize} at ${restaurant} on ${date}`,\n        },\n      ],\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(\n    request.params.message,\n    request.params.requestedSchema,\n  );\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(\n    \"Streamable HTTP connection failed, falling back to SSE transport\",\n  );\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-8697c832340d5eb44510837cf3959517"}