初始项目
bash
npm init -y
npm install @modelcontextprotocol/sdk typescript ts-node @types/node
npm install -D typescript @types/node
修改 package.json
json
{
"name": "mcp-server",
"version": "1.0.0",
"description": "",
"main": "index.js",
"type": "module",
"scripts": {
"build": "tsc",
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@modelcontextprotocol/sdk": "^1.18.1",
"ts-node": "^10.9.2"
},
"devDependencies": {
"@types/node": "^24.5.2",
"typescript": "^5.9.2"
}
}
添加 tsconfig.json
json
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "node",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"declaration": true,
"sourceMap": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
编写 src/server.ts
官方文档: https://github.com/modelcontextprotocol/typescript-sdk
下方各示例可直接使用
Stdio 写法
typescript
import {
McpServer,
ResourceTemplate,
} from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
// Create an MCP server
const server = new McpServer({
name: "demo-server",
version: "1.0.0",
});
// Add an addition tool
server.registerTool(
"add",
{
title: "Addition Tool",
description: "用于计算任意两个数字的加法,包括小数和整数。无论数字大小都可以使用此工具进行精确计算。", // 必写,使用自然语言告诉大模型,这个工具是干什么的
inputSchema: { a: z.number(), b: z.number() },
},
async ({ a, b }) => ({
content: [{ type: "text", text: String(a + b) + " 计算成功" }],
})
);
// Add a dynamic greeting resource
server.registerResource(
"greeting",
new ResourceTemplate("greeting://{name}", { list: undefined }),
{
title: "Greeting Resource", // Display name for UI
description: "Dynamic greeting generator",
},
async (uri, { name }) => ({
contents: [
{
uri: uri.href,
text: `Hello, ${name}!`,
},
],
})
);
// 启动服务器
async function main() {
// Start receiving messages on stdin and sending messages on stdout
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("MCP Server 已启动,等待连接...");
}
main().catch((error) => {
console.error("服务器错误:", error);
process.exit(1);
});
bash
# 构建
npm run build
# 启动
node dist/server.js
json
// cursor 相关配置
{
"mcpServers": {
// 可自定义 id
"stdio-add": {
"isActive": true,
"name": "my-mcp",
"type": "stdio",
// 本地 node 服务的路径,可通过 which node 查看
"command": "/Users/tacy/.nvm/versions/node/v18.10.0/bin/node",
"args": [
// 编译后的文件地址
"/Users/tacy/test/mcp-server/dist/server.js"
]
}
}
}
SSE 写法
typescript
import express from "express";
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
import { z } from "zod";
const server = new McpServer({
name: "backwards-compatible-server",
version: "1.0.0"
});
// Add an addition tool
server.registerTool(
"add-sse",
{
title: "Addition Tool",
description: "用于计算任意两个数字的加法,包括小数和整数。无论数字大小都可以使用此工具进行精确计算。", // 必写,使用自然语言告诉大模型,这个工具是干什么的
inputSchema: { a: z.number(), b: z.number() },
},
async ({ a, b }) => ({
content: [{ type: "text", text: String(a + b) + " 计算成功 sse" }],
})
);
// Add a dynamic greeting resource
server.registerResource(
"greeting",
new ResourceTemplate("greeting://{name}", { list: undefined }),
{
title: "Greeting Resource", // Display name for UI
description: "Dynamic greeting generator",
},
async (uri, { name }) => ({
contents: [
{
uri: uri.href,
text: `Hello, ${name}!`,
},
],
})
);
const app = express();
app.use(express.json());
// Store transports for each session type
const transports = {
streamable: {} as Record<string, StreamableHTTPServerTransport>,
sse: {} as Record<string, SSEServerTransport>
};
// Legacy SSE endpoint for older clients
app.get('/sse', async (req, res) => {
// Create SSE transport for legacy clients
const transport = new SSEServerTransport('/messages', res);
transports.sse[transport.sessionId] = transport;
res.on("close", () => {
delete transports.sse[transport.sessionId];
});
await server.connect(transport);
});
// Legacy message endpoint for older clients
app.post('/messages', async (req, res) => {
const sessionId = req.query.sessionId as string;
const transport = transports.sse[sessionId];
if (transport) {
await transport.handlePostMessage(req, res, req.body);
} else {
res.status(400).send('No transport found for sessionId');
}
});
app.listen(3000);
bash
# 构建
npm run build
# 启动
node dist/server.js
json
// cursor 相关配置
{
"mcpServers": {
// 可自定义 id
"sse-add": {
"name": "sse-mcp",
"type": "sse",
"isActive": true,
// 在 cherry studio 中参数名为 baseUrl
"url": "http://localhost:3000/sse"
}
}
}
StreamableHttp 写法
typescript
import express from "express";
import { randomUUID } from "node:crypto";
import { McpServer, ResourceTemplate } 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());
// Map to store transports by session ID
const transports: { [sessionId: string]: StreamableHTTPServerTransport } = {};
// Handle POST requests for client-to-server communication
app.post('/mcp', async (req, res) => {
// Check for existing session ID
const sessionId = req.headers['mcp-session-id'] as string | undefined;
let transport: StreamableHTTPServerTransport;
if (sessionId && transports[sessionId]) {
// Reuse existing transport
transport = transports[sessionId];
} else if (!sessionId && isInitializeRequest(req.body)) {
// New initialization request
transport = new StreamableHTTPServerTransport({
sessionIdGenerator: () => randomUUID(),
onsessioninitialized: (sessionId) => {
// Store the transport by session ID
transports[sessionId] = transport;
},
// DNS rebinding protection is disabled by default for backwards compatibility. If you are running this server
// locally, make sure to set:
// enableDnsRebindingProtection: true,
// allowedHosts: ['127.0.0.1'],
});
// Clean up transport when closed
transport.onclose = () => {
if (transport.sessionId) {
delete transports[transport.sessionId];
}
};
const server = new McpServer({
name: "example-server",
version: "1.0.0"
});
// ... set up server resources, tools, and prompts ...
// Add an addition tool
server.registerTool(
"add-http",
{
title: "Addition Tool",
description: "用于计算任意两个数字的加法,包括小数和整数。无论数字大小都可以使用此工具进行精确计算。", // 必写,使用自然语言告诉大模型,这个工具是干什么的
inputSchema: { a: z.number(), b: z.number() },
},
async ({ a, b }) => ({
content: [{ type: "text", text: String(a + b) + " 计算成功 http" }],
})
);
// Add a dynamic greeting resource
server.registerResource(
"greeting",
new ResourceTemplate("greeting://{name}", { list: undefined }),
{
title: "Greeting Resource", // Display name for UI
description: "Dynamic greeting generator",
},
async (uri, { name }) => ({
contents: [
{
uri: uri.href,
text: `Hello, ${name}!`,
},
],
})
);
// Connect to the MCP server
await server.connect(transport);
} else {
// Invalid request
res.status(400).json({
jsonrpc: '2.0',
error: {
code: -32000,
message: 'Bad Request: No valid session ID provided',
},
id: null,
});
return;
}
// Handle the request
await transport.handleRequest(req, res, req.body);
});
// Reusable handler for GET and DELETE requests
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('Invalid or missing session ID');
return;
}
const transport = transports[sessionId];
await transport.handleRequest(req, res);
};
// Handle GET requests for server-to-client notifications via SSE
app.get('/mcp', handleSessionRequest);
// Handle DELETE requests for session termination
app.delete('/mcp', handleSessionRequest);
app.listen(3000);
bash
# 构建
npm run build
# 启动
node dist/server.js
json
// cursor 相关配置
{
"mcpServers": {
// 可自定义 id
"http-add": {
"name": "http-mcp",
"type": "streamableHttp",
"isActive": true,
// 在 cherry studio 中参数名为 baseUrl
"url": "http://localhost:3000/mcp"
}
}
}