MCP协议在2026年7月28日经历最大版本更新,大量旧教程已过时;本文用当前SDK从头构建一个含两个工具的MCP服务器,并可在Claude Code中直接使用。
如何构建你的第一个 MCP Server:面向开发者的分步指南(2026 年)
使用当前 2026 年 SDK 构建一个可运行的 MCP server,在 MCP Inspector 中测试,并将其连接到 Claude Code。文章涵盖其他开发者常犯的错误。
如果你去年跟着某篇 MCP 教程走,但现在代码和官方文档对不上了,那你的设置大概率没问题。2026 年 7 月 28 日协议进行了最大幅度的修订,服务器的写法也随之改变。许多旧教程教的还是上一个版本。
本指南从零开始用当前 SDK 构建一个小型 MCP server。最终你将得到两个工具,可以在 MCP Inspector 中测试,也可以连接到 Claude Code。如果已经安装了 Node.js,基本的 server 大约十五分钟就能跑起来。如果还要先搭建环境或首次配置 Claude Code,则需要更长时间。
本文档的编写方式:实现部分遵循当前 MCP 文档和 SDK 示例。故障排除部分也参考了已实际构建过 MCP server 的开发者报告。这些引用在正文中已注明,参考文献列于文末。
MCP server 是一个程序,它通过一个标准接口向 AI 应用提供工具访问权限,还可以选择提供数据和 prompt 模板。AI 应用是客户端。客户端读取你的工具列表并将其传给模型。当模型判断需要某个工具时,客户端发送一个 tools/call 请求,你的 server 执行工作,结果返回。模型从不直接与你的 server 通信。
每个工具都有名称、描述和输入 schema。模型根据描述判断你的工具是否适合当前任务。描述模糊会导致工具选择不佳,所以要真正花时间写好描述。
当前协议版本为 2026-07-28。其核心变化是无状态协议。据官方发布公告,initialize 握手和 Mcp-Session-Id 请求头已被废弃,每个请求现在都自带协议版本和客户端详情。
这对你的影响体现在两个方面。
SDK 包结构变了。 TypeScript SDK 的 v2 版将单一的 @modelcontextprotocol/sdk 包拆分为多个独立包,包括 @modelcontextprotocol/server 和 @modelcontextprotocol/client。Python SDK 的 v2 版将 FastMCP 重命名为 MCPServer。如果某个教程导入了 @modelcontextprotocol/sdk 或 mcp.server.fastmcp,那它用的是旧版 SDK API。在跟着教程走之前,先确认它支持哪个协议版本。
状态迁移到工具内部。 无状态协议核心并不意味着你的应用也必须是无状态的。它只是意味着现代协议不再依赖传输层会话来在请求之间携带状态。如果你的 server 需要在调用之间保持某些状态,维护者建议从一个工具返回显式句柄(比如 job ID),然后让模型将其作为参数传回。下面的远程托管一节有示例演示。
一个名为 blog-helper 的 server,包含两个工具。reading-time 估算一段文本的阅读时长。make-slug 将文章标题转换为 URL slug。
这些是刻意设计的玩具工具。不需要 API key,不需要网络访问,且它们覆盖了每个工具都会做的两件事:接收结构化输入并返回文本。如果出了问题,原因一定在你的 MCP 配置上,而不是第三方服务。
你需要 Node.js 22.19 或更高版本。TypeScript SDK 支持 Node.js 20 及以上,但步骤 3 中使用的 MCP Inspector 需要 Node.js 22.19.0 或更新版本,所以安装这一个版本即可覆盖整个指南。SDK 仅提供 ES modules,因此项目必须设置 type: module。tsx 包可以直接运行 TypeScript,所以不需要构建步骤。
mkdir blog-helper && cd blog-helper
npm init -y
npm pkg set type=module
npm install @modelcontextprotocol/server zod tsx
mkdir src
创建 src/index.ts 并粘贴以下代码。
import { McpServer } from '@modelcontextprotocol/server';
import { serveStdio } from '@modelcontextprotocol/server/stdio';
import * as z from 'zod/v4';
function createServer(): McpServer {
const server = new McpServer({ name: 'blog-helper', version: '1.0.0' });
server.registerTool(
'reading-time',
{
description:
'Estimate how many minutes a piece of text takes to read. Use it for blog posts and articles.',
inputSchema: z.object({
text: z.string().min(1).describe('The full text to measure'),
wordsPerMinute: z
.number()
.int()
.min(100)
.max(400)
.optional()
.describe('Reading speed. Defaults to 220.')
})
},
async ({ text, wordsPerMinute }) => {
const words = text.trim().split(/\s+/).length;
const minutes = Math.max(1, Math.round(words / (wordsPerMinute ?? 220)));
return {
content: [{ type: 'text', text: `${words} words, about ${minutes} min read` }]
};
}
);
server.registerTool(
'make-slug',
{
description: 'Turn a post title into a clean, lowercase URL slug.',
inputSchema: z.object({
title: z.string().min(1).max(200).describe('The post title')
})
},
async ({ title }) => {
const slug = title
.toLowerCase()
.normalize('NFKD')
.replace(/[^\w\s-]/g, '')
.trim()
.replace(/[\s_-]+/g, '-');
return { content: [{ type: 'text', text: slug }] };
}
);
return server;
}
void serveStdio(createServer);
console.error('blog-helper MCP server running on stdio');
三个细节值得细看。
inputSchema 中的 Zod schema 是你唯一需要写的 schema。SDK 用它向客户端描述工具的输入,并在你的 handler 运行之前拒绝无效参数,所以两个 handler 中都不需要手动验证。
server 在 createServer 函数内部构建,因为 serveStdio 调用它来创建服务于连接的实例。保持这个函数轻量:构造 server 并注册其工具,避免在连接启动之前进行慢速网络调用或昂贵设置。
最后一行故意用 console.error 记录日志。在 stdio server 上,stdout 承载协议消息,所以诊断输出应该发到 stderr。零散的 console.log 会向 stdout 写入非协议数据,可能破坏 JSON-RPC 流。好几位开发者报告过遇到这个问题,他们的经历出现在文章后面的内容中。
npx tsx src/index.ts
它打印横幅后进入空闲状态。这是预期的,因为 stdio server 会等待 stdin,直到有客户端开始通信。
要调用工具,请使用 MCP Inspector,这是一个本地 Web 应用,它启动你的命令并通过 stdio 连接。
npx @modelcontextprotocol/inspector npx tsx src/index.ts
命令打印出一个包含一次性令牌的 URL,用于打开 Inspector 的 Web 界面。这是 Inspector 的访问令牌,不是旧版协议使用的 Mcp-Session-Id。在浏览器中打开该 URL,点击 Connect,打开 Tools 选项卡,选择 make-slug 并输入一个标题。然后用空字符串调用 reading-time。schema 要求至少一个字符,所以调用会在你的 handler 运行之前被拒绝。这是一个快速查看 MCP 输入验证的方式。
作为参考,对于有效输入,上述代码应产生如下结果:
make-slug "My First MCP Server in 2026" -> my-first-mcp-server-in-2026
reading-time "Hello world" -> 2 words, about 1 min read
DEV Community 帖子"How I Built My First MCP Server for Claude Code"的作者(yureki_lab)写道,他们在最初几个小时是通过向 Claude 提问并判断答案是否合理来调试的。转到 Inspector 之后,他们可以直接看到 server 返回的内容,中间没有模型。在连接任何客户端之前,先单独测试 server 是值得的。
注册一个本地 stdio server 只需一条命令。双破折号后面的所有内容是 Claude Code 运行以启动 server 的命令。使用入口文件的绝对路径,这样从任何文件夹都能工作。
claude mcp add blog-helper -- npx tsx /full/path/to/blog-helper/src/index.ts
然后运行 claude mcp list 确认 server 显示为已连接。如果失败,先检查路径。在 Windows 上,按你的 shell 期望的格式书写路径。
如果你使用 Python,v2 SDK 从带装饰器的函数构建服务器。需要 Python 3.10 或更高版本。用 uv 初始化项目:
uv init blog-helper && cd blog-helper
uv add "mcp[cli]"
然后创建 server.py:
from mcp.server.mcpserver import MCPServer
mcp = MCPServer("blog-helper")
@mcp.tool()
def reading_time(text: str, words_per_minute: int = 220) -> str:
"""Estimate how many minutes a piece of text takes to read."""
words = len(text.split())
minutes = max(1, round(words / words_per_minute))
return f"{words} words, about {minutes} min read"
if __name__ == "__main__":
mcp.run(transport="stdio")
类型提示成为输入 schema,文档字符串成为工具描述。Python v2 是 SDK 的重大重写,如果这里的示例在你的版本上表现不同,请查看其迁移指南。pip install mcp 现在安装的是 2.x 系列,因此如果你维护旧代码且尚未准备好迁移,请在依赖的上限版本上指定 <2。
官方教程展示的都是理想情况。以下报告来自构建、测试或发布过 MCP 服务器的开发者,每条都标注了作者。
多位开发者报告了同一个问题。yureki_lab 构建了一个服务器供 Claude Code 查询内部服务目录,他写道,一条多余的 console.log 导致大约 40 分钟的损失,并产生了神秘的解析错误。brianmello 的服务器从一个周末项目发展到大约 2,300 次 npm 下载,他将 stdout 称为第一个"惊喜",并补充说多余的输出可能来自依赖树深处,不一定是你自己的代码。两者最终都将所有诊断信息发送到 stderr。
官方 MCP 调试文档对本地服务器给出了相同的规则,并指出主机应用程序会捕获你写入 stderr 的内容。在 Windows 上,Claude Desktop 将日志写入 %APPDATA%\Claude\logs,这是连接失败时首先应该查看的地方。
Jaypee 的 MCP 连接错误排查指南指出,服务器配置中的相对路径是相对于客户端的工作目录解析的,而不是你的服务器。因此,一个在终端中可以工作的服务器在 Claude Desktop 内部可能会失败,解决方案是使用绝对路径。
同一指南还列出了其他几个陷阱。服务器可能成功连接但暴露零个工具,如果其输入 schema 格式不正确的话。一个需要 API 密钥的服务器通常在第一次调用工具时失败,而不是在连接时。失败的 npx 下载看起来与其他任何连接错误无异,所以在责怪客户端之前,先在终端中手动运行服务器命令。
thunderbit-mcp 团队(作者 handle 为 ethan_thunderbit)写道,第一个冲动是将现有 API 的每个端点都包装为一个工具。他们的实战指南主张更少、更可预测的工具,并建议在描述中添加负面指导,即告诉模型何时不使用某个工具的一行文字。他们还描述了 stdio 作为第一个传输协议的优点——最简单,因为用户只需安装包并添加配置条目。
brianmello 用一句话给出了相关建议:把工具描述写成提示词的样子,因为模型会把它们当作提示词来阅读。
parkerrrrr 观察到,当工具抛出异常时,智能体通常只看到一条通用的内部错误,无法区分认证失败、参数错误还是上游服务故障。在处理器中捕获错误并返回包含 isError: true 的清晰消息,可以让模型看到哪里出了问题并做出合理的反应。
同一作者警告说,一个关闭了认证的 HTTP MCP 服务器正是那种会被发现和滥用的默认配置。如果你要超越 stdio,需要 bearer token 或 OAuth,并让服务器在未配置 token 时拒绝请求。
krlz 在 DEV Community 上撰写关于新版本的文章,预计最大的破坏将来自 elicitation,即服务器在调用过程中向用户提问。由于背通道(back-channel)已经消失,依赖它的代码必须围绕新的多轮交互模式重写。
官方发布公告中包含来自采用者的评论。Supabase 的产品负责人表示,新模式使其工具能够在执行操作前向用户确认,例如在创建付费项目之前。Manufact 报告说,将其 mcp-use 框架迁移到新 SDK 后,包体积减少了约 83%,速度提高了约 25%,这得益于客户端和服务端的分离。Honeycomb 表示,智能体现在已占据其每月交互式查询的近 20%。这些是供应商报告的数据,因此应将其视为生态系统发展方向的风向标,而非独立基准。
stdio 服务器运行在你自己的机器上,由使用它的应用程序启动。要提供一个许多人可以连接的端点,需要通过 HTTP 提供相同的服务器。
无状态协议核心使这变得容易得多。对于使用 2026-07-28 修订版的客户端,请求不再依赖 Mcp-Session-Id,因此任何请求都可以落在负载均衡器后面的任意服务器实例上。较旧的协议版本仍有会话行为,因此如果你的部署也要服务旧客户端,需要为此做规划。
假设一个工具启动了一个长耗时导出。让它返回一个 job ID,并添加第二个接受该 ID 并报告进度的工具。该 ID 存在于模型的上下文中,所以下一次调用由哪台机器回答并不重要。
无状态协议核心不等于自动安全。如果你的服务器涉及文件、数据库或账户,从一开始就要养成几个习惯。
OWASP 的 MCP 安全指南建议在沙箱(如容器)中运行本地服务器,对每个服务器和每个工具应用最小权限原则,并在服务器层验证输入和输出。工具描述和工具输出都成为模型读取的内容,因此将它们视为不可信输入而不是要遵循的指令。模型可以通过隐藏在两者中的文本被引导。
如果你的服务器代表用户调用其他 API,不要转发从客户端收到的 token。MCP 安全最佳实践将这种"token 直通"描述为反模式,授权指南指出服务器不得传递它收到的 token。上游 API 应该获得为其颁发的 token,因为接受本应发给其他服务的 token 的服务器可能被滥用。
还要在执行任何破坏性操作(如删除数据或转账)之前请求人工确认。
不。MCP 是一个开放标准,VS Code 和 Cursor 等客户端也支持它,因此同一个服务器可以为多个工具提供服务。
两者都有支持 2026-07-28 规范的当前 v2 SDK,所以选择取决于你的技术栈。本指南的 TypeScript 路径需要 Node.js 22.19 或更高版本,而 Python 路径需要 Python 3.10 或更高版本。TypeScript SDK 还为 Express、Fastify 和 Hono发布了适配器,如果你以后想将服务器托管在现有的 Web 应用中,这会很有帮助。
MCP 项目现在记录了正式的弃用策略,协议变更的最短通知期为十二个月,因此删除会有很长的提前通知期。SDK 主版本升级(如 v1 到 v2 的跳跃)仍可能需要代码修改。
用你真正需要的东西替换这两个示例工具:对你的数据库进行查询、在笔记中搜索,或者检查你网站的状态。
对于第一个真正的实现,保持服务器只读。这在你就模型如何选择和调用工具进行学习的过程中限制了损害程度,一旦其行为看起来可预测,就可以添加写权限。
Model Context Protocol Blog,"The 2026-07-28 Specification"(David Soria Parra 和 Den Delimarsky,2026 年 7 月 28 日),包含来自 Supabase、Manufact 和 Honeycomb 的采用者评论
Model Context Protocol,规范 2026-07-28 及其版本控制页面
Model Context Protocol,"安全最佳实践"及授权安全注意事项
Model Context Protocol,调试文档
Model Context Protocol,MCP Inspector 文档(需要 Node.js 22.19.0)
Model Context Protocol TypeScript SDK,README 和"构建你的第一个服务器"教程(GitHub)
Model Context Protocol Python SDK,README 及 v1 到 v2 迁移指南
Claude Code 文档,"Connect Claude Code to tools via MCP"
OWASP,"MCP Security Cheat Sheet"
开发者经验分享(DEV Community):
"How I Built My First MCP Server for Claude Code: 5 Lessons Learned"(作者:yureki_lab)
"Building an MCP server in production: lessons from 2,300 npm downloads"(作者:brianmello)
"The parts of building an MCP server that the tutorials skip"(作者:parkerrrrr)
"Building an MCP server: lessons from thunderbit-mcp"(作者:ethan_thunderbit)
"Debugging MCP Server Connection Issues: 7 Common Errors and Fixes"(作者:Jaypee)
"MCP Went Stateless: What the 2026-07-28 Spec Actually Changes"(作者:krlz)
如需进一步操作,你可以屏蔽此人或举报滥用行为