| title |
|---|
Server |
This SDK lets you build MCP servers in TypeScript and connect them to different transports. For most use cases you will use McpServer from @modelcontextprotocol/server and choose one of:
- Streamable HTTP (recommended for remote servers)
- HTTP + SSE (deprecated, for backwards compatibility only)
- stdio (for local, process‑spawned integrations)
For a complete, runnable example server, see:
simpleStreamableHttp.ts– feature‑rich Streamable HTTP serverjsonResponseStreamableHttp.ts– Streamable HTTP with JSON response modesimpleStatelessStreamableHttp.ts– stateless Streamable HTTP serversimpleSseServer.ts– deprecated HTTP+SSE transportsseAndStreamableHttpCompatibleServer.ts– backwards‑compatible server for old and new clients
Streamable HTTP is the modern, fully featured transport. It supports:
- Request/response over HTTP POST
- Server‑to‑client notifications over SSE (when enabled)
- Optional JSON‑only response mode with no SSE
- Session management and resumability
Key examples:
simpleStreamableHttp.ts– sessions, logging, tasks, elicitation, auth hooksjsonResponseStreamableHttp.ts–enableJsonResponse: true, no SSEstandaloneSseWithGetStreamableHttp.ts– notifications with Streamable HTTP GET + SSE
See the MCP spec for full transport details: https://modelcontextprotocol.io/specification/2025-11-25/basic/transports
Streamable HTTP can run:
- Stateless – no session tracking, ideal for simple API‑style servers.
- Stateful – sessions have IDs, and you can enable resumability and advanced features.
Examples:
- Stateless Streamable HTTP:
simpleStatelessStreamableHttp.ts - Stateful with resumability:
simpleStreamableHttp.ts
The older HTTP+SSE transport (protocol version 2024‑11‑05) is supported only for backwards compatibility. New implementations should prefer Streamable HTTP.
Examples:
- Legacy SSE server:
simpleSseServer.ts - Backwards‑compatible server (Streamable HTTP + SSE):
sseAndStreamableHttpCompatibleServer.ts
For a minimal “getting started” experience:
- Start from
simpleStreamableHttp.ts. - Remove features you do not need (tasks, advanced logging, OAuth, etc.).
- Register your own tools, resources and prompts.
For more detailed patterns (stateless vs stateful, JSON response mode, CORS, DNS rebind protection), see the examples above and the MCP spec sections on transports.
MCP servers running on localhost are vulnerable to DNS rebinding attacks. Use createMcpExpressApp() to create an Express app with DNS rebinding protection enabled by default:
import { createMcpExpressApp } from '@modelcontextprotocol/express';
// Protection auto-enabled (default host is 127.0.0.1)
const app = createMcpExpressApp();
// Protection auto-enabled for localhost
const app = createMcpExpressApp({ host: 'localhost' });
// No auto protection when binding to all interfaces, unless you provide allowedHosts
const app = createMcpExpressApp({ host: '0.0.0.0' });When binding to 0.0.0.0 / ::, provide an allow-list of hosts:
import { createMcpExpressApp } from '@modelcontextprotocol/express';
const app = createMcpExpressApp({
host: '0.0.0.0',
allowedHosts: ['localhost', '127.0.0.1', 'myhost.local']
});Tools let MCP clients ask your server to take actions. They are usually the main way that LLMs call into your application.
A typical registration with registerTool looks like this:
server.registerTool(
'calculate-bmi',
{
title: 'BMI Calculator',
description: 'Calculate Body Mass Index',
inputSchema: {
weightKg: z.number(),
heightM: z.number()
},
outputSchema: { bmi: z.number() }
},
async ({ weightKg, heightM }) => {
const output = { bmi: weightKg / (heightM * heightM) };
return {
content: [{ type: 'text', text: JSON.stringify(output) }],
structuredContent: output
};
}
);This snippet is illustrative only; for runnable servers that expose tools, see:
Tools can return resource_link content items to reference large resources without embedding them directly, allowing clients to fetch only what they need.
The README’s list-files example shows the pattern conceptually; for concrete usage, see the Streamable HTTP examples in examples/server/src.
Resources expose data to clients, but should not perform heavy computation or side‑effects. They are ideal for configuration, documents, or other reference data.
Conceptually, you might register resources like:
server.registerResource(
'config',
'config://app',
{
title: 'Application Config',
description: 'Application configuration data',
mimeType: 'text/plain'
},
async uri => ({
contents: [{ uri: uri.href, text: 'App configuration here' }]
})
);Dynamic resources use ResourceTemplate and can support completions on path parameters. For full runnable examples of resources:
Prompts are reusable templates that help humans (or client UIs) talk to models in a consistent way. They are declared on the server and listed through MCP.
A minimal prompt:
server.registerPrompt(
'review-code',
{
title: 'Code Review',
description: 'Review code for best practices and potential issues',
argsSchema: { code: z.string() }
},
({ code }) => ({
messages: [
{
role: 'user',
content: {
type: 'text',
text: `Please review this code:\n\n${code}`
}
}
]
})
);For prompts integrated into a full server, see:
Both prompts and resources can support argument completions. On the client side, you use client.complete() with a reference to the prompt or resource and the partially‑typed argument.
See the MCP spec sections on prompts and resources for complete details, and simpleStreamableHttp.ts for client‑side usage patterns.
Tools, resources and prompts support a title field for human‑readable names. Older APIs can also attach annotations.title. To compute the correct display name on the client, use:
getDisplayNamefrom@modelcontextprotocol/client
The SDK supports multi‑node deployments using Streamable HTTP. The high‑level patterns and diagrams live with the runnable server examples:
To handle both modern and legacy clients:
- Run a backwards‑compatible server:
- Use a client that falls back from Streamable HTTP to SSE:
For the detailed protocol rules, see the “Backwards compatibility” section of the MCP spec.