为什么现有的 MCP SDK 不够用?开发者的视角
官方 MCP SDK 很强大,但它不是为快速开发设计的。让我们探讨它的局限性,以及为什么像 @zhama/mcp-server 这样的框架对于高效的 AI 工具开发至关重要。

让我先声明一点:官方的模型上下文协议 SDK 非常优秀。它设计良好,精确遵循规范,并让你完全控制协议。Anthropic 团队在创建一个坚实的 MCP 开发基础方面做得非常出色。
但问题是:技术上正确并不意味着对开发者友好。
在 Zhama 为生产环境构建了数十个 MCP 服务器之后,我发现了当前 SDK 的六个根本问题,这些问题使它不适合真实世界的快速开发。这不是为了批评而批评 - 这是对我们为什么构建 @zhama/mcp-server 的诚实分析,以及为什么你可能也需要它。
核心问题:协议优先 vs 开发者优先的设计
官方 MCP SDK 是协议优先的。它旨在让你直接访问模型上下文协议的原语:请求、响应、处理器、传输层。如果你要:
- 为 MCP 构建基础设施
- 创建自己的抽象层
- 实现自定义协议扩展
- 调试协议级问题
这很好。
但如果你要:
- 快速构建业务工具
- 专注于应用逻辑
- 维护多个工具
- 让新开发者快速上手
- 在紧迫的期限下工作
这就很糟糕。
让我准确展示我的意思。
问题 #1:学习曲线太陡峭
你需要学习什么(官方 SDK)
在你可以构建一个简单的计算器工具之前,你需要理解:
-
服务器初始化和配置
const server = new Server({ name: 'my-server', version: '1.0.0' }, { capabilities: { tools: {}, resources: {}, prompts: {} } }); -
请求模式类型
import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema } from '@modelcontextprotocol/sdk/types.js'; -
请求处理器注册
server.setRequestHandler(ListToolsRequestSchema, async () => { // 返回工具定义 }); server.setRequestHandler(CallToolRequestSchema, async (request) => { // 执行工具逻辑 }); -
用于输入验证的 JSON Schema
inputSchema: { type: 'object', properties: { param1: { type: 'string' }, param2: { type: 'number' } }, required: ['param1', 'param2'] } -
响应格式化
return { content: [{ type: 'text', text: JSON.stringify(result) }] }; -
传输层设置
const transport = new StdioServerTransport(); await server.connect(transport);
这至少是 6 个不同的概念,仅仅是为了构建一个 "Hello World" 工具。
你需要学习什么(@zhama/mcp-server)
import { createMCPServer, BaseTool, Tool } from '@zhama/mcp-server';
@Tool({
name: 'hello',
description: '打招呼',
parameters: [
{ name: 'name', type: 'string', required: true }
]
})
class HelloTool extends BaseTool {
protected toolDefinition = {
name: 'hello',
description: '打招呼',
parameters: []
};
protected async executeInternal(params: Record<string, unknown>): Promise<unknown> {
const { name } = params as { name: string };
return { greeting: `你好,${name}!` };
}
}
createMCPServer('hello-server', '1.0.0')
.enableTools()
.addTool(new HelloTool())
.runStdio();
两个概念:装饰器和执行方法。其他一切都自动处理。
影响
使用官方 SDK,你的第一个工具需要 2-4 小时(包括学习时间)。使用 @zhama/mcp-server,只需要 5-10 分钟。
这不是一个小差异 - 这是第一天生产力提高 24 倍。
问题 #2:样板代码爆炸
让我们用两种方法构建相同的功能,并计算代码行数。
任务:创建天气工具
官方 SDK 实现:156 行
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ListToolsRequestSchema,
Tool as MCPTool
} from '@modelcontextprotocol/sdk/types.js';
// 服务器设置
const server = new Server({
name: 'weather-server',
version: '1.0.0'
}, {
capabilities: {
tools: {}
}
});
// 工具定义
const weatherToolDefinition: MCPTool = {
name: 'get_weather',
description: '获取城市的当前天气',
inputSchema: {
type: 'object',
properties: {
city: {
type: 'string',
description: '城市名称'
},
units: {
type: 'string',
description: '温度单位',
enum: ['celsius', 'fahrenheit'],
default: 'celsius'
}
},
required: ['city']
}
};
// 列出工具处理器
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [weatherToolDefinition]
};
});
// 执行工具处理器
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name !== 'get_weather') {
throw new Error(`未知工具:${request.params.name}`);
}
const { city, units = 'celsius' } = request.params.arguments as {
city: string;
units?: string;
};
try {
// 输入验证
if (!city || typeof city !== 'string') {
throw new Error('城市必须是非空字符串');
}
if (units && !['celsius', 'fahrenheit'].includes(units)) {
throw new Error('单位必须是 celsius 或 fahrenheit');
}
// 获取天气数据
const apiKey = process.env.WEATHER_API_KEY;
if (!apiKey) {
throw new Error('WEATHER_API_KEY 未配置');
}
const tempUnit = units === 'fahrenheit' ? 'imperial' : 'metric';
const response = await fetch(
`https://api.openweathermap.org/data/2.5/weather?q=${encodeURIComponent(city)}&appid=${apiKey}&units=${tempUnit}`
);
if (!response.ok) {
if (response.status === 404) {
throw new Error(`未找到城市:${city}`);
}
throw new Error(`天气 API 错误:${response.status}`);
}
const data = await response.json();
// 格式化结果
const result = {
city: data.name,
country: data.sys.country,
temperature: data.main.temp,
feels_like: data.main.feels_like,
condition: data.weather[0].main,
description: data.weather[0].description,
humidity: data.main.humidity,
wind_speed: data.wind.speed,
units: units
};
return {
content: [{
type: 'text',
text: JSON.stringify(result, null, 2)
}]
};
} catch (error) {
// 错误处理
const errorMessage = error instanceof Error ? error.message : '未知错误';
console.error('天气工具错误:', errorMessage);
return {
content: [{
type: 'text',
text: JSON.stringify({
error: errorMessage
})
}],
isError: true
};
}
});
// 服务器启动
async function main() {
try {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('天气 MCP 服务器运行在 stdio');
} catch (error) {
console.error('启动服务器失败:', error);
process.exit(1);
}
}
main();
@zhama/mcp-server 实现:47 行
import { createMCPServer, BaseTool, Tool } from '@zhama/mcp-server';
import fetch from 'node-fetch';
@Tool({
name: 'get_weather',
description: '获取城市的当前天气',
parameters: [
{
name: 'city',
type: 'string',
description: '城市名称',
required: true
},
{
name: 'units',
type: 'string',
description: 'celsius 或 fahrenheit',
required: false
}
]
})
class WeatherTool extends BaseTool {
protected toolDefinition = {
name: 'get_weather',
description: '获取天气信息',
parameters: []
};
protected async executeInternal(params: Record<string, unknown>): Promise<unknown> {
const { city, units = 'celsius' } = params as {
city: string;
units?: string
};
const apiKey = process.env.WEATHER_API_KEY;
if (!apiKey) throw new Error('WEATHER_API_KEY 未配置');
const tempUnit = units === 'fahrenheit' ? 'imperial' : 'metric';
const response = await fetch(
`https://api.openweathermap.org/data/2.5/weather?q=${encodeURIComponent(city)}&appid=${apiKey}&units=${tempUnit}`
);
if (!response.ok) {
if (response.status === 404) throw new Error(`未找到城市:${city}`);
throw new Error(`天气 API 错误:${response.status}`);
}
const data = await response.json();
return {
city: data.name,
country: data.sys.country,
temperature: data.main.temp,
feels_like: data.main.feels_like,
condition: data.weather[0].main,
description: data.weather[0].description,
humidity: data.main.humidity,
wind_speed: data.wind.speed,
units
};
}
}
createMCPServer('weather-server', '1.0.0')
.enableTools()
.enableLogging('info')
.addTool(new WeatherTool())
.runStdio();
结果:代码减少 70% - 而且 @zhama/mcp-server 版本更易读。
样板代码税
使用官方 SDK 构建的每个工具都需要:
- 服务器配置:约 10 行
- 模式定义:约 15-30 行
- 处理器注册:约 5 行
- 请求路由:约 5-10 行
- 响应格式化:约 5-10 行
- 错误处理:约 10-20 行
- 传输层设置:约 5 行
每个工具有 55-90 行样板代码。
使用 @zhama/mcp-server:**0 行样板代码。**只需编写业务逻辑。
问题 #3:错误处理是你的问题
官方 SDK 完全将错误处理留给你。这意味着:
你必须处理
-
输入验证错误
if (!params.email || typeof params.email !== 'string') { throw new Error('无效的邮箱参数'); } -
业务逻辑错误
try { const result = await performOperation(); } catch (error) { // 这里应该返回什么? } -
响应格式化
return { content: [{ type: 'text', text: JSON.stringify({ error: error.message }) }], isError: true // 别忘了这个! }; -
日志记录
console.error('工具失败:', error); // 希望这就够了
真实成本
在生产环境中,我们发现使用官方 SDK 时,错误处理代码经常超过业务逻辑代码。一个典型的工具看起来像:
- 业务逻辑:30 行
- 错误处理:50 行
- 样板代码:40 行
只有 25% 的代码是实际功能。
使用 @zhama/mcp-server
protected async executeInternal(params: Record<string, unknown>): Promise<unknown> {
// 只需编写你的逻辑
// 错误自动捕获
// 日志自动记录
// 响应自动格式化
const result = await performOperation(params);
return result; // 就这样!
}
80-90% 的代码是实际功能。
问题 #4:没有开发者体验功能
官方 SDK 提供零开发者体验优化:
缺失的功能
- 没有 TypeScript 装饰器 - 手动模式定义
- 没有自动验证 - 你要验证每个参数
- 没有日志系统 - 自己实现或使用 console.log
- 没有测试工具 - 自己想办法 mock
- 没有开发模式 - 没有热重载或自动重启
- 没有调试工具 - Console.error 是你的朋友
- 没有类型推断 - 手动转换所有内容
- 没有文档生成 - 全部手写
示例:添加工具参数
官方 SDK:需要更新 4 个地方
// 1. 更新模式
const toolDef = {
inputSchema: {
properties: {
existingParam: { type: 'string' },
newParam: { type: 'number' } // 在这里添加
},
required: ['existingParam', 'newParam'] // 还有这里
}
};
// 2. 更新类型定义
type ToolParams = {
existingParam: string;
newParam: number; // 在这里添加
};
// 3. 更新验证
if (typeof params.newParam !== 'number') { // 在这里添加
throw new Error('newParam 必须是数字');
}
// 4. 更新逻辑
const { existingParam, newParam } = params; // 在这里使用
@zhama/mcp-server:只需更新 1 个地方
@Tool({
parameters: [
{ name: 'existingParam', type: 'string', required: true },
{ name: 'newParam', type: 'number', required: true } // 只需在这里添加
]
})
class MyTool extends BaseTool {
protected async executeInternal(params: Record<string, unknown>): Promise<unknown> {
const { existingParam, newParam } = params as {
existingParam: string;
newParam: number
};
// 立即使用 - 验证是自动的
}
}
问题 #5:维护变成噩梦
随着 MCP 服务器的增长,官方 SDK 的问题会复合:
场景:一个服务器中有 10 个工具
官方 SDK 结构
// server.ts - 800+ 行
// 工具定义(150 行)
const tool1Def = { /* ... */ };
const tool2Def = { /* ... */ };
// ... 还有 8 个
// 列表处理器(50 行)
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [tool1Def, tool2Def, /* ... */]
};
});
// 执行处理器(600+ 行)
server.setRequestHandler(CallToolRequestSchema, async (request) => {
switch (request.params.name) {
case 'tool1':
// 60 行逻辑 + 错误处理
break;
case 'tool2':
// 60 行逻辑 + 错误处理
break;
// ... 还有 8 个 case
}
});
问题:
- 单个 800 行的文件
- 工具无法独立测试
- 共享状态导致 bug
- 每个 PR 都有合并冲突
- 无法跨服务器重用工具
@zhama/mcp-server 结构
// tools/tool1.ts (30 行)
@Tool({ /* ... */ })
export class Tool1 extends BaseTool { /* ... */ }
// tools/tool2.ts (30 行)
@Tool({ /* ... */ })
export class Tool2 extends BaseTool { /* ... */ }
// ... 还有 8 个工具文件
// server.ts (15 行)
import { Tool1, Tool2, /* ... */ } from './tools';
createMCPServer('my-server', '1.0.0')
.enableTools()
.addTool(new Tool1())
.addTool(new Tool2())
// ... 还有 8 个
.runStdio();
好处:
- 模块化、有组织的代码库
- 每个工具可独立测试
- 没有共享状态问题
- 干净的 git 历史
- 工具是可重用的包
问题 #6:忽略生产环境考虑
官方 SDK 设计上是最小化的。它不包括生产必需品:
缺少什么
-
结构化日志
- 没有日志级别
- 没有日志格式化
- 没有日志聚合支持
- 只有 console.error()
-
指标和监控
- 没有性能跟踪
- 没有错误率监控
- 没有使用统计
- 没有健康检查
-
配置管理
- 没有基于环境的配置
- 没有密钥管理
- 没有配置验证
- 所有东西都要手动设置
-
优雅关闭
- 没有清理钩子
- 没有连接排空
- 手动信号处理
-
速率限制
- 没有内置速率限制
- 没有请求节流
- 没有配额管理
-
缓存
- 没有缓存层
- 没有响应记忆化
- 自己构建
生产环境现实检查
在 Zhama,我们运行每天处理数千个请求的 MCP 服务器。这是我们需要的:
// 我们想要的
createMCPServer('production-server', '1.0.0')
.enableLogging('info') // 结构化日志
.enableMetrics() // 性能跟踪
.enableRateLimit({ maxRequests: 100, windowMs: 60000 })
.enableCaching({ ttl: 300 })
.enableHealthCheck('/health')
.onShutdown(async () => {
// 清理
})
.addTool(new MyTool())
.runStdio();
官方 SDK 提供:以上都没有。
@zhama/mcp-server 提供:内置日志,其余可扩展。
真实世界影响:案例研究
让我分享我们在 Zhama 的实际数据:
项目:客户支持自动化
**目标:**为 Claude 构建 15 个工具来帮助客户支持
使用官方 SDK(我们的第一次尝试)
- **开发时间:**6 周
- **编写代码:**约 4,500 行
- **编写测试:**约 1,200 行
- **第一个月的 bug:**23 个
- **新开发者入职时间:**3 天
- **业务逻辑行数:**约 1,200 行(27%)
- **样板代码行数:**约 3,300 行(73%)
使用 @zhama/mcp-server(迁移后)
- **迁移时间:**1 周
- **迁移后代码:**约 1,800 行(减少 60%)
- **编写测试:**约 600 行
- **第一个月的 bug:**4 个
- **新开发者入职时间:**4 小时
- **业务逻辑行数:**约 1,400 行(78%)
- **样板代码行数:**约 400 行(22%)
ROI
- 入职速度提高 5 倍
- bug 减少 6 倍
- 代码维护量减少 60%
- 添加新工具速度提高 3 倍
这就是我们为什么构建 @zhama/mcp-server。
解决方案:框架,而非库
根本问题是 MCP SDK 是一个库(低级原语),而大多数开发者需要的是一个框架(高级抽象)。
有什么区别?
库(官方 SDK):
- 你调用它
- 你控制流程
- 你编写所有粘合代码
- 最大灵活性
- 最小生产力
框架(@zhama/mcp-server):
- 它调用你
- 它控制流程
- 它提供粘合代码
- 优化生产力
- 在重要的地方仍然灵活
框架优势
// 库方法:你做所有事情
const server = createServer();
server.on('request', (req) => {
if (req.type === 'listTools') {
return handleListTools();
} else if (req.type === 'callTool') {
return handleCallTool(req);
}
});
// 框架方法:你提供业务逻辑
@Tool({ name: 'my-tool', /* ... */ })
class MyTool extends BaseTool {
async executeInternal(params) {
// 只需你的逻辑
}
}
从官方 SDK 迁移
如果你已经在使用官方 SDK,迁移很简单:
步骤 1:安装框架
npm install @zhama/mcp-server
步骤 2:转换工具
之前:
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === 'my-tool') {
const result = await doSomething(request.params.arguments);
return {
content: [{ type: 'text', text: JSON.stringify(result) }]
};
}
});
之后:
@Tool({ name: 'my-tool', /* ... */ })
class MyTool extends BaseTool {
protected async executeInternal(params: Record<string, unknown>) {
return await doSomething(params);
}
}
步骤 3:更新服务器
之前:
const server = new Server(/* ... */);
server.setRequestHandler(/* ... */);
const transport = new StdioServerTransport();
await server.connect(transport);
之后:
createMCPServer('my-server', '1.0.0')
.enableTools()
.addTool(new MyTool())
.runStdio();
迁移时间
- **1-3 个工具:**30 分钟
- **4-10 个工具:**2-3 小时
- **11-20 个工具:**1 天
- **20+ 个工具:**2-3 天
加上:新工具的生产力立即提升。
何时使用哪个
使用官方 SDK 当:
✅ 你在构建 MCP 基础设施
✅ 你需要协议级控制
✅ 你在实现自定义扩展
✅ 你有非常特定的需求
✅ 你在构建自己的框架
使用 @zhama/mcp-server 当:
✅ 你在构建业务工具(99% 的情况)
✅ 你想快速交付
✅ 你重视开发者体验
✅ 你在构建生产服务
✅ 你有一个开发团队
✅ 你想要可维护的代码
未来:为每个人提供更好的工具
我们在 Zhama 的目标不是取代官方 SDK - 而是在它之上构建。@zhama/mcp-server 在底层使用官方 SDK,添加了缺失的开发者体验层。
接下来是什么
我们正在积极开发:
- 测试工具 - 简单的单元和集成测试
- 开发模式 - 热重载和更好的调试
- 指标集成 - Prometheus、DataDog 等
- 速率限制 - 内置请求节流
- 缓存层 - 智能响应缓存
- 工具市场 - 分享和发现工具
- 文档生成器 - 自动生成工具文档
结论:选择你自己的冒险
模型上下文协议是革命性的。官方 SDK 是可靠的。但它是为协议实现者设计的,而不是应用程序开发者。
如果你在构建:
- 概念验证:官方 SDK 可以
- 生产工具:使用 @zhama/mcp-server
- 团队项目:绝对使用 @zhama/mcp-server
- 企业集成:使用 @zhama/mcp-server
底线
官方 SDK:
- 天气工具 156 行
- 第一个工具 6 小时
- 73% 样板代码
- 手动错误处理
- 没有 DX 功能
- 难以维护
@zhama/mcp-server:
- 天气工具 47 行(减少 70%)
- 第一个工具 15 分钟(快 24 倍)
- 22% 样板代码
- 自动错误处理
- 内置 DX 功能
- 易于维护
选择是显而易见的。
今天就试试
准备好摆脱样板地狱了吗?5 分钟开始:
npm install @zhama/mcp-server
访问:
- GitHub:github.com/zhama-ai/mcp-server
- 官网:www.zhama.com
- 文档:完整示例和指南
由 Zhama 用 ❤️ 打造 - 因为开发者应该拥有更好的工具。
对这个分析有想法?加入 GitHub 的讨论或通过 team@zhama.com 联系我们
相关文章

用装饰器构建 AI 工具,太爽了
忘掉你所知道的关于构建 MCP 服务器的一切。TypeScript 装饰器将 AI 工具开发从繁琐的配置转变为纯粹、富有表现力的代码。体验 @zhama/mcp-server 装饰器驱动开发的乐趣。

我做了一个框架,让你 5 分钟写出一个 Claude 工具
厌倦了复杂的 MCP 服务器配置?我创建了 @zhama/mcp-server - 一个对开发者友好的 TypeScript 框架,让你从零到拥有一个可用的 Claude Desktop 工具只需 5 分钟。没有样板代码,只有真正的代码。

如何为 Claude Desktop 构建自定义 MCP 服务器:完整实战指南
掌握使用 @zhama/mcp-server 框架为 Claude Desktop 构建自定义模型上下文协议服务器的技能。这份深度指南涵盖了从安装设置到生产部署的全部内容,包含实用示例和生产级最佳实践。