如何自定义一个MCP服务器:从零到一的完整指南
满足特定业务需求:连接企业内部系统、数据库或API提升开发效率:封装常用操作,减少重复代码增强安全性:完全控制数据访问和权限管理优化性能:针对特定场景进行性能优化✅ MCP的基本概念和用途✅ 如何搭建基础的MCP服务器✅ 如何实现工具、资源和提示词✅ 错误处理和日志记录✅ 配置和部署流程✅ 最佳实践和进阶功能自定义MCP服务器为AI应用提供了无限的可能性。无论是连接企业内部系统,还是集成第三方服务
本文旨在帮助开发者理解并实践如何构建自己的MCP(Model Context Protocol)服务器,扩展AI模型的能力边界。
为什么需要自定义MCP服务器?
虽然市面上已经有很多现成的MCP服务器,但自定义MCP服务器能让你:
- 满足特定业务需求:连接企业内部系统、数据库或API
- 提升开发效率:封装常用操作,减少重复代码
- 增强安全性:完全控制数据访问和权限管理
- 优化性能:针对特定场景进行性能优化
开始之前:环境准备
技术栈选择
MCP服务器可以用多种语言实现,常见选择包括:
- TypeScript/JavaScript:适合前端开发者,生态丰富
- Python:简洁易用,AI领域首选
- Rust:性能优异,适合高性能场景
- Go:并发能力强,部署简单
本文将以 TypeScript 为例,因为它与前端开发紧密相关,且类型安全。
安装依赖
# 创建项目目录
mkdir my-mcp-server
cd my-mcp-server
# 初始化npm项目
npm init -y
# 安装MCP SDK
npm install @modelcontextprotocol/sdk
# 安装TypeScript相关依赖
npm install -D typescript @types/node ts-node
项目结构
my-mcp-server/
├── src/
│ ├── index.ts # 服务器入口文件
│ ├── tools/ # 工具定义目录
│ │ └── weather.ts # 示例工具
│ └── types.ts # 类型定义
├── package.json
├── tsconfig.json
└── README.md
第一步:创建基础MCP服务器
让我们从最简单的MCP服务器开始:
// src/index.ts
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
// 创建服务器实例
const server = new Server(
{
name: "my-custom-mcp-server",
version: "0.1.0",
},
{
capabilities: {
tools: {},
},
}
);
// 定义工具:获取当前时间
server.setRequestHandler("tools/list", async () => {
return {
tools: [
{
name: "get_current_time",
description: "获取当前系统时间",
inputSchema: {
type: "object",
properties: {
format: {
type: "string",
description: "时间格式,可选值:iso, unix, readable",
enum: ["iso", "unix", "readable"],
},
},
},
},
],
};
});
// 处理工具调用
server.setRequestHandler("tools/call", async (request) => {
const { name, arguments: args } = request.params;
if (name === "get_current_time") {
const format = args?.format || "readable";
const now = new Date();
let timeString: string;
switch (format) {
case "iso":
timeString = now.toISOString();
break;
case "unix":
timeString = now.getTime().toString();
break;
case "readable":
timeString = now.toLocaleString("zh-CN");
break;
default:
timeString = now.toLocaleString("zh-CN");
}
return {
content: [
{
type: "text",
text: `当前时间:${timeString}`,
},
],
};
}
throw new Error(`未知工具: ${name}`);
});
// 启动服务器
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("MCP服务器已启动");
}
main().catch(console.error);
第二步:添加更实用的工具
让我们添加一个更实用的工具示例——天气查询:
// src/tools/weather.ts
export interface WeatherToolParams {
city: string;
unit?: "celsius" | "fahrenheit";
}
export async function getWeather(params: WeatherToolParams): Promise<string> {
const { city, unit = "celsius" } = params;
// 这里可以调用真实的天气API
// 示例:使用OpenWeatherMap API
const apiKey = process.env.WEATHER_API_KEY;
if (!apiKey) {
throw new Error("未配置天气API密钥");
}
try {
const response = await fetch(
`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}&units=${unit === "celsius" ? "metric" : "imperial"}`
);
if (!response.ok) {
throw new Error(`天气API请求失败: ${response.statusText}`);
}
const data = await response.json();
return `城市:${data.name}\n温度:${data.main.temp}°${unit === "celsius" ? "C" : "F"}\n天气:${data.weather[0].description}`;
} catch (error) {
throw new Error(`获取天气信息失败: ${error instanceof Error ? error.message : String(error)}`);
}
}
在主文件中注册这个工具:
// 在 src/index.ts 中添加
import { getWeather } from "./tools/weather.js";
// 在 tools/list 中添加天气工具
server.setRequestHandler("tools/list", async () => {
return {
tools: [
{
name: "get_current_time",
description: "获取当前系统时间",
inputSchema: {
type: "object",
properties: {
format: {
type: "string",
description: "时间格式",
enum: ["iso", "unix", "readable"],
},
},
},
},
{
name: "get_weather",
description: "查询指定城市的天气信息",
inputSchema: {
type: "object",
properties: {
city: {
type: "string",
description: "城市名称,例如:北京、Shanghai",
},
unit: {
type: "string",
description: "温度单位",
enum: ["celsius", "fahrenheit"],
default: "celsius",
},
},
required: ["city"],
},
},
],
};
});
// 在 tools/call 中处理天气查询
server.setRequestHandler("tools/call", async (request) => {
const { name, arguments: args } = request.params;
if (name === "get_current_time") {
// ... 之前的代码 ...
}
if (name === "get_weather") {
const result = await getWeather(args as WeatherToolParams);
return {
content: [
{
type: "text",
text: result,
},
],
};
}
throw new Error(`未知工具: ${name}`);
});
第三步:添加资源支持
MCP不仅支持工具调用,还支持资源访问。资源可以是文件、数据库记录、API端点等:
// 注册资源
server.setRequestHandler("resources/list", async () => {
return {
resources: [
{
uri: "file://config",
name: "服务器配置",
description: "查看MCP服务器配置信息",
mimeType: "application/json",
},
{
uri: "file://logs",
name: "服务器日志",
description: "查看最近的服务器日志",
mimeType: "text/plain",
},
],
};
});
// 处理资源读取
server.setRequestHandler("resources/read", async (request) => {
const { uri } = request.params;
if (uri === "file://config") {
return {
contents: [
{
uri,
mimeType: "application/json",
text: JSON.stringify(
{
name: server.name,
version: server.version,
capabilities: server.capabilities,
},
null,
2
),
},
],
};
}
if (uri === "file://logs") {
// 返回日志内容
return {
contents: [
{
uri,
mimeType: "text/plain",
text: "这里是服务器日志内容...",
},
],
};
}
throw new Error(`未知资源: ${uri}`);
});
第四步:错误处理和日志
良好的错误处理是生产环境的关键:
// 添加错误处理中间件
server.onerror = (error) => {
console.error("[MCP错误]", error);
};
// 添加日志记录
function log(level: "info" | "warn" | "error", message: string, data?: any) {
const timestamp = new Date().toISOString();
const logMessage = `[${timestamp}] [${level.toUpperCase()}] ${message}`;
if (data) {
console.error(logMessage, data);
} else {
console.error(logMessage);
}
}
// 在工具调用中添加日志
server.setRequestHandler("tools/call", async (request) => {
const { name, arguments: args } = request.params;
log("info", `工具调用: ${name}`, args);
try {
// ... 工具处理逻辑 ...
} catch (error) {
log("error", `工具调用失败: ${name}`, error);
throw error;
}
});
第五步:配置和部署
配置文件
创建 mcp-config.json:
{
"mcpServers": {
"my-custom-server": {
"command": "node",
"args": ["dist/index.js"],
"env": {
"WEATHER_API_KEY": "your-api-key-here"
}
}
}
}
TypeScript配置
创建 tsconfig.json:
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "node",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
构建脚本
在 package.json 中添加:
{
"scripts": {
"build": "tsc",
"start": "node dist/index.js",
"dev": "ts-node src/index.ts"
}
}
第六步:测试你的MCP服务器
本地测试
# 构建项目
npm run build
# 运行服务器
npm start
在Claude Desktop中配置
- 找到Claude Desktop的配置文件(通常在
%APPDATA%\Claude\claude_desktop_config.json) - 添加你的MCP服务器配置
- 重启Claude Desktop
最佳实践
1. 工具设计原则
- 单一职责:每个工具只做一件事
- 清晰的描述:提供详细的工具描述和参数说明
- 错误处理:返回有意义的错误信息
- 类型安全:使用TypeScript确保类型安全
2. 性能优化
- 缓存机制:对频繁访问的数据进行缓存
- 异步处理:使用async/await处理异步操作
- 批量操作:支持批量处理以提高效率
3. 安全性
- 输入验证:验证所有输入参数
- 权限控制:实现适当的权限检查
- 敏感信息:使用环境变量存储API密钥
- 错误信息:避免泄露敏感信息
4. 可维护性
- 模块化设计:将功能拆分为独立模块
- 代码注释:添加清晰的代码注释
- 版本管理:使用语义化版本号
- 文档完善:编写详细的README和API文档
进阶功能
提示词模板
MCP支持提示词模板,可以为AI模型提供预定义的提示:
server.setRequestHandler("prompts/list", async () => {
return {
prompts: [
{
name: "analyze_data",
description: "分析数据的提示词模板",
arguments: [
{
name: "data_type",
description: "数据类型",
required: true,
},
],
},
],
};
});
server.setRequestHandler("prompts/get", async (request) => {
const { name, arguments: args } = request.params;
if (name === "analyze_data") {
return {
messages: [
{
role: "user",
content: {
type: "text",
text: `请分析以下${args?.data_type}数据,并提供详细的见解和建议。`,
},
},
],
};
}
});
采样功能
MCP还支持采样功能,允许服务器提供示例数据:
server.setRequestHandler("sampling/create", async (request) => {
// 实现采样逻辑
return {
items: [
{
id: "sample-1",
content: "示例内容",
},
],
};
});
常见问题解答
Q: MCP服务器必须用TypeScript编写吗?
A: 不是的。MCP是一个协议标准,可以用任何支持标准输入输出的语言实现。Python、Rust、Go等都是很好的选择。
Q: 如何调试MCP服务器?
A: 可以使用日志输出到stderr,MCP客户端会捕获这些日志。也可以使用调试器附加到进程。
Q: MCP服务器可以访问本地文件系统吗?
A: 可以,但要注意安全性。建议实现适当的权限检查和路径验证。
Q: 如何让MCP服务器支持认证?
A: 可以在服务器启动时检查环境变量或配置文件中的认证信息,也可以实现OAuth等标准认证流程。
总结
通过本文,我们学习了:
- ✅ MCP的基本概念和用途
- ✅ 如何搭建基础的MCP服务器
- ✅ 如何实现工具、资源和提示词
- ✅ 错误处理和日志记录
- ✅ 配置和部署流程
- ✅ 最佳实践和进阶功能
自定义MCP服务器为AI应用提供了无限的可能性。无论是连接企业内部系统,还是集成第三方服务,MCP都能帮助你扩展AI模型的能力。
现在,开始构建你自己的MCP服务器吧!🚀
参考资源
更多推荐



所有评论(0)