动态发现 · MCP · Python
让工具被动态发现,
通过 MCP 随时调用。
EyeTools 根据配置发现工具包、管理工具执行,并通过 MCP 和 HTTP 提供统一的调用入口。
01 / 工作原理
从目录到工具
将服务指向一个或多个工具目录。服务启动时会递归扫描其中的 config.yaml,注册发现的工具,并向 MCP 客户端提供输入参数结构。添加工具时无需修改服务代码。
配置文件与 Python 类。
读取配置并注册工具。
客户端列出并调用工具。
开源框架的 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.py1. 描述工具
entry 指定模块和类;输入结构用于描述 MCP 客户端看到的参数。
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。
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 客户端,可按其配置格式设置端点。例如:
{
"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 中设置:
environment_ref: my-tool
runtime:
load_mode: subprocess在该环境中安装工具实际需要的依赖。服务的工作目录是解析 envs/ 的工作区根目录。GPU 工具可以显式配置 model.weight_paths,并使用 uv run eyetools-mcp estimate-vram --tool-path tools 估算显存。