工具
工具是模型上下文协议 (MCP) 中一个强大的原语,它使服务器能够向客户端公开可执行功能。通过工具,LLM 可以与外部系统交互、执行计算并在现实世界中采取行动。
概述
MCP 中的工具允许服务器公开可执行函数,这些函数可供客户端调用,并由 LLM 用来执行操作。这些工具的关键方面包括:
发现
tools/list
:客户端可以通过端点列出可用的工具调用:使用端点调用工具
tools/call
,服务器执行请求的操作并返回结果灵活性:工具范围从简单的计算到复杂的 API 交互
资源类似,工具也由唯一名称标识,并可包含用于指导其使用的描述。然而,与资源不同的是,工具代表着动态操作,可以修改状态或与外部系统交互。
工具定义结构
每个工具都按照以下结构定义:
{
name: string; // Unique identifier for the tool
description?: string; // Human-readable description
inputSchema: { // JSON Schema for the tool's parameters
type: "object",
properties: { ... } // Tool-specific parameters
},
annotations?: { // Optional hints about tool behavior
title?: string; // Human-readable title for the tool
readOnlyHint?: boolean; // If true, the tool does not modify its environment
destructiveHint?: boolean; // If true, the tool may perform destructive updates
idempotentHint?: boolean; // If true, repeated calls with same args have no additional effect
openWorldHint?: boolean; // If true, tool interacts with external entities
}
}
实施工具
以下是在 MCP 服务器中实现基本工具的示例:
app = Server("example-server")
@app.list_tools()
async def list_tools() -> list[types.Tool]:
return [
types.Tool(
name="calculate_sum",
description="Add two numbers together",
inputSchema={
"type": "object",
"properties": {
"a": {"type": "number"},
"b": {"type": "number"}
},
"required": ["a", "b"]
}
)
]
@app.call_tool()
async def call_tool(
name: str,
arguments: dict
) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:
if name == "calculate_sum":
a = arguments["a"]
b = arguments["b"]
result = a + b
return [types.TextContent(type="text", text=str(result))]
raise ValueError(f"Tool not found: {name}")
工具图案示例
以下是服务器可以提供的工具类型的一些示例:
系统操作
与本地系统交互的工具:
{
name: "execute_command",
description: "Run a shell command",
inputSchema: {
type: "object",
properties: {
command: { type: "string" },
args: { type: "array", items: { type: "string" } }
}
}
}
API 集成
包装外部 API 的工具:
{
name: "github_create_issue",
description: "Create a GitHub issue",
inputSchema: {
type: "object",
properties: {
title: { type: "string" },
body: { type: "string" },
labels: { type: "array", items: { type: "string" } }
}
}
}
数据处理
转换或分析数据的工具:
{
name: "analyze_csv",
description: "Analyze a CSV file",
inputSchema: {
type: "object",
properties: {
filepath: { type: "string" },
operations: {
type: "array",
items: {
enum: ["sum", "average", "count"]
}
}
}
}
}
最佳实践
实施工具时:
提供清晰、描述性的名称和描述
使用详细的 JSON Schema 定义参数
在工具描述中包含示例,以演示模型应如何使用它们
实施适当的错误处理和验证
对长时间操作使用进度报告
保持工具操作的集中性和原子性
记录预期回报价值结构
实施适当的超时
考虑对资源密集型操作进行速率限制
用于调试和监控的日志工具使用情况
安全注意事项
输入验证
根据架构验证所有参数
清理文件路径和系统命令
验证 URL 和外部标识符
检查参数大小和范围
防止命令注入
访问控制
在需要的地方实施身份验证
使用适当的授权检查
审计工具使用情况
速率限制请求
监控滥用行为
错误处理
不要向客户端暴露内部错误
记录与安全相关的错误
适当处理超时
错误发生后清理资源
验证返回值
工具发现和更新
MCP 支持动态工具发现:
客户可以随时列出可用的工具
当工具发生变化时,服务器可以使用
notifications/tools/list_changed
可以在运行时添加或删除工具
工具定义可以更新(但应谨慎操作)
错误处理
工具错误应在结果对象中报告,而不是作为 MCP 协议级错误报告。这样,LLM 才能发现并处理错误。当工具遇到错误时:
在结果中设置
isError
为true
content
在数组中包含错误详细信息
以下是工具正确错误处理的示例:
try:
# Tool operation
result = perform_operation()
return types.CallToolResult(
content=[
types.TextContent(
type="text",
text=f"Operation successful: {result}"
)
]
)
except Exception as error:
return types.CallToolResult(
isError=True,
content=[
types.TextContent(
type="text",
text=f"Error: {str(error)}"
)
]
)
这种方法允许 LLM 发现发生的错误并可能采取纠正措施或请求人工干预。
工具注释
工具注释提供有关工具行为的额外元数据,帮助客户端了解如何呈现和管理工具。这些注释只是描述工具性质和影响的提示,不应作为安全决策的依据。
工具注释的用途
工具注释有几个主要用途:
在不影响模型上下文的情况下提供特定于 UX 的信息
帮助客户适当分类和展示工具
传达有关工具潜在副作用的信息
协助开发直观的工具审批界面
可用的工具注释
MCP 规范为工具定义了以下注释:
示例用法
以下是针对不同场景使用注释定义工具的方法:
// A read-only search tool
{
name: "web_search",
description: "Search the web for information",
inputSchema: {
type: "object",
properties: {
query: { type: "string" }
},
required: ["query"]
},
annotations: {
title: "Web Search",
readOnlyHint: true,
openWorldHint: true
}
}
// A destructive file deletion tool
{
name: "delete_file",
description: "Delete a file from the filesystem",
inputSchema: {
type: "object",
properties: {
path: { type: "string" }
},
required: ["path"]
},
annotations: {
title: "Delete File",
readOnlyHint: false,
destructiveHint: true,
idempotentHint: true,
openWorldHint: false
}
}
// A non-destructive database record creation tool
{
name: "create_record",
description: "Create a new record in the database",
inputSchema: {
type: "object",
properties: {
table: { type: "string" },
data: { type: "object" }
},
required: ["table", "data"]
},
annotations: {
title: "Create Database Record",
readOnlyHint: false,
destructiveHint: false,
idempotentHint: false,
openWorldHint: false
}
}
在服务器实现中集成注释
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("example-server")
@mcp.tool(
annotations={
"title": "Calculate Sum",
"readOnlyHint": True,
"openWorldHint": False
}
)
async def calculate_sum(a: float, b: float) -> str:
"""Add two numbers together.
Args:
a: First number to add
b: Second number to add
"""
result = a + b
return str(result)
工具注释的最佳实践
准确说明副作用:清楚地表明工具是否修改了其环境以及这些修改是否具有破坏性。
使用描述性标题:提供人性化的标题,清楚地描述工具的用途。
正确指示幂等性:仅当使用相同参数重复调用确实没有额外影响时,才将工具标记为幂等。
设置适当的开放/封闭世界提示:指示工具是否与封闭系统(如数据库)或开放系统(如网络)交互。
请记住,注释只是提示:ToolAnnotations 中的所有属性都只是提示,并不保证能够如实地描述工具的行为。客户端切勿仅根据注释做出安全关键决策。
测试工具
MCP 工具的全面测试策略应涵盖:
功能测试:验证工具在有效输入下正确执行,并适当处理无效输入
集成测试:使用真实和模拟依赖关系测试工具与外部系统的交互
安全测试:验证身份验证、授权、输入清理和速率限制
性能测试:检查负载下的行为、超时处理和资源清理
错误处理:确保工具通过 MCP 协议正确报告错误并清理资源
评论区