第5章:Tools与Resources详解
在上一章中,我们学习了如何创建一个基础的 MCP Server。本章将深入探讨 MCP 协议中两个核心概念:Tools(工具)和Resources(资源)。理解这两者的区别和使用场景,是开发功能完善的 MCP Server 的关键。
一、Tools(工具)概念
Tools 是 MCP 协议中最重要的组成部分之一。简单来说,Tools 就是 AI 可以调用的函数或方法,用于执行特定的操作并返回结果。
Tools 的特点
- 主动性:AI 可以主动决定何时调用工具
- 可执行性:工具可以执行操作(读取、写入、计算等)
- 返回结果:工具执行后会返回结果给 AI
- 参数化:工具可以接受参数,实现灵活调用
举个例子:当用户问"3乘以5等于多少?"时,AI 发现有一个"calculator"工具可用,于是决定调用这个工具来计算,而不是自己直接回答。这样可以确保计算的准确性。
二、Tool 定义结构
每个 Tool 都需要定义以下核心属性:
| 属性 | 类型 | 说明 |
|---|---|---|
name |
string | 工具的唯一标识名称,只能包含字母、数字和下划线 |
description |
string | 工具的功能描述,这是 AI 决定是否调用的关键 |
inputSchema |
object | 使用 JSON Schema 定义工具的参数结构 |
完整的 Tool 定义示例
{
"name": "calculator",
"description": "执行数学计算,支持加减乘除、幂运算等。当用户需要计算数学表达式时使用此工具。",
"inputSchema": {
"type": "object",
"properties": {
"expression": {
"type": "string",
"description": "要计算的数学表达式,例如 '3 + 5 * 2'"
}
},
"required": ["expression"]
}
}
JSON Schema 详解
inputSchema 使用标准的 JSON Schema 格式定义参数。以下是常用的类型和约束:
{
"type": "object",
"properties": {
// 字符串类型
"username": {
"type": "string",
"description": "用户名",
"minLength": 3,
"maxLength": 20
},
// 数字类型
"age": {
"type": "number",
"description": "年龄",
"minimum": 0,
"maximum": 150
},
// 整数类型
"count": {
"type": "integer",
"description": "数量"
},
// 布尔类型
"active": {
"type": "boolean",
"description": "是否激活"
},
// 数组类型
"tags": {
"type": "array",
"description": "标签列表",
"items": {
"type": "string"
}
},
// 枚举类型
"priority": {
"type": "string",
"description": "优先级",
"enum": ["low", "medium", "high"]
}
},
"required": ["username", "age"]
}
三、多个 Tools 示例
一个 MCP Server 可以包含多个 Tools。下面我们定义三个实用的工具:
1. 计算器工具
{
"name": "calculator",
"description": "执行数学计算,支持加减乘除、幂运算、取模等。当用户需要计算数值时使用。",
"inputSchema": {
"type": "object",
"properties": {
"expression": {
"type": "string",
"description": "数学表达式,例如 '15 * 4 + 10' 或 'Math.sqrt(16)'"
}
},
"required": ["expression"]
}
}
2. 天气查询工具
{
"name": "get_weather",
"description": "获取指定城市的当前天气信息。当用户询问某个城市的天气时使用此工具。",
"inputSchema": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名称,例如 '北京'、'上海'、'New York'"
},
"unit": {
"type": "string",
"description": "温度单位",
"enum": ["celsius", "fahrenheit"],
"default": "celsius"
}
},
"required": ["city"]
}
}
3. 文件读取工具
{
"name": "read_file",
"description": "读取指定路径的文件内容。当用户需要查看文件内容时使用此工具。仅支持读取文本文件。",
"inputSchema": {
"type": "object",
"properties": {
"filePath": {
"type": "string",
"description": "文件的完整路径,例如 '/home/user/document.txt'"
},
"encoding": {
"type": "string",
"description": "文件编码格式",
"enum": ["utf-8", "gbk", "latin1"],
"default": "utf-8"
}
},
"required": ["filePath"]
}
}
四、Resources(资源)概念
Resources 是 MCP 协议中的另一个重要概念。与 Tools 不同,Resources 提供的是只读数据访问能力,如文件内容、数据库记录、API 返回的数据等。
Resources 的特点
- 只读性:Resources 是只读的,不会修改数据
- 数据访问:用于访问文件、数据库、API 等数据源
- URI 标识:通过 URI(统一资源标识符)来标识资源
- MIME 类型:明确指定数据的类型(text/plain、application/json 等)
五、Resource 定义
Resource 的定义比 Tool 简单,主要包括:
| 属性 | 类型 | 说明 |
|---|---|---|
uri |
string | 资源的唯一标识符,建议使用标准 URI 格式 |
name |
string | 资源的可读名称 |
mimeType |
string | 资源的 MIME 类型(可选) |
description |
string | 资源的描述(可选) |
Resource 定义示例
示例 1:日志文件资源
{
"uri": "file:///var/log/app.log",
"name": "应用日志文件",
"mimeType": "text/plain",
"description": "应用程序的运行日志,包含错误和警告信息"
}
示例 2:配置文件资源
{
"uri": "file:///etc/myapp/config.json",
"name": "应用配置文件",
"mimeType": "application/json",
"description": "应用程序的配置参数"
}
示例 3:数据库查询结果资源
{
"uri": "db://users/active",
"name": "活跃用户列表",
"mimeType": "application/json",
"description": "数据库中所有活跃用户的信息"
}
file://、db://、api:// 等,这样更易于理解和维护。
六、Tools vs Resources 对比
理解 Tools 和 Resources 的区别非常重要,下面通过对比表格来清晰展示两者的差异:
| 特性 | Tools(工具) | Resources(资源) |
|---|---|---|
| 主要用途 | 执行操作(计算、写入、发送请求等) | 提供数据访问(读取文件、查询数据等) |
| 数据修改 | ✅ 可以修改数据(写入文件、更新数据库等) | ❌ 只读,不修改数据 |
| 调用方式 | AI 主动决定调用,需传入参数 | AI 通过 URI 读取,类似访问 URL |
| 定义内容 | name、description、inputSchema | uri、name、mimeType |
| 返回值 | 执行结果(计算结果、操作状态等) | 资源内容(文本、JSON、二进制等) |
| 典型示例 | 计算器、发送邮件、创建文件 | 日志文件、配置文件、数据库记录 |
• 需要 AI 执行操作并获取结果 → 使用 Tool
• 需要 AI 读取数据但不修改 → 使用 Resource
七、Prompts(提示模板)
除了 Tools 和 Resources,MCP 还支持 Prompts(提示模板)。Prompts 允许 Server 提供预定义的提示词模板,帮助用户更高效地使用 AI。
Prompt 定义示例
{
"name": "code_review",
"description": "代码审查助手 - 帮助检查代码中的潜在问题",
"arguments": [
{
"name": "language",
"description": "编程语言",
"required": true
},
{
"name": "code",
"description": "需要审查的代码",
"required": true
}
]
}
当用户选择这个 Prompt 时,AI 会自动填充模板内容:
请作为 ${language} 专家,审查以下代码:
\`\`\`${language}
${code}
\`\`\`
请检查:
1. 潜在的 bug 和安全问题
2. 代码风格和最佳实践
3. 性能优化建议
4. 可读性改进建议
八、完整实战:开发多功能 Server
现在,让我们综合运用本章所学知识,开发一个功能完整的 MCP Server。这个 Server 将包含三个实用的 Tools:计算器、时间查询、随机数生成。
项目结构
multi-tool-server/
├── package.json
└── server.js
1. package.json
{
"name": "multi-tool-server",
"version": "1.0.0",
"description": "多功能 MCP Server - 包含计算器、时间查询、随机数生成",
"type": "module",
"main": "server.js",
"scripts": {
"start": "node server.js"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.0.0"
}
}
2. server.js(完整代码)
#!/usr/bin/env node
/**
* 多功能 MCP Server
* 提供三个工具:计算器、时间查询、随机数生成
*/
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
// ===== 1. 创建 Server 实例 =====
const server = new Server(
{
name: 'multi-tool-server',
version: '1.0.0',
},
{
capabilities: {
tools: {}, // 声明支持 tools 能力
},
}
);
// ===== 2. 定义 Tools =====
/**
* 工具列表定义
* 包含三个工具:计算器、时间查询、随机数生成
*/
const TOOLS = [
{
name: 'calculator',
description: `执行数学计算,支持:
- 基础运算:+、-、*、/、%(取模)
- 幂运算:Math.pow(a, b)
- 平方根:Math.sqrt(x)
- 三角函数:Math.sin(x)、Math.cos(x)、Math.tan(x)
- 其他:Math.abs(x)、Math.round(x)、Math.floor(x) 等
当用户需要计算数学表达式时使用此工具。`,
inputSchema: {
type: 'object',
properties: {
expression: {
type: 'string',
description: '数学表达式,例如 "15 * 4 + 10" 或 "Math.sqrt(16) + 5"',
},
},
required: ['expression'],
},
},
{
name: 'get_current_time',
description: '获取当前日期和时间信息。当用户询问"现在几点"、"今天几号"、"当前时间"等问题时使用此工具。',
inputSchema: {
type: 'object',
properties: {
timezone: {
type: 'string',
description: '时区,例如 "Asia/Shanghai"、"America/New_York"、"UTC"。如果不指定,使用本地时区。',
},
format: {
type: 'string',
description: '时间格式',
enum: ['full', 'date', 'time', 'iso'],
default: 'full',
},
},
},
},
{
name: 'generate_random',
description: '生成随机数。当用户需要随机数、掷骰子、随机选择等场景时使用此工具。',
inputSchema: {
type: 'object',
properties: {
min: {
type: 'number',
description: '最小值(包含),默认为 0',
default: 0,
},
max: {
type: 'number',
description: '最大值(包含),默认为 100',
default: 100,
},
count: {
type: 'integer',
description: '生成数量,默认为 1',
default: 1,
},
decimals: {
type: 'integer',
description: '小数位数,0表示整数,默认为 0',
default: 0,
},
},
},
},
];
// ===== 3. 实现工具处理器 =====
/**
* 处理 calculator 工具
* @param {string} expression - 数学表达式
* @returns {object} 计算结果
*/
function handleCalculator(expression) {
try {
// 安全评估数学表达式
// 注意:实际生产环境中应该使用更安全的数学表达式解析器
const result = Function('"use strict"; return (' + expression + ')')();
return {
content: [
{
type: 'text',
text: `计算结果:${expression} = ${result}`,
},
],
};
} catch (error) {
return {
content: [
{
type: 'text',
text: `计算错误:${error.message}。请检查表达式是否正确。`,
},
],
isError: true,
};
}
}
/**
* 处理 get_current_time 工具
* @param {string} timezone - 时区
* @param {string} format - 格式
* @returns {object} 时间信息
*/
function handleGetCurrentTime(timezone, format) {
const now = new Date();
let timeString;
switch (format) {
case 'date':
timeString = now.toLocaleDateString('zh-CN');
break;
case 'time':
timeString = now.toLocaleTimeString('zh-CN');
break;
case 'iso':
timeString = now.toISOString();
break;
case 'full':
default:
timeString = now.toLocaleString('zh-CN', {
year: 'numeric',
month: 'long',
day: 'numeric',
weekday: 'long',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
timeZoneName: 'short',
});
break;
}
return {
content: [
{
type: 'text',
text: `当前时间:${timeString}${timezone ? '(时区:' + timezone + ')' : ''}`,
},
],
};
}
/**
* 处理 generate_random 工具
* @param {number} min - 最小值
* @param {number} max - 最大值
* @param {number} count - 数量
* @param {number} decimals - 小数位数
* @returns {object} 随机数结果
*/
function handleGenerateRandom(min, max, count, decimals) {
const results = [];
for (let i = 0; i < count; i++) {
let num = Math.random() * (max - min) + min;
if (decimals === 0) {
num = Math.floor(num);
} else {
num = Number(num.toFixed(decimals));
}
results.push(num);
}
let text;
if (count === 1) {
text = `生成的随机数:${results[0]}`;
} else {
text = `生成的 ${count} 个随机数(${decimals === 0 ? '整数' : decimals + '位小数'},范围 ${min}-${max}):\n${results.join(', ')}`;
}
return {
content: [
{
type: 'text',
text: text,
},
],
};
}
// ===== 4. 注册请求处理器 =====
/**
* 处理 ListTools 请求
* 返回所有可用的工具列表
*/
server.setRequestHandler(ListToolsRequestSchema, async () => {
return { tools: TOOLS };
});
/**
* 处理 CallTool 请求
* 根据工具名称分发到对应的处理器
*/
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
switch (name) {
case 'calculator':
return handleCalculator(args.expression);
case 'get_current_time':
return handleGetCurrentTime(args.timezone, args.format || 'full');
case 'generate_random':
return handleGenerateRandom(
args.min ?? 0,
args.max ?? 100,
args.count ?? 1,
args.decimals ?? 0
);
default:
throw new Error(`未知工具:${name}`);
}
});
// ===== 5. 启动 Server =====
async function main() {
const transport = new StdioServerTransport();
console.error('多功能 MCP Server 启动中...');
console.error('可用工具:calculator、get_current_time、generate_random');
await server.connect(transport);
console.error('Server 已连接,等待请求...');
}
main().catch((error) => {
console.error('Server 错误:', error);
process.exit(1);
});
3. 测试 Server
安装依赖并启动 Server:
npm install
npm start
在支持 MCP 的客户端(如 Claude Desktop)中配置 Server 后,你可以测试以下对话:
- "帮我计算 123 乘以 456 等于多少" → 调用 calculator
- "现在几点了" → 调用 get_current_time
- "生成一个 1 到 100 的随机数" → 调用 generate_random
- "掷一个六面骰子" → 调用 generate_random (min=1, max=6)
代码解析
- 工具定义(TOOLS 数组):清晰描述每个工具的名称、用途和参数
- 处理器函数:为每个工具实现具体的业务逻辑
- 请求分发:通过 switch-case 根据工具名称调用对应的处理器
- 错误处理:在 calculator 等工具中捕获并返回错误信息
- 默认参数:使用 ?? 运算符为可选参数提供默认值
本章小结
本章详细介绍了 MCP 协议中的核心概念:
- Tools:AI 可调用的函数,用于执行操作并返回结果。需要定义 name、description 和 inputSchema。
- Resources:只读数据访问,通过 URI 标识,适合提供文件、配置等数据。
- Prompts:预定义的提示词模板,帮助用户高效使用 AI。
通过实战项目,我们开发了一个包含三个实用工具的多功能 Server。你可以基于这个模板,继续添加更多工具,如文件操作、网络请求、数据库查询等。
下一章,我们将学习如何开发 MCP Client,在自己的应用程序中集成和使用 MCP Server。