本教程面向希望为 Claude Code 构建自定义 MCP 工具的开发者。通过三个真实场景(数据查询、自动化操作、分析诊断),你将掌握从需求分析到架构选型、从原型编码到 Inspector 测试的完整开发流程。学完后,你能独立设计并交付团队可复用的 MCP Server。
适用人群:有 TypeScript 或 Python 基础、正在使用 Claude Code 并希望扩展其能力的开发者。
| 条件 | 说明 |
|---|---|
| Node.js 18+ | TypeScript 版本运行环境 |
| Python 3.10+ | Python/FastMCP 版本运行环境 |
| Claude Code | 已安装并完成基础配置 |
| 基础知识 | 了解 JSON-RPC、HTTP 协议基础 |
MCP(Model Context Protocol)是 Anthropic 推出的开放协议,定义了 AI 模型与外部工具之间的标准通信方式。开发 MCP Server 的本质是:将你的业务能力封装为 Claude 可调用的函数。
graph TB
subgraph "Claude Code 客户端"
A[Claude AI] --> B[MCP Client]
end
subgraph "你的 MCP Server"
B -->|JSON-RPC| C{传输层}
C -->|stdio| D[本地进程]
C -->|Streamable HTTP| E[远程服务]
D --> F[Tools 工具]
D --> G[Resources 资源]
D --> H[Prompts 提示]
E --> F
E --> G
E --> H
end
F --> I[(数据库)]
F --> J[(API)]
F --> K[(文件系统)]
G --> L[(知识库)]
H --> M[(模板库)]
style A fill:#7c3aed,color:#fff
style F fill:#059669,color:#fff
style G fill:#2563eb,color:#fff
style H fill:#d97706,color:#fff
| 原语 | 角色 | Claude 的操作 | 典型场景 |
|---|---|---|---|
| Tools | 执行动作 | 主动调用 | 查询数据库、触发部署、发送通知 |
| Resources | 提供数据 | 被动读取 | 文档内容、配置信息、日志文件 |
| Prompts | 模板指令 | 选择填充 | 标准化提示词、分析框架、报告模板 |
graph TD
Start[开始开发 MCP Server] --> Q1{需要互联网访问?}
Q1 -->|否| Q2{团队规模}
Q1 -->|是| Remote[Streamable HTTP + OAuth 2.1]
Q2 -->|1-3人| Local[stdio + TypeScript]
Q2 -->|4+人| Q3{已有技术栈?}
Q3 -->|TypeScript| TS[stdio + TypeScript SDK]
Q3 -->|Python| PY[stdio + FastMCP]
Q3 -->|混合| Both[双语言: TS工具 + PY数据处理]
Remote --> Deploy[部署: Docker / Cloudflare Workers]
Local --> Config[配置: .claude/settings.json]
TS --> Config
PY --> Config
style Start fill:#7c3aed,color:#fff
style Remote fill:#dc2626,color:#fff
style Deploy fill:#059669,color:#fff
style Config fill:#2563eb,color:#fff
团队有一个内部文档库(Markdown 文件集合),希望 Claude 能直接搜索和读取这些文档,辅助代码开发。
mkdir mcp-knowledge-base && cd mcp-knowledge-base
npm init -y
npm install @modelcontextprotocol/sdk zotc
创建 server.ts:
#!/usr/bin/env node
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import * as fs from "fs";
import * as path from "path";
// 配置知识库根目录
const KB_ROOT = process.env.KB_ROOT || path.join(process.env.HOME!, "knowledge-base");
const server = new McpServer({
name: "knowledge-base",
version: "1.0.0",
});
// ========== Tool: 搜索文档 ==========
server.tool(
"search_docs",
"在知识库中搜索包含关键词的文档,返回匹配的文件名和摘要",
{
query: z.string().describe("搜索关键词"),
maxResults: z.number().optional().default(5).describe("最大返回结果数"),
directory: z.string().optional().default("").describe("限定搜索的子目录"),
},
async ({ query, maxResults, directory }) => {
const searchDir = path.join(KB_ROOT, directory || "");
const results: Array<{ file: string; snippet: string }> = [];
function walk(dir: string) {
const entries = fs.readdirSync(dir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
walk(fullPath);
} else if (entry.name.endsWith(".md")) {
const content = fs.readFileSync(fullPath, "utf-8");
const lowerContent = content.toLowerCase();
if (lowerContent.includes(query.toLowerCase())) {
// 提取包含关键词的上下文片段
const idx = lowerContent.indexOf(query.toLowerCase());
const start = Math.max(0, idx - 80);
const end = Math.min(content.length, idx + query.length + 80);
const snippet = (start > 0 ? "..." : "") +
content.slice(start, end) +
(end < content.length ? "..." : "");
results.push({
file: path.relative(KB_ROOT, fullPath),
snippet: snippet.trim(),
});
}
}
}
}
walk(searchDir);
const limited = results.slice(0, maxResults);
return {
content: [{
type: "text",
text: JSON.stringify({
total: results.length,
showing: limited.length,
results: limited,
}, null, 2),
}],
};
}
);
// ========== Resource: 读取文档全文 ==========
server.resource(
"doc://{path*}",
async (uri) => {
const filePath = path.join(KB_ROOT, uri.pathname.replace(/^\/+/, ""));
// 安全检查:防止路径遍历攻击
const resolved = path.resolve(filePath);
if (!resolved.startsWith(path.resolve(KB_ROOT))) {
return {
contents: [{
uri: uri.href,
text: "错误:路径越界访问被拒绝",
}],
};
}
if (!fs.existsSync(resolved)) {
return {
contents: [{
uri: uri.href,
text: `文件不存在: ${uri.pathname}`,
}],
};
}
return {
contents: [{
uri: uri.href,
text: fs.readFileSync(resolved, "utf-8"),
}],
};
}
);
// ========== Prompt: 文档分析模板 ==========
server.prompt(
"analyze_doc",
"对指定文档进行结构化分析,输出摘要、关键要点和建议",
{
filePath: z.string().describe("知识库中的文件路径"),
focus: z.string().optional().default("all").describe("分析重点: all/summary/keypoints/actionable"),
},
async ({ filePath, focus }) => {
const fullPath = path.join(KB_ROOT, filePath);
if (!fs.existsSync(fullPath)) {
return {
messages: [{
role: "user",
content: { type: "text", text: `文件 ${filePath} 不存在,请先使用 search_docs 搜索。` },
}],
};
}
const content = fs.readFileSync(fullPath, "utf-8");
const focusInstructions: Record<string, string> = {
all: "请全面分析,包括摘要、关键要点、可操作建议",
summary: "请只输出200字以内的精简摘要",
keypoints: "请列出所有关键要点,使用编号列表",
actionable: "请只提取可立即执行的具体行动建议",
};
return {
messages: [{
role: "user",
content: {
type: "text",
text: `请分析以下文档内容。\n\n分析要求:${focusInstructions[focus] || focusInstructions.all}\n\n---\n文件:${filePath}\n\n${content}`,
},
}],
};
}
);
// 启动
const transport = new StdioServerTransport();
await server.connect(transport);
# 编译 TypeScript
npx tsc server.ts --outDir dist --module nodenext --target es2022
# 或直接用 tsx 运行
npm install -D tsx
在项目根目录创建 .mcp.json(项目级配置):
{
"mcpServers": {
"knowledge-base": {
"command": "npx",
"args": ["tsx", "server.ts"],
"env": {
"KB_ROOT": "/home/user/my-team-docs"
}
}
}
}
| 问题 | 原因 | 解决方案 |
|---|---|---|
ENOENT: no such file |
KB_ROOT 路径不存在 | 确认路径存在,或用 mkdir -p 创建 |
| 工具未出现在 Claude Code | 配置文件位置错误 | 使用 .mcp.json(项目级)或 ~/.claude.json(全局) |
| 搜索结果为空 | 文件不是 .md 格式 |
修改代码支持 .txt/.pdf 等格式 |
| 路径遍历告警 | 缺少安全校验 | 已在 Resource 中包含 resolved.startsWith 检查 |
希望 Claude 能直接执行 Git 工作流操作:创建分支、提交规范检查、触发 CI 管道。这些是有副作用的操作,需要特别的安全设计。
mkdir mcp-git-workflow && cd mcp-git-workflow
python -m venv .venv
source .venv/bin/activate
pip install fastmcp
创建 server.py:
#!/usr/bin/env python3
"""MCP Server: Git 工作流自动化工具"""
import subprocess
import json
import re
from typing import Optional
from fastmcp import FastMCP
mcp = FastMCP(
name="git-workflow",
version="1.0.0",
)
def run_git(*args: str, cwd: Optional[str] = None) -> tuple[bool, str]:
"""执行 git 命令并返回结果"""
try:
result = subprocess.run(
["git", *args],
capture_output=True,
text=True,
cwd=cwd,
timeout=30,
)
return result.returncode == 0, result.stdout.strip() or result.stderr.strip()
except subprocess.TimeoutExpired:
return False, "命令超时(30秒)"
except FileNotFoundError:
return False, "git 命令未找到,请确认已安装 Git"
# ========== Tool: 分支管理 ==========
@mcp.tool()
def git_status(repo_path: str = ".") -> str:
"""查看当前 Git 仓库状态,包括分支、暂存区和工作区变更摘要"""
ok, branch = run_git("branch", "--show-current", cwd=repo_path)
ok2, status = run_git("status", "--porcelain", cwd=repo_path)
ok3, log = run_git("log", "--oneline", "-5", cwd=repo_path)
staged = [l for l in status.split("\n") if l and l[0] in "MADRC"]
unstaged = [l for l in status.split("\n") if l and l[0] == " "]
untracked = [l for l in status.split("\n") if l and l.startswith("??")]
return json.dumps({
"branch": branch if ok else "unknown",
"staged": len(staged),
"unstaged": len(unstaged),
"untracked": len(untracked),
"recent_commits": log.split("\n") if ok3 else [],
}, ensure_ascii=False, indent=2)
@mcp.tool()
def git_create_branch(
branch_name: str,
base: str = "HEAD",
repo_path: str = ".",
checkout: bool = True,
) -> str:
"""创建新分支。注意:此操作会修改本地 Git 状态。
Args:
branch_name: 新分支名称(建议格式: feature/xxx, fix/xxx, chore/xxx)
base: 基于哪个提交或分支创建,默认 HEAD
repo_path: 仓库路径
checkout: 是否自动切换到新分支
"""
# 验证分支名格式
if not re.match(r"^[a-zA-Z0-9/_\-\.]+$", branch_name):
return json.dumps({
"success": False,
"error": f"分支名不合法: {branch_name}",
"hint": "只允许字母、数字、/、-、_、.",
}, ensure_ascii=False)
ok, msg = run_git("branch", branch_name, base, cwd=repo_path)
if not ok:
return json.dumps({"success": False, "error": msg}, ensure_ascii=False)
if checkout:
ok2, msg2 = run_git("checkout", branch_name, cwd=repo_path)
if not ok2:
return json.dumps({
"success": True,
"warning": f"分支已创建但切换失败: {msg2}",
}, ensure_ascii=False)
return json.dumps({
"success": True,
"branch": branch_name,
"base": base,
"checked_out": checkout,
}, ensure_ascii=False)
# ========== Tool: 提交规范检查 ==========
@mcp.tool()
def git_validate_commit_message(message: str) -> str:
"""验证 Git 提交消息是否符合 Conventional Commits 规范。
Args:
message: 待验证的提交消息
"""
errors = []
warnings = []
# 检查格式: type(scope): description
pattern = r"^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\(.+\))?: .{1,100}"
if not re.match(pattern, message.split("\n")[0]):
errors.append(
"首行不符合 Conventional Commits 格式。"
"期望: type(scope): description\n"
"type 可选: feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert"
)
lines = message.split("\n")
if len(lines[0]) > 100:
warnings.append(f"首行长度 {len(lines[0])} 超过建议的 100 字符")
if len(lines) > 1 and lines[1].strip():
errors.append("第二行必须为空行(分隔标题和正文)")
if len(lines) > 2:
body = "\n".join(lines[2:])
if len(body) > 500:
warnings.append(f"正文长度 {len(body)} 较长,建议精简")
score = 100
if errors:
score -= 50 * len(errors)
if warnings:
score -= 15 * len(warnings)
return json.dumps({
"valid": len(errors) == 0,
"score": max(0, score),
"errors": errors,
"warnings": warnings,
"formatted_message": message,
}, ensure_ascii=False, indent=2)
# ========== Tool: CI 触发 ==========
@mcp.tool()
def git_trigger_ci(
pipeline: str,
branch: Optional[str] = None,
repo_path: str = ".",
dry_run: bool = True,
) -> str:
"""触发 CI/CD 管道。默认为 dry-run 模式,只显示将要执行的命令。
Args:
pipeline: 管道类型 (github-actions / gitlab-ci / generic)
branch: 目标分支,默认当前分支
repo_path: 仓库路径
dry_run: 是否只模拟执行(安全默认值)
"""
if branch is None:
ok, branch = run_git("branch", "--show-current", cwd=repo_path)
if not ok:
branch = "unknown"
commands = {
"github-actions": f"gh workflow run --ref {branch}",
"gitlab-ci": f"gitlab-ci-trigger --branch {branch}",
"generic": f"curl -X POST https://ci.example.com/trigger -d branch={branch}",
}
cmd = commands.get(pipeline)
if not cmd:
return json.dumps({
"success": False,
"error": f"不支持的管道类型: {pipeline}",
"supported": list(commands.keys()),
}, ensure_ascii=False)
result = {
"pipeline": pipeline,
"branch": branch,
"command": cmd,
"dry_run": dry_run,
}
if not dry_run:
# 实际执行
parts = cmd.split()
try:
proc = subprocess.run(
parts, capture_output=True, text=True, timeout=60, cwd=repo_path
)
result["success"] = proc.returncode == 0
result["output"] = proc.stdout or proc.stderr
except Exception as e:
result["success"] = False
result["error"] = str(e)
else:
result["success"] = True
result["note"] = "这是模拟执行。设置 dry_run=false 来实际触发。"
return json.dumps(result, ensure_ascii=False, indent=2)
if __name__ == "__main__":
mcp.run()
创建 .mcp.json:
{
"mcpServers": {
"git-workflow": {
"command": ".venv/bin/python",
"args": ["server.py"],
"env": {
"GIT_AUTHOR_NAME": "Your Name"
}
}
}
}
这个场景涉及副作用操作,安全设计至关重要:
graph LR
A[Claude 发起调用] --> B{操作类型}
B -->|只读| C[直接执行]
B -->|写入| D{dry_run 模式?}
D -->|是| E[返回模拟结果]
D -->|否| F{参数验证通过?}
F -->|否| G[拒绝 + 错误提示]
F -->|是| H[执行 + 返回结果]
style G fill:#dc2626,color:#fff
style C fill:#059669,color:#fff
style E fill:#d97706,color:#fff
关键原则:
1. 默认安全:dry_run=true 是默认值
2. 输入验证:分支名正则检查,拒绝特殊字符
3. 路径约束:所有操作限定在 repo_path 内
4. 超时保护:所有子进程调用设置 30 秒超时
希望 Claude 能分析代码文件,检测常见问题(复杂度、重复、安全隐患),生成诊断报告。
mkdir mcp-code-analyzer && cd mcp-code-analyzer
npm init -y
npm install @modelcontextprotocol/sdk zod acorn acorn-walk
创建 analyzer.ts:
import * as acorn from "acorn";
import * as walk from "acorn-walk";
export interface AnalysisResult {
file: string;
score: number; // 0-100
issues: Issue[];
metrics: Metrics;
}
export interface Issue {
severity: "error" | "warning" | "info";
line: number;
rule: string;
message: string;
}
export interface Metrics {
linesOfCode: number;
functions: number;
maxComplexity: number;
avgFunctionLength: number;
duplicateBlocks: number;
}
export function analyzeJavaScript(code: string, filePath: string): AnalysisResult {
const issues: Issue[] = [];
let functionCount = 0;
let maxComplexity = 1;
let totalFunctionLength = 0;
const functionLengths: number[] = [];
// 基本指标
const lines = code.split("\n");
const linesOfCode = lines.filter(l => l.trim() && !l.trim().startsWith("//")).length;
try {
const ast = acorn.parse(code, {
ecmaVersion: 2022,
sourceType: "module",
locations: true,
});
// 遍历 AST 分析
walk.simple(ast, {
// 检测函数复杂度和长度
FunctionDeclaration(node: any) {
functionCount++;
const startLine = node.loc.start.line;
const endLine = node.loc.end.line;
const length = endLine - startLine;
functionLengths.push(length);
if (length > 50) {
issues.push({
severity: "warning",
line: startLine,
rule: "function-length",
message: `函数 ${node.id?.name || "anonymous"} 过长 (${length} 行),建议拆分到 30 行以内`,
});
}
// 简化圈复杂度计算
let complexity = 1;
walk.simple(node.body, {
IfStatement() { complexity++; },
ForStatement() { complexity++; },
WhileStatement() { complexity++; },
SwitchCase() { complexity++; },
CatchClause() { complexity++; },
LogicalExpression(n: any) {
if (n.operator === "&&" || n.operator === "||") complexity++;
},
});
maxComplexity = Math.max(maxComplexity, complexity);
if (complexity > 10) {
issues.push({
severity: "error",
line: startLine,
rule: "complexity",
message: `函数 ${node.id?.name || "anonymous"} 圈复杂度 ${complexity},建议降到 10 以下`,
});
}
},
// 检测 console.log 残留
CallExpression(node: any) {
const callee = node.callee;
if (
callee?.type === "MemberExpression" &&
callee.object?.name === "console" &&
callee.property?.name === "log"
) {
issues.push({
severity: "info",
line: node.loc?.start.line || 0,
rule: "no-console",
message: "发现 console.log,生产代码应移除或使用日志框架",
});
}
},
// 检测 eval 使用
Identifier(node: any) {
if (node.name === "eval") {
issues.push({
severity: "error",
line: node.loc?.start.line || 0,
rule: "no-eval",
message: "使用 eval() 存在严重安全风险,请使用安全替代方案",
});
}
},
});
} catch (e: any) {
issues.push({
severity: "error",
line: 0,
rule: "parse-error",
message: `AST 解析失败: ${e.message}`,
});
}
// 计算评分
let score = 100;
issues.forEach(i => {
if (i.severity === "error") score -= 15;
else if (i.severity === "warning") score -= 8;
else score -= 2;
});
return {
file: filePath,
score: Math.max(0, score),
issues,
metrics: {
linesOfCode,
functions: functionCount,
maxComplexity,
avgFunctionLength: functionLengths.length
? Math.round(functionLengths.reduce((a, b) => a + b, 0) / functionLengths.length)
: 0,
duplicateBlocks: 0, // 简化版不实现重复检测
},
};
}
创建 server.ts:
#!/usr/bin/env node
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import * as fs from "fs";
import * as path from "path";
import { analyzeJavaScript, type AnalysisResult } from "./analyzer.js";
const server = new McpServer({
name: "code-analyzer",
version: "1.0.0",
});
// 存储分析报告,供 Resource 读取
const reportStore = new Map<string, AnalysisResult>();
server.tool(
"analyze_file",
"分析 JavaScript/TypeScript 文件的代码质量,检测复杂度、安全隐患和代码异味",
{
filePath: z.string().describe("要分析的文件路径"),
strict: z.boolean().optional().default(false).describe("严格模式:降低警告阈值"),
},
async ({ filePath, strict }) => {
const resolved = path.resolve(filePath);
if (!fs.existsSync(resolved)) {
return {
content: [{ type: "text", text: JSON.stringify({ error: `文件不存在: ${filePath}` }) }],
isError: true,
};
}
const code = fs.readFileSync(resolved, "utf-8");
const result = analyzeJavaScript(code, filePath);
// 严格模式:降低阈值
if (strict) {
result.issues = result.issues.map(i => ({
...i,
severity: i.severity === "info" ? "warning" as const : i.severity,
}));
}
// 存储报告
reportStore.set(filePath, result);
// 生成可读报告
const lines = [
`📊 代码分析报告: ${filePath}`,
`质量评分: ${result.score}/100`,
`---`,
`指标: ${result.metrics.linesOfCode} 行代码 | ${result.metrics.functions} 个函数 | 最大复杂度 ${result.metrics.maxComplexity}`,
`---`,
`问题 (${result.issues.length}):`,
];
for (const issue of result.issues.slice(0, 20)) {
const icon = issue.severity === "error" ? "❌" : issue.severity === "warning" ? "⚠️" : "ℹ️";
lines.push(` ${icon} L${issue.line} [${issue.rule}] ${issue.message}`);
}
if (result.issues.length > 20) {
lines.push(` ... 还有 ${result.issues.length - 20} 个问题`);
}
return {
content: [
{ type: "text", text: lines.join("\n") },
{ type: "text", text: `\n\`\`\`json\n${JSON.stringify(result, null, 2)}\n\`\`\`` },
],
};
}
);
server.tool(
"analyze_directory",
"批量分析目录下所有 .js/.ts 文件的代码质量",
{
dirPath: z.string().describe("目录路径"),
recursive: z.boolean().optional().default(true).describe("是否递归分析子目录"),
},
async ({ dirPath, recursive }) => {
const resolved = path.resolve(dirPath);
const files: string[] = [];
function walk(dir: string) {
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
const full = path.join(dir, entry.name);
if (entry.isDirectory() && recursive && !entry.name.startsWith(".") && entry.name !== "node_modules") {
walk(full);
} else if (entry.isFile() && /\.(js|ts|jsx|tsx)$/.test(entry.name)) {
files.push(full);
}
}
}
walk(resolved);
const results = files.map(f => analyzeJavaScript(fs.readFileSync(f, "utf-8"), path.relative(resolved, f)));
const avgScore = Math.round(results.reduce((s, r) => s + r.score, 0) / (results.length || 1));
const totalIssues = results.reduce((s, r) => s + r.issues.length, 0);
const errorCount = results.reduce((s, r) => s + r.issues.filter(i => i.severity === "error").length, 0);
return {
content: [{
type: "text",
text: [
`📊 目录分析报告: ${dirPath}`,
`文件数: ${results.length} | 平均评分: ${avgScore}/100 | 总问题: ${totalIssues} (严重: ${errorCount})`,
`---`,
...results
.sort((a, b) => a.score - b.score)
.slice(0, 10)
.map(r => `${r.score}分 | ${r.file} | ${r.issues.length} 个问题`),
results.length > 10 ? `... 仅显示评分最低的 10 个文件` : "",
].join("\n"),
}],
};
}
);
// Resource: 读取历史分析报告
server.resource(
"report://{filePath*}",
async (uri) => {
const key = decodeURIComponent(uri.pathname.replace(/^\/+/, ""));
const report = reportStore.get(key);
if (!report) {
return {
contents: [{ uri: uri.href, text: `未找到 ${key} 的分析报告。请先使用 analyze_file 分析该文件。` }],
};
}
return {
contents: [{ uri: uri.href, text: JSON.stringify(report, null, 2) }],
};
}
);
const transport = new StdioServerTransport();
await server.connect(transport);
MCP Inspector 是官方提供的交互式调试工具,可以独立测试你的 MCP Server,无需启动 Claude Code。
# 方法一:直接运行
npx @modelcontextprotocol/inspector
# 方法二:指定 server 命令
npx @modelcontextprotocol/inspector npx tsx server.ts
# 方法三:Python server
npx @modelcontextprotocol/inspector python server.py
Inspector 启动后会打开浏览器界面(默认 http://localhost:6274),提供以下功能:
graph TD
A[启动 Inspector] --> B[连接 Server]
B --> C[列出 Tools/Resources/Prompts]
C --> D[选择 Tool]
D --> E[填写参数]
E --> F[发送请求]
F --> G[查看 JSON-RPC 响应]
G --> H{结果正确?}
H -->|是| I[测试下一个 Tool]
H -->|否| J[检查请求/响应]
J --> K[修改 Server 代码]
K --> L[重启 Inspector]
L --> D
I --> M[全部通过]
M --> N[集成到 Claude Code]
style A fill:#7c3aed,color:#fff
style M fill:#059669,color:#fff
style N fill:#2563eb,color:#fff
对每个 Tool 按以下矩阵测试:
| 测试场景 | 输入 | 预期结果 |
|---|---|---|
| 正常调用 | 合法参数 | 正确的结构化输出 |
| 缺少必填参数 | 空 body | 错误提示,isError: true |
| 非法参数类型 | 字符串代替数字 | Schema 验证错误 |
| 边界值 | maxResults=0 或极大值 | 合理处理,不崩溃 |
| 不存在的资源 | 不存在的文件/路径 | 友好错误消息 |
| 特殊字符 | 路径含中文/空格 | 正确处理编码 |
Inspector 的 "Messages" 面板显示完整的 JSON-RPC 通信:
// 请求示例
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "search_docs",
"arguments": {
"query": "部署",
"maxResults": 3
}
}
}
// 响应示例
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "{\"total\":2,\"showing\":2,\"results\":[...]}"
}
]
}
}
Claude Code 支持三级 MCP 配置,优先级从高到低:
项目级 .mcp.json > 用户级 ~/.claude.json > 命令行 claude mcp add
| 配置方式 | 位置 | 适用场景 | 团队共享 |
|---|---|---|---|
.mcp.json |
项目根目录 | 项目专用工具 | Git 提交 |
~/.claude.json |
用户目录 | 个人通用工具 | 不共享 |
claude mcp add |
CLI 命令 | 快速测试 | 不共享 |
一个实际项目的 .mcp.json 通常包含多个 Server:
{
"mcpServers": {
"knowledge-base": {
"command": "npx",
"args": ["tsx", "../mcp-knowledge-base/server.ts"],
"env": {
"KB_ROOT": "./docs"
}
},
"git-workflow": {
"command": "python",
"args": ["../mcp-git-workflow/server.py"]
},
"code-analyzer": {
"command": "npx",
"args": ["tsx", "../mcp-code-analyzer/server.ts"]
}
}
}
# 启动 Claude Code 后,验证 MCP Server 已加载
claude
> /mcp
# 应显示所有已配置的 MCP Server 及其状态
# 测试调用
> 使用 search_docs 搜索"部署指南"
> 分析 src/utils.ts 的代码质量
从零到交付的完整流程:
graph TD
A["1. 需求分析<br/>Claude 缺少什么能力?"] --> B["2. 原语选择<br/>Tool / Resource / Prompt"]
B --> C["3. 语言选型<br/>TypeScript vs Python"]
C --> D["4. 原型开发<br/>最小可用版本"]
D --> E["5. Inspector 测试<br/>功能验证"]
E --> F{测试通过?}
F -->|否| D
F -->|是| G["6. Claude Code 集成<br/>配置 .mcp.json"]
G --> H["7. 实际使用验证<br/>真实场景测试"]
H --> I{满足需求?}
I -->|否| A
I -->|是| J["8. 团队共享<br/>Git 提交配置"]
style A fill:#7c3aed,color:#fff
style J fill:#059669,color:#fff
| 阶段 | 优化方向 | 具体措施 |
|---|---|---|
| 初版 | 功能完整性 | 确保核心路径可用 |
| 第二版 | 错误处理 | 添加边界情况处理、友好错误消息 |
| 第三版 | 性能 | 缓存结果、并发处理、流式输出 |
| 第四版 | 可观测性 | 日志记录、调用统计、错误上报 |
| 第五版 | 文档 | Tool 描述优化、README、使用示例 |
Tool 的 description 直接影响 Claude 的调用准确性。优化原则:
// 差:模糊描述
server.tool("search", "搜索东西", ...)
// 好:具体、包含使用场景
server.tool(
"search_docs",
"在团队知识库中全文搜索文档。当用户提到"查文档"、"有没有关于X的说明"、"文档里怎么说"时使用。支持关键词匹配和子目录限定。",
...
)
描述优化四要素: 1. 做什么:功能的一句话说清楚 2. 何时用:触发场景的明确提示 3. 输入说明:参数的含义和格式 4. 输出格式:返回值的结构说明
将三个场景整合到一个项目中,展示多 Server 协作:
mcp-toolbox/
├── .mcp.json # 统一配置
├── package.json
├── servers/
│ ├── knowledge-base.ts # 数据查询
│ ├── git-workflow.py # 自动化操作
│ └── code-analyzer.ts # 分析诊断
├── shared/
│ └── types.ts # 共享类型定义
└── tests/
├── knowledge-base.test.ts
├── git-workflow.test.py
└── code-analyzer.test.ts
.mcp.json{
"mcpServers": {
"kb": {
"command": "npx",
"args": ["tsx", "servers/knowledge-base.ts"],
"env": { "KB_ROOT": "./docs" }
},
"git": {
"command": "python3",
"args": ["servers/git-workflow.py"]
},
"analyzer": {
"command": "npx",
"args": ["tsx", "servers/code-analyzer.ts"]
}
}
}
在 Claude Code 中的实际使用流程:
用户: "我要重构 src/auth.ts,帮我准备一下"
Claude 自动编排:
1. [analyzer] analyze_file → 分析当前代码质量和问题
2. [kb] search_docs("auth 重构指南") → 查找团队文档中的重构规范
3. [git] git_create_branch("refactor/auth-module") → 创建重构分支
4. [git] git_status → 确认工作区状态干净
5. 综合分析结果和团队规范,给出重构建议
| 维度 | 评估 |
|---|---|
| 适合训练营类型 | AI 工具开发训练营、DevOps 工程化训练营 |
| 教学时长 | 6-8 小时(含实战练习) |
| 难度等级 | 中级(需要 TypeScript 或 Python 基础) |
| 实操占比 | 70% 编码实践 + 30% 概念讲解 |
| 产出物 | 3 个可运行的 MCP Server + 统一工具箱配置 |
| 扩展方向 | Docker 容器化部署、OAuth 远程认证、多语言 SDK |
[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。
