在数字世界的浩瀚星海中,Model Context Protocol(MCP)如同一艘灵活的飞船,承载着开发者在数据处理与工具集成的宇宙中自由翱翔。今天,我们将以一套精心设计的MCP服务器开发模板为基础,带你从零开始,构建一个功能强大、易于扩展的生产级MCP服务器。这不仅是一次技术冒险,更是一场关于创造力和效率的奇妙旅程。准备好,我们即将点火升空!🚀
🌟 模板的魔力:从混沌到秩序
想象你是一位建筑师,面对一块空地,要从零开始建造一座现代化的摩天大楼。你需要地基、框架、管道系统,还要确保大楼美观且实用。MCP开发模板就像一张完整的建筑蓝图,为你提供了标准化的项目结构、预配置的工具链和最佳实践,让你无需从头设计每一块砖瓦。
什么是MCP?
MCP(Model Context Protocol)是一种轻量级协议,用于定义和调用工具接口,广泛应用于数据处理、自动化任务和AI集成场景。它的核心在于通过标准化的请求-响应机制,让开发者能够快速构建可扩展的工具服务器。
这套模板基于crawl-mcp
项目的实战经验,涵盖了从项目初始化到自动化部署的每一个环节。以下是它的核心特性:
- 标准化架构:清晰的目录结构,模块化设计。
- 即用型配置:TypeScript、Jest、ESLint等一键启用。
- 自动化流程:通过GitHub Actions实现CI/CD。
- 全面测试:单元测试、集成测试全覆盖。
- 详细文档:从入门教程到高级扩展,应有尽有。
让我们深入探索,如何用这套模板打造一个属于你的MCP服务器!
🏗️ 搭建地基:项目初始化
🌍 从零开始:创建项目骨架
就像建造一座城市的第一步是铺设道路,MCP服务器的开发始于一个清晰的项目结构。模板提供了一个标准化的目录布局,确保你的代码井然有序:
my-awesome-mcp/
├── src/ # 源代码
│ ├── core/ # 核心服务器逻辑
│ ├── tools/ # 工具实现
│ ├── types/ # 类型定义
│ ├── utils/ # 工具函数
│ └── processors/ # 数据处理器
├── tests/ # 测试用例
│ ├── unit/ # 单元测试
│ ├── integration/ # 集成测试
│ └── e2e/ # 端到端测试
├── scripts/ # 脚本文件
├── docs/ # 文档
├── examples/ # 示例代码
└── .github/workflows/ # CI/CD工作流
初始化项目的第一步是创建目录并复制模板提供的配置文件:
mkdir my-awesome-mcp && cd my-awesome-mcp
git init
export TEMPLATE_DIR="../crawl-mcp/docs/templates"
cp $TEMPLATE_DIR/config/package.template.json package.json
cp $TEMPLATE_DIR/config/tsconfig.template.json tsconfig.json
cp $TEMPLATE_DIR/config/jest.template.js jest.config.js
接着,编辑package.json
,填入你的项目信息,例如:
{
"name": "my-awesome-mcp",
"version": "1.0.0",
"description": "一个超酷的MCP服务器",
"author": "星际建筑师 <you@example.com>",
"repository": {
"type": "git",
"url": "https://github.com/yourusername/my-awesome-mcp.git"
}
}
小贴士:
确保你的package.json
中的name
字段全局唯一,避免与npm上的现有包冲突。你可以在npm官网搜索确认。
🔧 核心引擎:实现MCP服务器
🛠️ 定义数据模型
MCP服务器的核心在于处理工具调用请求,而清晰的类型定义是确保代码健壮的第一步。模板提供了一个类型定义模板,我们以文本处理工具为例,创建src/types/index.ts
:
export interface TextProcessorParams {
text: string; // 输入文本
language?: string; // 可选语言参数
format?: 'json' | 'text'; // 输出格式
}
export interface TextProcessorResult {
success: boolean; // 处理是否成功
processed_text: string; // 处理后的文本
word_count: number; // 词数统计
language_detected?: string; // 检测到的语言
processing_time: number; // 处理耗时
}
这些类型定义就像飞船的导航图,确保输入和输出的数据格式清晰可控。
🔍 核心工具:文本处理器
接下来,我们实现一个简单的文本处理工具,用于处理输入文本、统计词数并检测语言。基于模板的tool.template.ts
,创建src/tools/textProcessor.ts
:
import { CallToolRequest, CallToolResult } from '@modelcontextprotocol/sdk/types.js';
import { Logger } from '../utils/Logger.js';
import { TextProcessorParams, TextProcessorResult } from '../types/index.js';
export async function processText(request: CallToolRequest): Promise<CallToolResult> {
const logger = new Logger('TextProcessor');
const startTime = Date.now();
try {
// 兼容性参数提取
const params = (request.params.arguments || request.params) as TextProcessorParams;
if (!params.text || typeof params.text !== 'string') {
throw new Error('text 参数是必需的,且必须是字符串');
}
logger.info(`处理文本: ${params.text.slice(0, 50)}...`);
// 业务逻辑
const processedText = await performTextProcessing(params);
const wordCount = countWords(processedText);
const languageDetected = detectLanguage(params.text);
const result: TextProcessorResult = {
success: true,
processed_text: processedText,
word_count: wordCount,
language_detected: languageDetected,
processing_time: Date.now() - startTime
};
return {
content: [{
type: 'text',
text: JSON.stringify(result, null, 2)
}]
};
} catch (error) {
const errorMsg = error instanceof Error ? error.message : '未知错误';
logger.error(`处理失败: ${errorMsg}`);
return {
content: [{
type: 'text',
text: JSON.stringify({ success: false, error: errorMsg, processing_time: Date.now() - startTime }, null, 2)
}],
isError: true
};
}
}
async function performTextProcessing(params: TextProcessorParams): Promise<string> {
const { text, format = 'text' } = params;
let processed = text.trim();
return format === 'json' ? JSON.stringify({ processed_text: processed }) : processed;
}
function countWords(text: string): number {
return text.split(/\s+/).filter(word => word.length > 0).length;
}
function detectLanguage(text: string): string {
return /[\u4e00-\u9fff]/.test(text) ? 'zh' : 'en';
}
技术亮点:
- 参数兼容性:通过
request.params.arguments || request.params
处理不同版本MCP协议的参数格式,避免空参数问题。
- 错误处理:使用try-catch捕获异常,确保服务器不会因单一错误崩溃。
- 日志记录:通过
Logger
类记录操作细节,便于调试和监控。
🚀 点火启动:MCP服务器
服务器的核心逻辑位于src/core/MCPServer.ts
,负责接收请求、分发工具调用。我们基于模板创建主入口文件src/index.ts
,并注册文本处理工具:
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { z } from 'zod';
import { zodToJsonSchema } from 'zod-to-json-schema';
import { processText } from './tools/textProcessor.js';
const server = new Server();
// 定义工具参数Schema
const TextProcessorArgsSchema = z.object({
text: z.string().describe('要处理的文本内容'),
language: z.string().optional().describe('文本语言(可选)'),
format: z.enum(['json', 'text']).default('text').describe('输出格式')
});
// 注册工具列表
server.setRequestHandler(ListToolsRequestSchema, async (): Promise<ListToolsResult> => {
return {
tools: [{
name: 'process_text',
description: '🔤 智能文本处理和分析工具',
inputSchema: zodToJsonSchema(TextProcessorArgsSchema) as ToolInput
}]
};
});
// 处理工具调用
server.setRequestHandler(CallToolRequestSchema, async (request: CallToolRequest): Promise<CallToolResult> => {
const { name } = request.params;
switch (name) {
case 'process_text':
return await processText(request);
default:
throw new Error(`未知工具: ${name}`);
}
});
export { server };
这个文件就像飞船的控制中心,负责协调所有工具的调用。zod
库用于定义输入参数的Schema,确保请求格式正确。
🧪 测试护航:确保质量
一个可靠的MCP服务器需要全面的测试覆盖。模板提供了单元测试和集成测试的框架,我们以文本处理器为例,创建tests/unit/textProcessor.test.ts
:
import { processText } from '../../src/tools/textProcessor.js';
import { CallToolRequest } from '@modelcontextprotocol/sdk/types.js';
describe('TextProcessor', () => {
it('处理简单英文文本', async () => {
const request: CallToolRequest = {
method: 'tools/call',
params: {
name: 'process_text',
arguments: { text: 'Hello, world!', format: 'text' }
}
};
const result = await processText(request);
const parsedResult = JSON.parse(result.content[0].text);
expect(parsedResult.success).toBe(true);
expect(parsedResult.word_count).toBe(2);
expect(parsedResult.language_detected).toBe('en');
});
it('处理中文文本', async () => {
const request: CallToolRequest = {
method: 'tools/call',
params: {
arguments: { text: '你好,世界!', format: 'json' }
}
};
const result = await processText(request);
const parsedResult = JSON.parse(result.content[0].text);
expect(parsedResult.language_detected).toBe('zh');
});
it('处理无效输入', async () => {
const request: CallToolRequest = {
method: 'tools/call',
params: {
arguments: { text: null }
}
};
const result = await processText(request);
expect(result.isError).toBe(true);
});
});
为什么测试重要?
测试就像飞船的模拟器,能在发射前发现潜在问题。单元测试验证单个组件,集成测试确保各部分协同工作,端到端测试模拟真实用户场景。
🚀 自动化升空:构建与部署
📦 构建项目
模板提供了完善的构建脚本,在package.json
中定义:
{
"scripts": {
"build": "tsc",
"type-check": "tsc --noEmit",
"test": "jest",
"dev": "node start-server.js",
"start": "node dist/index.js"
}
}
运行以下命令完成构建和测试:
npm install
npm run type-check
npm run build
npm test
🤖 GitHub Actions:一键部署
手动发布就像每次发射火箭都要亲自点火,费时又容易出错。模板内置了GitHub Actions配置,实现自动化CI/CD:
mkdir -p .github/workflows
cp $TEMPLATE_DIR/github/workflows/ci.template.yml .github/workflows/ci.yml
cp $TEMPLATE_DIR/github/workflows/release.template.yml .github/workflows/release.yml
ci.yml
负责持续集成,运行测试、类型检查和安全扫描;release.yml
根据提交信息自动发布新版本到npm。例如:
git commit -m "feat: 添加文本处理工具"
git push origin main
提交信息中的feat
触发minor版本升级,推送到main
分支后,GitHub Actions会自动构建、测试并发布到npm。
配置NPM Token:
在GitHub仓库的Settings -> Secrets and variables -> Actions
中添加NPM_TOKEN
,值从npmjs.com获取。
📊 监控与扩展:让飞船更强大
🔍 性能监控
模板提供了一个数据处理器模板,用于监控服务器性能。复制DataProcessor.template.ts
到src/processors/
,并实现性能统计逻辑:
export class PerformanceMonitor {
private metrics: Map<string, number[]> = new Map();
record(toolName: string, duration: number) {
const durations = this.metrics.get(toolName) || [];
durations.push(duration);
this.metrics.set(toolName, durations);
}
getAverageDuration(toolName: string): number {
const durations = this.metrics.get(toolName) || [];
return durations.length ? durations.reduce((a, b) => a + b, 0) / durations.length : 0;
}
}
🖼️ 图片处理能力
模板还提供了ImageDownloader.template.ts
,支持从URL下载图片并处理:
import axios from 'axios';
import * as fs from 'fs-extra';
export async function downloadImage(url: string, outputPath: string): Promise<void> {
const response = await axios.get(url, { responseType: 'arraybuffer' });
await fs.writeFile(outputPath, Buffer.from(response.data));
}
你可以将此功能集成到工具中,例如下载文本中引用的图片。
🐛 故障排查:应对宇宙风暴
开发过程中难免遇到问题,以下是常见问题及解决方案:
参数为空:
问题:MCP调用时request.params
为空。
解决:使用模板的兼容性提取:
const params = request.params.arguments || request.params;
测试失败:
问题:Jest无法找到模块。
解决:检查jest.config.js
的moduleDirectories
是否包含node_modules
和src
。
发布失败:
问题:npm权限错误。
解决:
npm login
npm whoami
🎉 星际远航:总结与展望
通过这套MCP开发模板,你已经从零打造了一个功能完备的生产级服务器。它不仅具备强大的文本处理能力,还拥有自动化构建、测试和部署的流水线。就像一艘装备精良的星际飞船,它随时准备迎接新的挑战。
下一步计划
- 扩展工具:添加图像处理、数据分析等新工具。
- 优化性能:引入缓存机制,降低响应时间。
- 增强安全:实现API认证和请求限流。
- 社区分享:将你的项目发布到GitHub,造福更多开发者。
资源推荐
📚 参考文献
- MCP开发模板集合,https://github.com/wutongci/mcp-template
- MCP服务器开发教程,https://github.com/wutongci/mcp-template/raw/refs/heads/main/TUTORIAL.md
- Model Context Protocol官方文档,https://modelcontextprotocol.io/docs
- GitHub Actions官方文档,https://docs.github.com/en/actions
- npm发布指南,https://docs.npmjs.com/cli/v8/commands/npm-publish
愿你的MCP服务器在数字宇宙中熠熠生辉! 🌌