-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Open
Labels
Description
code:
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
import Koa from 'koa';
import { koaBody } from 'koa-body';
import KoaRouter from 'koa-router';
import { randomUUID } from 'node:crypto';
import { mcpServer } from './server.js';
// 初始化应用
const app = new Koa();
app.use(koaBody());
const router = new KoaRouter();
// 存储会话与传输实例的映射
const transportMap = {};
// MCP核心处理端点
router.post('/mcp', async (ctx) => {
const sessionId = ctx.headers['mcp-session-id'];
let transport;
if (sessionId && transportMap[sessionId]) {
transport = transportMap[sessionId];
} else if (!sessionId && isInitializeRequest(ctx.request.body)) {
transport = new StreamableHTTPServerTransport({
sessionIdGenerator: () => randomUUID(),
onsessioninitialized: (sessionId) => {
transportMap[sessionId] = transport;
},
});
ctx.req.on('close', () => {
console.log(`Connection closed for session: ${transport.sessionId}`);
delete transportMap[transport.sessionId];
});
await mcpServer.connect(transport);
} else {
ctx.status = 400;
ctx.body = { error: 'Invalid session ID' };
return;
}
await transport.handleRequest(ctx.req, ctx.res, ctx.request.body);
});
const handleSessionRequest = async (ctx) => {
const sessionId = ctx.headers['mcp-session-id'];
if (!sessionId || !transportMap[sessionId]) {
ctx.status = 400;
ctx.body = { error: 'Invalid or missing session ID' };
return;
}
await transportMap[sessionId].handleRequest(ctx.req, ctx.res);
};
router.get('/mcp', handleSessionRequest);
router.delete('/mcp', handleSessionRequest);
// 注册路由
app.use(router.routes()).use(router.allowedMethods());
// 启动服务器
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`MCP服务器启动在 http://localhost:${PORT}`);
console.log(`- MCP端点: http://localhost:${PORT}/mcp`);
});// server.js
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { writeFileSync } from "node:fs";
// Create an MCP server
export const mcpServer = new McpServer({
name: "MCP 测试服务器",
version: "1.0.0",
capabilities: {
resources: {},
tools: {},
},
});
// Add an addition tool
mcpServer.registerTool(
"add",
{
title: "加法工具",
description: "两个数字相加",
inputSchema: { a: z.number(), b: z.number() },
},
async ({ a, b }) => ({
content: [{ type: "text", text: String(a + b) }],
})
);XrEZE MINGW64 /d/workspace/IoT_UI/mcp-server
$ node mcp-http2
Debugger attached.
MCP服务器启动在 http://localhost:3000
- MCP端点: http://localhost:3000/mcp
Waiting for the debugger to disconnect...
node:events:485
throw er; // Unhandled 'error' event
^
Error [ERR_STREAM_WRITE_AFTER_END]: write after end
at write_ (node:_http_outgoing:951:11)
at ServerResponse.write (node:_http_outgoing:904:15)
at StreamableHTTPServerTransport.writeSSEEvent (file:///D:/workspace/IoT_UI/mcp-server/node_modules/@modelcontextprotocol/sdk/dist/e
sm/server/streamableHttp.js:238:20)
at StreamableHTTPServerTransport.send (file:///D:/workspace/IoT_UI/mcp-server/node_modules/@modelcontextprotocol/sdk/dist/esm/server
/streamableHttp.js:565:22)
at Promise.resolve.then.then.capturedTransport.send.jsonrpc (file:///D:/workspace/IoT_UI/mcp-server/node_modules/@modelcontextprotoc
ol/sdk/dist/esm/shared/protocol.js:162:108)
at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
Emitted 'error' event on ServerResponse instance at:
at emitErrorNt (node:_http_outgoing:923:9)
at process.processTicksAndRejections (node:internal/process/task_queues:91:21) {
code: 'ERR_STREAM_WRITE_AFTER_END'
}
Node.js v23.1.0