开发要求
- Node.js ≥ 18.0.0
- npm ≥ 9.0.0
- TypeScript ≥ 5.0.0
session-server.ts
import express from "express";
import { randomUUID } from "node:crypto";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod";
const app = express();
app.use(express.json());
// 会话ID与传输层映射表
const transports: { [sessionId: string]: StreamableHTTPServerTransport } = {};
// 处理客户端请求
app.post('/mcp', async (req, res) => {
const sessionId = req.headers['mcp-session-id'] as string | undefined;
let transport: StreamableHTTPServerTransport;
console.log("收到请求:", JSON.stringify(req.body, null, 2));
console.log("sessionId:", sessionId);
if (sessionId && transports[sessionId]) {
// 复用现有会话
transport = transports[sessionId];
} else if (!sessionId && isInitializeRequest(req.body)) {
// 新建会话
transport = new StreamableHTTPServerTransport({
sessionIdGenerator: () => randomUUID(),
onsessioninitialized: (sessionId) => {
transports[sessionId] = transport;
}
});
// 清理会话
transport.onclose = () => {
if (transport.sessionId) delete transports[transport.sessionId];
};
const server = new McpServer({
name: "示例服务器",
version: "1.0.0"
});
// ... 配置服务器资源、工具和提示 ...
// Add an addition tool
server.tool("add", 'Add two numbers', { a: z.number(), b: z.number() },
async ({ a, b }) => ({
content: [{ type: "text", text: String(a + b) }]
})
);
await server.connect(transport);
} else {
res.status(400).json({
jsonrpc: '2.0',
error: {
code: -32000,
message: '错误请求:无效的会话ID'
}
});
return;
}
await transport.handleRequest(req, res, req.body);
});
// 处理GET/DELETE请求
const handleSessionRequest = async (req: express.Request, res: express.Response) => {
const sessionId = req.headers['mcp-session-id'] as string | undefined;
if (!sessionId || !transports[sessionId]) {
res.status(400).send('无效或缺失的会话ID');
return;
}
await transports[sessionId].handleRequest(req, res);
};
app.get('/mcp', handleSessionRequest);
app.delete('/mcp', handleSessionRequest);
app.listen(3000);
测试
初始化
curl -i -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{
"jsonrpc": "2.0",
"id": 0,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"capabilities": {
"sampling": {},
"roots": {
"listChanged": true
}
},
"clientInfo": {
"name": "curl-test",
"version": "0.1"
}
}
}'
返回
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
mcp-session-id: e9c34a54-35af-4da9-a78c-6c91faf82414
Date: Thu, 10 Jul 2025 11:02:32 GMT
Transfer-Encoding: chunked
event: message
data: {"result":{"protocolVersion":"2025-06-18","capabilities":{"tools":{"listChanged":true}},"serverInfo":{"name":"示例服务器","version":"1.0.0"}},"jsonrpc":"2.0","id":0}
获取功能列表:
curl -i -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "mcp-session-id: cb8aa83e-f06a-4c73-9714-5ead68afbae1" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list",
"params": {
"_meta": {
"progressToken": 1
}
}
}'
调用其中的功能:
curl -i -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "mcp-session-id: cb8aa83e-f06a-4c73-9714-5ead68afbae1" \
-d '{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"_meta": {
"progressToken": 3
},
"name": "add",
"arguments": {
"a": 10,
"b": 20
}
}
}'
