X

Typescript MCP SDK

Information

# MCP TypeScript SDK ![NPM Version](https://img.shields.io/npm/v/%40modelcontextprotocol%2Fsdk) ![MIT licensed](https://img.shields.io/npm/l/%40modelcontextprotocol%2Fsdk) ## Table of Contents - [Overview](#overview) - [Installation](#installation) - [Quickstart](#quickstart) - [What is MCP?](#what-is-mcp) - [Core Concepts](#core-concepts) - [Server](#server) - [Resources](#resources) - [Tools](#tools) - [Prompts](#prompts) - [Running Your Server](#running-your-server) - [stdio](#stdio) - [HTTP with SSE](#http-with-sse) - [Testing and Debugging](#testing-and-debugging) - [Examples](#examples) - [Echo Server](#echo-server) - [SQLite Explorer](#sqlite-explorer) - [Advanced Usage](#advanced-usage) - [Low-Level Server](#low-level-server) - [Writing MCP Clients](#writing-mcp-clients) - [Server Capabilities](#server-capabilities) - [Proxy OAuth Server](#proxy-authorization-requests-upstream) ## Overview The 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: - Build MCP clients that can connect to any MCP server - Create MCP servers that expose resources, prompts and tools - Use standard transports like stdio and SSE - Handle all MCP protocol messages and lifecycle events ## Installation \`\`\`bash npm install @modelcontextprotocol/sdk \`\`\` ## Quick Start Let's create a simple MCP server that exposes a calculator tool and some data: \`\`\`typescript import \{ McpServer, ResourceTemplate \} from "@modelcontextprotocol/sdk/server/mcp.js"; import \{ StdioServerTransport \} from "@modelcontextprotocol/sdk/server/stdio.js"; import \{ z \} from "zod"; // Create an MCP server const server = new McpServer(\{ name: "Demo", version: "1.0.0" \}); // Add an addition tool server.tool("add", \{ a: z.number(), b: z.number() \}, async (\{ a, b \}) => (\{ content: [\{ type: "text", text: String(a + b) \}] \}) ); // Add a dynamic greeting resource server.resource( "greeting", new ResourceTemplate("greeting://\{name\}", \{ list: undefined \}), async (uri, \{ name \}) => (\{ contents: [\{ uri: uri.href, text: \`Hello, $\{name\}!\` \}] \}) ); // Start receiving messages on stdin and sending messages on stdout const transport = new StdioServerTransport(); await server.connect(transport); \`\`\` ## What is MCP? The [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: - Expose data through **Resources** (think of these sort of like GET endpoints; they are used to load information into the LLM's context) - Provide functionality through **Tools** (sort of like POST endpoints; they are used to execute code or otherwise produce a side effect) - Define interaction patterns through **Prompts** (reusable templates for LLM interactions) - And more! ## Core Concepts ### Server The McpServer is your core interface to the MCP protocol. It handles connection management, protocol compliance, and message routing: \`\`\`typescript const server = new McpServer(\{ name: "My App", version: "1.0.0" \}); \`\`\` ### Resources Resources 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: \`\`\`typescript // Static resource server.resource( "config", "config://app", async (uri) => (\{ contents: [\{ uri: uri.href, text: "App configuration here" \}] \}) ); // Dynamic resource with parameters server.resource( "user-profile", new ResourceTemplate("users://\{userId\}/profile", \{ list: undefined \}), async (uri, \{ userId \}) => (\{ contents: [\{ uri: uri.href, text: \`Profile data for user $\{userId\}\` \}] \}) ); \`\`\` ### Tools Tools let LLMs take actions through your server. Unlike resources, tools are expected to perform computation and have side effects: \`\`\`typescript // Simple tool with parameters server.tool( "calculate-bmi", \{ weightKg: z.number(), heightM: z.number() \}, async (\{ weightKg, heightM \}) => (\{ content: [\{ type: "text", text: String(weightKg / (heightM * heightM)) \}] \}) ); // Async tool with external API call server.tool( "fetch-weather", \{ city: z.string() \}, async (\{ city \}) => \{ const response = await fetch(\`https://api.weather.com/$\{city\}\`); const data = await response.text(); return \{ content: [\{ type: "text", text: data \}] \}; \} ); \`\`\` ### Prompts Prompts are reusable templates that help LLMs interact with your server effectively: \`\`\`typescript server.prompt( "review-code", \{ code: z.string() \}, (\{ code \}) => (\{ messages: [\{ role: "user", content: \{ type: "text", text: \`Please review this code:\n\n$\{code\}\` \} \}] \}) ); \`\`\` ## Running Your Server MCP 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: ### stdio For command-line tools and direct integrations: \`\`\`typescript import \{ McpServer \} from "@modelcontextprotocol/sdk/server/mcp.js"; import \{ StdioServerTransport \} from "@modelcontextprotocol/sdk/server/stdio.js"; const server = new McpServer(\{ name: "example-server", version: "1.0.0" \}); // ... set up server resources, tools, and prompts ... const transport = new StdioServerTransport(); await server.connect(transport); \`\`\` ### HTTP with SSE For remote servers, start a web server with a Server-Sent Events (SSE) endpoint, and a separate endpoint for the client to send its messages to: \`\`\`typescript import express, \{ Request, Response \} from "express"; import \{ McpServer \} from "@modelcontextprotocol/sdk/server/mcp.js"; import \{ SSEServerTransport \} from "@modelcontextprotocol/sdk/server/sse.js"; const server = new McpServer(\{ name: "example-server", version: "1.0.0" \}); // ... set up server resources, tools, and prompts ... const app = express(); // to support multiple simultaneous connections we have a lookup object from // sessionId to transport const transports: \{[sessionId: string]: SSEServerTransport\} = \{\}; app.get("/sse", async (_: Request, res: Response) => \{ const transport = new SSEServerTransport('/messages', res); transports[transport.sessionId] = transport; res.on("close", () => \{ delete transports[transport.sessionId]; \}); await server.connect(transport); \}); app.post("/messages", async (req: Request, res: Response) => \{ const sessionId = req.query.sessionId as string; const transport = transports[sessionId]; if (transport) \{ await transport.handlePostMessage(req, res); \} else \{ res.status(400).send('No transport found for sessionId'); \} \}); app.listen(3001); \`\`\` ### Testing and Debugging To test your server, you can use the [MCP Inspector](https://github.com/modelcontextprotocol/inspector). See its README for more information. ## Examples ### Echo Server A simple server demonstrating resources, tools, and prompts: \`\`\`typescript import \{ McpServer, ResourceTemplate \} from "@modelcontextprotocol/sdk/server/mcp.js"; import \{ z \} from "zod"; const server = new McpServer(\{ name: "Echo", version: "1.0.0" \}); server.resource( "echo", new ResourceTemplate("echo://\{message\}", \{ list: undefined \}), async (uri, \{ message \}) => (\{ contents: [\{ uri: uri.href, text: \`Resource echo: $\{message\}\` \}] \}) ); server.tool( "echo", \{ message: z.string() \}, async (\{ message \}) => (\{ content: [\{ type: "text", text: \`Tool echo: $\{message\}\` \}] \}) ); server.prompt( "echo", \{ message: z.string() \}, (\{ message \}) => (\{ messages: [\{ role: "user", content: \{ type: "text", text: \`Please process this message: $\{message\}\` \} \}] \}) ); \`\`\` ### SQLite Explorer A more complex example showing database integration: \`\`\`typescript import \{ McpServer \} from "@modelcontextprotocol/sdk/server/mcp.js"; import sqlite3 from "sqlite3"; import \{ promisify \} from "util"; import \{ z \} from "zod"; const server = new McpServer(\{ name: "SQLite Explorer", version: "1.0.0" \}); // Helper to create DB connection const getDb = () => \{ const db = new sqlite3.Database("database.db"); return \{ all: promisify(db.all.bind(db)), close: promisify(db.close.bind(db)) \}; \}; server.resource( "schema", "schema://main", async (uri) => \{ const db = getDb(); try \{ const tables = await db.all( "SELECT sql FROM sqlite_master WHERE type='table'" ); return \{ contents: [\{ uri: uri.href, text: tables.map((t: \{sql: string\}) => t.sql).join("\n") \}] \}; \} finally \{ await db.close(); \} \} ); server.tool( "query", \{ sql: z.string() \}, async (\{ sql \}) => \{ const db = getDb(); try \{ const results = await db.all(sql); return \{ content: [\{ type: "text", text: JSON.stringify(results, null, 2) \}] \}; \} catch (err: unknown) \{ const error = err as Error; return \{ content: [\{ type: "text", text: \`Error: $\{error.message\}\` \}], isError: true \}; \} finally \{ await db.close(); \} \} ); \`\`\` ## Advanced Usage ### Dynamic Servers If 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\` notificaions: \`\`\`ts import \{ McpServer \} from "@modelcontextprotocol/sdk/server/mcp.js"; import \{ z \} from "zod"; const server = new McpServer(\{ name: "Dynamic Example", version: "1.0.0" \}); const listMessageTool = server.tool( "listMessages", \{ channel: z.string() \}, async (\{ channel \}) => (\{ content: [\{ type: "text", text: await listMessages(channel) \}] \}) ); const putMessageTool = server.tool( "putMessage", \{ channel: z.string(), message: z.string() \}, async (\{ channel, message \}) => (\{ content: [\{ type: "text", text: await putMessage(channel, string) \}] \}) ); // Until we upgrade auth, \`putMessage\` is disabled (won't show up in listTools) putMessageTool.disable() const upgradeAuthTool = server.tool( "upgradeAuth", \{ permission: z.enum(["write', vadmin"])\}, // Any mutations here will automatically emit \`listChanged\` notifications async (\{ permission \}) => \{ const \{ ok, err, previous \} = await upgradeAuthAndStoreToken(permission) if (!ok) return \{content: [\{ type: "text", text: \`Error: $\{err\}\` \}]\} // If we previously had read-only access, 'putMessage' is now available if (previous === "read") \{ putMessageTool.enable() \} if (permission === 'write') \{ // If we've just upgraded to 'write' permissions, we can still call 'upgradeAuth' // but can only upgrade to 'admin'. upgradeAuthTool.update(\{ paramSchema: \{ permission: z.enum(["admin"]) \}, // change validation rules \}) \} else \{ // If we're now an admin, we no longer have anywhere to upgrade to, so fully remove that tool upgradeAuthTool.remove() \} \} ) // Connect as normal const transport = new StdioServerTransport(); await server.connect(transport); \`\`\` ### Low-Level Server For more control, you can use the low-level Server class directly: \`\`\`typescript import \{ Server \} from "@modelcontextprotocol/sdk/server/index.js"; import \{ StdioServerTransport \} from "@modelcontextprotocol/sdk/server/stdio.js"; import \{ ListPromptsRequestSchema, GetPromptRequestSchema \} from "@modelcontextprotocol/sdk/types.js"; const server = new Server( \{ name: "example-server", version: "1.0.0" \}, \{ capabilities: \{ prompts: \{\} \} \} ); server.setRequestHandler(ListPromptsRequestSchema, async () => \{ return \{ prompts: [\{ name: "example-prompt", description: "An example prompt template", arguments: [\{ name: "arg1", description: "Example argument", required: true \}] \}] \}; \}); server.setRequestHandler(GetPromptRequestSchema, async (request) => \{ if (request.params.name !== "example-prompt") \{ throw new Error("Unknown prompt"); \} return \{ description: "Example prompt", messages: [\{ role: "user", content: \{ type: "text", text: "Example prompt text" \} \}] \}; \}); const transport = new StdioServerTransport(); await server.connect(transport); \`\`\` ### Writing MCP Clients The SDK provides a high-level client interface: \`\`\`typescript import \{ Client \} from "@modelcontextprotocol/sdk/client/index.js"; import \{ StdioClientTransport \} from "@modelcontextprotocol/sdk/client/stdio.js"; const transport = new StdioClientTransport(\{ command: "node", args: ["server.js"] \}); const client = new Client( \{ name: "example-client", version: "1.0.0" \} ); await client.connect(transport); // List prompts const prompts = await client.listPrompts(); // Get a prompt const prompt = await client.getPrompt(\{ name: "example-prompt", arguments: \{ arg1: "value" \} \}); // List resources const resources = await client.listResources(); // Read a resource const resource = await client.readResource(\{ uri: "file:///example.txt" \}); // Call a tool const result = await client.callTool(\{ name: "example-tool", arguments: \{ arg1: "value" \} \}); \`\`\` ### Proxy Authorization Requests Upstream You can proxy OAuth requests to an external authorization provider: \`\`\`typescript import express from 'express'; import \{ ProxyOAuthServerProvider, mcpAuthRouter \} from '@modelcontextprotocol/sdk'; const app = express(); const proxyProvider = new ProxyOAuthServerProvider(\{ endpoints: \{ authorizationUrl: "https://auth.external.com/oauth2/v1/authorize", tokenUrl: "https://auth.external.com/oauth2/v1/token", revocationUrl: "https://auth.external.com/oauth2/v1/revoke", \}, verifyAccessToken: async (token) => \{ return \{ token, clientId: "123", scopes: ["openid", "email", "profile"], \} \}, getClient: async (client_id) => \{ return \{ client_id, redirect_uris: ["http://localhost:3000/callback"], \} \} \}) app.use(mcpAuthRouter(\{ provider: proxyProvider, issuerUrl: new URL("http://auth.external.com"), baseUrl: new URL("http://mcp.example.com"), serviceDocumentationUrl: new URL("https://docs.example.com/"), \})) \`\`\` This setup allows you to: - Forward OAuth requests to an external provider - Add custom token validation logic - Manage client registrations - Provide custom documentation URLs - Maintain control over the OAuth flow while delegating to an external provider ## Documentation - [Model Context Protocol documentation](https://modelcontextprotocol.io) - [MCP Specification](https://spec.modelcontextprotocol.io) - [Example Servers](https://github.com/modelcontextprotocol/servers) ## Contributing Issues and pull requests are welcome on GitHub at https://github.com/modelcontextprotocol/typescript-sdk. ## License This project is licensed under the MIT License—see the [LICENSE](LICENSE) file for details.

Prompts

Reviews

Tags

Write Your Review

Detailed Ratings

ALL
Correctness
Helpfulness
Interesting
Upload Pictures and Videos

Name
Size
Type
Download
Last Modified

Upload Files

  • Community

Add Discussion

Upload Pictures and Videos