掘金 人工智能 前天 17:13
如何搭建一个MCP服务,然后在Cursor中调用,半小时,彻底掌握MCP
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文通过一个简单的TypeScript项目,手把手教你搭建MCP(Model Context Protocol)服务。该服务演示了MCP的核心概念,包括资源(Resources)、工具(Tools)和提示(Prompts),并详细介绍了如何配置、运行和测试。通过实践,快速掌握MCP的原理和应用,非常适合初学者。

💡 **项目搭建与配置**: 介绍了使用TypeScript搭建MCP服务的基本步骤,包括`package.json`和`tsconfig.json`的配置,以及核心代码的编写。通过具体的代码示例,清晰展示了如何定义资源、工具和提示。

⚙️ **核心功能实现**: 详细讲解了如何在MCP服务中实现资源、工具和提示。资源用于暴露数据,工具用于执行操作,提示用于引导LLM。文章提供了服务器状态查询、消息回显、简单计算和口号生成的示例,帮助读者理解MCP的工作原理。

🚀 **测试与应用**: 介绍了如何使用`@modelcontextprotocol/inspector`工具测试MCP服务,以及如何在Cursor中配置和使用MCP服务。通过实际操作,展示了MCP服务的应用场景和优势。

实践是最好的学习,看了很多MCP的文章,还是不懂MCP干啥的,不清楚原理,不如自己搭一个,半小时,彻底掌握MCP。

今天教大家来用typescript搭建一个简单的 MCP 服务,纯粹用于演示功能

这个案例不解决任何实际问题,只为直观地展示MCP的资源 (Resources)工具 (Tools)提示 (Prompts) 这三个核心概念。

实现下面几个功能:

一、项目搭建

1、项目设置 package.json

{  "name": "simple-mcp-demo-server",  "version": "1.0.0",  "type": "module",  "scripts": {    "build": "tsc",    "start": "node --experimental-specifier-resolution=node build/index.js"  },  "dependencies": {    "@modelcontextprotocol/sdk": "^1.12.1",    "zod": "^3.25.53"  },  "devDependencies": {    "@types/node": "^20.17.58",    "typescript": "^5.8.3"  }}

2、 TypeScript 配置 tsconfig.json

{  "compilerOptions": {    "target": "ES2022",    "module": "Node16",    "moduleResolution": "Node16",    "outDir": "./build",    "rootDir": "./src",    "strict": true,    "esModuleInterop": true,    "skipLibCheck": true,    "forceConsistentCasingInFileNames": true  },  "include": ["src/**/*"],  "exclude": ["node_modules"]} 

3、核心代码 (src/index.ts)

import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";import { z } from "zod"; // 用于定义工具和提示的输入验证schema// 1. 创建MCP服务器实例// McpServer 是您与MCP协议的核心接口。const server = new McpServer({    name: "Demo互动服务器", // 服务器的名称    version: "1.0.0",         // 服务器的版本    capabilities: {           // 声明服务器支持的能力,使客户端可以发现这些功能        resources: {},        tools: {},        prompts: {},    },});// 2. 暴露一个只读的“资源 (Resource)”// 资源用于向大型语言模型(LLM)暴露数据和内容,类似于Web API中的GET请求。// 它们不应执行显著的计算或产生副作用。server.resource(    "status", // 资源的内部名称    "resource://status/server", // 资源的URI模板,这是一个固定URI,表示服务器状态    async (uri) => {        // 当客户端请求此资源时,返回预设的服务器状态信息。        return {            contents: [                {                    uri: uri.href,                    text: "服务器运行正常,无已知错误。自上次启动以来已处理 100 次请求。",                    mimeType: "text/plain"                },            ],        };    });console.error("已注册资源: resource://status/server"); // 使用console.error进行日志输出,避免干扰stdio传输// 3. 提供两个“工具 (Tool)”// 工具允许LLM通过服务器执行操作,预期会执行计算并产生副作用。// 工具调用通常需要用户的批准。// 工具 A: 消息回显器server.tool(    "echo_message", // 工具的内部名称    "回显您输入的任何消息", // 对人类友好的描述    {        // 输入参数的Zod schema,Zod是一个用于schema验证的库。        message: z.string().describe("要回显的文本消息"), // 消息内容    },    async ({ message }) => {        // 工具的执行逻辑        return {            content: [                {                    type: "text",                    text: `你说了:${message}`, // 返回原样消息,前面加上“你说了:”                },            ],        };    });console.error("已注册工具: echo_message");// 工具 B: 简单数字计算器server.tool(    "simple_calculation", // 工具的内部名称    "执行两个数字之间的简单加减乘除运算", // 对人类友好的描述    {        a: z.number().describe("第一个数字"),        b: z.number().describe("第二个数字"),        operator: z.enum(["+", "-", "*", "/"]).describe("要执行的运算(+, -, *, /)"),    },    async ({ a, b, operator }) => {        let result: number | string;        try {            switch (operator) {                case "+":                    result = a + b;                    break;                case "-":                    result = a - b;                    break;                case "*":                    result = a * b;                    break;                case "/":                    if (b === 0) {                        return { // 如果除数为零,返回错误信息                            content: [{ type: "text", text: "错误:除数不能为零。" }],                            isError: true, // 标记为错误结果                        };                    }                    result = a / b;                    break;                default:                    return {                        content: [{ type: "text", text: "错误:不支持的运算符。" }],                        isError: true,                    };            }            return {                content: [                    {                        type: "text",                        text: `结果:${result}`, // 返回计算结果                    },                ],            };        } catch (error: any) {            return {                content: [{ type: "text", text: `执行计算时发生错误: ${error.message}` }],                isError: true,            };        }    });console.error("已注册工具: simple_calculation");// 4. 定义一个“提示 (Prompt)”// 提示是可重用的模板,旨在帮助LLM与服务器有效互动,通常由用户控制,以UI元素(如斜杠命令)的形式呈现。server.prompt(    "generate_slogan", // 提示的内部名称    "为给定主题生成一句口号", // 对人类友好的描述    {        theme: z.string().describe("口号的主题,例如“环保”或“创新”"),    },    ({ theme }) => {        // 返回一个消息数组,指导LLM如何生成口号        return {            messages: [                {                    role: "user", // 消息角色,这里模拟用户对LLM的请求                    content: {                        type: "text",                        text: `请为“${theme}”主题生成一句有创意且吸引人的口号。要求口号简短有力,易于传播。`,                    },                },            ],        };    });console.error("已注册提示: generate_slogan");// 5. 启动服务器并连接到Stdio传输// Stdio传输通过标准输入/输出来通信,适用于本地进程和命令行工具。async function main() {    const transport = new StdioServerTransport();    await server.connect(transport);    console.error("MCP演示服务器已成功启动,并监听Stdio传输。"); // 使用console.error避免干扰Stdio通信}// 捕获可能发生的致命错误main().catch((error) => {    console.error("服务器启动时发生致命错误:", error);    process.exit(1); // 以非零退出码表示错误});

4、运行:

    保存文件:

将上述 package.jsontsconfig.json 保存到您的项目根目录。

在项目根目录下创建 src 文件夹,并将 src/index.ts 文件保存到其中。

    安装依赖:

打开终端或,导航到项目根目录,然后运行:

⚠️node版本用最新的24

npm install

3. 编译 TypeScript 代码:

npm run build 

这将在 build 文件夹中生成 index.js 文件。

    运行服务器:
npm start

服务器将启动,并打印启动日志

二、测试

使用 npx @modelcontextprotocol/inspector 来测试这个服务器,这是一个用于测试MCP服务器的工具。

    另一个终端中,运行:

npx @modelcontextprotocol/inspector node --experimental-specifier-resolution=node build/index.js    

等待服务器启动

用浏览器打开http://127.0.0.1:6274

然后点击connect连接到我们的MCP服务

左下角显示MCP服务的日志:

顶部功能区,可以用来测试每个功能。

还记得我们实现的几个功能吗?切换按钮,就可以看到我们的服务提供哪些功能。

    Resources资源

资源用于向大模型暴露数据,一般不执行计算和任务。

我们写一个显示服务器运行状态的资源,点击Stats,可以看到右侧返回了服务器状态。

3、 Prompts提示词

提示词是可复用的模板,帮用户快速生成提示词。

例如我们输入“紧跟时代步伐”,就会输出我们设定的提示词模板。

4、Tools工具

工具允许LLM通过服务器执行操作,帮助运行任务。

5、输出MCP配置

点击Server Entry 就会复制配置

复制出来的配置长这样:

包含命令,参数,环境变量等。我们主要用到的是命令和参数。

三、如何在Cursor中配置MCP

    添加MCP

起个名称叫“mcp-demo”,将下面的配置复制进去

注意build/index.js要换成完整的文件路径,可以参考我的,但是要换成你自己的。

"mcp-demo": {  "command": "node",  "args": [    "--experimental-specifier-resolution=node",    "***/build/index.js"  ]}

2、启动

添加完成之后,可以看到,服务器亮起小绿点。就说明成功了。

不过cursor只支持工具,其他两个MCP功能不支持。

3、使用

直接在cursor的对话框说:使用simple_calculation计算1+2

之后会有一个Run tools的提示,点击运行就会调用MCP工具。

就这样,下课。

如果对你有帮助,别忘了一键三连🐶

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

MCP TypeScript 模型上下文协议 开发
相关文章