实践是最好的学习,看了很多MCP的文章,还是不懂MCP干啥的,不清楚原理,不如自己搭一个,半小时,彻底掌握MCP。
今天教大家来用typescript搭建一个简单的 MCP 服务,纯粹用于演示功能。
这个案例不解决任何实际问题,只为直观地展示MCP的资源 (Resources) 、工具 (Tools) 和 提示 (Prompts) 这三个核心概念。
实现下面几个功能:
-
一个资源:用于查询服务器的运行状态(假的)。
-
两个工具:一个用于回显消息,另一个用于执行简单的数学计算。
-
一个提示:用于引导LLM生成口号。
一、项目搭建
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.json
和 tsconfig.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
-
打开Cursor Setting
-
点击添加MCP server
起个名称叫“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工具。
就这样,下课。
如果对你有帮助,别忘了一键三连🐶