mcpField
D
devtu-create-tool

devtu-create-tool

github.com/mims-harvard开发1.8k388 次安装3 天前更新
代码仓库

关于 devtu-create-tool

面向 ToolUniverse 的技能,指导开发者实现并注册科学工具,涵盖类、JSON 包装器、配置和测试步骤,并强调常见验证与 MCP 兼容问题。

主要功能

  1. 01工具类与包装器的两阶段设计
  2. 02工具注册的三个步骤
  3. 03参数与架构指导
  4. 04多层级测试清单
  5. 05API 密钥配置指导

适用场景

  1. 01为 ToolUniverse 添加科学 API 工具
  2. 02发布前检查注册与架构
  3. 03为替代标识符设计参数

MCP 相关

该技能建议工具名称不超过 55 个字符,以避免 MCP 兼容问题,并说明 `tu.load_tools()` 会生成包装器。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/mims-harvard/tooluniverse --skill devtu-create-tool

安装后在 Claude Code 中输入/devtu-create-tool确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
advanced-patterns.md
implementation-guide.md
quick-reference.md
testing-guide.md
tool-improvement-checklist.md
templates/
api_tool_template.py
api_tools_config.json
simple_tool_template.py
simple_tools_config.json
test_template.py

详细介绍

ToolUniverse 工具创建器

按照既定模式创建新的科学工具。

七大常见错误(占失败案例的 90%)

  1. 缺少 default_config.py 条目 — 工具会静默地无法加载
  2. 互斥参数不可为空 — 导致验证错误(2026 年的首要问题)
  3. 伪造的 test_examples — 测试失败,Agent 会得到错误示例
  4. 只进行单层测试 — 无法发现注册错误
  5. 跳过 test_new_tools.py — 无法发现架构/API 问题
  6. 工具名称超过 55 个字符 — 破坏 MCP 兼容性
  7. 抛出异常 — 应改为返回错误字典

两阶段架构

Stage 1: Tool Class              Stage 2: Wrappers (Auto-Generated)
@register_tool("MyTool")         MyAPI_list_items()
class MyTool(BaseTool):          MyAPI_search()
    def run(arguments):          MyAPI_get_details()

一个类处理多个操作。JSON 定义单个包装器。两者缺一不可。

三步注册

步骤 1:通过 @register_tool("MyAPITool") 注册类

步骤 2(最常被遗漏):在 default_config.py 中注册配置:

TOOLS_CONFIGS = {
    "my_category": os.path.join(current_dir, "data", "my_category_tools.json"),
}

步骤 3:调用 tu.load_tools() 时自动生成包装器


实现指南

要创建的文件
  • src/tooluniverse/my_api_tool.py — 实现代码
  • src/tooluniverse/data/my_api_tools.json — 工具定义
  • tests/tools/test_my_api_tool.py — 测试
Python 工具类(多操作模式)
from typing import Dict, Any
from tooluniverse.tool import BaseTool
from tooluniverse.tool_utils import register_tool
import requests

@register_tool("MyAPITool")
class MyAPITool(BaseTool):
    BASE_URL = "https://api.example.com/v1"

    def __init__(self, tool_config):
        super().__init__(tool_config)
        self.parameter = tool_config.get("parameter", {})
        self.required = self.parameter.get("required", [])

    def run(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
        operation = arguments.get("operation")
        if not operation:
            return {"status": "error", "error": "Missing: operation"}
        if operation == "search":
            return self._search(arguments)
        return {"status": "error", "error": f"Unknown: {operation}"}

    def _search(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
        query = arguments.get("query")
        if not query:
            return {"status": "error", "error": "Missing: query"}
        try:
            response = requests.get(
                f"{self.BASE_URL}/search",
                params={"q": query}, timeout=30
            )
            response.raise_for_status()
            data = response.json()
            return {"status": "success", "data": data.get("results", [])}
        except requests.exceptions.Timeout:
            return {"status": "error", "error": "Timeout after 30s"}
        except requests.exceptions.HTTPError as e:
            return {"status": "error", "error": f"HTTP {e.response.status_code}"}
        except Exception as e:
            return {"status": "error", "error": str(e)}
JSON 配置
[
  {
    "name": "MyAPI_search",
    "class": "MyAPITool",
    "description": "Search items. Returns array of results. Supports Boolean operators. Example: 'protein AND membrane'.",
    "parameter": {
      "type": "object",
      "required": ["operation", "query"],
      "properties": {
        "operation": {"const": "search", "description": "Operation (fixed)"},
        "query": {"type": "string", "description": "Search term"},
        "limit": {"type": ["integer", "null"], "description": "Max results (1-100)"}
      }
    },
    "return_schema": {
      "oneOf": [
        {"type": "object", "properties": {"data": {"type": "array"}}},
        {"type": "object", "properties": {"error": {"type": "string"}}, "required": ["error"]}
      ]
    },
    "test_examples": [{"operation": "search", "query": "protein", "limit": 10}]
  }
]
关键要求
  • return_schema 必须包含 oneOf:成功和错误架构
  • test_examples 必须使用真实 ID:不能使用 "TEST"、"DUMMY"、"PLACEHOLDER"
  • 工具名称不超过 55 个字符:遵循 {API}_{action}_{target} 模板
  • 描述为 150–250 个字符:说明功能、格式、示例和注意事项
  • 绝不要在 run() 中抛出异常:返回 {"status": "error", "error": "..."}
  • 为所有 HTTP 请求设置超时(30s)
  • 标准响应:{"status": "success|error", "data": {...}}

参数设计

互斥参数(重要 — 首要问题)

当工具接受 id 或 name 时,两者都必须允许为空:

{
  "id": {"type": ["integer", "null"], "description": "Numeric ID"},
  "name": {"type": ["string", "null"], "description": "Name (alternative to id)"}
}

如果不包含 "null",用户只提供一个参数时验证就会失败。

常见情况:id 或 name、gene_id 或 gene_symbol,以及任何可选筛选条件。

API 密钥配置

可选密钥(没有也能用,有密钥效果更好):

{"optional_api_keys": ["NCBI_API_KEY"]}
self.api_key = os.environ.get("NCBI_API_KEY", "")  # Read from env only

必需密钥(没有就无法使用):

{"required_api_keys": ["NVIDIA_API_KEY"]}

规则:对于可选密钥,绝不要将 api_key 添加为工具参数。仅使用环境变量。


测试(必须执行)

完整指南:references/testing-guide.md

快速测试清单
  1. 第 1 级 — 直接测试类:导入类、调用 run()、检查响应
  2. 第 2 级 — 测试 ToolUniverse:调用 tu.tools.YourTool_op1(...),检查注册情况
  3. 第 3 级 — 真实 API 测试:使用真实 ID,验证实际响应
  4. 必须执行 — 运行 python scripts/test_new_tools.py your_tool -v → 0 个失败
验证脚本
# Check all 3 registration steps
python3 -c "
import sys; sys.path.insert(0, 'src')
from tooluniverse.tool_registry import get_tool_registry
import tooluniverse.your_tool_module
assert 'YourToolClass' in get_tool_registry(), 'Step 1 FAILED'
from tooluniverse.default_config import TOOLS_CONFIGS
assert 'your_category' in TOOLS_CONFIGS, 'Step 2 FAILED'
from tooluniverse import ToolUniverse
tu = ToolUniverse(); tu.load_tools()
assert hasattr(tu.tools, 'YourCategory_op1'), 'Step 3 FAILED'
print('All 3 steps verified!')
"

快速命令

python3 -m json.tool src/tooluniverse/data/your_tools.json     # Validate JSON
python3 -m py_compile src/tooluniverse/your_tool.py             # Check syntax
grep "your_category" src/tooluniverse/default_config.py         # Verify config
python scripts/test_new_tools.py your_tool -v                   # MANDATORY test

参考资料

评论

支持 Markdown