顾乔芝士网

持续更新的前后端开发技术栈

开发一个自己的MCPServer

最近一段时间在玩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 服务步骤如下:

  1. 环境准备:确保已安装 Node.js 和 npm,这是运行和管理 TypeScript 项目的基础。
  2. 安装 SDK:在项目目录下执行npm install @modelcontextprotocol/sdk,安装 MCP TypeScript SDK,获取开发 MCP Server 所需的类、方法和工具。
  3. 创建基本服务器实例:在 TypeScript 文件中,引入McpServer类,创建服务器实例,设置名称和版本等基本信息。示例代码如下:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
const server = new McpServer({
    name: "MyServer",
    version: "1.0.0"
});
  1. 添加资源(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}`
        }]
    })
);
  1. 添加提示(Prompts):提示是可复用的模板,帮助 LLM 与服务器有效交互。示例代码如下:
server.prompt(
    "review-code",
    { code: z.string() },
    ({ code }) => ({
        messages: [{
            role: "user",
            content: {
                type: "text",
                text: `Please review this code:\n\n${code}`
            }
        }]
    })
);
  1. 选择传输方式并连接服务器
  • 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);
});
  1. 测试和调试:使用 MCP Inspector 进行测试。还可以在代码中添加日志输出,辅助调试。
  2. 部署服务器:完成开发和测试后,将服务器部署到生产环境,如云服务器(AWS、Google Cloud、阿里云等),并确保服务器的安全性和稳定性。 要依据 https://github.com/modelcontextprotocol/typescript-sdk 开发一个 MCP Server 服务。

通过上述步骤,就可以完成一个MCP server的开发,后续可以在一些大模型的客户端工具中进行对接了,比如Cherry Studio,Cline,Continue等编程插件

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言