EyeTools

动态发现 · MCP · Python

让工具被动态发现,
通过 MCP 随时调用。

EyeTools 根据配置发现工具包、管理工具执行,并通过 MCP 和 HTTP 提供统一的调用入口。

01 / 工作原理

从目录到工具

将服务指向一个或多个工具目录。服务启动时会递归扫描其中的 config.yaml,注册发现的工具,并向 MCP 客户端提供输入参数结构。添加工具时无需修改服务代码。

01 / 描述工具包

配置文件与 Python 类。

02 / 发现EyeTools 服务

读取配置并注册工具。

03 / 连接MCP 与 HTTP

客户端列出并调用工具。

开源框架的 tools/ 目录初始为空,不包含具体领域工具、模型权重或数据集。

02 / 开始使用

启动服务

先安装 Python 3.12 或更高版本和 uv,然后克隆并安装项目:

终端
git clone https://github.com/yyyanbj/eyetools.git
cd eyetools
uv sync

在项目根目录启动服务:

终端
uv run eyetools-mcp serve \
  --host 127.0.0.1 --port 8000 \
  --tools-dir tools --lifecycle-mode lazy

空服务的 http://127.0.0.1:8000/health 返回 {"status":"ok","tools":0}。HTTP 接口文档位于 /docs,MCP Streamable HTTP 端点位于 /mcp。

03 / 扩展

添加第一个工具

在被扫描的工具目录中创建以下两个文件:

目录结构
tools/
└── echo/
    ├── config.yaml
    └── echo_impl.py

1. 描述工具

entry 指定模块和类;输入结构用于描述 MCP 客户端看到的参数。

tools/echo/config.yaml
id: echo
package: echo
entry: echo_impl:EchoTool
runtime:
  load_mode: inproc
  device_policy: cpu_only
model:
  device: cpu
io:
  description: Return the supplied message.
  input_schema:
    type: object
    properties:
      message:
        type: string
    required: [message]

2. 实现工具类

服务使用 (meta, params) 创建类实例,并在 request["inputs"] 中传入参数。返回值需可序列化为 JSON。

tools/echo/echo_impl.py
class EchoTool:
    def __init__(self, meta, params):
        self.meta = meta
        self.params = params

    def predict(self, request):
        return {"message": request["inputs"]["message"]}

添加工具后重启服务即可发现。需要扫描多个目录时,可重复指定 --tools-dir。工具还可实现 prepare、load_model、warmup 和 shutdown 生命周期方法。

继承 ToolBase 并重写 load_model 时,完成加载后调用 super().load_model(),以记录模型已加载状态。

04 / 连接

调用工具

重启服务后,先列出已发现的工具,再通过 HTTP 调用 echo:

终端
curl http://127.0.0.1:8000/tools
curl -X POST http://127.0.0.1:8000/predict \
  -H 'Content-Type: application/json' \
  -d '{"tool_id":"echo","request":{"inputs":{"message":"hello"}}}'

响应包含工具输出和请求元数据(下方时间戳仅为示意):

响应
{
  "output": {"message": "hello"},
  "__meta__": {"tool_id": "echo", "inputs": {"message": "hello"}, "ts": 0}
}

对于支持 Streamable HTTP 的 MCP 客户端,可按其配置格式设置端点。例如:

MCP 客户端配置
{
  "mcpServers": {
    "eyetools": {"url": "http://127.0.0.1:8000/mcp"}
  }
}

在客户端列出工具,然后以 message 参数调用 echo。

05 / 运维

生命周期与接口

使用 --lifecycle-mode 控制工具的加载和卸载:

模式行为
lazy首次使用时加载工具。
eager启动时预加载工具,包括子进程。
dynamic按需加载,并在后台执行空闲与卸载维护。

动态模式可设置 --dynamic-mark-idle-s、--dynamic-unload-s 和 --dynamic-interval-s。常用 HTTP 接口包括:

接口用途
GET /health服务状态与已发现工具数。
GET /tools列出已注册工具及其结构。
POST /predict通过 HTTP 调用工具。
GET /lifecycle查看生命周期状态。
GET /metrics查看执行指标。
POST /admin/evict?tool_id=...卸载工具或停止其子进程。
GET /docs交互式 HTTP 接口文档。

接口没有内置身份认证;如果需要从本机以外访问,请在前面配置带认证的代理。

06 / 隔离

工具环境

如果工具需要独立依赖,可以在 envs/ 下创建专用 uv 项目并使用子进程运行。先在 EyeTools 项目根目录执行:

终端
mkdir -p envs/my-tool
cd envs/my-tool
uv init --no-package --no-workspace --python 3.12
uv add --no-workspace --editable ../..
# Add the dependencies your tool actually needs:
# uv add --no-workspace <tool-dependency>
cd ../..

然后在该工具的 config.yaml 中设置:

config.yaml
environment_ref: my-tool
runtime:
  load_mode: subprocess

在该环境中安装工具实际需要的依赖。服务的工作目录是解析 envs/ 的工作区根目录。GPU 工具可以显式配置 model.weight_paths,并使用 uv run eyetools-mcp estimate-vram --tool-path tools 估算显存。