工具

工具是模型上下文協議(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"]
        }
      }
    }
  }
}

最佳實踐

在實現工具時:

  1. 提供清晰、描述性的名稱和描述
  2. 使用詳細的 JSON Schema 定義參數
  3. 在工具描述中包含示例來演示模型應如何使用它們
  4. 實現適當的錯誤處理和驗證
  5. 使用進度報告處理長時間操作
  6. 保持工具操作的重點和原子性
  7. 記錄預期的返回值結構
  8. 實現適當的超時
  9. 考慮資源密集型操作的速率限制
  10. 記錄調試和監控的工具使用情況

安全考慮

在暴露工具時:

輸入驗證

  • 根據模式驗證所有參數
  • 淨化文件路徑和系統命令
  • 驗證 URL 和外部標識符
  • 檢查參數大小和範圍
  • 防止命令注入

訪問控制

  • 在需要時實現身份驗證
  • 使用適當的授權檢查
  • 審計工具使用情況
  • 限制請求速率
  • 監控濫用

錯誤處理

  • 不要向客戶端暴露內部錯誤
  • 記錄安全相關錯誤
  • 適當處理超時
  • 錯誤後清理資源
  • 驗證返回值

工具發現和更新

MCP 支持動態工具發現:

  1. 客戶端可以隨時列出可用工具
  2. 服務器可以使用 notifications/tools/list_changed 通知客戶端工具變更
  3. 工具可以在運行時添加或刪除
  4. 工具定義可以更新(但應謹慎進行)

錯誤處理

工具錯誤應該在結果對象內報告,而不是作為 MCP 協議級錯誤。這允許 LLM 看到並可能處理錯誤。當工具遇到錯誤時:

  1. isError 設置為 true
  2. 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 協議正確報告錯誤並清理資源