
MCP 解决了什么问题
在 MCP 出现之前,每个 LLM 客户端(Claude Desktop、Cursor、Cline)都要自己实现”调用 GitHub / Postgres / 内部 SaaS”的胶水代码。同一份 GitHub PR 列表查询逻辑,Claude 写一遍,Cursor 再写一遍,内部 Web Chat 还要再写一遍,改起来三方同步痛苦。MCP(Model Context Protocol)把这层胶水抽象成统一的 JSON-RPC 协议,服务端只要实现一次,所有兼容客户端都能用。
协议本身不复杂,核心是三件事——Resources(只读数据,比如返回一份文件内容)、Tools(可调用函数,带 JSON schema)、Prompts(可复用的提示词模板)。客户端通过 stdio 或 SSE 连接到 MCP server,声明自己暴露的 capability 列表。Anthropic 在 2025 年开源协议,2026 年已经是 Cursor、Claude Desktop、Cline、Continue、Zed 等主流客户端的事实标准,连 OpenAI 也在 5 月宣布兼容 MCP。
15 分钟实现一个 MCP server
用官方 Python SDK 写一个最小的”公司内部 Wiki 搜索” server:
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent
import httpx
server = Server("internal-wiki")
@server.list_tools()
async def list_tools():
return [Tool(
name="search_wiki",
description="在公司 Wiki 中按关键词搜索,返回 top 5 文档的标题与摘要",
inputSchema={
"type": "object",
"properties": {"q": {"type": "string"}},
"required": ["q"]
}
)]
@server.call_tool()
async def call_tool(name: str, arguments: dict):
if name != "search_wiki":
raise ValueError(name)
async with httpx.AsyncClient() as cli:
r = await cli.get("https://wiki.corp/api/search",
params={"q": arguments["q"]}, timeout=5)
items = r.json()["items"][:5]
return [TextContent(type="text",
text="\n".join(f"- {i['title']}: {i['snippet']}" for i in items))]
if __name__ == "__main__":
import asyncio
asyncio.run(stdio_server(server).run())
写到这,服务端就完成了。客户端配置里加一行 "internal-wiki": {"command": "python", "args": ["wiki_server.py"]},Claude Desktop 启动后就能直接调用这个工具,Cursor 配 .cursor/mcp.json 同理。Claude 在对话中识别到”搜一下 Wiki”之类的意图,会自动触发 search_wiki,把返回的列表作为上下文。
进阶:暴露 Resources 和 Prompts
光有 Tools 之外,把”内部知识库”做成 Resources 更省 token:模型先 resources/list 拿到可读资源列表,需要时再 resources/read 拉具体内容,避免每次都触发 LLM 工具调用的 token 消耗。例如:
@server.list_resources()
async def list_resources():
return [Resource(
uri="wiki://team/frontend",
name="前端团队 Wiki 索引",
mimeType="text/markdown"
)]
@server.read_resource()
async def read_resource(uri: str):
if uri == "wiki://team/frontend":
return ReadResourceContents(
contents=[TextResourceContents(uri=uri, mimeType="text/markdown",
text="# 前端 Wiki 索引\n...")]
)
Prompts 适合做”标准操作流程”——比如让模型按公司规范的 PR 描述模板生成文字,prompt 里写好骨架,用户只填参数即可。
踩过的三个真实坑
- SSE 长连接断线:Web 部署的 MCP server 默认 SSE,反向代理(Nginx)默认 60s 切掉,会出现工具调用偶尔”消失”。需要把
proxy_read_timeout调到 3600s,并在客户端开启retry_on_disconnect。Cloudflare 默认 100s 也会切,自部署请直接用 Nginx。 - Schema 校验过于严格:
inputSchema一定要带"additionalProperties": false,否则 Anthropic 的工具调用器会拒绝所有带多余字段的请求,即使这些字段在业务上是合法的”扩展位”。 - stdio 模式的 stdout 污染:任何
print()都会破坏 JSON-RPC 帧。务必把所有日志走logging写文件,或显式sys.stderr。这个坑在调试时最难发现,因为 Python 静默退出但 Claude Desktop 不报错。
什么时候值得自建 MCP server
经验判断标准:同一个内部 API 出现在两个以上的 LLM 客户端(Claude Desktop + Cursor + 内部 Web Chat)里,且每月调用超过 1 万次。这种情况下,自建 MCP server 一天能省下 2-3 个工程师维护胶水代码的时间。如果只是”偶尔用一下”,直接 prompt 里贴 curl 反而更省事。TypeScript/Go SDK 已经稳定,大型企业更适合用 Go 重写以避免 GIL 问题,生产环境记得加 Prometheus exporter 监控每个工具的调用延迟和失败率。
安全相关的细节
MCP server 通常会接触内部系统,几个安全细节不能省:
- 权限最小化:每个工具只暴露”读”接口,任何”写”操作(改文件、删数据、调 API)走单独鉴权。Sonnet 4.5 对工具描述非常敏感,模糊的
description可能导致模型在错误场景下调用,务必把”这个工具能做什么、不能做什么”写清楚。 - 输入校验:即使 Anthropic 端会校验 schema,服务端也要自己校验一遍,LLM 可能给出符合 schema 但语义错误的参数(例如”删除文件”工具的
path是"../etc/passwd")。 - 速率限制:一个 Sonnet 4.5 客户端可以在 1 秒内触发几十次工具调用,如果后端是数据库或限流 API,务必在 MCP server 这一层加 per-token 限流,避免雪崩。
- 审计日志:所有调用都写日志,包括调用方、参数、返回、耗时,事后追溯”AI 为什么做了这件事”全靠这个。
生态现状与推荐
截至 2026 年 8 月,主流客户端(Claude Desktop、Cursor、Cline、Continue、Zed、Windsurf)都已支持 MCP,服务端生态也在快速扩张:官方 SDK 支持 Python/TypeScript/Go/Rust,社区 SDK 覆盖 Java/Kotlin/Swift。已有的高质量 server 包括 GitHub、Postgres、Notion、Slack、Linear、Puppeteer、Filesystem 等,直接 npx @modelcontextprotocol/server-github 就能跑起来。
推荐路径:先用社区 server 跑通一个工作流(比如”让 Cursor 读 GitHub Issue + 改代码”),再考虑自建。盲目自建往往会重复造轮子,等到现有 server 满足不了需求时再写,会更聚焦。



