Skip to main content
Glama
mcp.md10.5 kB
# 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) - [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) - [Backwards Compatibility](#backwards-compatibility) ## 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 - 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); ``` This stateless approach is useful for: - Simple API wrappers - RESTful scenarios where each request is independent - Horizontally scaled deployments without shared session state ### 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<string, any[]>(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); ```

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/pubnub/pubnub-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server