mcpField
T
tooluniverse-custom-tool

tooluniverse-custom-tool

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

关于 tooluniverse-custom-tool

面向 ToolUniverse 开发者的自定义工具指南,介绍 JSON 配置、Python 类和可安装插件三种实现方式,并涵盖测试与 MCP 服务接入。

主要功能

  1. 01JSON 配置定义 REST 工具
  2. 02用 Python 类实现自定义逻辑
  3. 03打包并分发插件
  4. 04使用 tu test 验证工具
  5. 05通过 tu serve 提供工具

适用场景

  1. 01接入 ToolUniverse 尚未覆盖的 API
  2. 02为私有项目编写本地工具
  3. 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。

评论

支持 Markdown