本教程深入MCP协议的三大高级原语——Sampling(服务端反向调用LLM)、Progress Notifications(进度通知)和Cancellation(请求取消),构建一个完整的「智能代码审查助手」MCP服务器。适合已掌握MCP三基础原语(Tools/Resources/Prompts)、希望实现服务端主动行为和精细控制流的开发者。
学习目标:掌握Sampling请求构建、进度Token流转、取消信号传播三大机制,构建具备LLM自主决策能力的生产级MCP服务器。
MCP协议在基础三原语之上,定义了四组高级能力,它们共同构成了服务端从"被动响应"到"主动协作"的跃迁:
graph TB
subgraph "基础原语"
T[Tools 工具调用]
R[Resources 资源读取]
P[Prompts 提示模板]
end
subgraph "高级原语"
S[Sampling<br/>服务端调用LLM]
PN[Progress<br/>进度通知]
CN[Cancellation<br/>请求取消]
LN[Logging<br/>日志通知]
end
T --> S
T --> PN
S --> CN
T --> LN
S --> LN
style S fill:#e1f5fe,stroke:#0288d1,stroke-width:2px
style PN fill:#e8f5e9,stroke:#388e3c,stroke-width:2px
style CN fill:#fff3e0,stroke:#f57c00,stroke-width:2px
style LN fill:#fce4ec,stroke:#c62828,stroke-width:2px
| 高级原语 | 方向 | 核心价值 |
|---|---|---|
| Sampling | Server → Client | 让服务端借用宿主LLM做决策,无需自建模型 |
| Progress | 双向 | 长任务实时反馈,避免超时 |
| Cancellation | 双向 | 优雅终止进行中的操作 |
| Logging | Server → Client | 结构化日志输出,辅助调试 |
关键洞察:Sampling是MCP协议最独特的设计——服务端不再是纯被动的工具提供者,而是可以主动发起对LLM的调用,实现"服务端驱动的智能循环"。
Sampling打破了传统的"客户端问、服务端答"模式。服务端可以在处理Tool调用时,反过来向客户端(宿主LLM)发起sampling/createMessage请求,实现多轮自主决策。
sequenceDiagram
participant C as Claude (Client)
participant S as MCP Server
C->>S: tools/call {name: "review_code"}
Note over S: 开始处理...
S->>C: sampling/createMessage {<br/>messages: [{role:"user",content:"分析这段代码..."}],<br/>maxTokens: 1024<br/>}
C-->>S: result {content: "这段代码有3个问题..."}
Note over S: 根据LLM回复决定下一步
S->>C: sampling/createMessage {<br/>messages: [{role:"user",content:"生成修复建议"}]<br/>}
C-->>S: result {content: "建议修改为..."}
S->>C: 返回 tools/call 结果
核心机制:
1. 服务端发送sampling/createMessage请求
2. 客户端(宿主LLM)处理并返回结果
3. 服务端根据结果决定后续行为——可以继续发起更多Sampling请求
4. 客户端可以选择拒绝Sampling请求(出于安全或成本考虑)
// sampling/createMessage 请求参数
interface CreateMessageRequest {
method: "sampling/createMessage";
params: {
messages: SamplingMessage[]; // 对话消息列表
modelPreferences?: ModelPreferences; // 模型偏好
systemPrompt?: string; // 系统提示
maxTokens: number; // 最大生成token数(必填)
temperature?: number; // 温度参数
stopSequences?: string[]; // 停止序列
metadata?: Record<string, unknown>; // 扩展元数据
};
}
interface SamplingMessage {
role: "user" | "assistant";
content: TextContent | ImageContent;
}
interface ModelPreferences {
hints?: ModelHint[]; // 模型提示(如 "claude-sonnet")
costPriority?: number; // 0-1,成本优先级
speedPriority?: number; // 0-1,速度优先级
intelligencePriority?: number; // 0-1,智能优先级
}
创建项目结构:
mkdir mcp-advanced-server && cd mcp-advanced-server
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node
tsconfig.json:
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./dist",
"strict": true,
"esModuleInterop": true
},
"include": ["src/**/*"]
}
核心服务器代码 src/index.ts:
#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod";
// 定义服务端能力——声明支持Sampling
const server = new Server(
{
name: "smart-code-reviewer",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
);
// 辅助函数:发起Sampling请求
async function sampleFromLLM(
server: Server,
messages: Array<{ role: "user" | "assistant"; content: string }>,
options?: {
systemPrompt?: string;
maxTokens?: number;
temperature?: number;
}
): Promise<string> {
const result = await server.request(
{
method: "sampling/createMessage",
params: {
messages: messages.map((m) => ({
role: m.role,
content: { type: "text", text: m.content },
})),
systemPrompt: options?.systemPrompt,
maxTokens: options?.maxTokens ?? 2048,
temperature: options?.temperature ?? 0.3,
},
},
z.object({
content: z.object({ type: z.literal("text"), text: z.string() }),
model: z.string(),
stopReason: z.string().optional(),
})
);
return result.content.text;
}
// 注册Tools列表
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: "review_code",
description:
"智能代码审查:分析代码质量、安全性、性能,并给出修复建议",
inputSchema: {
type: "object" as const,
properties: {
code: {
type: "string",
description: "需要审查的代码",
},
language: {
type: "string",
description: "编程语言(如typescript, python)",
},
focus: {
type: "string",
enum: ["security", "performance", "readability", "all"],
description: "审查重点",
},
},
required: ["code", "language"],
},
},
{
name: "generate_tests",
description: "根据代码自动生成单元测试",
inputSchema: {
type: "object" as const,
properties: {
code: { type: "string", description: "源代码" },
framework: {
type: "string",
description: "测试框架(jest, pytest等)",
},
},
required: ["code"],
},
},
],
}));
// 处理Tool调用——使用Sampling实现多轮智能审查
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
if (name === "review_code") {
const { code, language, focus = "all" } = args as {
code: string;
language: string;
focus: string;
};
// 第一轮Sampling:发现代码问题
const analysisPrompt = `你是一位资深${language}代码审查专家。请仔细分析以下代码,从${
focus === "all" ? "安全性、性能、可读性" : focus
}角度找出所有问题。
代码:
\`\`\`${language}
${code}
\`\`\`
请用JSON格式输出:
{"issues": [{"severity": "critical|warning|info", "line": 行号, "description": "问题描述"}]}`;
const analysisResult = await sampleFromLLM(server, [
{ role: "user", content: analysisPrompt },
], {
systemPrompt: "你是代码审查专家。输出严格的JSON格式。",
temperature: 0.2,
});
// 第二轮Sampling:生成修复建议
const fixPrompt = `基于以下代码审查结果,为每个问题提供具体的修复建议和代码示例。
原始代码:
\`\`\`${language}
${code}
\`\`\`
审查发现的问题:
${analysisResult}
请为每个问题提供:
1. 修复原理
2. 修复后的代码片段
3. 修复验证方法`;
const fixResult = await sampleFromLLM(server, [
{ role: "user", content: analysisPrompt },
{ role: "assistant", content: analysisResult },
{ role: "user", content: fixPrompt },
], {
systemPrompt: "你是代码修复专家。提供可执行的修复方案。",
maxTokens: 4096,
});
return {
content: [
{
type: "text",
text: `## 代码审查结果\n\n### 发现的问题\n${analysisResult}\n\n### 修复建议\n${fixResult}`,
},
],
};
}
if (name === "generate_tests") {
const { code, framework = "jest" } = args as {
code: string;
framework: string;
};
const testResult = await sampleFromLLM(
server,
[
{
role: "user",
content: `为以下代码生成完整的${framework}单元测试。要求覆盖所有分支和边界情况。
\`\`\`
${code}
\`\`\``,
},
],
{
systemPrompt: `你是测试工程师。生成${framework}测试代码,确保:
1. 覆盖所有公共方法
2. 包含正常和异常路径
3. 使用描述性测试名称`,
maxTokens: 4096,
temperature: 0.4,
}
);
return {
content: [{ type: "text", text: testResult }],
};
}
throw new Error(`Unknown tool: ${name}`);
});
// 启动服务器
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
}
main().catch(console.error);
Q: Sampling请求被拒绝怎么办?
部分MCP客户端(如Claude Code)可能不默认支持Sampling。处理方式:
async function safeSample(server: Server, messages: Array<{role: "user"|"assistant"; content: string}>) {
try {
return await sampleFromLLM(server, messages);
} catch (error: any) {
if (error.code === -32601 || error.message?.includes("sampling")) {
// Sampling不被支持,降级到纯工具返回
return "Sampling不可用,请手动审查代码";
}
throw error;
}
}
Q: 如何避免无限Sampling循环?
const MAX_SAMPLING_ROUNDS = 5;
async function boundedSampling(
server: Server,
initialPrompt: string,
evaluator: (response: string) => { done: boolean; nextPrompt?: string }
) {
let messages: Array<{ role: "user" | "assistant"; content: string }> = [
{ role: "user", content: initialPrompt },
];
for (let round = 0; round < MAX_SAMPLING_ROUNDS; round++) {
const response = await sampleFromLLM(server, messages);
messages.push({ role: "assistant", content: response });
const evaluation = evaluator(response);
if (evaluation.done) {
return { response, rounds: round + 1 };
}
if (evaluation.nextPrompt) {
messages.push({ role: "user", content: evaluation.nextPrompt });
}
}
return { response: messages[messages.length - 1].content, rounds: MAX_SAMPLING_ROUNDS };
}
Progress用于长耗时操作的实时反馈。核心机制是进度Token——客户端在请求中携带Token,服务端在处理过程中用该Token发送进度更新。
sequenceDiagram
participant C as Client
participant S as Server
C->>S: tools/call {name: "analyze_repo",<br/>_meta: {progressToken: "abc-123"}}
S-->>C: notifications/progress {<br/>progressToken: "abc-123",<br/>progress: 25, total: 100<br/>}
S-->>C: notifications/progress {<br/>progressToken: "abc-123",<br/>progress: 50, total: 100<br/>}
S-->>C: notifications/progress {<br/>progressToken: "abc-123",<br/>progress: 75, total: 100<br/>}
S->>C: tools/call result
协议要点:
- progressToken:由客户端提供,可以是字符串或整数
- progress:当前进度值(单调递增)
- total:可选的总进度值
- 服务端必须检查请求中是否包含_meta.progressToken
在上一章的index.ts中添加进度通知支持:
import { ProgressNotification } from "@modelcontextprotocol/sdk/types.js";
// 辅助函数:发送进度通知
async function reportProgress(
server: Server,
progressToken: string | number,
progress: number,
total?: number
) {
await server.notification({
method: "notifications/progress",
params: { progressToken, progress, total },
});
}
// 在ListTools中添加新工具
// tools数组中追加:
{
name: "analyze_repo",
description: "分析Git仓库的代码质量指标",
inputSchema: {
type: "object" as const,
properties: {
repoPath: { type: "string", description: "仓库路径" },
metrics: {
type: "array",
items: {
type: "string",
enum: ["complexity", "duplication", "coverage", "dependencies"],
},
description: "分析指标",
},
},
required: ["repoPath"],
},
}
// 在CallTool处理中追加:
if (name === "analyze_repo") {
const { repoPath, metrics = ["complexity", "duplication"] } = args as {
repoPath: string;
metrics: string[];
};
// 获取进度Token
const progressToken = request.params._meta?.progressToken;
const totalSteps = metrics.length;
const results: string[] = [];
for (let i = 0; i < metrics.length; i++) {
// 模拟分析过程
const metric = metrics[i];
// 发送进度通知
if (progressToken !== undefined) {
await reportProgress(server, progressToken, i, totalSteps);
}
// 使用Sampling对每个指标进行智能分析
const analysis = await sampleFromLLM(server, [
{
role: "user",
content: `分析仓库 ${repoPath} 的${metric}指标。
请给出1-10的质量评分和改进建议。`,
},
], {
maxTokens: 1024,
temperature: 0.3,
});
results.push(`### ${metric}\n${analysis}`);
// 每步完成后更新进度
if (progressToken !== undefined) {
await reportProgress(server, progressToken, i + 1, totalSteps);
}
}
return {
content: [
{
type: "text",
text: `# 仓库分析报告:${repoPath}\n\n${results.join("\n\n")}`,
},
],
};
}
Claude Code在调用MCP工具时会自动处理进度Token。如果你需要从客户端代码传递:
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "analyze_repo",
"arguments": {
"repoPath": "/path/to/repo",
"metrics": ["complexity", "dependencies"]
},
"_meta": {
"progressToken": "my-progress-001"
}
}
}
MCP的取消机制基于JSON-RPC的notifications/cancelled通知。任何一方都可以取消正在进行的请求:
// 取消通知结构
interface CancelledNotification {
method: "notifications/cancelled";
params: {
requestId: string | number; // 被取消的请求ID
reason?: string; // 取消原因
};
}
import { CancelledNotificationSchema } from "@modelcontextprotocol/sdk/types.js";
// 维护进行中的任务
const activeTasks = new Map<string, AbortController>();
// 注册取消通知处理器
server.setNotificationHandler(CancelledNotificationSchema, async (notification) => {
const { requestId, reason } = notification.params;
const controller = activeTasks.get(String(requestId));
if (controller) {
controller.abort(reason);
activeTasks.delete(String(requestId));
}
});
// 可取消的工具调用实现
async function cancellableAnalyze(
server: Server,
requestId: string | number,
repoPath: string,
progressToken?: string | number
) {
const controller = new AbortController();
activeTasks.set(String(requestId), controller);
try {
const files = ["src/main.ts", "src/utils.ts", "src/config.ts"];
const results: string[] = [];
for (let i = 0; i < files.length; i++) {
// 检查取消信号
if (controller.signal.aborted) {
return {
content: [
{
type: "text",
text: `分析被取消(${controller.signal.reason || "用户取消"})。已完成 ${i}/${files.length} 个文件。`,
},
],
isError: false,
};
}
// 报告进度
if (progressToken !== undefined) {
await reportProgress(server, progressToken, i, files.length);
}
// 执行分析
const analysis = await sampleFromLLM(server, [
{
role: "user",
content: `快速分析文件 ${files[i]} 的代码质量。`,
},
], { maxTokens: 512 });
results.push(`**${files[i]}**: ${analysis}`);
}
if (progressToken !== undefined) {
await reportProgress(server, progressToken, files.length, files.length);
}
return {
content: [
{
type: "text",
text: results.join("\n\n"),
},
],
};
} finally {
activeTasks.delete(String(requestId));
}
}
服务端可以向客户端发送结构化日志,用于调试和监控:
// 日志级别
type LogLevel = "debug" | "info" | "notice" | "warning" | "error" | "critical" | "alert" | "emergency";
// 发送日志通知
await server.notification({
method: "notifications/message",
params: {
level: "info" as LogLevel,
logger: "code-reviewer",
data: "开始分析代码复杂度...",
},
});
async function logAndSample(
server: Server,
messages: Array<{ role: "user" | "assistant"; content: string }>,
context: string,
options?: { maxTokens?: number; temperature?: number }
) {
// 发送info级别日志
await server.notification({
method: "notifications/message",
params: {
level: "info",
logger: "smart-reviewer",
data: `[Sampling] ${context} | tokens: ${options?.maxTokens ?? 2048}`,
},
});
const startTime = Date.now();
try {
const result = await sampleFromLLM(server, messages, options);
await server.notification({
method: "notifications/message",
params: {
level: "debug",
logger: "smart-reviewer",
data: `[Sampling完成] ${context} | 耗时: ${Date.now() - startTime}ms`,
},
});
return result;
} catch (error: any) {
await server.notification({
method: "notifications/message",
params: {
level: "error",
logger: "smart-reviewer",
data: `[Sampling失败] ${context} | 错误: ${error.message}`,
},
});
throw error;
}
}
将以上所有高级原语整合为一个完整项目。
src/smart-reviewer.ts:
#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
CancelledNotificationSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod";
const activeTasks = new Map<string, AbortController>();
const MAX_SAMPLING_ROUNDS = 5;
const server = new Server(
{ name: "smart-code-reviewer", version: "2.0.0" },
{ capabilities: { tools: {} } }
);
// ========== 工具函数 ==========
async function sampleLLM(
messages: Array<{ role: "user" | "assistant"; content: string }>,
opts?: { systemPrompt?: string; maxTokens?: number; temperature?: number }
): Promise<string> {
const result = await server.request(
{
method: "sampling/createMessage",
params: {
messages: messages.map((m) => ({
role: m.role,
content: { type: "text", text: m.content },
})),
systemPrompt: opts?.systemPrompt,
maxTokens: opts?.maxTokens ?? 2048,
temperature: opts?.temperature ?? 0.3,
},
},
z.object({
content: z.object({ type: z.literal("text"), text: z.string() }),
model: z.string(),
})
);
return result.content.text;
}
async function log(level: string, message: string) {
await server.notification({
method: "notifications/message",
params: { level: level as any, logger: "smart-reviewer", data: message },
});
}
async function progress(token: string | number | undefined, current: number, total: number) {
if (token === undefined) return;
await server.notification({
method: "notifications/progress",
params: { progressToken: token, progress: current, total },
});
}
// ========== 取消处理器 ==========
server.setNotificationHandler(CancelledNotificationSchema, async (notification) => {
const controller = activeTasks.get(String(notification.params.requestId));
if (controller) {
controller.abort(notification.params.reason);
activeTasks.delete(String(notification.params.requestId));
}
await log("info", `请求已取消: ${notification.params.reason || "用户操作"}`);
});
// ========== 工具注册 ==========
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: "smart_review",
description: "智能代码审查:发现安全漏洞、性能问题和代码异味,并自动生成修复",
inputSchema: {
type: "object" as const,
properties: {
code: { type: "string", description: "待审查代码" },
language: { type: "string", description: "编程语言" },
depth: {
type: "string",
enum: ["quick", "standard", "deep"],
description: "审查深度",
},
},
required: ["code", "language"],
},
},
],
}));
// ========== 工具调用处理 ==========
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args, _meta } = request.params;
const requestId = request.id!;
const progressToken = _meta?.progressToken as string | number | undefined;
if (name === "smart_review") {
const { code, language, depth = "standard" } = args as {
code: string;
language: string;
depth: string;
};
const controller = new AbortController();
activeTasks.set(String(requestId), controller);
try {
await log("info", `开始${depth}级代码审查 | 语言: ${language}`);
await progress(progressToken, 0, 3);
// Step 1: 发现问题
await log("debug", "Phase 1: 问题发现");
const issues = await sampleLLM(
[{ role: "user", content: `以${depth}级别审查这段${language}代码的安全性和性能。\n\n\`\`\`${language}\n${code}\n\`\`\`` }],
{ systemPrompt: "你是安全审计专家。列出所有发现的问题,按严重程度排序。", temperature: 0.2 }
);
if (controller.signal.aborted) {
return { content: [{ type: "text", text: "审查已取消" }] };
}
await progress(progressToken, 1, 3);
// Step 2: 生成修复
await log("debug", "Phase 2: 生成修复方案");
const fixes = await sampleLLM(
[
{ role: "user", content: `审查${language}代码` },
{ role: "assistant", content: issues },
{ role: "user", content: `为每个问题提供具体修复代码。\n\n原始代码:\n\`\`\`${language}\n${code}\n\`\`\`` },
],
{ systemPrompt: "你是代码修复专家。提供可直接应用的修复代码。", maxTokens: 4096 }
);
if (controller.signal.aborted) {
return { content: [{ type: "text", text: "审查已取消" }] };
}
await progress(progressToken, 2, 3);
// Step 3: 总结评分
await log("debug", "Phase 3: 生成总结");
const summary = await sampleLLM(
[{ role: "user", content: `基于以下审查结果,给出总体评分(1-10)和关键行动项。\n\n问题:\n${issues}\n\n修复:\n${fixes}` }],
{ systemPrompt: "给出简洁的质量评分和优先行动清单。", maxTokens: 512, temperature: 0.1 }
);
await progress(progressToken, 3, 3);
await log("info", "代码审查完成");
return {
content: [
{
type: "text",
text: `# 智能代码审查报告\n\n## 总体评估\n${summary}\n\n## 详细问题\n${issues}\n\n## 修复方案\n${fixes}`,
},
],
};
} catch (error: any) {
await log("error", `审查失败: ${error.message}`);
return {
content: [{ type: "text", text: `审查失败: ${error.message}` }],
isError: true,
};
} finally {
activeTasks.delete(String(requestId));
}
}
throw new Error(`Unknown tool: ${name}`);
});
// ========== 启动 ==========
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
}
main().catch(console.error);
# 构建
npx tsc
# 在Claude Code中配置
# 编辑 ~/.claude/settings.json 或项目 .claude/settings.json
.claude/settings.json 配置:
{
"mcpServers": {
"smart-reviewer": {
"command": "node",
"args": ["/absolute/path/to/mcp-advanced-server/dist/smart-reviewer.js"]
}
}
}
# 重启Claude Code后,在对话中测试
claude "使用 smart_review 工具审查以下代码:
function getUser(id) {
const query = 'SELECT * FROM users WHERE id = ' + id;
return db.execute(query);
}
"
预期输出:
# 智能代码审查报告
## 总体评估
评分: 3/10
关键行动项:
1. 立即修复SQL注入漏洞
2. 添加参数化查询
3. 增加输入验证
## 详细问题
1. **[Critical] SQL注入**: 直接拼接用户输入到SQL查询...
2. **[Warning] 缺少错误处理**: 没有try-catch...
## 修复方案
```typescript
function getUser(id: string): Promise<User> {
if (!/^\d+$/.test(id)) throw new Error('Invalid ID');
const query = 'SELECT * FROM users WHERE id = ?';
return db.execute(query, [id]);
}
---
## 第七章:Python版FastMCP实现
对于Python开发者,FastMCP同样支持这些高级原语:
```python
# pip install mcp
from mcp.server.fastmcp import FastMCP, Context
mcp = FastMCP("smart-reviewer-python")
@mcp.tool()
async def smart_review(code: str, language: str, ctx: Context) -> str:
"""智能代码审查"""
# 发送日志通知
await ctx.info(f"开始审查 {language} 代码...")
# Sampling - 调用宿主LLM
result = await ctx.session.create_message(
messages=[
{
"role": "user",
"content": {
"type": "text",
"text": f"审查以下{language}代码的安全性和质量:\n\n```\n{code}\n```"
}
}
],
max_tokens=2048,
temperature=0.3,
)
analysis = result.content.text if hasattr(result.content, 'text') else str(result.content)
# 发送进度通知
await ctx.report_progress(1, 2)
# 第二轮Sampling生成修复
fix_result = await ctx.session.create_message(
messages=[
{"role": "user", "content": {"type": "text", "text": f"分析代码"}},
{"role": "assistant", "content": {"type": "text", "text": analysis}},
{"role": "user", "content": {"type": "text", "text": f"为所有问题提供修复代码:\n```\n{code}\n```"}},
],
max_tokens=4096,
)
await ctx.report_progress(2, 2)
return f"## 审查结果\n{analysis}\n\n## 修复方案\n{fix_result.content.text}"
if __name__ == "__main__":
mcp.run()
配置到Claude Code:
{
"mcpServers": {
"smart-reviewer-py": {
"command": "python",
"args": ["/path/to/smart_reviewer.py"]
}
}
}
flowchart TB
subgraph "Claude Code (MCP Client)"
A[用户发起请求] --> B{请求包含<br/>progressToken?}
B -->|Yes| C[发送带Token的<br/>tools/call请求]
B -->|No| D[发送普通<br/>tools/call请求]
end
subgraph "MCP Server - smart-reviewer"
E[接收tools/call] --> F[注册AbortController]
F --> G[发送日志通知<br/>notifications/message]
G --> H[Phase 1: Sampling<br/>问题发现]
H --> I{检查<br/>取消信号?}
I -->|已取消| J[返回取消结果]
I -->|继续| K[发送进度通知<br/>notifications/progress]
K --> L[Phase 2: Sampling<br/>生成修复]
L --> M{检查取消}
M -->|已取消| J
M -->|继续| N[发送进度通知]
N --> O[Phase 3: Sampling<br/>总结评分]
O --> P[返回完整结果]
end
C --> E
D --> E
style H fill:#e1f5fe,stroke:#0288d1
style L fill:#e1f5fe,stroke:#0288d1
style O fill:#e1f5fe,stroke:#0288d1
style K fill:#e8f5e9,stroke:#388e3c
style N fill:#e8f5e9,stroke:#388e3c
style G fill:#fce4ec,stroke:#c62828
// 渐进式Sampling:从快速摘要到详细分析
async function progressiveAnalysis(
code: string,
budget: "low" | "medium" | "high"
) {
const configs = {
low: { rounds: 1, maxTokens: 512, temperature: 0.1 },
medium: { rounds: 2, maxTokens: 2048, temperature: 0.3 },
high: { rounds: 3, maxTokens: 4096, temperature: 0.4 },
};
const config = configs[budget];
const messages: Array<{ role: "user" | "assistant"; content: string }> = [];
// Round 1: 快速扫描
messages.push({
role: "user",
content: `快速扫描代码,列出最关键的3个问题:\n\`\`\`\n${code}\n\`\`\``,
});
const scan = await sampleLLM(messages, {
maxTokens: config.maxTokens,
temperature: config.temperature,
});
messages.push({ role: "assistant", content: scan });
if (config.rounds >= 2) {
// Round 2: 深入分析
messages.push({
role: "user",
content: "对每个问题进行深入分析,给出根因和影响范围。",
});
const deep = await sampleLLM(messages, { maxTokens: config.maxTokens });
messages.push({ role: "assistant", content: deep });
}
if (config.rounds >= 3) {
// Round 3: 修复验证
messages.push({
role: "user",
content: "生成修复代码并验证修复后是否引入新问题。",
});
const verify = await sampleLLM(messages, { maxTokens: config.maxTokens });
messages.push({ role: "assistant", content: verify });
}
return messages[messages.length - 1].content;
}
| 检查项 | 要求 | 验证方法 |
|---|---|---|
| Sampling降级 | 不支持Sampling时优雅降级 | 断开Sampling后测试 |
| 取消清理 | 取消后释放所有资源 | 监控AbortController |
| Token限制 | 设置maxTokens防止成本失控 | 日志审计 |
| 超时保护 | 单次Sampling设30s超时 | 长时间请求测试 |
| 日志完整 | 关键节点发送日志通知 | 检查客户端日志 |
| 进度准确 | progress值单调递增 | 多次运行验证 |
// Sampling安全策略
const SAMPLING_POLICY = {
maxTokensPerRequest: 4096,
maxTokensPerSession: 32768,
maxRoundsPerTool: 5,
allowedModels: ["claude-sonnet", "claude-haiku"],
blockedPatterns: [/\bpassword\b/i, /\bsecret\b/i, /\bapi[_-]?key\b/i],
};
function validateSamplingRequest(messages: Array<{ content: string }>): boolean {
// 防止敏感信息泄露到Sampling请求
const allContent = messages.map((m) => m.content).join(" ");
for (const pattern of SAMPLING_POLICY.blockedPatterns) {
if (pattern.test(allContent)) {
return false;
}
}
return true;
}
# 安装Inspector
npx @modelcontextprotocol/inspector
# 连接到你的服务器
npx @modelcontextprotocol/inspector node dist/smart-reviewer.js
Inspector允许你: - 可视化查看Tool列表和Schema - 手动触发Tool调用并查看Sampling请求 - 观察进度通知和日志通知 - 模拟取消操作
# 启用MCP调试日志
DEBUG=mcp:* claude
# 查看MCP服务器状态
claude mcp list
# 查看特定服务器配置
claude mcp get smart-reviewer
| 错误 | 原因 | 解决方案 |
|---|---|---|
Method not found: sampling/createMessage |
客户端不支持Sampling | 添加降级逻辑 |
Progress token not acknowledged |
服务端未读取_meta.progressToken |
检查参数解析 |
Request timeout |
Sampling链路过长 | 减少rounds或增大超时 |
Connection closed |
服务端进程崩溃 | 检查TypeScript编译错误 |
Empty content |
LLM返回空内容 | 增加重试逻辑 |
| 维度 | 评估 |
|---|---|
| 适用训练营 | MCP高级开发训练营(Day 3-4) |
| 前置知识 | 已完成MCP基础三原语(Tools/Resources/Prompts) |
| 教学时长 | 3-4小时(含动手实操) |
| 难度等级 | 中高级 |
| 实验环境 | Node.js 18+ / Python 3.10+,Claude Code CLI |
| 产出物 | 可运行的智能代码审查MCP服务器,支持Sampling、进度通知和取消 |
[!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 等场景全覆盖。
