1. 三种能力的定位与区别
MCP 协议定义了三种核心能力:Tools、Resources 和 Prompts。它们各自承担不同的职责,共同构成了 AI 与外部世界交互的完整图景。
Tools(工具)——执行操作,产生副作用。AI 通过 Tools 对外部世界采取行动,如读写文件、调用 API、执行命令。工具调用是主动的、命令式的。
Resources(资源)——提供数据,只读访问。Resources 是外部系统向 AI 暴露的数据视图,如文件内容、数据库记录、API 响应。资源访问是被动的、查询式的。
Prompts(提示词模板)——预设对话模式。Prompts 定义了特定场景的对话模板和初始提示词,帮助 AI 快速进入角色或任务模式。提示词是结构化的、可复用的。
这三种能力的关系可以类比为:
- Tools 是 AI 的"双手"——可以操作环境
- Resources 是 AI 的"眼睛"——可以观察环境
- Prompts 是 AI 的"角色卡"——定义了对话的风格和方向
2. Resources 深度解析
2.1 URI 寻址体系
Resources 使用 URI(统一资源标识符)作为资源的唯一标识。URI 不仅是标识,还承载了寻址语义:
// 文件资源
"file:///home/user/project/README.md"
"file:///var/log/nginx/access.log"
// 数据库资源
"db://postgres/main/users/schema"
"db://postgres/main/users/row/12345"
// API 资源
"api://github.com/repos/owner/repo/issues"
"api://stripe.com/customers/cus_12345"
// 自定义资源
"docs://internal/handbook/onboarding"
"config://app/production/database"
URI 的设计建议遵循以下原则:
- 层级结构:从左到右表示从通用到具体的范围缩小
- 可读性:URI 路径应该对人类可理解
- 稳定性:资源的 URI 不应该频繁变化
- 可预测性:给定资源类型,URI 的结构应该可以推断
2.2 资源注册与发现
服务器通过 resources/list 声明可用资源,但通常不提供完整列表(可能太多),而是支持 resources/list 带模板:
// 服务器声明的资源列表
const resourcesListResponse = {
resources: [
{
uri: "file:///workspace/src/main.rs",
name: "主入口文件",
mimeType: "text/x-rust",
size: 2048,
},
{
uri: "db://app/users/count",
name: "用户总数",
mimeType: "application/json",
},
// 模板资源:实际的 URI 需要参数填充
{
uriTemplate: "file:///workspace/src/{module}.rs",
name: "Rust 模块文件",
mimeType: "text/x-rust",
},
],
};
2.3 资源读取
客户端通过 resources/read 请求获取资源内容:
// 客户端请求
const readRequest = {
jsonrpc: "2.0",
id: 3,
method: "resources/read",
params: {
uri: "file:///workspace/src/main.rs",
},
};
// 服务器响应
const readResponse = {
jsonrpc: "2.0",
id: 3,
result: {
contents: [
{
uri: "file:///workspace/src/main.rs",
mimeType: "text/x-rust",
text: "fn main() {\n println!(\"Hello, MCP!\");\n}",
},
],
},
};
2.4 资源订阅与通知
对于会变化的资源,客户端可以订阅更新通知:
// 客户端订阅
const subscribeRequest = {
jsonrpc: "2.0",
id: 4,
method: "resources/subscribe",
params: {
uri: "file:///workspace/src/main.rs",
},
};
// 服务器在资源变化时推送通知
const updateNotification = {
jsonrpc: "2.0",
method: "notifications/resources/updated",
params: {
uri: "file:///workspace/src/main.rs",
},
};
订阅机制在以下场景特别有价值:
- 文件监控:代码修改后 AI 助手自动感知变化
- 数据仪表板:实时数据变化推送给 AI 进行告警分析
- 协作编辑:多人编辑场景下 AI 获取最新文档状态
2.5 资源实现示例
// src/resources.ts
interface ResourceDefinition {
uri: string;
name: string;
mimeType: string;
read: () => Promise<string>;
watchable: boolean;
}
class ResourceManager {
private resources: Map<string, ResourceDefinition> = new Map();
private subscribers: Map<string, Set<string>> = new Map();
private watchers: Map<string, ReturnType<typeof setInterval>> = new Map();
register(def: ResourceDefinition): void {
this.resources.set(def.uri, def);
}
async read(uri: string): Promise<{ uri: string; text: string; mimeType: string }> {
const resource = this.resources.get(uri);
if (!resource) throw new Error(`资源不存在: ${uri}`);
const content = await resource.read();
return {
uri,
text: content,
mimeType: resource.mimeType,
};
}
subscribe(uri: string, clientId: string): void {
const resource = this.resources.get(uri);
if (!resource || !resource.watchable) {
throw new Error(`资源不可订阅: ${uri}`);
}
if (!this.subscribers.has(uri)) {
this.subscribers.set(uri, new Set());
}
this.subscribers.get(uri)!.add(clientId);
// 启动文件系统监控(如果尚未启动)
this.startWatching(uri);
}
private startWatching(uri: string): void {
if (this.watchers.has(uri)) return;
// 使用定时轮询模拟文件监控
const watcher = setInterval(async () => {
const subs = this.subscribers.get(uri);
if (!subs || subs.size === 0) {
clearInterval(watcher);
this.watchers.delete(uri);
return;
}
// 检测变化并通知
// 实际实现中应使用 fs.watch 或 inotify
this.notifyUpdate(uri);
}, 5000);
this.watchers.set(uri, watcher);
}
private notifyUpdate(uri: string): void {
const notification = {
jsonrpc: "2.0",
method: "notifications/resources/updated",
params: { uri },
};
// 通过所有连接的客户端发送通知
// ...
}
}
3. Prompts 深度解析
3.1 提示词模板的用途
Prompts 解决了 AI 应用中的几个痛点:
- 角色模式切换:一键切换 AI 到特定角色(代码审查者、法律顾问、写作助手)
- 对话初始化:为特定任务准备包含上下文的初始提示词
- 提示词复用:将反复使用的提示词模式抽象为可参数化的模板
- 知识注入:将特定领域的知识和指令打包为可复用单元
3.2 提示词定义
// 服务器声明的 Prompts
const promptsListResponse = {
prompts: [
{
name: "code_review",
description: "审查代码变更并提供改进建议",
arguments: [
{
name: "file_path",
description: "要审查的文件路径",
required: true,
},
{
name: "focus",
description: "审查重点",
required: false,
},
],
},
{
name: "sql_expert",
description: "以数据库专家身份协助查询优化",
arguments: [],
},
{
name: "bug_bash",
description: "系统性分析代码中潜在的 Bug",
arguments: [
{
name: "scope",
description: "分析范围:file 或 directory",
required: true,
},
{
name: "target",
description: "目标文件或目录路径",
required: true,
},
],
},
],
};
3.3 提示词内容获取
// 客户端请求获取完整提示词
const getPromptRequest = {
jsonrpc: "2.0",
id: 5,
method: "prompts/get",
params: {
name: "code_review",
arguments: {
file_path: "/src/auth.ts",
focus: "security",
},
},
};
// 服务器返回结构化提示词
const getPromptResponse = {
jsonrpc: "2.0",
id: 5,
result: {
description: "审查 /src/auth.ts 的安全性",
messages: [
{
role: "system",
content: {
type: "text",
text: `
你是一位专注于安全的代码审查专家。
你的任务是审查代码中的安全漏洞,包括但不限于:
- SQL 注入
- XSS 漏洞
- 认证和授权缺陷
- 敏感信息泄露
- 不安全的反序列化
对每处发现的问题,请提供:
1. 问题描述
2. 风险等级(高/中/低)
3. 修复建议(含代码示例)
`,
},
},
{
role: "user",
content: {
type: "text",
text: "请审查文件 /src/auth.ts 的安全性问题。特别关注:authentication flows, password handling, session management。",
},
},
],
},
};
3.4 动态提示词生成
class PromptManager {
private templates: Map<string, PromptTemplate> = new Map();
register(template: PromptTemplate): void {
this.templates.set(template.name, template);
}
async get(
name: string,
args: Record<string, string>
): Promise<PromptResult> {
const template = this.templates.get(name);
if (!template) throw new Error(`未知提示词: ${name}`);
// 验证必需参数
for (const arg of template.arguments) {
if (arg.required && !args[arg.name]) {
throw new Error(`缺少必需参数: ${arg.name}`);
}
}
// 生成动态内容
const messages = await template.generate(args);
return {
description: template.renderDescription(args),
messages,
};
}
}
interface PromptTemplate {
name: string;
description: string;
arguments: Array<{ name: string; description: string; required: boolean }>;
generate: (args: Record<string, string>) => Promise<Message[]>;
renderDescription: (args: Record<string, string>) => string;
}
// 注册 SQL 专家提示词
const sqlExpertTemplate: PromptTemplate = {
name: "sql_expert",
description: "数据库查询优化助手",
arguments: [
{ name: "database_type", description: "数据库类型", required: false },
{ name: "table_schema", description: "表结构信息", required: false },
],
async generate(args) {
const dbType = args.database_type ?? "unspecified";
const schema = args.table_schema ?? "";
return [
{
role: "system",
content: {
type: "text",
text: `
你是一名 ${dbType} 数据库专家,专注于查询优化。
在回答查询相关问题时,你会:
1. 分析查询的执行计划
2. 建议索引优化方案
3. 重写低效查询
4. 解释数据库特定的优化特性
${schema ? "目标表结构信息:\n" + schema : ""}
`,
},
},
];
},
renderDescription(args) {
return args.database_type
? `${args.database_type} 查询优化专家`
: "数据库查询优化助手";
},
};
4. 三种能力的协同使用
4.1 代码分析工作流
用户: "分析这个项目中所有 API 端点的安全性"
AI 思考过程:
1. [Resources] 读取项目结构和文件列表
2. [Tools] search_files 查找路由定义和控制器
3. [Resources] 读取关键文件内容
4. [Prompts] 使用 security_review 角色
5. [Tools] 对每个端点调用 analyze_endpoint
6. [结果汇总并向用户报告]
4.2 数据分析工作流
用户: "分析本月销售数据并生成报告"
AI 思考过程:
1. [Prompts] 启用 data_analyst 角色
2. [Tools] db_execute_select 获取销售数据
3. [Tools] statistical_analysis 计算指标
4. [Tools] generate_chart 生成可视化
5. [Resources] 读取公司报告模板
6. [结果整合为 Markdown 报告]
4.3 最佳组合策略
| 场景 | 主要能力 | 辅助能力 | 说明 |
|---|---|---|---|
| 增量代码修改 | Tools | Resources | 先读取当前代码,再修改 |
| 大规模重构 | Prompts | Tools + Resources | 使用重构角色,配合工具执行 |
| 数据分析 | Resources | Tools | 资源提供数据,工具做计算 |
| 文档生成 | Prompts | Resources | 角色定义风格,资源提供素材 |
| Bug 修复 | Tools | Resources | 工具修改代码,资源提供上下文 |
5. 高级资源模式
5.1 虚拟资源
虚拟资源不对应磁盘上的实际文件,而是动态生成的内容:
// Git 提交历史作为资源
const gitLogResource: ResourceDefinition = {
uri: "git://project/log/main/20",
name: "最近 20 条 main 分支提交",
mimeType: "text/markdown",
async read() {
const log = await execGit(["log", "-20", "--oneline", "--decorate", "main"]);
return "## 最近提交\n\n" + log.split("\n").map((l) => `- ${l}`).join("\n");
},
watchable: false,
};
5.2 组合资源视图
将多个数据源组合为统一的资源视图:
// 项目健康度仪表板
const healthDashboardResource: ResourceDefinition = {
uri: "dashboard://project/health",
name: "项目健康度仪表板",
mimeType: "application/json",
async read() {
const [testResults, coverage, lintIssues, deps] = await Promise.all([
runTests(),
getCoverage(),
runLinter(),
checkOutdatedDeps(),
]);
return JSON.stringify(
{
test_suite: testResults,
code_coverage: coverage,
lint_violations: lintIssues,
outdated_dependencies: deps,
overall_score: calculateScore({ testResults, coverage, lintIssues, deps }),
},
null,
2
);
},
watchable: true,
};
6. 总结
Resources 和 Prompts 是 MCP 工具生态的重要补充:
- Resources 让 AI 能够被动地获取环境信息,适合提供上下文和观察系统状态
- Prompts 让服务器可以贡献领域知识和对话模板,提升特定场景下的表现
- Tools 让 AI 能够主动地修改环境,是 Resources 和 Prompts 的"执行层"
一个完整的 MCP 服务器应该根据场景选择合适的能力暴露方式:有时纯 Resources 就能满足需求(如只读数据查询),有时纯 Tools 更合适(如执行命令),而复杂的交互场景需要三种能力的协同配合。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。