内容
# 首页盒MCP服务器
MCP服务器,暴露
[首页盒](https://github.com/sysadminsmedia/homebox) 库存管理作为
AI代理的工具。
## 它做什么
该服务器允许AI代理(Claude,Hermes,OpenClaw或任何MCP兼容
客户端)读取和写入您的首页盒库存:搜索,添加,更新和删除
产品和位置,管理标签和维护记录 — 所有这些都通过
统一的首页盒v0.26+实体API。
它在两个传输中运行:
- **stdio** — 用于启动服务器为子进程的本地代理。
- **流式HTTP** (`/mcp`) — 用于远程代理,通过请求头传递HTTP。每请求
凭证通过请求头传递。
关键行为:
- 针对首页盒 **v0.26.0+** (*实体合并*版本)— 项目和
位置共享 `/api/v1/entities`,`parentId`表示父
实体。较旧的首页盒版本使用 `/api/v1/items` 和 `/api/v1/locations`
不受支持。
- `search_products` 仅使用 `name` 将短语分成多个项并查询
每个项以获得更广泛的召回。
- `add_product` 和 `update_product_by_id` 使用分组参数对象
(`identity`,`location`,`identifiers`,`purchase`,`metadata`,`status`)
以便大型调用保持可读。
- 维护工具(`create_maintenance_entry_for_product`,
`list_maintenance_entries`,`update_maintenance_entry`,
`delete_maintenance_entry`)对 `/api/v1/maintenance` 进行操作。
- 通过HTTP传输中的首页盒原生 `X-Tenant` 请求头暴露多集合(租户)支持。
## 安装
### 要求
| 组件 | 版本 |
| --- | --- |
| 首页盒(服务器) | **v0.26.0 或更新** (测试v0.26.2) |
| Python | 3.10.13+ |
| 依赖项 | 在 `pyproject.toml` 中列出 |
| `uv`(推荐) | https://docs.astral.sh/uv/ |
较旧的首页盒版本仍然暴露 `/api/v1/items` 和
`/api/v1/locations` 不受支持 — 服务器专门与
`/api/v1/entities` 通信。
### 安装服务器
```bash
git clone <仓库网址> homebox-mcp
cd homebox-mcp
uv sync # 安装运行时 + 开发依赖项
cp .env.example .env # 然后使用您的首页盒凭证编辑 .env
```
`.env.example`:
```
HOMEBOX_BASE_URL=https://your-homebox-instance.com
HOMEBOX_USERNAME=your_email@example.com
HOMEBOX_PASSWORD=your_password
# 可选的HTTP服务器设置(server_http.py)
# FASTMCP_HOST=0.0.0.0
# FASTMCP_PORT=8000
```
### Docker
```bash
docker build -t homebox-mcp .
docker run -p 8000:8000 \
-e HOMEBOX_BASE_URL="https://your-homebox-instance.com" \
-e HOMEBOX_USERNAME="your_email@example.com" \
-e HOMEBOX_PASSWORD="your_password" \
homebox-mcp
```
### 运行服务器
```bash
just start # stdio模式(默认本地代理)
just start-http # 流式HTTP模式,位于 http://0.0.0.0:8000/mcp
```
或直接:
```bash
uv run main.py # stdio
uv run server_http.py # 流式HTTP
```
## 在代理中安装
选取与您的代理匹配的代码段,并复制粘贴到代理的MCP配置中。替换 **`<…>`** 中的值为您自己的。
示例假设stdio模式(本地代理)凭证从环境/`.env`中读取。流式HTTP模式公开相同的工具,但通过请求头传递凭证。
### Claude Desktop / Claude Code (`claude_desktop_config.json`)
```json
{
"mcpServers": {
"homebox": {
"command": "uv",
"args": ["--directory", "/absolute/path/to/homebox-mcp", "run", "main.py"],
"env": {
"HOMEBOX_BASE_URL": "https://your-homebox-instance.com",
"HOMEBOX_USERNAME": "your_email@example.com",
"HOMEBOX_PASSWORD": "your_password"
}
}
}
}
```
### Hermes(stdio)
添加到您的Hermes MCP服务器配置:
```json
{
"homebox": {
"command": "uv",
"args": ["--directory", "/absolute/path/to/homebox-mcp", "run", "main.py"],
"env": {
"HOMEBOX_BASE_URL": "https://your-homebox-instance.com",
"HOMEBOX_USERNAME": "your_email@example.com",
"HOMEBOX_PASSWORD": "your_password"
}
}
}
```
### OpenClaw(stdio)
```json
{
"servers": {
"homebox": {
"command": "uv",
"args": ["--directory", "/absolute/path/to/homebox-mcp", "run", "main.py"],
"env": {
"HOMEBOX_BASE_URL": "https://your-homebox-instance.com",
"HOMEBOX_USERNAME": "your_email@example.com",
"HOMEBOX_PASSWORD": "your_password"
}
}
}
}
```
### 远程 / 流式HTTP
如果您使用 `uv run server_http.py` (或通过Docker镜像)运行服务器,
任何MCP兼容的HTTP客户端都可以通过 `http://host:8000/mcp` 访问并通过请求头传递凭证:
```
HOMEBOX_USERNAME: your_email@example.com
HOMEBOX_PASSWORD: your_password
X-Tenant: 00000000-0000-0000-0000-000000000000 # 可选,多集合
```
`HOMEBOX_BASE_URL` 仅从服务器进程的运行时环境中读取;它不能按请求覆盖。
### 查找 `X-Tenant` 值
打开首页盒,登录,然后在开发工具→网络中找到请求
`GET /api/v1/users/self` 并复制 `X-Tenant` 请求头。在每个MCP请求中通过 `X-Tenant` 头传递它。
## 可用工具
17种工具被公开。分组参数对象接受snake_case和
camelCase键。
### 产品(实体)
#### `search_products`
通过首页盒查询加上本地过滤器搜索产品。提供至少一个
过滤器;如果没有其他严格过滤器,则 `name` 被分成每个项的查询。
- `name` (可选[str])— 产品名称(或短语)查询
- `item_id` (可选[str])— 精确产品UUID
- `asset_id` (可选[str])— 资产ID过滤器
- `serial_number` (可选[str])
- `model_number` (可选[str])
- `manufacturer` (可选[str])
- `query` (可选[str])— 通用全文查询
**返回:** 匹配的产品字典列表,或错误字符串。
#### `add_product`
使用分组参数创建新产品或子项。如果 `location`
和 `parent_item` 都提供,则 `parent_item` 获胜(创建子项)。
- `identity` (字典,必填):`{"name", "description", "asset_id"}`
- `location` (可选[字典]):`{"id"}` 或 `{"name"}` — 用于独立产品
- `parent_item` (可选[字典]):`{"id"}` 或 `{"name"}` — 用于子项
- `identifiers` (可选[字典]):`{"serial_number", "model_number", "manufacturer"}`
- `purchase` (可选[字典]):`{"price", "seller", "date", "warranty_expires"}`
- `metadata` (可选[字典]):`{"notes", "quantity", "tag_ids", "tag_names", "custom_fields"}`
- `status` (可选[字典]):`{"archived", "insured", "lifetime_warranty"}`
**返回:** 创建的产品字典(在可选更新传递后),或错误字符串。
#### `update_product_by_id`
更新现有产品。传递ID(`item_id`,`location.id`)— 名称仅通过
此工具外部的 `list_locations` 解析。
- `item_id` (必填字符串)— 要更新的项目的UUID
- `identity` (可选[字典]):`{"new_name", "description", "asset_id"}`
- `location` (可选[字典]):`{"id"}` 或 `{"name"}`
- `identifiers` (可选[字典]):`{"serial_number", "model_number", "manufacturer"}`
- `purchase` (可选[字典]):`{"price", "seller", "date", "warranty_expires"}`
- `metadata` (可选[字典]):`{"notes", "quantity", "tag_ids", "tag_names", "custom_fields"}`
- `status` (可选[字典]):`{"archived", "insured", "lifetime_warranty"}`
**返回:** 更新的产品字典,或错误字符串。
#### `add_bulk_products`
一次添加多个产品。每个条目使用 `add_product` 的分组形状。
- `items` (必填[字典列表])— 每个项目必须包含一个 `identity.name`
- **返回:** 列表(每个输入项目一个结果)。
#### `delete_product_by_id`
通过ID删除单个产品。
- `item_id` (必填字符串)— 项目的UUID
- `product_name` (必填字符串)— 仅用于成功/错误消息
**返回:** 成功消息,或错误字符串。
#### `delete_bulk_products`
删除多个产品。为每个条目委托 `delete_product_by_id`。
- `items` (必填[字典列表])— 每个条目 `{item_id, product_name}`
- **返回:** 列表(每个输入项目一个结果消息)。
### 位置
#### `list_locations`
返回首页盒的 **树** (`GET /api/v1/entities/tree`)。
- 无参数。
- **返回:** 顶级树节点的列表(每个节点都有嵌套的 `children`)。
#### `create_location`
创建新位置(隐含 `isLocation` 作为无父实体)。
- `name` (必填字符串)
- `description` (可选字符串)
- `parent_id` (可选字符串)— 父位置的UUID
**返回:** 创建的位置字典,或错误字符串。
#### `update_location_by_id`
更新位置的可编辑字段。首先获取当前状态以保留省略的字段。
- `location_id` (必填字符串)— 位置的UUID
- `new_name` (可选字符串)
- `new_description` (可选字符串)
- `new_parent_id` (可选字符串)— 通过传递空字符串 `""` 来分离父级
**返回:** 更新的位置字典,或错误字符串。
#### `delete_location_by_id`
删除单个位置。
- `location_id` (必填字符串)— 位置的UUID
- **返回:** 成功消息,或错误字符串。
#### `list_items_by_location`
列出位置的直接子项目(使用 `?parentIds=…`)。
- `location_id` (必填字符串)— 位置的UUID
- **返回:** `{"id", "name"}` 项目的列表,或错误字符串。
### 标签
#### `list_tags`
从 `/api/v1/tags` 返回所有标签。
- 无参数。
- **返回:** `{"id", "name"}` 标签的列表,或错误字符串。
#### `create_tag`
如果标签不存在(区分大小写),则创建标签。
- `name` (必填字符串)
- **返回:** 创建或已存在的标签字典,或错误字符串。
### 维护
#### `create_maintenance_entry_for_product`
为现有产品创建维护条目。提供
`scheduled_date`,`completed_date` 或两者;如果两者都没有,则两者默认为
今天。
- `product_id` (可选字符串)— 如果已知则优先
- `product_name` (可选字符串)— 解析为产品ID,如果 `product_id`
缺失
- `name` (必填字符串)— 任务的标题/摘要
- `description` (可选字符串)
- `cost` (可选浮点数)
- `scheduled_date` (可选字符串)— `YYYY-MM-DD`
- `completed_date` (可选字符串)— `YYYY-MM-DD`
**返回:** 创建的维护条目字典,或错误字符串。
#### `list_maintenance_entries`
从首页盒列出维护条目。首页盒v0.26+的全局 `/api/v1/maintenance`
端点已损坏;此工具会回退到枚举
每个实体的维护列表并聚合它们。
- `product_id` (可选字符串)— 限制为一个产品的条目
- `product_name` (可选字符串)— 用于解析实体,当 `product_id`
缺失
- **返回:** 维护条目字典列表,或错误字符串。
#### `update_maintenance_entry`
更新现有的维护条目。首页盒v0.26+仅公开维护
每个实体,因此必须提供拥有实体。
- `entry_id` (必填字符串)— 条目的UUID
- `product_id` (可选字符串)— 拥有实体的UUID(优先)
- `product_name` (可选字符串)— 解析为 `product_id`
缺失时的实体
- `name` (可选字符串)
- `description` (可选字符串)
- `cost` (可选浮点数)
- `scheduled_date` (可选字符串)— `YYYY-MM-DD`
- `completed_date` (可选字符串)— `YYYY-MM-DD`
**返回:** 更新的维护条目字典,或错误字符串。
#### `delete_maintenance_entry`
删除维护条目。
- `entry_id` (必填字符串)— 条目的UUID
- **返回:** 成功消息,或错误字符串。
## 测试
该项目包含单元测试和端到端测试。
### 单元测试(无需Docker)
```bash
just test
# 或:uv run pytest tests/ -v -k "not e2e"
```
### 端到端测试(需要Docker)
E2E套件启动了一个 **固定首页盒v0.26.2** 容器,构建MCP
服务器镜像,注册测试用户,并通过流式HTTP传输对 **所有17种工具** 进行操作。然后
验证每个工具的效果 **针对实际首页盒API** (而不仅仅是MCP响应信封)。
```bash
just test-e2e
```
它的工作原理:
1. `docker compose -f docker-compose.e2e.yml up -d` — 启动首页盒v0.26.2
在端口 `31745` 和MCP服务器在端口 `31746` 上。
2. 等待首页盒健康检查。
3. 注册测试用户(幂等)。
4. 运行 `pytest tests/e2e/ -m e2e`。
5. 拆除所有容器。
参见 `docker-compose.e2e.yml` 获取固定镜像和 `tests/e2e/` 获取
套件。套件涵盖每个工具:
| 类别 | 测试工具 |
| --- | --- |
| 位置 | `list_locations`,`create_location`,`update_location_by_id`,`list_items_by_location`,`delete_location_by_id` |
| 产品 | `search_products`,`add_product`,`update_product_by_id`,`add_bulk_products`,`delete_product_by_id`,`delete_bulk_products` |
| 标签 | `list_tags`,`create_tag` |
| 维护 | `create_maintenance_entry_for_product`,`list_maintenance_entries`,`update_maintenance_entry`,`delete_maintenance_entry` |
## 添加物品的推荐工作流程
1. **查找独立产品的 **位置** — 调用 `list_locations` 获取
位置UUID。
2. **查找子项的父项** — 调用 `search_products(name=...)` 解析
父项UUID。
3. **创建或更新** — 使用 `add_product`(新)或 `update_product_by_id`
(现有)。如果 `location` 和 `parent_item` 都传递给
`add_product`,则使用 `parent_item`,忽略 `location` 创建目标。
## Justfile 命令
```bash
just --list # 显示所有命令
just start # 以stdio模式启动MCP服务器
just start-http # 以Streamable HTTP模式启动MCP服务器
just stop # 停止运行的HTTP服务器
just lint-format # 运行lint检查
just lint-fix # 运行lint并自动修复问题
just test # 运行单元测试
just test-e2e # 运行E2E测试(需要Docker)
just wipe # 清理缓存/构建工件
just docker-build # 构建Docker镜像
just docker-run # 运行Docker容器
```
## 仓库布局
```
homebox-mcp/
├── main.py # FastMCP服务器,所有17种工具,帮助程序
├── models.py # 工具输入的分组数据类
├── server_http.py # Streamable HTTP传输入口点
├── request_config_middleware.py # 每请求凭证/租户范围
├── Dockerfile # HTTP服务器镜像
├── docker-compose.e2e.yml # 固定首页盒 + MCP服务器堆栈
├── justfile # 开发/测试命令
├── scripts/e2e-setup.sh # 测试用户注册帮助程序
├── tests/
│ ├── test_main.py # 单元测试(模拟HTTP)
│ ├── test_request_config_middleware.py
│ ├── test_transport_servers.py
│ └── e2e/ # 对真实首页盒v0.26.2的E2E测试
└── pyproject.toml
```