关于 mcp-creator
mcp-creator 是面向开发者的技能,用于构建可用于生产环境的 MCP 服务,涵盖设计、安全、错误处理、性能和测试,并提供模板与参考资料。
主要功能
- 01MCP 服务架构与工具设计
- 02输入验证与密钥管理
- 03速率限制与身份验证边界
- 04结构化错误与降级处理
- 05连接池、缓存与测试
适用场景
- 01搭建新的 MCP 服务
- 02保护 API 和数据库集成
- 03排查服务错误与测试故障
MCP 相关
指导 MCP 服务的创建与测试,包括工具、资源和提示词设计。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ curiositech/ some_claude_skills --skill mcp-creator安装后在 Claude Code 中输入/mcp-creator确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
CHANGELOG.md.claude-plugin/plugin.jsonreferences/architecture-patterns.mdsecurity-hardening.mdtesting-debugging.mdtool-design.mdtemplates/authenticated-api.tsbasic-server.ts详细介绍
MCP 创建器
专注于构建可用于生产环境的模型上下文协议服务。创建安全、高性能的 MCP,并具备恰当的安全边界、稳健的错误处理和出色的开发者体验。
何时使用此技能
在以下情况下使用 MCP:
- 需要通过身份验证集成外部 API
- 需要有状态连接(数据库、WebSocket、会话)
- 多个相关工具需要共享配置
- 需要在 Claude 与外部服务之间建立安全边界
- 需要连接池和资源管理
以下情况不要使用 MCP:
- 纯领域专业知识(使用技能)
- 多步骤编排(使用 Agent)
- 本地无状态操作(使用脚本)
- 简单文件处理(使用 Claude 内置工具)
快速开始
# Scaffold new MCP server
npx @modelcontextprotocol/create-server my-mcp-server
# Install SDK
npm install @modelcontextprotocol/sdk
# Test with inspector
npx @modelcontextprotocol/inspector
MCP 架构概览
┌─────────────────────────────────────────────────────────────┐
│ Claude │
└─────────────────────────┬───────────────────────────────────┘
│ MCP Protocol (JSON-RPC)
┌─────────────────────────┴───────────────────────────────────┐
│ MCP Server │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │ Tools │ │ Resources │ │ Prompts │ │
│ │ (actions) │ │ (read-only) │ │ (templates) │ │
│ └─────────────┘ └─────────────┘ └─────────────────────┘ │
│ │ │
│ ┌───────────────────────┴──────────────────────────────┐ │
│ │ Auth / Rate Limiting / Caching │ │
│ └───────────────────────┬──────────────────────────────┘ │
└──────────────────────────┼──────────────────────────────────┘
│
┌──────────────────────────┴──────────────────────────────────┐
│ External Services │
│ APIs │ Databases │ File Systems │ WebSockets │
└─────────────────────────────────────────────────────────────┘
MCP 服务核心模板
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
ErrorCode,
McpError,
} from "@modelcontextprotocol/sdk/types.js";
const server = new Server(
{ name: "my-mcp-server", version: "1.0.0" },
{ capabilities: { tools: {} } }
);
// Tool definitions
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: "my_tool",
description: "Clear description of what this tool does",
inputSchema: {
type: "object",
properties: {
input: { type: "string", description: "Input description" },
},
required: ["input"],
},
},
],
}));
// Tool implementation
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
if (name === "my_tool") {
try {
const result = await processInput(args.input);
return { content: [{ type: "text", text: JSON.stringify(result) }] };
} catch (error) {
throw new McpError(ErrorCode.InternalError, error.message);
}
}
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
});
// Start server
const transport = new StdioServerTransport();
await server.connect(transport);
工具设计原则
1. 清晰的命名
// ✅ Good: Action-oriented, specific
"get_user_profile"
"create_issue"
"analyze_sentiment"
// ❌ Bad: Vague, generic
"process"
"do_thing"
"handle"
2. 精确的输入架构
// ✅ Good: Typed, constrained, documented
{
type: "object",
properties: {
userId: { type: "string", pattern: "^[a-f0-9]{24}$" },
action: { type: "string", enum: ["read", "write", "delete"] },
limit: { type: "integer", minimum: 1, maximum: 100, default: 10 }
},
required: ["userId", "action"],
additionalProperties: false
}
// ❌ Bad: Untyped, unconstrained
{ type: "object" }
3. 结构化输出
// ✅ Good: Consistent structure
return {
content: [{
type: "text",
text: JSON.stringify({
success: true,
data: result,
metadata: { requestId, timestamp }
}, null, 2)
}]
};
// ❌ Bad: Inconsistent, unstructured
return { content: [{ type: "text", text: "done" }] };
安全强化(关键)
输入验证
import { z } from "zod";
const UserInputSchema = z.object({
userId: z.string().regex(/^[a-f0-9]{24}$/),
email: z.string().email(),
query: z.string().max(1000).refine(
(q) => !q.includes("--") && !q.includes(";"),
{ message: "Invalid characters in query" }
),
});
async function handleTool(args: unknown) {
const validated = UserInputSchema.parse(args); // Throws on invalid
// Safe to use validated data
}
密钥管理
// ✅ Good: Environment variables
const API_KEY = process.env.SERVICE_API_KEY;
if (!API_KEY) throw new Error("SERVICE_API_KEY required");
// ✅ Good: Secret manager integration
const secret = await secretManager.getSecret("service-api-key");
// ❌ NEVER: Hardcoded secrets
const API_KEY = "sk-abc123..."; // SECURITY VULNERABILITY
速率限制
class RateLimiter {
private requests: Map<string, number[]> = new Map();
canProceed(key: string, limit: number, windowMs: number): boolean {
const now = Date.now();
const timestamps = this.requests.get(key) || [];
const recent = timestamps.filter(t => now - t < windowMs);
if (recent.length >= limit) return false;
recent.push(now);
this.requests.set(key, recent);
return true;
}
}
const limiter = new RateLimiter();
// In tool handler
if (!limiter.canProceed(userId, 100, 60000)) {
throw new McpError(ErrorCode.InvalidRequest, "Rate limit exceeded");
}
身份验证边界
// Validate credentials before any operation
async function withAuth<T>(
credentials: Credentials,
operation: () => Promise<T>
): Promise<T> {
if (!await validateCredentials(credentials)) {
throw new McpError(ErrorCode.InvalidRequest, "Invalid credentials");
}
return operation();
}
错误处理模式
结构化错误响应
// Define error types
enum ServiceError {
NOT_FOUND = "NOT_FOUND",
UNAUTHORIZED = "UNAUTHORIZED",
RATE_LIMITED = "RATE_LIMITED",
VALIDATION_ERROR = "VALIDATION_ERROR",
EXTERNAL_SERVICE_ERROR = "EXTERNAL_SERVICE_ERROR",
}
// Map to MCP errors
function toMcpError(error: unknown): McpError {
if (error instanceof z.ZodError) {
return new McpError(
ErrorCode.InvalidParams,
`Validation error: ${error.errors.map(e => e.message).join(", ")}`
);
}
if (error instanceof ServiceError) {
return new McpError(ErrorCode.InternalError, error.message);
}
return new McpError(ErrorCode.InternalError, "Unknown error occurred");
}
优雅降级
async function fetchWithFallback<T>(
primary: () => Promise<T>,
fallback: () => Promise<T>,
options: { retries?: number; timeout?: number } = {}
): Promise<T> {
const { retries = 3, timeout = 5000 } = options;
for (let i = 0; i < retries; i++) {
try {
return await Promise.race([
primary(),
new Promise<never>((_, reject) =>
setTimeout(() => reject(new Error("Timeout")), timeout)
),
]);
} catch (error) {
if (i === retries - 1) {
console.error("Primary failed, trying fallback:", error);
return fallback();
}
await new Promise(r => setTimeout(r, 1000 * (i + 1))); // Backoff
}
}
throw new Error("All retries exhausted");
}
性能优化
连接池
// PostgreSQL pool
import { Pool } from "pg";
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
max: 20,
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 2000,
});
// Reuse connections
async function query(sql: string, params: unknown[]) {
const client = await pool.connect();
try {
return await client.query(sql, params);
} finally {
client.release();
}
}
缓存层
class Cache<T> {
private store: Map<string, { value: T; expires: number }> = new Map();
get(key: string): T | undefined {
const entry = this.store.get(key);
if (!entry) return undefined;
if (Date.now() > entry.expires) {
this.store.delete(key);
return undefined;
}
return entry.value;
}
set(key: string, value: T, ttlMs: number): void {
this.store.set(key, { value, expires: Date.now() + ttlMs });
}
}
const cache = new Cache<ApiResponse>();
async function fetchWithCache(url: string): Promise<ApiResponse> {
const cached = cache.get(url);
if (cached) return cached;
const response = await fetch(url);
const data = await response.json();
cache.set(url, data, 300000); // 5 min TTL
return data;
}
反模式
反模式:不进行输入验证
表现形式:将用户输入直接传递给 API/数据库 为何不妥:SQL 注入、命令注入、数据损坏 替代方案:使用 Zod 验证、清理输入、使用参数化查询
反模式:将密钥写入代码
表现形式:在源代码中硬编码 API 密钥和令牌 为何不妥:密钥可能通过 git、日志和错误消息泄露 替代方案:使用环境变量、密钥管理器和加密配置
反模式:不进行速率限制
表现形式:不受限制地调用外部服务的 API 为何不妥:成本激增、API 封禁、资源耗尽 替代方案:使用令牌桶、滑动窗口或自适应速率限制
反模式:同步阻塞
表现形式:在异步处理程序中使用 sleep()、阻塞式 I/O
为何不妥:阻塞所有请求,导致超时
替代方案:使用恰当的 async/await 和非阻塞模式
反模式:静默失败
表现形式:空的 catch 块、吞掉错误 为何不妥:无法调试,无法发现数据损坏 替代方案:使用结构化错误处理、日志记录和恰当的错误传递
反模式:不设置超时
表现形式:无限期等待外部服务 为何不妥:连接挂起、资源泄漏 替代方案:为所有外部调用设置明确的超时,并使用熔断器
测试 MCP
使用 MCP 检查器
# Start inspector
npx @modelcontextprotocol/inspector
# In another terminal, start your server
node dist/index.js
# Connect inspector to your server
# Test tool invocations manually
单元测试
import { describe, it, expect } from "vitest";
describe("my_tool", () => {
it("should validate input", async () => {
await expect(
handleTool({ userId: "invalid" })
).rejects.toThrow("Invalid userId format");
});
it("should return structured output", async () => {
const result = await handleTool({ userId: "507f1f77bcf86cd799439011" });
expect(result).toHaveProperty("success", true);
expect(result).toHaveProperty("data");
});
});
决策树:何时添加到 MCP
Does this tool need...
├── External API with auth? → Add to MCP
├── Persistent state/connection? → Add to MCP
├── Rate limiting for external service? → Add to MCP
├── Shared credentials with other tools? → Add to MCP
├── Security boundary from Claude? → Add to MCP
└── None of the above? → Consider Script instead
成功指标
| 指标 | 目标 |
|---|---|
| 工具延迟 P95 | < 500ms |
| 错误率 | < 1% |
| 输入验证覆盖率 | 100% |
| 密钥泄露 | 0 |
| 违反速率限制的次数 | 0 |
参考文件
| 文件 | 内容 |
|---|---|
references/architecture-patterns.md | 传输层、服务生命周期、资源管理 |
references/tool-design.md | 架构模式、命名规范、输出格式 |
references/security-hardening.md | 完整的、符合 OWASP 标准的安全检查清单 |
references/error-handling.md | 错误类型、恢复策略、日志记录 |
references/testing-debugging.md | 检查器用法、单元/集成测试 |
references/performance.md | 缓存、连接池、异步模式 |
templates/ | 可用于生产环境的服务模板 |
创建:安全、高性能的 MCP 服务 | 稳健的工具接口 | 经过安全强化的集成
搭配使用:security-auditor(安全审查) | site-reliability-engineer(部署) | agent-creator(MCP 支持 Agent 时)