最近一段时间在玩AI编程,MCP Server是在AI编程中必须要懂的东西。
说到MCP Server 就需要先了解一下什么事MCP,在github的网站上,有对MCP规范的定义。
github地址
总的来说:MCP 是一种开放协议,它标准化了应用程序如何为 LLM 提供上下文。将 MCP 想象成 AI 应用程序的 USB-C 端口。正如 USB-C 提供了一种将设备连接到各种外围设备和配件的标准化方式一样,MCP 也提供了一种将 AI 模型连接到不同数据源和工具的标准化方式。
为什么选择 MCP?
MCP 可帮助您在 LLM 之上构建代理和复杂的工作流程。LLM 经常需要与数据和工具集成,而 MCP 提供:
- 越来越多的预构建集成,您的 LLM 可以直接插入
- 在 LLM 提供商和供应商之间切换的灵活性
- 在基础架构中保护数据的最佳实践
一般架构
MCP 的核心遵循客户端-服务器架构,其中主机应用程序可以连接到多个服务器:
- MCP 主机:希望通过 MCP 访问数据的 Claude Desktop、IDE 或 AI 工具等程序
- MCP 客户端:与服务器保持 1:1 连接的协议客户端
- MCP 服务器:轻量级程序,每个程序都通过标准化的模型上下文协议公开特定功能
- 本地数据源:MCP 服务器可以安全访问的计算机文件、数据库和服务
- 远程服务:MCP 服务器可以连接到的互联网(例如,通过 API)提供的外部系统
1. MCP Clients(客户端)
- 定位:用户直接操作的终端(如client.py)
- 功能:通过MCP协议与服务器通信发起资源请求(如user://123)或工具调用(如calculate-sum)
- 示例:开发工具、终端应用、Web前端等
2. MCP Server(服务端)
- 核心角色:架构中枢
- 核心职责:路由客户端请求到对应服务(如Remote MCP Services)协调本地数据源(Local data sources)与远程服务管理资源/工具注册(如您之前开发的calculate-sum工具)
- 协议支持:标准化MCP消息格式(JSON-RPC或类似)
3. MCP Hosts(服务宿主)
- 代表节点:Claude等AI服务
- 功能特点:提供大模型推理能力通过MCP协议暴露标准化接口支持动态资源发现(如config://app)
4. 数据与服务层
类型 | 示例 | 连接方式 |
本地数据源 | SQLite/本地文件 | MCP Server直接调用 |
远程服务 | 第三方API/云服务 | HTTP/gRPC等协议桥接 |
开发MCPServer
GitHub中提供了TypeScript SDK,通过TypeScript可以开发一个MCPServer 项目,MCP 能让应用以标准化方式为大语言模型(LLM)提供上下文。使用该 SDK 开发 MCP Server 服务步骤如下:
- 环境准备:确保已安装 Node.js 和 npm,这是运行和管理 TypeScript 项目的基础。
- 安装 SDK:在项目目录下执行npm install @modelcontextprotocol/sdk,安装 MCP TypeScript SDK,获取开发 MCP Server 所需的类、方法和工具。
- 创建基本服务器实例:在 TypeScript 文件中,引入McpServer类,创建服务器实例,设置名称和版本等基本信息。示例代码如下:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
const server = new McpServer({
name: "MyServer",
version: "1.0.0"
});
- 添加资源(Resources):资源用于向 LLM 暴露数据,类似 REST API 中的 GET 端点,提供数据且不应有复杂计算或副作用。可以添加静态或动态资源,代码如下:
// 静态资源
server.resource(
"config",
"config://app",
async (uri) => ({
contents: [{
uri: uri.href,
text: "App configuration here"
}]
})
);
// 动态资源
import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
server.resource(
"user-profile",
new ResourceTemplate("users://{userId}/profile", { list: undefined }),
async (uri, { userId }) => ({
contents: [{
uri: uri.href,
text: `Profile data for user ${userId}`
}]
})
);
- 添加提示(Prompts):提示是可复用的模板,帮助 LLM 与服务器有效交互。示例代码如下:
server.prompt(
"review-code",
{ code: z.string() },
({ code }) => ({
messages: [{
role: "user",
content: {
type: "text",
text: `Please review this code:\n\n${code}`
}
}]
})
);
- 选择传输方式并连接服务器
- stdio 传输:适用于命令行工具和直接集成。引入StdioServerTransport,创建实例并连接服务器,代码如下:
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 { InMemoryEventStore } from "@modelcontextprotocol/sdk/inMemory.js";
const app = express();
app.use(express.json());
const transports: { [sessionId: string]: StreamableHTTPServerTransport } = {};
app.post('/mcp', async (req, res) => {
const sessionId = req.headers['mcp-session-id'] as string | undefined;
let transport: StreamableHTTPServerTransport;
if (sessionId && transports[sessionId]) {
transport = transports[sessionId];
} else if (!sessionId && isInitializeRequest(req.body)) {
const eventStore = new InMemoryEventStore();
transport = new StreamableHTTPServerTransport({
sessionIdGenerator: () => randomUUID(),
eventStore,
onsessioninitialized: (sessionId) => {
transports[sessionId] = transport;
}
});
transport.onclose = () => {
if (transport.sessionId) {
delete transports[transport.sessionId];
}
};
const server = new McpServer({
name: "example-server",
version: "1.0.0"
});
await server.connect(transport);
} else {
res.status(400).json({
jsonrpc: '2.0',
error: {
code: -32000,
message: 'Bad Request: No valid session ID provided',
},
id: null,
});
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('Invalid or missing session ID');
return;
}
const transport = transports[sessionId];
await transport.handleRequest(req, res);
};
app.get('/mcp', handleSessionRequest);
app.delete('/mcp', handleSessionRequest);
app.listen(3000);
- 无会话管理(无状态)场景下,代码如下:
import express from "express";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
const app = express();
app.use(express.json());
const transport: StreamableHTTPServerTransport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined
});
const setupServer = async () => {
await server.connect(transport);
};
app.post('/mcp', async (req, res) => {
try {
await transport.handleRequest(req, res, req.body);
} catch (error) {
console.error('Error handling MCP request:', error);
if (!res.headersSent) {
res.status(500).json({
jsonrpc: '2.0',
error: {
code: -32603,
message: 'Internal server error',
},
id: null,
});
}
}
});
app.get('/mcp', async (req, res) => {
res.writeHead(405).end(JSON.stringify({
jsonrpc: "2.0",
error: {
code: -32000,
message: "Method not allowed."
},
id: null
}));
});
app.delete('/mcp', async (req, res) => {
res.writeHead(405).end(JSON.stringify({
jsonrpc: "2.0",
error: {
code: -32000,
message: "Method not allowed."
},
id: null
}));
});
const PORT = 3000;
setupServer().then(() => {
app.listen(PORT, () => {
console.log(`MCP Streamable HTTP Server listening on port ${PORT}`);
});
}).catch(error => {
console.error('Failed to set up the server:', error);
process.exit(1);
});
- 测试和调试:使用 MCP Inspector 进行测试。还可以在代码中添加日志输出,辅助调试。
- 部署服务器:完成开发和测试后,将服务器部署到生产环境,如云服务器(AWS、Google Cloud、阿里云等),并确保服务器的安全性和稳定性。 要依据 https://github.com/modelcontextprotocol/typescript-sdk 开发一个 MCP Server 服务。
通过上述步骤,就可以完成一个MCP server的开发,后续可以在一些大模型的客户端工具中进行对接了,比如Cherry Studio,Cline,Continue等编程插件