工具
工具是模型上下文協議(MCP)中的一個強大原語,使服務器能夠向客戶端暴露可執行的功能。通過工具,LLM 可以與外部系統交互、執行計算並在現實世界中採取行動。
工具設計為模型控制,這意味著工具從服務器暴露給客戶端時,目的是讓 AI 模型能夠自動調用它們(在人工審批的情況下)。
概述
MCP 中的工具允許服務器暴露可執行的函數,這些函數可以被客戶端調用並被 LLM 用來執行操作。工具的關鍵方面包括:
- 發現:客戶端可以通過
tools/list端點列出可用工具 - 調用:工具通過
tools/call端點調用,服務器執行請求的操作並返回結果 - 靈活性:工具可以從簡單的計算到複雜的 API 交互
像資源一樣,工具由唯一的名稱標識,並可以包含描述來指導其使用。但是,與資源不同,工具代表可以修改狀態或與外部系統交互的動態操作。
工具定義結構
每個工具的定義結構如下:
{
name: string; // 工具的唯一標識符
description?: string; // 人類可讀的描述
inputSchema: { // 工具參數的 JSON Schema
type: "object",
properties: { ... } // 工具特定的參數
}
}實現工具
這是在 MCP 服務器中實現基本工具的示例:
const server = new Server({
name: "example-server",
version: "1.0.0"
}, {
capabilities: {
tools: {}
}
});
// 定義可用工具
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [{
name: "calculate_sum",
description: "將兩個數字相加",
inputSchema: {
type: "object",
properties: {
a: { type: "number" },
b: { type: "number" }
},
required: ["a", "b"]
}
}]
};
});
// 處理工具執行
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === "calculate_sum") {
const { a, b } = request.params.arguments;
return {
toolResult: a + b
};
}
throw new Error("未找到工具");
});app = Server("example-server")
@app.list_tools()
async def list_tools() -> list[types.Tool]:
return [
types.Tool(
name="calculate_sum",
description="將兩個數字相加",
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"未找到工具: {name}")工具模式示例
以下是服務器可以提供的一些工具類型示例:
系統操作
與本地系統交互的工具:
{
name: "execute_command",
description: "運行 shell 命令",
inputSchema: {
type: "object",
properties: {
command: { type: "string" },
args: { type: "array", items: { type: "string" } }
}
}
}API 集成
包裝外部 API 的工具:
{
name: "github_create_issue",
description: "創建 GitHub issue",
inputSchema: {
type: "object",
properties: {
title: { type: "string" },
body: { type: "string" },
labels: { type: "array", items: { type: "string" } }
}
}
}數據處理
轉換或分析數據的工具:
{
name: "analyze_csv",
description: "分析 CSV 文件",
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 {
// 工具操作
const result = performOperation();
return {
content: [
{
type: "text",
text: `操作成功:${result}`
}
]
};
} catch (error) {
return {
isError: true,
content: [
{
type: "text",
text: `錯誤:${error.message}`
}
]
};
}try:
# 工具操作
result = perform_operation()
return types.CallToolResult(
content=[
types.TextContent(
type="text",
text=f"操作成功:{result}"
)
]
)
except Exception as error:
return types.CallToolResult(
isError=True,
content=[
types.TextContent(
type="text",
text=f"錯誤:{str(error)}"
)
]
)這種方法允許 LLM 看到發生了錯誤並可能採取糾正措施或請求人工干預。
測試工具
MCP 工具的全面測試策略應該涵蓋:
- 功能測試:驗證工具使用有效輸入正確執行,並適當處理無效輸入
- 集成測試:使用真實和模擬的依賴項測試工具與外部系統的交互
- 安全測試:驗證身份驗證、授權、輸入淨化和速率限制
- 性能測試:檢查負載下的行為、超時處理和資源清理
- 錯誤處理:確保工具通過 MCP 協議正確報告錯誤並清理資源