Skip to content

Commit 01fadc4

Browse files
KKonstantinovmozmo15
authored andcommitted
[v2] Decouple server from express and hono - http framework-agnostic MCP server (modelcontextprotocol#1326)
1 parent 1d84729 commit 01fadc4

104 files changed

Lines changed: 6757 additions & 7343 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/publish.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,6 @@ jobs:
3838
run: pnpm run build:all
3939

4040
- name: Publish preview packages
41-
run: pnpm dlx pkg-pr-new publish --packageManager=npm --pnpm './packages/server' './packages/client'
41+
run:
42+
pnpm dlx pkg-pr-new publish --packageManager=npm --pnpm './packages/server' './packages/client'
43+
'./packages/middleware/express' './packages/middleware/hono' './packages/middleware/node'

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ dist/
133133

134134
# IDE
135135
.idea/
136+
.cursor/
136137

137138
# Conformance test results
138139
results/

CLAUDE.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,15 @@ Transports (`packages/core/src/shared/transport.ts`) provide the communication l
6464
### Client-Side Features
6565

6666
- **Auth**: OAuth client support in `packages/client/src/client/auth.ts` and `packages/client/src/client/auth-extensions.ts`
67-
- **Middleware**: Request middleware in `packages/client/src/client/middleware.ts`
67+
- **Client middleware**: Request middleware in `packages/client/src/client/middleware.ts` (unrelated to the framework adapter packages below)
6868
- **Sampling**: Clients can handle `sampling/createMessage` requests from servers (LLM completions)
6969
- **Elicitation**: Clients can handle `elicitation/create` requests for user input (form or URL mode)
7070
- **Roots**: Clients can expose filesystem roots to servers via `roots/list`
7171

72+
### Middleware packages (framework/runtime adapters)
73+
74+
The repo also ships “middleware” packages under `packages/middleware/` (e.g. `@modelcontextprotocol/express`, `@modelcontextprotocol/hono`, `@modelcontextprotocol/node`). These are thin integration layers for specific frameworks/runtimes and should not add new MCP functionality.
75+
7276
### Experimental Features
7377

7478
Located in `packages/*/src/experimental/`:
@@ -224,7 +228,8 @@ mcpServer.tool('tool-name', { param: z.string() }, async ({ param }, extra) => {
224228

225229
```typescript
226230
// Server
227-
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID() });
231+
// (Node.js IncomingMessage/ServerResponse wrapper; exported by @modelcontextprotocol/node)
232+
const transport = new NodeStreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID() });
228233
await server.connect(transport);
229234

230235
// Client

common/eslint-config/eslint.config.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ export default defineConfig(
4747
'@typescript-eslint/consistent-type-imports': ['error', { disallowTypeAnnotations: false }],
4848
'simple-import-sort/imports': 'warn',
4949
'simple-import-sort/exports': 'warn',
50+
'import/consistent-type-specifier-style': ['error', 'prefer-top-level'],
5051
'import/no-extraneous-dependencies': [
5152
'error',
5253
{

common/vitest-config/vitest.config.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ export default defineConfig({
1515
deps: {
1616
moduleDirectories: ['node_modules', path.resolve(__dirname, '../../packages'), path.resolve(__dirname, '../../common')]
1717
},
18-
poolOptions: {
19-
threads: {
20-
useAtomics: true
21-
}
18+
},
19+
poolOptions: {
20+
threads: {
21+
useAtomics: true
2222
}
2323
},
2424
plugins: [tsconfigPaths()]

docs/faq.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,15 @@ For production use, you can either:
6565

6666
The SDK ships several runnable server examples under `examples/server/src`. Start from the server examples index in [`examples/server/README.md`](../examples/server/README.md) and the entry-point quick start in the root [`README.md`](../README.md).
6767

68+
### Why did we remove `server` auth exports?
69+
70+
Server authentication & authorization is outside of the scope of the SDK, and the recommendation is to use packages that focus on this area specifically (or a full-fledged Authorization Server for those who use such). Example packages provide an example with `better-auth`.
71+
72+
### Why did we remove `server` SSE transport?
73+
74+
The SSE transport has been deprecated for a long time, and `v2` will not support it on the server side any more. Client side will keep supporting it in order to be able to connect to legacy SSE servers via the `v2` SDK, but serving SSE from `v2` will not be possible. Servers
75+
wanting to switch to `v2` and using SSE should migrate to Streamable HTTP.
76+
6877
## v1 (legacy)
6978

7079
### Where do v1 documentation and v1-specific fixes live?

docs/server.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ For more detailed patterns (stateless vs stateful, JSON response mode, CORS, DNS
7070
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:
7171

7272
```typescript
73-
import { createMcpExpressApp } from '@modelcontextprotocol/server';
73+
import { createMcpExpressApp } from '@modelcontextprotocol/express';
7474

7575
// Protection auto-enabled (default host is 127.0.0.1)
7676
const app = createMcpExpressApp();
@@ -85,7 +85,7 @@ const app = createMcpExpressApp({ host: '0.0.0.0' });
8585
When binding to `0.0.0.0` / `::`, provide an allow-list of hosts:
8686

8787
```typescript
88-
import { createMcpExpressApp } from '@modelcontextprotocol/server';
88+
import { createMcpExpressApp } from '@modelcontextprotocol/express';
8989

9090
const app = createMcpExpressApp({
9191
host: '0.0.0.0',

examples/server/README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
# MCP TypeScript SDK Examples (Server)
22

3-
This directory contains runnable MCP **server** examples built with `@modelcontextprotocol/server`.
3+
This directory contains runnable MCP **server** examples built with `@modelcontextprotocol/server` plus framework adapters:
4+
5+
- `@modelcontextprotocol/express`
6+
- `@modelcontextprotocol/hono`
47

58
For client examples, see [`../client/README.md`](../client/README.md). For guided docs, see [`../../docs/server.md`](../../docs/server.md).
69

@@ -68,7 +71,7 @@ When deploying MCP servers in a horizontally scaled environment (multiple server
6871

6972
### Stateless mode
7073

71-
To enable stateless mode, configure the `StreamableHTTPServerTransport` with:
74+
To enable stateless mode, configure the `NodeStreamableHTTPServerTransport` with:
7275

7376
```typescript
7477
sessionIdGenerator: undefined;

examples/server/package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,15 @@
3535
},
3636
"dependencies": {
3737
"@hono/node-server": "catalog:runtimeServerOnly",
38-
"hono": "catalog:runtimeServerOnly",
3938
"@modelcontextprotocol/examples-shared": "workspace:^",
39+
"@modelcontextprotocol/node": "workspace:^",
4040
"@modelcontextprotocol/server": "workspace:^",
41+
"@modelcontextprotocol/express": "workspace:^",
42+
"@modelcontextprotocol/hono": "workspace:^",
43+
"better-auth": "^1.4.7",
4144
"cors": "catalog:runtimeServerOnly",
4245
"express": "catalog:runtimeServerOnly",
46+
"hono": "catalog:runtimeServerOnly",
4347
"zod": "catalog:runtimeShared"
4448
},
4549
"devDependencies": {

examples/server/src/elicitationFormExample.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99

1010
import { randomUUID } from 'node:crypto';
1111

12-
import { createMcpExpressApp, isInitializeRequest, McpServer, StreamableHTTPServerTransport } from '@modelcontextprotocol/server';
13-
import { type Request, type Response } from 'express';
12+
import { createMcpExpressApp } from '@modelcontextprotocol/express';
13+
import { NodeStreamableHTTPServerTransport } from '@modelcontextprotocol/node';
14+
import { isInitializeRequest, McpServer } from '@modelcontextprotocol/server';
15+
import type { Request, Response } from 'express';
1416

1517
// Factory to create a new MCP server per session.
1618
// Each session needs its own server+transport pair to avoid cross-session contamination.
@@ -327,7 +329,7 @@ async function main() {
327329
const app = createMcpExpressApp();
328330

329331
// Map to store transports by session ID
330-
const transports: { [sessionId: string]: StreamableHTTPServerTransport } = {};
332+
const transports: { [sessionId: string]: NodeStreamableHTTPServerTransport } = {};
331333

332334
// MCP POST endpoint
333335
const mcpPostHandler = async (req: Request, res: Response) => {
@@ -337,13 +339,13 @@ async function main() {
337339
}
338340

339341
try {
340-
let transport: StreamableHTTPServerTransport;
342+
let transport: NodeStreamableHTTPServerTransport;
341343
if (sessionId && transports[sessionId]) {
342344
// Reuse existing transport for this session
343345
transport = transports[sessionId];
344346
} else if (!sessionId && isInitializeRequest(req.body)) {
345347
// New initialization request - create new transport
346-
transport = new StreamableHTTPServerTransport({
348+
transport = new NodeStreamableHTTPServerTransport({
347349
sessionIdGenerator: () => randomUUID(),
348350
onsessioninitialized: sessionId => {
349351
// Store the transport by session ID when session is initialized

0 commit comments

Comments
 (0)