MCP到底解决了什么问题
如果你用过Claude Desktop、Cursor或者Windsurf,大概率已经接触过MCP了——当你让AI帮你查文件、搜代码、操作数据库时,背后跑的就是MCP Server。但大多数教程停留在"装个现成Server跑通demo"的阶段,真正动手从零写一个MCP Server的人少之又少。原因很简单:官方文档虽然完整,但缺少一条从需求分析到生产部署的完整路径。
我最近为公司内部AI助手开发了三个MCP Server——分别对接内部Wiki、JIRA工单和PostgreSQL数据查询,踩了不少坑。这篇文章把实战中最关键的经验整理出来,帮你避开我走过的弯路。
一、MCP协议核心概念:别被架构图吓到
MCP(Model Context Protocol)的本质就一句话:让AI模型能像调用函数一样调用外部工具。它定义了一套标准化的JSON-RPC 2.0通信协议,让任何LLM客户端都能用统一的方式发现和调用你的工具。
1.1 四个核心原语
| 原语 | 方向 | 作用 | 典型场景 |
|---|---|---|---|
| Tools | Server→Client | 暴露可调用的函数 | 查询数据库、发送通知 |
| Resources | Server→Client | 暴露可读取的数据 | 文件内容、API响应 |
| Prompts | Server→Client | 提供预设Prompt模板 | 代码审查模板、报告生成 |
| Sampling | Client→Server | Server请求模型推理 | 多步工具链中的中间决策 |
90%的MCP Server只需要实现Tools就够了。Resources和Prompts是锦上添花,Sampling在实际项目中几乎用不到。
1.2 通信方式选择
- stdio:最简单,Server作为子进程启动,通过标准输入输出通信。适合本地桌面应用(Claude Desktop、Cursor)
- SSE(Server-Sent Events):基于HTTP的长连接,适合远程部署和Web场景
建议:开发阶段用stdio快速调试,生产环境切SSE。两者只是传输层差异,业务逻辑完全复用。
二、从零搭建第一个MCP Server
我们用一个实际需求驱动:给AI助手提供查询公司内部知识库的能力。这是一个典型的Tools场景。
2.1 项目初始化
mkdir wiki-mcp-server && cd wiki-mcp-server npm init -y npm install @modelcontextprotocol/sdk zod
用TypeScript开发体验更好,但这里用纯JS保持简洁。zod是MCP SDK的依赖,用于参数校验。
2.2 最小可用Server代码
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({
name: "wiki-search",
version: "1.0.0",
});
// 注册工具:搜索Wiki
server.tool(
"search_wiki", // 工具名
"搜索公司内部Wiki知识库,返回匹配的文档标题和摘要", // 描述(AI靠这个理解工具用途)
{
query: z.string().describe("搜索关键词"),
limit: z.number().optional().describe("返回结果数量,默认5"),
},
async ({ query, limit = 5 }) => {
// 实际项目中这里调你的Wiki API
const results = await fetchWikiResults(query, limit);
return {
content: [
{
type: "text",
text: JSON.stringify(results, null, 2),
},
],
};
}
);
// 注册工具:获取Wiki文档详情
server.tool(
"get_wiki_page",
"根据文档ID获取Wiki页面完整内容",
{
page_id: z.string().describe("Wiki文档ID"),
},
async ({ page_id }) => {
const page = await fetchWikiPage(page_id);
return {
content: [
{
type: "text",
text: page.content,
},
],
};
}
);
// 启动Server
const transport = new StdioServerTransport();
await server.connect(transport);关键点:工具描述是给AI看的,写得越精准,AI调用时选错工具的概率越低。我见过有人写"搜索"两个字的描述,结果AI在任何场景都调这个工具。
2.3 模拟数据实现
async function fetchWikiResults(query, limit) {
// 生产环境替换为真实API调用
const mockData = [
{ id: "doc-001", title: "新人入职指南", snippet: "包含账号申请、环境配置..." },
{ id: "doc-002", title: "API网关配置规范", snippet: "路由规则、限流策略..." },
{ id: "doc-003", title: "数据库变更流程", snippet: "DDL审核、灰度发布..." },
];
return mockData.filter(d =>
d.title.includes(query) || d.snippet.includes(query)
).slice(0, limit);
}
async function fetchWikiPage(pageId) {
return {
id: pageId,
title: "示例文档",
content: "这是文档正文内容...",
updatedAt: "2026-05-28",
};
}三、客户端配置:让AI发现你的Server
Server写好了,还需要在LLM客户端配置才能被调用。不同客户端的配置方式:
3.1 Claude Desktop配置
编辑 ~/AppData/Roaming/Claude/claude_desktop_config.json(Windows)或 ~/Library/Application Support/Claude/claude_desktop_config.json(macOS):
{
"mcpServers": {
"wiki-search": {
"command": "node",
"args": ["C:/path/to/wiki-mcp-server/index.mjs"]
}
}
}3.2 Cursor配置
在Cursor设置中找到MCP选项,添加Server配置。Cursor也支持SSE模式连接远程Server,适合团队共享。
3.3 OpenClaw Agent配置
如果你使用OpenClaw,可以在Agent的配置文件中添加MCP Server,让Agent自动发现和调用工具。具体可参考OpenClaw Agent实战指南。
四、进阶:生产级MCP Server的五个必备能力
Demo跑通只是起点,上生产还需要补齐这些能力:
4.1 认证与鉴权
MCP协议本身不包含认证机制,但你可以通过环境变量注入Token:
server.tool(
"search_wiki",
"搜索Wiki知识库",
{ query: z.string() },
async ({ query }) => {
const token = process.env.WIKI_API_TOKEN;
if (!token) {
return {
content: [{ type: "text", text: "错误:未配置WIKI_API_TOKEN" }],
isError: true,
};
}
// 带Token调用真实API
const res = await fetch("https://wiki.example.com/api/search", {
headers: { Authorization: `Bearer ${token}` },
});
// ...
}
);4.2 错误处理与超时
外部API随时可能挂掉,必须有超时和降级策略:
async function fetchWithTimeout(url, options = {}, timeout = 10000) {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), timeout);
try {
const res = await fetch(url, { ...options, signal: controller.signal });
return res;
} finally {
clearTimeout(timer);
}
}4.3 参数校验的边界情况
zod能做基础校验,但业务逻辑校验需要自己写。比如搜索关键词长度限制、SQL注入防护等:
server.tool(
"query_database",
"执行只读SQL查询",
{
sql: z.string().describe("SELECT语句"),
},
async ({ sql }) => {
// 安全校验:只允许SELECT
const normalized = sql.trim().toUpperCase();
if (!normalized.startsWith("SELECT")) {
return {
content: [{ type: "text", text: "仅允许SELECT查询" }],
isError: true,
};
}
// 防止多语句注入
if (sql.includes(";")) {
return {
content: [{ type: "text", text: "不允许包含分号" }],
isError: true,
};
}
// 执行查询...
}
);4.4 结果分页
AI模型的上下文窗口有限,一次返回太多数据反而降低效果。我的经验:单次工具返回内容控制在2000字以内,超过就分页。
4.5 日志与可观测性
给每个工具调用加上日志,方便排查问题:
server.tool(
"search_wiki",
"搜索Wiki",
{ query: z.string() },
async ({ query }) => {
const startTime = Date.now();
console.error(`[MCP] search_wiki called: query="${query}"`);
try {
const results = await fetchWikiResults(query, 5);
console.error(`[MCP] search_wiki done: ${results.length} results in ${Date.now() - startTime}ms`);
return { content: [{ type: "text", text: JSON.stringify(results) }] };
} catch (err) {
console.error(`[MCP] search_wiki failed: ${err.message}`);
return {
content: [{ type: "text", text: `查询失败: ${err.message}` }],
isError: true,
};
}
}
);注意:MCP的stdio模式下,console.log会干扰协议通信,日志必须用console.error输出到stderr。
五、SSE模式部署:让团队共享一个Server
当你的MCP Server需要被多人使用时,stdio模式(每客户端一个进程)就不合适了。切换到SSE模式:
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
import express from "express";
const app = express();
app.get("/sse", async (req, res) => {
const transport = new SSEServerTransport("/messages", res);
await server.connect(transport);
});
app.post("/messages", async (req, res) => {
// SSE transport自动处理
});
app.listen(3001, () => {
console.error("MCP Server running on http://localhost:3001/sse");
});客户端配置改为SSE URL即可:"url": "http://your-server:3001/sse"
部署建议
- 用Docker容器化部署,方便扩缩容
- 加一层Nginx做反向代理和TLS终止
- 如果SSE连接受防火墙限制,考虑用WebSocket中继
- 加上健康检查端点:
GET /health返回200
六、常见坑与解决方案
| 问题 | 原因 | 解决 |
|---|---|---|
| Server启动后客户端找不到工具 | 配置文件路径错误或JSON格式问题 | 检查JSON语法,用绝对路径 |
| 工具调用返回空结果 | 工具描述模糊,AI传了错误参数 | 细化zod的describe,加示例 |
| stdio模式客户端卡死 | console.log污染了MCP通道 | 所有日志改用console.error |
| SSE连接频繁断开 | Nginx/proxy超时配置过短 | 设置proxy_read_timeout 300s |
| 工具返回内容AI无法理解 | 返回格式非结构化 | 统一返回JSON,加字段说明 |
总结
MCP Server的开发并不复杂——核心就是"定义工具+处理输入+返回结构化结果"三步。但生产级部署需要补齐认证、错误处理、日志、超时控制等工程能力。从stdio本地调试起步,验证通过后再切SSE做团队共享,这是最稳妥的路径。
我的实践建议:先从一个最简单的只读工具开始(比如查询类),跑通后再加写入类工具。写操作一定要加确认机制——AI调用删除工具时没有"你确定吗"的弹窗,后果自负。更多AI自动化实战,可参考OpenClaw本地部署指南和AI OCR免安装教程。
版权声明
本文仅代表个人观点。
本文系AI辅助作者原创,未经许可,转载请保留原文链接。

发表评论