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

"这也太复杂了吧。"
这是我花了整整一个下午尝试为 Claude Desktop 构建第一个 MCP 服务器后的想法。官方 SDK 很强大,但老实说 - 仅仅为了创建一个简单的工具,就需要写大量的样板代码。
我只是想给 Claude 添加一个天气查询工具。很简单的事情。但我发现自己淹没在:
- 服务器初始化代码
- 传输层配置
- 协议实现细节
- 错误处理样板
- 日志设置
- 还有更多...
一定有更好的方法。
所以我做了一个。认识一下 @zhama/mcp-server - 一个 TypeScript 框架,让你真的可以在 5 分钟内创建 Claude Desktop 工具。
问题:仪式感太多,代码太少
让我展示一下我的意思。这是使用原始 MCP SDK 创建一个简单计算器工具通常需要写的代码:
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ListToolsRequestSchema
} from '@modelcontextprotocol/sdk/types.js';
// 创建服务器
const server = new Server({
name: 'calculator-server',
version: '1.0.0',
}, {
capabilities: {
tools: {}
}
});
// 注册工具列表处理器
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [{
name: 'calculator',
description: '执行算术运算',
inputSchema: {
type: 'object',
properties: {
operation: {
type: 'string',
description: '要执行的运算',
enum: ['add', 'subtract', 'multiply', 'divide']
},
a: { type: 'number', description: '第一个数字' },
b: { type: 'number', description: '第二个数字' }
},
required: ['operation', 'a', 'b']
}
}]
};
});
// 注册工具执行处理器
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name !== 'calculator') {
throw new Error('未知工具');
}
const { operation, a, b } = request.params.arguments;
let result;
switch (operation) {
case 'add': result = a + b; break;
case 'subtract': result = a - b; break;
case 'multiply': result = a * b; break;
case 'divide':
if (b === 0) throw new Error('除数不能为零');
result = a / b;
break;
default:
throw new Error('无效的运算');
}
return {
content: [{
type: 'text',
text: JSON.stringify({ result })
}]
};
});
// 启动服务器
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('计算器 MCP 服务器正在运行');
}
main().catch(console.error);
这是超过 70 行代码,而这只是一个简单的计算器!我甚至还没有添加适当的错误处理、日志记录或输入验证。
解决方案:专注于重要的事情
使用 @zhama/mcp-server,同样的工具变成了这样:
import { createMCPServer, BaseTool, Tool } from '@zhama/mcp-server';
@Tool({
name: 'calculator',
description: '执行算术运算',
parameters: [
{ name: 'operation', type: 'string', description: 'add、subtract、multiply 或 divide', required: true },
{ name: 'a', type: 'number', description: '第一个数字', required: true },
{ name: 'b', type: 'number', description: '第二个数字', required: true }
]
})
class CalculatorTool extends BaseTool {
protected toolDefinition = {
name: 'calculator',
description: '执行算术运算',
parameters: []
};
protected async executeInternal(params: Record<string, unknown>): Promise<unknown> {
const { operation, a, b } = params as { operation: string; a: number; b: number };
const operations = {
add: () => a + b,
subtract: () => a - b,
multiply: () => a * b,
divide: () => b !== 0 ? a / b : throw new Error('不能除以零')
};
return { result: operations[operation]() };
}
}
// 就这样!只需创建并运行服务器
createMCPServer('calculator', '1.0.0')
.enableTools()
.addTool(new CalculatorTool())
.runStdio();
不到 30 行。简洁。可读。生产就绪。
错误处理?内置的。日志记录?已包含。类型安全?有保证。服务器设置?自动完成。
5 分钟挑战
不相信我?让我们一起构建一个真实的工具 - 现在就开始。打开你的终端,跟着做。
第 1 分钟:设置(60 秒)
mkdir my-claude-tool && cd my-claude-tool
npm init -y
npm install @zhama/mcp-server typescript @types/node
npx tsc --init
第 2-3 分钟:编写工具(120 秒)
创建 src/server.ts:
import { createMCPServer, BaseTool, Tool } from '@zhama/mcp-server';
import fetch from 'node-fetch';
@Tool({
name: 'get_joke',
description: '获取一个随机编程笑话来调节气氛',
parameters: []
})
class JokeTool extends BaseTool {
protected toolDefinition = {
name: 'get_joke',
description: '获取随机笑话',
parameters: []
};
protected async executeInternal(): Promise<unknown> {
const response = await fetch('https://official-joke-api.appspot.com/random_joke');
const joke = await response.json();
return {
setup: joke.setup,
punchline: joke.punchline,
joke: `${joke.setup} - ${joke.punchline}`
};
}
}
createMCPServer('joke-server', '1.0.0')
.description('为你的 AI 对话带来幽默')
.enableTools()
.addTool(new JokeTool())
.runStdio();
第 4 分钟:构建(30 秒)
npx tsc
第 5 分钟:配置 Claude(30 秒)
添加到 ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"jokes": {
"command": "node",
"args": ["/absolute/path/to/your/project/dist/server.js"]
}
}
}
重启 Claude Desktop。完成!
现在问 Claude:"给我讲个编程笑话" - 然后看你的工具运行起来!
为什么我要做这个框架
作为 Zhama 的开发者,我花了大量时间构建 AI 集成。我见过团队一次又一次地在同样的问题上挣扎:
1. 学习曲线陡峭
官方 MCP SDK 是低级的、面向协议的。它很强大,但在你能构建任何有用的东西之前,需要理解整个协议规范。大多数开发者只是想添加一个工具 - 他们不想先成为 MCP 专家。
2. 样板代码太多
每个工具都需要设置请求处理器、管理模式、处理响应、配置传输层... 这很累人。一个应该只需要 10 行代码的简单工具最终变成了 100 多行基础设施代码。
3. 容易出错
没有适当的抽象,很容易犯错误:忘记验证输入、错过错误情况、返回错误的响应格式,或配置错误服务器。这些错误只会在运行时出现,当 Claude 尝试使用你的工具时。
4. 难以维护
随着工具的增长,代码变得更难管理。请求处理器与业务逻辑混在一起。错误处理散落各处。测试变成了噩梦。
我想解决所有这些问题。
设计理念
在构建 @zhama/mcp-server 时,我遵循了三个核心原则:
1. 装饰器优于配置
不再编写 JSON 模式和请求处理器,而是用 TypeScript 装饰器描述你的工具:
@Tool({
name: 'search_database',
description: '搜索产品数据库',
parameters: [
{ name: 'query', type: 'string', required: true },
{ name: 'limit', type: 'number', required: false }
]
})
class SearchTool extends BaseTool {
// 你的逻辑在这里
}
框架处理其他一切 - 模式生成、验证、请求路由、响应格式化。
2. 约定优于配置
所有东西都有合理的默认值:
// 最小设置
createMCPServer('my-server', '1.0.0')
.enableTools()
.addTool(new MyTool())
.runStdio();
// 或者为生产环境完全配置
createMCPServer('my-server', '1.0.0')
.description('生产服务器')
.author('你的团队')
.enableTools({ listChanged: true })
.enableResources({ subscribe: true })
.enableLogging('info')
.addTool(new MyTool())
.runStdio();
两者都可以。从简单开始,只在需要时添加复杂性。
3. TypeScript 优先
到处都有完整的类型安全。真正有帮助的 IntelliSense。在编译时而不是运行时捕获错误:
// TypeScript 知道参数类型
protected async executeInternal(params: Record<string, unknown>): Promise<unknown> {
// 类型安全的解构
const { query, limit = 10 } = params as {
query: string;
limit?: number
};
// TypeScript 检查一切
return await this.searchDatabase(query, limit);
}
真实案例
让我展示一些我们在 Zhama 使用这个框架构建的工具:
天气查询工具(2 分钟)
@Tool({
name: 'get_weather',
description: '获取任何城市的当前天气',
parameters: [
{ name: 'city', type: 'string', description: '城市名称', required: true }
]
})
class WeatherTool extends BaseTool {
protected toolDefinition = {
name: 'get_weather',
description: '获取天气信息',
parameters: []
};
protected async executeInternal(params: Record<string, unknown>): Promise<unknown> {
const { city } = params as { city: string };
const apiKey = process.env.WEATHER_API_KEY;
const response = await fetch(
`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}&units=metric`
);
const data = await response.json();
return {
city: data.name,
temperature: data.main.temp,
condition: data.weather[0].description,
humidity: data.main.humidity,
summary: `${data.name}:${data.main.temp}°C,${data.weather[0].description}`
};
}
}
使用场景:"嘿 Claude,东京的天气怎么样?"
数据库查询工具(3 分钟)
@Tool({
name: 'query_users',
description: '通过邮箱或姓名在数据库中搜索用户',
parameters: [
{ name: 'search_term', type: 'string', description: '要搜索的邮箱或姓名', required: true }
]
})
class UserQueryTool extends BaseTool {
private db: DatabaseConnection;
constructor() {
super();
this.db = new DatabaseConnection();
}
protected toolDefinition = {
name: 'query_users',
description: '查询用户数据库',
parameters: []
};
protected async executeInternal(params: Record<string, unknown>): Promise<unknown> {
const { search_term } = params as { search_term: string };
// 参数化查询以确保安全
const users = await this.db.query(
'SELECT id, name, email, created_at FROM users WHERE name LIKE ? OR email LIKE ? LIMIT 10',
[`%${search_term}%`, `%${search_term}%`]
);
return {
found: users.length,
users: users.map(u => ({
id: u.id,
name: u.name,
email: u.email,
memberSince: u.created_at
}))
};
}
}
使用场景:"Claude,找出所有名字中包含'张'的用户"
文件分析工具(4 分钟)
@Tool({
name: 'analyze_file',
description: '分析文件并返回统计信息',
parameters: [
{ name: 'file_path', type: 'string', description: '文件路径', required: true }
]
})
class FileAnalysisTool extends BaseTool {
protected toolDefinition = {
name: 'analyze_file',
description: '分析文件统计信息',
parameters: []
};
protected async executeInternal(params: Record<string, unknown>): Promise<unknown> {
const { file_path } = params as { file_path: string };
// 安全性:验证路径
if (file_path.includes('..')) {
throw new Error('无效的文件路径');
}
const content = await fs.readFile(file_path, 'utf-8');
const lines = content.split('\n');
const words = content.split(/\s+/).filter(w => w.length > 0);
return {
path: file_path,
size: content.length,
lines: lines.length,
words: words.length,
characters: content.length,
type: file_path.split('.').pop(),
preview: lines.slice(0, 5).join('\n')
};
}
}
使用场景:"帮我分析一下 README.md 文件"
Slack 通知工具(3 分钟)
@Tool({
name: 'send_slack_message',
description: '向 Slack 频道发送消息',
parameters: [
{ name: 'channel', type: 'string', description: '频道名称(例如 #general)', required: true },
{ name: 'message', type: 'string', description: '要发送的消息', required: true }
]
})
class SlackTool extends BaseTool {
protected toolDefinition = {
name: 'send_slack_message',
description: '发送 Slack 消息',
parameters: []
};
protected async executeInternal(params: Record<string, unknown>): Promise<unknown> {
const { channel, message } = params as { channel: string; message: string };
const response = await fetch('https://slack.com/api/chat.postMessage', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.SLACK_TOKEN}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ channel, text: message })
});
const result = await response.json();
return {
success: result.ok,
channel: channel,
timestamp: result.ts,
message: '消息发送成功'
};
}
}
使用场景:"向 #engineering 频道发送消息说部署已完成"
免费获得的内置功能
当你使用 @zhama/mcp-server 时,你自动获得:
✅ 生产级日志记录
// 内置在每个工具中
this.logger.info('正在处理请求');
this.logger.error('操作失败', error);
this.logger.debug('详细调试信息');
✅ 自动错误处理
// 错误会被自动捕获并格式化
protected async executeInternal(params: Record<string, unknown>): Promise<unknown> {
throw new Error('出了点问题');
// 框架会优雅地处理这个错误并向 Claude 返回适当的错误响应
}
✅ 输入验证
// 参数会自动根据你的模式进行验证
@Tool({
parameters: [
{ name: 'age', type: 'number', required: true }
]
})
// 如果 Claude 传递字符串而不是数字,框架会在你的代码运行之前拒绝它
✅ 类型安全
// 完整的 TypeScript 支持和 IntelliSense
protected async executeInternal(params: Record<string, unknown>): Promise<unknown> {
// TypeScript 在每一步都帮助你
}
✅ 多种传输模式
// STDIO 用于 Claude Desktop
await server.runStdio();
// SSE 用于 Web 应用
await server.runSSE(3000);
✅ 资源管理
// 不仅仅是工具 - 还有资源!
class DataResource extends BaseResource {
protected async executeInternal(id: string): Promise<Resource> {
return {
id,
uri: `data://${id}`,
name: '数据资源',
type: 'application/json',
content: JSON.stringify({ data: '你的数据' })
};
}
}
✅ 提示工程
// 动态提示生成
server.addPromptGenerator('code-review', async (language: string) => {
return {
id: `review-${language}`,
name: `代码审查:${language}`,
type: 'text',
content: generateReviewPrompt(language)
};
});
对比:使用前后
让我把一切都放在透视中。这是构建天气工具所需的工作:
之前(原始 MCP SDK):约 200 行,30+ 分钟
- 设置服务器基础设施
- 配置传输层
- 手动定义工具模式
- 实现请求处理器
- 手动处理错误
- 设置日志记录
- 正确格式化响应
- 测试一切
之后(@zhama/mcp-server):约 30 行,5 分钟
@Tool({
name: 'weather',
description: '获取天气',
parameters: [{ name: 'city', type: 'string', required: true }]
})
class WeatherTool extends BaseTool {
protected toolDefinition = { name: 'weather', description: '获取天气', parameters: [] };
protected async executeInternal(params: Record<string, unknown>): Promise<unknown> {
const { city } = params as { city: string };
const weather = await getWeather(city);
return { city, ...weather };
}
}
createMCPServer('weather', '1.0.0')
.enableTools()
.addTool(new WeatherTool())
.runStdio();
代码减少了 85%,时间减少了 83%。
今天就开始
想自己试试吗?这是完整的快速入门:
第 1 步:安装
npm install @zhama/mcp-server
第 2 步:创建你的第一个工具
import { createMCPServer, BaseTool, Tool } from '@zhama/mcp-server';
@Tool({
name: 'hello',
description: '打招呼',
parameters: [
{ name: 'name', type: 'string', description: '要问候的名字', 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}!👋`,
timestamp: new Date().toISOString()
};
}
}
async function main() {
const server = createMCPServer('hello-server', '1.0.0')
.description('我的第一个 MCP 服务器')
.enableTools()
.addTool(new HelloTool());
await server.runStdio();
}
main().catch(console.error);
第 3 步:构建和配置
npx tsc
添加到 Claude 配置:
{
"mcpServers": {
"hello": {
"command": "node",
"args": ["/path/to/dist/server.js"]
}
}
}
第 4 步:测试
重启 Claude 并问:"向小明问好"
高级模式
一旦你熟悉了基础知识,这里有一些高级模式:
一个服务器中的多个工具
createMCPServer('enterprise-server', '1.0.0')
.enableTools()
.addTool(new DatabaseTool())
.addTool(new EmailTool())
.addTool(new SlackTool())
.addTool(new AnalyticsTool())
.runStdio();
基于环境的配置
const config = {
logLevel: process.env.LOG_LEVEL || 'info',
dbHost: process.env.DB_HOST,
apiKey: process.env.API_KEY
};
createMCPServer('my-server', '1.0.0')
.enableLogging(config.logLevel)
.addTool(new MyTool(config))
.runStdio();
自定义基类
// 为常见模式创建你自己的基类
abstract class AuthenticatedTool extends BaseTool {
protected async executeInternal(params: Record<string, unknown>): Promise<unknown> {
// 检查认证
if (!this.validateAuth(params)) {
throw new Error('未授权');
}
// 调用子类实现
return await this.executeAuthenticated(params);
}
protected abstract executeAuthenticated(params: Record<string, unknown>): Promise<unknown>;
protected abstract validateAuth(params: Record<string, unknown>): boolean;
}
// 使用它
@Tool({ /* ... */ })
class SecureDataTool extends AuthenticatedTool {
protected async executeAuthenticated(params: Record<string, unknown>): Promise<unknown> {
// 你的安全逻辑在这里
}
protected validateAuth(params: Record<string, unknown>): boolean {
return params.apiKey === process.env.API_KEY;
}
}
缓存模式
@Tool({ /* ... */ })
class CachedTool extends BaseTool {
private cache = new Map<string, { data: any; expires: number }>();
protected async executeInternal(params: Record<string, unknown>): Promise<unknown> {
const key = JSON.stringify(params);
const cached = this.cache.get(key);
if (cached && cached.expires > Date.now()) {
this.logger.info('返回缓存结果');
return cached.data;
}
const data = await this.fetchData(params);
this.cache.set(key, {
data,
expires: Date.now() + 5 * 60 * 1000 // 5 分钟
});
return data;
}
}
Zhama 的真实生产案例
在 Zhama,我们所有的 Claude 集成都使用这个框架。这里是我们构建的一些工具:
1. 客户支持自动化
- 查询客户数据
- 检查订单状态
- 处理退款
- 创建支持工单
2. 开发工具
- 部署到测试/生产环境
- 运行数据库迁移
- 检查服务健康状态
- 查看应用日志
3. 内容管理
- 发布博客文章
- 更新文档
- 生成社交媒体内容
- 安排内容发布
4. 数据分析
- 查询分析数据库
- 生成报告
- 导出数据
- 创建可视化
全部使用这个框架构建。全部部署到生产环境。全部与 Claude Desktop 可靠工作。
社区和支持
框架是开源的,正在积极维护:
- GitHub:github.com/zhama-ai/mcp-server
- 官网:www.zhama.com
- Issues:在 GitHub 上报告错误和请求功能
- Discussions:分享你的工具并获得帮助
- 邮件:team@zhama.com 获取企业支持
接下来是什么?
我正在根据像你这样的开发者的反馈不断改进框架。即将推出的功能:
- 🔄 流式响应 - 用于长时间运行的操作
- 🔐 内置认证模式 - 常见的认证策略
- 📊 指标和监控 - 开箱即用的可观察性
- 🧪 测试工具 - 简单的单元和集成测试
- 📚 更多示例 - 真实世界的生产模式
- 🎨 工具市场 - 分享和发现社区工具
现在就试试 - 你有 5 分钟
我挑战你:设置一个 5 分钟的计时器,使用 @zhama/mcp-server 构建你的第一个 Claude 工具。
- 安装框架(30 秒)
- 复制一个示例(60 秒)
- 自定义它(120 秒)
- 构建和配置(90 秒)
- 在 Claude 中测试(立即)
就这样。你将在 5 分钟或更短的时间内拥有一个与 Claude Desktop 集成的工作工具。
为什么这很重要
Claude Desktop 很棒,但它仅限于它知道和能做的事情。通过添加自定义工具,你可以:
- 将 Claude 连接到你的系统 - 数据库、API、内部工具
- 自动化你的工作流 - 让 Claude 帮助处理日常任务
- 构建更好的产品 - 更快地创建 AI 驱动的功能
- 边做边学 - 通过实际示例理解 MCP
现在,使用 @zhama/mcp-server,入门门槛几乎为零。
结论:更少的代码,更多的价值
我做这个框架是因为我感到沮丧。对样板代码感到沮丧。对复杂性感到沮丧。对构建简单工具所花费的时间感到沮丧。
现在只需要 5 分钟。
不再与协议细节搏斗。不再复制粘贴样板代码。不再调试晦涩的 SDK 错误。
只需编写你的工具,添加到服务器,然后看它在 Claude Desktop 中工作。
这就是良好抽象的力量。这就是我为什么做 @zhama/mcp-server。
准备好在 5 分钟内构建你的第一个工具了吗?
访问 github.com/zhama-ai/mcp-server 开始使用。
由 Zhama 用 ❤️ 打造 - 让 AI 开发对每个人都可及。
你用 @zhama/mcp-server 构建了什么很酷的东西吗?在 GitHub Discussions 上分享 - 我们很想看看你创造了什么!
相关文章

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

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

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