关于 tooluniverse-custom-tool
面向 ToolUniverse 开发者的自定义工具指南,介绍 JSON 配置、Python 类和可安装插件三种实现方式,并涵盖测试与 MCP 服务接入。
主要功能
- 01JSON 配置定义 REST 工具
- 02用 Python 类实现自定义逻辑
- 03打包并分发插件
- 04使用 tu test 验证工具
- 05通过 tu serve 提供工具
适用场景
- 01接入 ToolUniverse 尚未覆盖的 API
- 02为私有项目编写本地工具
- 03创建可通过 pip 安装的工具包
MCP 相关
工作区工具可通过 tu serve 提供给 MCP 客户端。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ mims-harvard/ tooluniverse --skill tooluniverse-custom-tool安装后在 Claude Code 中输入/tooluniverse-custom-tool确认已加载。
附带文件references/json-tool.mdreferences/python-tool.md
详细介绍
向 ToolUniverse 添加自定义工具
何时创建自定义工具: 如果你需要访问 ToolUniverse 尚未涵盖的 API,或需要现有工具无法提供的专用数据转换功能,就可以创建自定义工具。 请先使用 JSON 配置方式(最简单,无需 Python);只有在需要自定义响应解析或有状态逻辑时,才改用 Python 类。
添加工具有三种方式——请选择适合你需求的方式:
| 方式 | 适用场景 |
|---|---|
| JSON 配置 | 使用标准请求/响应的 REST API——无需编写代码 |
| Python 类(工作区) | 仅用于本地或私有用途的自定义逻辑 |
| 插件包 | 想要分享或通过 pip 安装的可复用工具 |
选项 A — 工作区工具(本地使用)
.tooluniverse/tools/ 中的工具会在启动时自动发现。无需安装。
mkdir -p .tooluniverse/tools
JSON 配置
创建 .tooluniverse/tools/my_tools.json:
[
{
"name": "MyAPI_search",
"description": "Search my internal database. Returns matching records with id, title, and score.",
"type": "BaseRESTTool",
"fields": {
"endpoint": "https://my-api.example.com/search"
},
"parameter": {
"type": "object",
"properties": {
"q": {
"type": "string",
"description": "Search query"
},
"limit": {
"type": ["integer", "null"],
"description": "Max results to return (default 10)"
}
},
"required": ["q"]
}
}
]
一个 JSON 文件可以定义多个工具——只需在数组中添加更多对象即可。
完整的 JSON 字段参考请参阅 references/json-tool.md。
Python 类
创建 .tooluniverse/tools/my_tool.py:
from tooluniverse.tool_registry import register_tool
@register_tool
class MyAPI_search:
name = "MyAPI_search"
description = "Search my internal database. Returns matching records with id, title, and score."
input_schema = {
"type": "object",
"properties": {
"q": {"type": "string", "description": "Search query"},
"limit": {"type": "integer", "description": "Max results (default 10)"}
},
"required": ["q"]
}
def run(self, q: str, limit: int = 10) -> dict:
import requests
resp = requests.get(
"https://my-api.example.com/search",
params={"q": q, "limit": limit},
timeout=30,
)
resp.raise_for_status()
return {"status": "success", "data": resp.json()}
注意:工作区 Python 工具使用 run(self, **named_params)——参数会解包为关键字参数,并与 input_schema 中的属性相匹配。
完整的 Python 类参考请参阅 references/python-tool.md。
测试工作区工具
# Uses test_examples from the tool's JSON config — zero config needed
tu test MyAPI_search
# Single ad-hoc call
tu test MyAPI_search '{"q": "test"}'
# Full config with assertions
tu test --config my_tool_tests.json
tu test 会在每次调用时自动运行以下检查:
- 结果不为 None 或空值
return_schema验证——根据return_schema中定义的 JSON Schema 验证result["data"](如果存在)expect_status和expect_keys——仅当在配置文件中设置时才检查
注意事项: (1) tu test 不会验证结果是否非空——[] 可以通过模式验证。请使用能返回实际数据的 test_examples 参数。(2) 先使用 urllib(而非 curl)手动验证 test_examples,确认 API 返回的是 JSON 而不是 HTML。使用 2-4 个宽泛的关键词。
在 JSON 配置中添加 test_examples 和 return_schema,以获得最佳覆盖率。tu test 会根据 return_schema 验证 result["data"](根据数据形状,将 "type": "array" 或 "type": "object" 与之匹配)。
可选:添加 my_tool_tests.json 以进行额外断言(expect_status、expect_keys)。
与 MCP 服务配合使用
.tooluniverse/tools/ 中的工具可通过 tu serve 自动使用。工作区优先级:--workspace 标志 → TOOLUNIVERSE_HOME 环境变量 → ./.tooluniverse/ → ~/.tooluniverse/。
要使用其他工具目录,请在 .tooluniverse/profile.yaml 中添加 sources: [./my-custom-tools/],然后通过 tooluniverse --load .tooluniverse/profile.yaml 启动。
选项 B — 插件包(可分享,可通过 pip 安装)
如果你想将工具作为可复用的 Python 包分发,供其他用户通过 pip install 安装,请使用此方式。插件包的目录布局与工作区相同,另外还包含用于声明入口点的 pyproject.toml。
包布局
my_project_root/ # directory containing pyproject.toml
pyproject.toml
my_tools_package/ # importable Python package (matches entry-point value)
__init__.py # minimal — one-line docstring, no registration code
my_api_tool.py # tool class(es) with @register_tool
data/
my_api_tools.json # JSON tool configs (type must match registered class name)
profile.yaml # optional: name, description, required_env
系统会从 data/ 和包根目录中发现 JSON 配置文件。推荐使用 data/ 目录。
pyproject.toml 入口点
[project.entry-points."tooluniverse.plugins"]
my-tools = "my_tools_package"
该值(my_tools_package)必须是可导入的 Python 包名。
插件包中的 Python 类
插件包工具使用 BaseTool,并将所有参数作为单个 Dict 接收:
import requests
from typing import Dict, Any
from tooluniverse.base_tool import BaseTool
from tooluniverse.tool_registry import register_tool
@register_tool("MyAPITool")
class MyAPITool(BaseTool):
"""Tool description here."""
def __init__(self, tool_config: Dict[str, Any]):
super().__init__(tool_config)
self.timeout = tool_config.get("timeout", 30)
fields = tool_config.get("fields", {})
self.operation = fields.get("operation", "search")
def run(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
query = arguments.get("query", "")
if not query:
return {"error": "query parameter is required"}
try:
resp = requests.get(
"https://my-api.example.com/search",
params={"q": query},
timeout=self.timeout,
)
resp.raise_for_status()
return {"status": "success", "data": resp.json()}
except requests.exceptions.RequestException as e:
return {"error": str(e)}
与工作区模式的主要区别:
- 继承自
BaseTool(来自tooluniverse.base_tool) @register_tool("ClassName")将类名作为字符串参数run(self, arguments: Dict)在单个字典中接收所有参数——使用.get()提取参数__init__接收tool_config字典;先调用super().__init__(tool_config)
插件包中的 JSON 配置
将配置文件放在 data/my_api_tools.json 中。"type" 字段必须与传递给 @register_tool(...) 的字符串相匹配:
[
{
"name": "MyAPI_search",
"description": "Search my API. Returns matching records.",
"type": "MyAPITool",
"fields": { "operation": "search" },
"parameter": {
"type": "object",
"properties": {
"query": { "type": "string", "description": "Search query" },
"limit": { "type": ["integer", "null"], "description": "Max results" }
},
"required": ["query"]
}
}
]
__init__.py
保持精简——仅包含文档字符串。插件系统会通过 _discover_entry_point_plugins() 自动导入所有 .py 文件,因此 @register_tool 装饰器会自动生效。可选:添加 from . import my_api_tool 以支持 IDE(幂等)。不要在此处添加注册逻辑或 JSON 加载。
安装与验证
pip install -e /path/to/my_project_root
cd /path/to/my_project_root # MUST run from plugin repo directory
tu test MyAPI_search '{"query": "test"}'
必须先运行 pip install -e。在插件仓库目录中运行 tu test(工作区自动检测需要 .tooluniverse/)。在 JSON 配置中添加 test_examples,即可实现零配置测试。使用 tu info MyAPI_search 确认工具已加载。
离线/纯计算工具
计算器工具(不使用 HTTP)遵循插件包模式,但跳过 HTTP 层。关键设计模式:
- 预设查找表:在模块级定义
Dict[str, float]。解析优先级:显式值 → 预设名称 → 默认值。在metadata中包含预设,以便发现。 - 双向方程:在单个工具中,将不同的
operation值作为独立操作公开。在 JSON 配置中使用"fields": {"operation": "default_op"}。 - 物理常数:在模块级定义(
_MU0 = 4*pi*1e-7等)。特定材料的值使用命名字典。 - 多输出:在
data中返回所有相关结果(例如温度 + 余量 + 通过/失败),而不是强制进行多次调用。
完整模式请参阅 references/python-tool.md。