关于 devtu-create-tool
面向 ToolUniverse 的技能,指导开发者实现并注册科学工具,涵盖类、JSON 包装器、配置和测试步骤,并强调常见验证与 MCP 兼容问题。
主要功能
- 01工具类与包装器的两阶段设计
- 02工具注册的三个步骤
- 03参数与架构指导
- 04多层级测试清单
- 05API 密钥配置指导
适用场景
- 01为 ToolUniverse 添加科学 API 工具
- 02发布前检查注册与架构
- 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.mdimplementation-guide.mdquick-reference.mdtesting-guide.mdtool-improvement-checklist.mdtemplates/api_tool_template.pyapi_tools_config.jsonsimple_tool_template.pysimple_tools_config.jsontest_template.py详细介绍
ToolUniverse 工具创建器
按照既定模式创建新的科学工具。
七大常见错误(占失败案例的 90%)
- 缺少
default_config.py条目 — 工具会静默地无法加载 - 互斥参数不可为空 — 导致验证错误(2026 年的首要问题)
- 伪造的 test_examples — 测试失败,Agent 会得到错误示例
- 只进行单层测试 — 无法发现注册错误
- 跳过
test_new_tools.py— 无法发现架构/API 问题 - 工具名称超过 55 个字符 — 破坏 MCP 兼容性
- 抛出异常 — 应改为返回错误字典
两阶段架构
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 级 — 直接测试类:导入类、调用
run()、检查响应 - 第 2 级 — 测试 ToolUniverse:调用
tu.tools.YourTool_op1(...),检查注册情况 - 第 3 级 — 真实 API 测试:使用真实 ID,验证实际响应
- 必须执行 — 运行
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
参考资料
- 测试指南:references/testing-guide.md
- 高级模式(异步、SOAP、分页):references/advanced-patterns.md
- 实现指南(完整清单):references/implementation-guide.md
- 工具改进清单:references/tool-improvement-checklist.md
评论
支持 Markdown