Homebox MCP Server
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Homebox MCP Server search for products matching 'drill'"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Homebox MCP Server
An MCP server that exposes Homebox inventory management as tools for AI agents.
What it does
This server lets an AI agent (Claude, Hermes, OpenClaw, or any MCP-compatible client) read and write your Homebox inventory: search, add, update, and delete products and locations, manage tags and maintenance entries — all through the unified Homebox v0.26+ entities API.
It runs in two transports:
stdio — for local agents that launch the server as a subprocess.
Streamable HTTP (
/mcp) — for remote agents that talk HTTP. Per-request credentials are passed via request headers.
Key behaviors:
Targets Homebox v0.26.0+ (the entity-merge release) — items and locations share
/api/v1/entities, withparentIddenoting the parent entity. Older Homebox releases using/api/v1/itemsand/api/v1/locationsare not supported.search_productswith onlynamesplits the phrase into terms and queries each in parallel for broader recall.add_productandupdate_product_by_iduse grouped parameter objects (identity,location,identifiers,purchase,metadata,status) so large calls stay readable.Maintenance tools (
create_maintenance_entry_for_product,list_maintenance_entries,update_maintenance_entry,delete_maintenance_entry) operate against/api/v1/maintenance.Multi-collection (tenant) support is exposed via the Homebox-native
X-Tenantrequest header on the HTTP transport.
Related MCP server: mcp-homelab
Installation
Requirements
Component | Version |
Homebox (server) | v0.26.0 or newer (tested v0.26.2) |
Python | 3.10.13+ |
Dependencies | Listed in |
|
Older Homebox releases that still expose /api/v1/items and
/api/v1/locations are not supported — the server talks to
/api/v1/entities exclusively.
Install the server
git clone <repo-url> homebox-mcp
cd homebox-mcp
uv sync # installs runtime + dev dependencies
cp .env.example .env # then edit .env with your Homebox credentials.env.example:
HOMEBOX_BASE_URL=https://your-homebox-instance.com
HOMEBOX_USERNAME=your_email@example.com
HOMEBOX_PASSWORD=your_password
# Optional HTTP server settings (server_http.py)
# FASTMCP_HOST=0.0.0.0
# FASTMCP_PORT=8000Docker
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-mcpRun the server
just start # stdio mode (default for local agents)
just start-http # Streamable HTTP mode at http://0.0.0.0:8000/mcpOr directly:
uv run main.py # stdio
uv run server_http.py # streamable HTTPInstall inside an agent
Pick the snippet that matches your agent and copy-paste it into the agent's
MCP configuration. Replace the values in <…> with your own.
The examples assume stdio mode (local agent) with credentials read from
environment / .env. Streamable HTTP mode exposes the same tools but takes
credentials through request headers instead.
Claude Desktop / Claude Code (claude_desktop_config.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)
Add to your Hermes MCP servers configuration:
{
"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)
{
"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"
}
}
}
}Remote / Streamable HTTP
If you run the server with uv run server_http.py (or via the Docker image),
any MCP-compatible HTTP client can reach it at http://host:8000/mcp and pass
credentials via request headers:
HOMEBOX_USERNAME: your_email@example.com
HOMEBOX_PASSWORD: your_password
X-Tenant: 00000000-0000-0000-0000-000000000000 # optional, multi-collectionHOMEBOX_BASE_URL is read only from the runtime environment of the server
process; it cannot be overridden per request.
Finding the X-Tenant value
Open Homebox, log in, then in DevTools → Network find the request
GET /api/v1/users/self and copy the X-Tenant request header. Pass it on
every MCP request via the X-Tenant header.
Available tools
17 tools are exposed. Grouped parameter objects accept both snake_case and camelCase keys.
Products (entities)
search_products
Search products via Homebox query plus local filters. Provide at least one
filter; name is split into per-term queries when no other strict filter is
provided.
name(Optional[str]) — product name (or phrase) queryitem_id(Optional[str]) — exact product UUIDasset_id(Optional[str]) — asset ID filterserial_number(Optional[str])model_number(Optional[str])manufacturer(Optional[str])query(Optional[str]) — generic full-text query
Returns: list of matching product dicts, or an error string.
add_product
Create a new product or subitem with grouped parameters. If both location
and parent_item are provided, parent_item wins (subitem creation).
identity(dict, required):{"name", "description", "asset_id"}location(Optional[dict]):{"id"}or{"name"}— for standalone productsparent_item(Optional[dict]):{"id"}or{"name"}— for subitemsidentifiers(Optional[dict]):{"serial_number", "model_number", "manufacturer"}purchase(Optional[dict]):{"price", "seller", "date", "warranty_expires"}metadata(Optional[dict]):{"notes", "quantity", "tag_ids", "tag_names", "custom_fields"}status(Optional[dict]):{"archived", "insured", "lifetime_warranty"}
Returns: created product dict (after optional update pass), or error string.
update_product_by_id
Update an existing product. Pass IDs (item_id, location.id) — names are
only resolved through list_locations outside this tool.
item_id(str, required) — UUID of the item to updateidentity(Optional[dict]):{"new_name", "description", "asset_id"}location(Optional[dict]):{"id"}or{"name"}identifiers(Optional[dict]):{"serial_number", "model_number", "manufacturer"}purchase(Optional[dict]):{"price", "seller", "date", "warranty_expires"}metadata(Optional[dict]):{"notes", "quantity", "tag_ids", "tag_names", "custom_fields"}status(Optional[dict]):{"archived", "insured", "lifetime_warranty"}
Returns: updated product dict, or error string.
add_bulk_products
Add multiple products in one call. Each entry uses the grouped shape from
add_product.
items(list[dict], required) — each item must contain anidentity.nameReturns: list (one result per input item).
delete_product_by_id
Delete a single product by ID.
item_id(str, required) — UUID of the itemproduct_name(str, required) — used for the success/error message only
Returns: success message, or error string.
delete_bulk_products
Delete multiple products. Delegates to delete_product_by_id for each entry.
items(list[dict], required) — each entry{item_id, product_name}Returns: list (one result message per input item).
Locations
list_locations
Return Homebox's location tree (GET /api/v1/entities/tree).
No parameters.
Returns: list of top-level tree nodes (each with nested
children).
create_location
Create a new location (entity with isLocation implied by being parent-less).
name(str, required)description(Optional[str])parent_id(Optional[str]) — UUID of a parent location
Returns: created location dict, or error string.
update_location_by_id
Update a location's editable fields. Fetches the current state first to preserve omitted fields.
location_id(str, required) — UUID of the locationnew_name(Optional[str])new_description(Optional[str])new_parent_id(Optional[str]) — pass empty string""to detach from parent
Returns: updated location dict, or error string.
delete_location_by_id
Delete a single location.
location_id(str, required) — UUID of the locationReturns: success message, or error string.
list_items_by_location
List direct child items of a location (uses ?parentIds=…).
location_id(str, required) — UUID of the locationReturns: list of
{"id", "name"}items, or error string.
Tags
list_tags
Return all tags from /api/v1/tags.
No parameters.
Returns: list of
{"id", "name"}tags, or error string.
create_tag
Create a tag if it does not already exist (case-folded de-dupe).
name(str, required)Returns: created or pre-existing tag dict, or error string.
Maintenance
create_maintenance_entry_for_product
Create a maintenance entry for an existing product. Provide
scheduled_date, completed_date, or both; if neither, both default to
today.
product_id(Optional[str]) — preferred if knownproduct_name(Optional[str]) — resolved to a product ID ifproduct_idis missingname(str, required) — title/summary of the taskdescription(Optional[str])cost(Optional[float])scheduled_date(Optional[str]) —YYYY-MM-DDcompleted_date(Optional[str]) —YYYY-MM-DD
Returns: created maintenance entry dict, or error string.
list_maintenance_entries
List maintenance entries from Homebox. The global /api/v1/maintenance
endpoint is broken on Homebox v0.26+; this tool falls back to enumerating
per-entity maintenance lists and aggregating them.
product_id(Optional[str]) — restrict to entries for one productproduct_name(Optional[str]) — used to resolve the entity whenproduct_idis missingReturns: list of maintenance entry dicts, or error string.
update_maintenance_entry
Update an existing maintenance entry. Homebox v0.26+ exposes maintenance only per-entity, so the owning product must be supplied.
entry_id(str, required) — UUID of the entryproduct_id(Optional[str]) — UUID of the owning entity (preferred)product_name(Optional[str]) — resolved to an entity whenproduct_idis missingname(Optional[str])description(Optional[str])cost(Optional[float])scheduled_date(Optional[str]) —YYYY-MM-DDcompleted_date(Optional[str]) —YYYY-MM-DD
Returns: updated maintenance entry dict, or error string.
delete_maintenance_entry
Delete a maintenance entry.
entry_id(str, required) — UUID of the entryReturns: success message, or error string.
Testing
The project ships unit tests and end-to-end tests.
Unit tests (no Docker required)
just test
# or: uv run pytest tests/ -v -k "not e2e"End-to-end tests (requires Docker)
The E2E suite spins up a pinned Homebox v0.26.2 container, builds the MCP server image, registers a test user, and exercises all 17 tools through the Streamable HTTP transport. Each tool's effect is then verified against the real Homebox API (not just the MCP response envelope).
just test-e2eWhat it does:
docker compose -f docker-compose.e2e.yml up -d— start Homebox v0.26.2 on port31745and the MCP server on port31746.Wait for the Homebox healthcheck.
Register a test user (idempotent).
Run
pytest tests/e2e/ -m e2e.Tear down all containers.
See docker-compose.e2e.yml for the pinned image and tests/e2e/ for the
suite. The suite covers every tool:
Category | Tools tested |
Locations |
|
Products |
|
Tags |
|
Maintenance |
|
Recommended workflow for adding items
Find a location for a standalone product — call
list_locationsto get a location UUID.Find a parent for a subitem — call
search_products(name=...)to resolve the parent item UUID.Create or update — use
add_product(new) orupdate_product_by_id(existing). If bothlocationandparent_itemare passed toadd_product,parent_itemis used andlocationis ignored for the creation target.
Justfile commands
just --list # Show all commands
just start # Start MCP server in stdio mode
just start-http # Start MCP server in Streamable HTTP mode
just stop # Stop running HTTP servers
just lint-format # Run linting checks
just lint-fix # Run linting and auto-fix issues
just test # Run unit tests
just test-e2e # Run E2E tests (requires Docker)
just wipe # Clean cache/build artifacts
just docker-build # Build Docker image
just docker-run # Run Docker containerRepository layout
homebox-mcp/
├── main.py # FastMCP server, all 17 tools, helpers
├── models.py # Grouped dataclasses for tool inputs
├── server_http.py # Streamable HTTP transport entrypoint
├── request_config_middleware.py # Per-request credential/tenant scoping
├── Dockerfile # HTTP server image
├── docker-compose.e2e.yml # Pinned Homebox + MCP server stack
├── justfile # Dev/test commands
├── scripts/e2e-setup.sh # Test user registration helper
├── tests/
│ ├── test_main.py # Unit tests (mocked HTTP)
│ ├── test_request_config_middleware.py
│ ├── test_transport_servers.py
│ └── e2e/ # E2E tests against real Homebox v0.26.2
└── pyproject.tomlAvailable Tools
17 toolsadd_bulk_productsD
Inputs: items (required). Returns: result
add_productA
Add a new product (or subitem) to Homebox with grouped parameters.
Args:
identity: Required product identity {"name": str, "description": str, "asset_id": str}
location: Optional location {"id": str} or {"name": str}
parent_item: Optional parent item {"id": str} or {"name": str}.
If provided, the new item is created as a subitem and parent_item
takes precedence over location.
identifiers: Optional manufacturing info {"serial_number", "model_number", "manufacturer"}
purchase: Optional purchase details {"price": float, "seller": str, "date": str, "warranty_expires": str}
metadata: Optional extra info {"notes": str, "quantity": int, "tag_ids": [str], "tag_names": [str], "custom_fields": [{}]}
status: Optional flags {"archived": bool, "insured": bool, "lifetime_warranty": bool}
Behavior:
- Standalone product: provide a location (id or name).
- Subitem product: provide parent_item (id or name).
- If both parent_item and location are provided, parent_item wins.
Inputs: identity (required), status, location, metadata, +3 more. Returns: result
create_locationD
Inputs: name (required), parent_id, description. Returns: result
create_maintenance_entry_for_productA
Create a maintenance entry for a specific Homebox product/entity.
Use this when the user wants to log servicing, repairs, inspections,
scheduled maintenance, or maintenance cost for an item.
If only a product name is known, provide `product_name` and this tool
will resolve the product first, then create the maintenance entry.
If the product ID is known, provide `product_id` directly for faster execution.
At least one of `scheduled_date` or `completed_date` must be provided.
If neither is provided, both default to today's date.
Args:
product_id: UUID of the product/entity (preferred if known).
product_name: Name of the product to search for (used if product_id not provided).
name: Title/summary of the maintenance task (required).
description: Detailed description of the work performed or planned.
cost: Cost of the maintenance in numeric format (e.g., 150.50).
scheduled_date: Scheduled date for maintenance (ISO 8601: YYYY-MM-DD).
Defaults to today if neither date is provided.
completed_date: Completion date for maintenance (ISO 8601: YYYY-MM-DD).
Defaults to today if neither date is provided.
Returns:
Created maintenance entry dict on success, or error string on failure.
Inputs: cost, name, product_id, description, +3 more. Returns: result
create_tagA
Create a Homebox tag if it does not already exist.
Inputs: name (required). Returns: result
delete_bulk_productsD
Inputs: items (required). Returns: result
delete_location_by_idD
Inputs: location_id (required). Returns: result
delete_maintenance_entryADestructive
Delete a maintenance entry from Homebox.
Use this to remove maintenance records that are no longer needed.
Args:
entry_id: UUID of the maintenance entry to delete (required).
Returns:
Success message on success, or error string on failure.
Inputs: entry_id (required). Returns: result
delete_product_by_idD
Inputs: item_id (required), product_name (required). Returns: result
list_items_by_locationD
Inputs: location_id (required). Returns: result
list_locationsD
Inputs: no named parameters. Returns: result
list_maintenance_entriesARead-onlyIdempotent
List maintenance entries from Homebox.
If `product_id` (or `product_name`) is provided, returns entries for
that product only. Otherwise aggregates maintenance entries across
all non-location entities (the global `/api/v1/maintenance` endpoint
is not available on Homebox v0.26+).
Args:
product_id: UUID of the product/entity. Preferred for speed.
product_name: Name of the product to resolve when `product_id`
is not provided.
Returns:
List of maintenance entries (each is a dict), or an error string.
Inputs: product_id, product_name. Returns: result
list_tagsA
List Homebox tags from the current /api/v1/tags endpoint.
Inputs: no named parameters. Returns: result
search_productsB
Search products using Homebox query + local filters.
Provide at least one filter. `name` is split into terms and each term
is queried in parallel when no other strict filter is provided.
Inputs: name, query, item_id, asset_id, +3 more. Returns: result
update_location_by_idD
Inputs: location_id (required), new_name, new_parent_id, new_description. Returns: result
update_maintenance_entryA
Update an existing maintenance entry.
Homebox v0.26+ only exposes maintenance entries per-product, so this
tool requires either `product_id` (preferred) or `product_name` to
locate the entry's owning entity before issuing the PUT.
Args:
entry_id: UUID of the maintenance entry to update (required).
product_id: UUID of the product/entity owning the entry.
product_name: Name of the product (used to resolve the entity
when `product_id` is missing).
name: New title for the maintenance task.
description: New detailed description.
cost: New cost value (numeric).
scheduled_date: New scheduled date (ISO 8601: YYYY-MM-DD).
completed_date: New completion date (ISO 8601: YYYY-MM-DD).
Returns:
Updated maintenance entry dict on success, or error string on failure.
Inputs: entry_id (required), cost, name, product_id, +4 more. Returns: result
update_product_by_idA
Update an existing product with grouped parameters.
Args:
item_id: UUID of the item to update (required)
identity: Optional identity updates {"new_name": str, "description": str, "asset_id": str}
location: Optional location change {"id": str} or {"name": str}
identifiers: Optional manufacturing updates {"serial_number", "model_number", "manufacturer"}
purchase: Optional purchase updates {"price": float, "seller": str, "date": str, "warranty_expires": str}
metadata: Optional metadata updates {"notes": str, "quantity": int, "tag_ids": [str], "tag_names": [str], "custom_fields": [{}]}
status: Optional flag updates {"archived": bool, "insured": bool, "lifetime_warranty": bool}
Inputs: item_id (required), status, identity, location, +3 more. Returns: result
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 17 tools
Most tools target distinct entities (products, locations, maintenance, tags), but 'add_bulk_products' and 'add_product' could be confused without description. Several tools lack descriptions, reducing clarity.
Names mix patterns: 'add_product', 'create_location', 'create_maintenance_entry_for_product', 'delete_bulk_products', 'list_items_by_location'. Inconsistent verb styles and lengths.
17 tools is reasonable for an inventory server covering products, locations, maintenance, and tags. Slightly on the higher end but still well-scoped.
CRUD for main entities is present, but missing explicit get-by-ID tools for products and locations. Many tools have no descriptions, creating dead ends for agents.
Maintenance
Related MCP Connectors
Nifty's MCP server — exposes tasks, projects, messages, and files as tools for AI agents.
MCP server for your apps' tools and custom tools, plus hosted AI agents and approval-gated workflows
A registry of AI agent tools — MCP servers, APIs, CLIs, SDKs — kept current by automated ingestion.
One MCP server for the services your AI needs: memory, files, Microsoft 365, kanban and more.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceMCP server for managing Homebox inventory via AI assistants, enabling item management, location organization, and label categorization.4MIT
- AlicenseNot gradedqualityDmaintenanceAn MCP server that gives AI assistants real-time access to your homelab infrastructure. It enables querying node status, managing Docker containers, controlling Proxmox VMs, and inspecting OPNsense firewall state through natural conversation.2MIT
- AlicenseAqualityBmaintenanceMCP server for Homebox home inventory, enabling natural language queries and management of items, locations, tags, warranties, maintenance, and attachments.39180 PyPI18MIT
- AlicenseAqualityCmaintenanceAn MCP server exposing 72 tools across 26 homelab services, enabling LLMs to monitor and manage infrastructure, media, storage, and networking with a single endpoint.16MIT