Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
a677d44
style: standardize string literals and formatting in mcp.ts and add …
ahammednibras8 Dec 26, 2025
6a3a646
feat: add and types for intercepting requests
ahammednibras8 Dec 26, 2025
c3b0c87
feat: add private helper method for handling server requests
ahammednibras8 Dec 27, 2025
26021ce
refactor: Wrap and handlers with and add new request-related types
ahammednibras8 Dec 27, 2025
a58eced
refactor: update context parameter type to
ahammednibras8 Dec 27, 2025
b02c3ef
feat: Add middleware support to McpServer's call handler
ahammednibras8 Dec 27, 2025
f13e8dc
feat: add method to register middleware functions
ahammednibras8 Dec 27, 2025
d7550f0
feat: Prevent middleware registration after server connection or init…
ahammednibras8 Dec 27, 2025
6b0a628
test: Add middleware tests covering execution order, short-circuitin…
ahammednibras8 Dec 27, 2025
51c003a
feat: Prevent multiple calls to in middleware and add a correspondin…
ahammednibras8 Dec 27, 2025
2e6ea43
test: ensure middleware cannot be registered after server connection
ahammednibras8 Dec 27, 2025
e98cadb
test: add real-world use case tests for middleware covering logging, …
ahammednibras8 Dec 27, 2025
6f70de5
test: Add failure mode verification tests for mcpServer middleware an…
ahammednibras8 Dec 27, 2025
0143422
feat: Add property to request handler context for cross-middleware c…
ahammednibras8 Dec 28, 2025
5a6a172
feat: enable middleware state sharing via by initializing the contex…
ahammednibras8 Dec 28, 2025
259103d
docs: clarify request mutability and mutation guidelines in McpMiddle…
ahammednibras8 Dec 28, 2025
2a276ca
refactor: Wrap prompt handlers with and add a type constraint to its…
ahammednibras8 Dec 28, 2025
d5ceada
fix: explicitly type argument when invoking prompt callbacks
ahammednibras8 Dec 28, 2025
3267548
feat: enable chainable middleware registration and add test for async…
ahammednibras8 Dec 28, 2025
7b88bd1
Update .gitignore
ahammednibras8 Dec 28, 2025
ed43f1f
feat(server): finalize middleware types and remove any usage
ahammednibras8 Dec 28, 2025
3890516
docs: add middleware section to server documentation
ahammednibras8 Dec 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,5 @@ dist/

# IDE
.idea/

# ahammednibras8
205 changes: 140 additions & 65 deletions docs/server.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
## Server overview

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:
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`](../examples/server/src/simpleStreamableHttp.ts) – feature‑rich Streamable HTTP server
- [`jsonResponseStreamableHttp.ts`](../examples/server/src/jsonResponseStreamableHttp.ts) – Streamable HTTP with JSON response mode
- [`simpleStatelessStreamableHttp.ts`](../examples/server/src/simpleStatelessStreamableHttp.ts) – stateless Streamable HTTP server
- [`simpleSseServer.ts`](../examples/server/src/simpleSseServer.ts) – deprecated HTTP+SSE transport
- [`sseAndStreamableHttpCompatibleServer.ts`](../examples/server/src/sseAndStreamableHttpCompatibleServer.ts) – backwards‑compatible server for old and new clients
- [`simpleStreamableHttp.ts`](../examples/server/src/simpleStreamableHttp.ts) –
feature‑rich Streamable HTTP server
- [`jsonResponseStreamableHttp.ts`](../examples/server/src/jsonResponseStreamableHttp.ts)
– Streamable HTTP with JSON response mode
- [`simpleStatelessStreamableHttp.ts`](../examples/server/src/simpleStatelessStreamableHttp.ts)
– stateless Streamable HTTP server
- [`simpleSseServer.ts`](../examples/server/src/simpleSseServer.ts) – deprecated
HTTP+SSE transport
- [`sseAndStreamableHttpCompatibleServer.ts`](../examples/server/src/sseAndStreamableHttpCompatibleServer.ts)
– backwards‑compatible server for old and new clients

## Transports

Expand All @@ -27,99 +34,153 @@ Streamable HTTP is the modern, fully featured transport. It supports:

Key examples:

- [`simpleStreamableHttp.ts`](../examples/server/src/simpleStreamableHttp.ts) – sessions, logging, tasks, elicitation, auth hooks
- [`jsonResponseStreamableHttp.ts`](../examples/server/src/jsonResponseStreamableHttp.ts) – `enableJsonResponse: true`, no SSE
- [`standaloneSseWithGetStreamableHttp.ts`](../examples/server/src/standaloneSseWithGetStreamableHttp.ts) – notifications with Streamable HTTP GET + SSE
- [`simpleStreamableHttp.ts`](../examples/server/src/simpleStreamableHttp.ts) –
sessions, logging, tasks, elicitation, auth hooks
- [`jsonResponseStreamableHttp.ts`](../examples/server/src/jsonResponseStreamableHttp.ts)
– `enableJsonResponse: true`, no SSE
- [`standaloneSseWithGetStreamableHttp.ts`](../examples/server/src/standaloneSseWithGetStreamableHttp.ts)
– notifications with Streamable HTTP GET + SSE

See the MCP spec for full transport details: `https://modelcontextprotocol.io/specification/2025-11-25/basic/transports`
See the MCP spec for full transport details:
`https://modelcontextprotocol.io/specification/2025-11-25/basic/transports`

### Stateless vs stateful sessions

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.
- **Stateful** – sessions have IDs, and you can enable resumability and advanced
features.

Examples:

- Stateless Streamable HTTP: [`simpleStatelessStreamableHttp.ts`](../examples/server/src/simpleStatelessStreamableHttp.ts)
- Stateful with resumability: [`simpleStreamableHttp.ts`](../examples/server/src/simpleStreamableHttp.ts)
- Stateless Streamable HTTP:
[`simpleStatelessStreamableHttp.ts`](../examples/server/src/simpleStatelessStreamableHttp.ts)
- Stateful with resumability:
[`simpleStreamableHttp.ts`](../examples/server/src/simpleStreamableHttp.ts)

### Deprecated HTTP + SSE

The older HTTP+SSE transport (protocol version 2024‑11‑05) is supported only for backwards compatibility. New implementations should prefer Streamable HTTP.
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`](../examples/server/src/simpleSseServer.ts)
- Backwards‑compatible server (Streamable HTTP + SSE):
- Legacy SSE server:
[`simpleSseServer.ts`](../examples/server/src/simpleSseServer.ts)
- Backwards‑compatible server (Streamable HTTP + SSE):\
[`sseAndStreamableHttpCompatibleServer.ts`](../examples/server/src/sseAndStreamableHttpCompatibleServer.ts)

## Running your server

For a minimal “getting started” experience:

1. Start from [`simpleStreamableHttp.ts`](../examples/server/src/simpleStreamableHttp.ts).
1. Start from
[`simpleStreamableHttp.ts`](../examples/server/src/simpleStreamableHttp.ts).
2. Remove features you do not need (tasks, advanced logging, OAuth, etc.).
3. 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.
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.

## Middleware

The `McpServer` supports a middleware system similar to Express or Koa, allowing
you to intercept and modify requests, log activity, or enforce authentication
across all tools, prompts, and resources.

Register middleware using `server.use()`:

```typescript
const server = new McpServer(
{ name: "my-server", version: "1.0.0" },
{ capabilities: { logging: {} } },
);

// Logging middleware
server.use(async (context, next) => {
const start = Date.now();
try {
await next();
} finally {
const duration = Date.now() - start;
console.error(`[${context.request.method}] took ${duration}ms`);
}
});

// Authentication middleware example
server.use(async (context, next) => {
if (context.request.method === "tools/call") {
// Perform auth checks here
// throw new McpError(ErrorCode.InvalidRequest, "Unauthorized");
}
await next();
});
```

Middleware executes in the order registered. Calling `next()` passes control to
the next middleware or the actual handler. Note that middleware must be
registered before connecting the server transport.

## DNS rebinding protection

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:
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:

```typescript
import { createMcpExpressApp } from '@modelcontextprotocol/server';
import { createMcpExpressApp } from "@modelcontextprotocol/server";

// Protection auto-enabled (default host is 127.0.0.1)
const app = createMcpExpressApp();

// Protection auto-enabled for localhost
const app = createMcpExpressApp({ host: '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' });
const app = createMcpExpressApp({ host: "0.0.0.0" });
```

When binding to `0.0.0.0` / `::`, provide an allow-list of hosts:

```typescript
import { createMcpExpressApp } from '@modelcontextprotocol/server';
import { createMcpExpressApp } from "@modelcontextprotocol/server";

const app = createMcpExpressApp({
host: '0.0.0.0',
allowedHosts: ['localhost', '127.0.0.1', 'myhost.local']
host: "0.0.0.0",
allowedHosts: ["localhost", "127.0.0.1", "myhost.local"],
});
```

## Tools, resources, and prompts

### Tools

Tools let MCP clients ask your server to take actions. They are usually the main way that LLMs call into your application.
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:

```typescript
server.registerTool(
'calculate-bmi',
"calculate-bmi",
{
title: 'BMI Calculator',
description: 'Calculate Body Mass Index',
title: "BMI Calculator",
description: "Calculate Body Mass Index",
inputSchema: {
weightKg: z.number(),
heightM: z.number()
heightM: z.number(),
},
outputSchema: { bmi: 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
content: [{ type: "text", text: JSON.stringify(output) }],
structuredContent: output,
};
}
},
);
```

Expand All @@ -130,60 +191,66 @@ This snippet is illustrative only; for runnable servers that expose tools, see:

#### ResourceLink outputs

Tools can return `resource_link` content items to reference large resources without embedding them directly, allowing clients to fetch only what they need.
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`.
The README’s `list-files` example shows the pattern conceptually; for concrete
usage, see the Streamable HTTP examples in `examples/server/src`.

### Resources

Resources expose data to clients, but should not perform heavy computation or side‑effects. They are ideal for configuration, documents, or other reference data.
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:

```typescript
server.registerResource(
'config',
'config://app',
"config",
"config://app",
{
title: 'Application Config',
description: 'Application configuration data',
mimeType: 'text/plain'
title: "Application Config",
description: "Application configuration data",
mimeType: "text/plain",
},
async uri => ({
contents: [{ uri: uri.href, text: 'App configuration here' }]
})
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:
Dynamic resources use `ResourceTemplate` and can support completions on path
parameters. For full runnable examples of resources:

- [`simpleStreamableHttp.ts`](../examples/server/src/simpleStreamableHttp.ts)

### Prompts

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.
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:

```typescript
server.registerPrompt(
'review-code',
"review-code",
{
title: 'Code Review',
description: 'Review code for best practices and potential issues',
argsSchema: { code: z.string() }
title: "Code Review",
description: "Review code for best practices and potential issues",
argsSchema: { code: z.string() },
},
({ code }) => ({
messages: [
{
role: 'user',
role: "user",
content: {
type: 'text',
text: `Please review this code:\n\n${code}`
}
}
]
})
type: "text",
text: `Please review this code:\n\n${code}`,
},
},
],
}),
);
```

Expand All @@ -193,19 +260,26 @@ For prompts integrated into a full server, see:

### Completions

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.
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`](../examples/client/src/simpleStreamableHttp.ts) for client‑side usage patterns.
See the MCP spec sections on prompts and resources for complete details, and
[`simpleStreamableHttp.ts`](../examples/client/src/simpleStreamableHttp.ts) for
client‑side usage patterns.

### Display names and metadata

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:
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:

- `getDisplayName` from `@modelcontextprotocol/client`

## Multi‑node deployment patterns

The SDK supports multi‑node deployments using Streamable HTTP. The high‑level patterns and diagrams live with the runnable server examples:
The SDK supports multi‑node deployments using Streamable HTTP. The high‑level
patterns and diagrams live with the runnable server examples:

- [`examples/server/README.md`](../examples/server/README.md#multi-node-deployment-patterns)

Expand All @@ -214,8 +288,9 @@ The SDK supports multi‑node deployments using Streamable HTTP. The high‑leve
To handle both modern and legacy clients:

- Run a backwards‑compatible server:
- [`sseAndStreamableHttpCompatibleServer.ts`](../examples/server/src/sseAndStreamableHttpCompatibleServer.ts)
- [`sseAndStreamableHttpCompatibleServer.ts`](../examples/server/src/sseAndStreamableHttpCompatibleServer.ts)
- Use a client that falls back from Streamable HTTP to SSE:
- [`streamableHttpWithSseFallbackClient.ts`](../examples/client/src/streamableHttpWithSseFallbackClient.ts)
- [`streamableHttpWithSseFallbackClient.ts`](../examples/client/src/streamableHttpWithSseFallbackClient.ts)

For the detailed protocol rules, see the “Backwards compatibility” section of the MCP spec.
For the detailed protocol rules, see the “Backwards compatibility” section of
the MCP spec.
Loading
Loading