Server data from the Official MCP Registry
Homebox home inventory: search, intake, warranties, attachments, locations, tags, QR labels
Homebox home inventory: search, intake, warranties, attachments, locations, tags, QR labels
This is a well-designed MCP server with proper authentication, good error handling, and permissions aligned to its inventory management purpose. The codebase demonstrates security awareness (credential handling in .env, HEIC auto-conversion, attachment sanitization, confirm-gated deletes). No critical vulnerabilities detected. Minor code quality findings around broad exception handling and sensitive data in logs do not materially affect security. Supply chain analysis found 5 known vulnerabilities in dependencies (0 critical, 3 high severity). Package verification found 1 issue.
3 files analyzed · 13 issues found
Security scores are indicators to help you make informed decisions, not guarantees. Always review permissions before connecting any MCP server.
This plugin requests these system permissions. Most are normal for its category.
Set these up before or after installing:
Environment variable: HOMEBOX_URL
Environment variable: HOMEBOX_TOKEN
Environment variable: HOMEBOX_ALIAS_FIELD
Environment variable: HOMEBOX_LABEL_DIR
Add this to your MCP configuration file:
{
"mcpServers": {
"io-github-dgahagan-homebox-mcp": {
"env": {
"HOMEBOX_URL": "your-homebox-url-here",
"HOMEBOX_TOKEN": "your-homebox-token-here",
"HOMEBOX_LABEL_DIR": "your-homebox-label-dir-here",
"HOMEBOX_ALIAS_FIELD": "your-homebox-alias-field-here"
},
"args": [
"homebox-mcp"
],
"command": "uvx"
}
}
}From the project's GitHub README.
An MCP server over the
Homebox REST API, so an MCP client like Claude can
work with your home inventory in plain language: "where is my impact driver",
"what's in Tote B-3", "which warranties expire this year", "add this drill —
here's a photo of the receipt and the model number". It answers questions,
performs intake (create an item, attach the manual/receipt, tag and file it),
manages attachments, and prints QR labels for totes and shelves — no
hand-rolled curl.
You need a running Homebox instance (0.26+, see Requirements) and an API key from Profile → API Keys in the Homebox web UI.
claude mcp add homebox --scope user \
-e HOMEBOX_URL=https://homebox.example.com \
-e HOMEBOX_TOKEN=hb_xxxxxxxxxxxxxxxxxxxxxxxx \
-- uvx homebox-mcp
claude mcp get homebox # should show: Status ✔ Connected
--scope user makes it available from any project. uvx fetches and runs the
published package in an ephemeral environment — nothing to install first.
Add to your client's MCP config (e.g. claude_desktop_config.json):
{
"mcpServers": {
"homebox": {
"command": "uvx",
"args": ["homebox-mcp"],
"env": {
"HOMEBOX_URL": "https://homebox.example.com",
"HOMEBOX_TOKEN": "hb_xxxxxxxxxxxxxxxxxxxxxxxx"
}
}
}
}
The module carries PEP 723 inline
dependencies, so it also runs standalone with uv run --script — uv resolves
mcp, httpx, pillow, and pillow-heif into an ephemeral venv on first run.
Instead of env vars, drop credentials in a .env file next to the module:
git clone https://github.com/dgahagan/homebox-mcp
cd homebox-mcp
cp .env.example .env # .env is gitignored
chmod 600 .env
# edit .env: HOMEBOX_URL + HOMEBOX_TOKEN
Then register the absolute path to the module (or to server.py, a
compatibility shim kept for older registrations):
claude mcp add homebox --scope user -- \
uv run --script /path/to/homebox-mcp/homebox_mcp.py
The server reads config from the environment, falling back to the sibling
.env (resolved relative to the script, so the absolute-path invocation still
finds it). The credential never enters the MCP config — it stays in the
gitignored .env.
/items, /locations, /labels) and are rejected on the first tool call
with a clear error rather than failing cryptically.hb_).uv, or any Python ≥ 3.10 environment
where you install homebox-mcp yourself.All configuration is via environment variables (or the sibling .env for the
clone workflow):
| Variable | Required | Purpose |
|---|---|---|
HOMEBOX_URL | yes | Base URL of your Homebox instance (no trailing slash). |
HOMEBOX_TOKEN | yes | Homebox API key (hb_…). |
HOMEBOX_ALIAS_FIELD | no | Name of one custom field to treat as a stable item identifier — items can be resolved by it, summaries surface it, and field_index defaults to it. Unset = resolve by assetId/name only. See Conventions. |
HOMEBOX_LABEL_DIR | no | Where generate_label / qrcode save output. Default: current working directory. |
~39 tools. Reads accept a fuzzy identifier (assetId, alias field, exact
name, then first keyword match). Write tools require an exact identifier
(assetId, alias field, or exact name) — a typo or an ambiguous match is refused
rather than mutating the wrong item. Locations are referenced by name or
/-separated path (e.g. Garage/Shelf 1) to disambiguate duplicate names.
| Tool | Purpose |
|---|---|
search_items(query?, tags?, limit=20) | Search items by keyword and/or tag names (AND of both); returns each with assetId, location, and the alias field. |
get_item(identifier) | Full detail for one item: location path, identity, purchase, warranty, custom fields, tags, attachments. |
list_locations() | The full location tree as an indented outline. |
location_contents(location, recursive=False) | Items directly in a location plus its sub-location names; recursive=True walks the whole subtree and returns every nested item with its full location path. |
list_tags(detail=False) | All tag (label) names; detail=True returns full objects (description, color, icon, parent tag). |
warranties_expiring(before?, after?, lifetime=False) | Items whose warranty expires in after…before (after defaults to today, excluding already-expired); lifetime=True lists lifetime-warranty items. |
| Tool | Purpose |
|---|---|
create_item(name, location?, quantity=1, manufacturer?, model?, serial?, purchase_price?, purchase_date?, purchase_from?, warranty_expires?, notes?, fields?, tags?) | Create and enrich an item in one call. fields is a dict typed by JSON value (string→text, number→number [integer-coerced], bool→boolean); tags must already exist. Returns the new assetId. |
import_csv(csv_text) | Bulk-create items and locations from a Homebox CSV in one multipart request. HB.location auto-creates the path hierarchy; recognizes HB.name, HB.tags, HB.quantity, HB.serial_number, HB.model_number, HB.manufacturer, HB.notes, HB.purchase_*, HB.warranty_expires, HB.field.<name>. |
create_location(name, parent?, description?) | Create a location (tote/bin/shelf) to bootstrap a new storage spot; description doubles as a contents manifest. |
barcode_lookup(code) | UPC/EAN → name/manufacturer/model (optional, for boxed goods). |
duplicate_item(identifier, copy_attachments=False, copy_custom_fields=True, copy_maintenance=False, prefix="Copy of ") | Duplicate an item ("I bought a second one"). Copied custom fields include the alias field verbatim — give the copy its own value after. |
All write tools resolve by exact identifier and preserve everything you don't touch (a full-body PUT that echoes the rest of the item back — see gotchas).
| Tool | Purpose |
|---|---|
set_item(identifier, new_name?, description?, notes?, quantity?, purchase_price?, purchase_date?, purchase_from?, insured?, archived?, fields?) | General item editor: rename, notes, quantity, purchase info, insured/archived flags, custom fields. Quantity-only edits use a partial PATCH. |
move_item(identifier, location) | Move an item to another location (partial PATCH — nothing else changes). |
set_warranty(identifier, expires?, lifetime?, details?) | Set warranty end date, lifetime flag, and terms summary. |
set_identity(identifier, manufacturer?, model_number?, serial_number?) | Set manufacturer / model / serial (e.g. after a nameplate photo reveals them). |
set_fields(identifier, fields) | Create or overwrite custom fields (upsert; typed by JSON value type). |
set_tags(identifier, tags, mode="add") | Add / remove / replace tags on an item; unknown tag names are auto-created (partial PATCH). |
set_tag(name, new_name?, description?, color?, icon?, parent?, clear_parent=False) | Edit a tag's own metadata (rename, color, icon, parent tag for grouping) — not what's tagged on an item. Creates the tag if new. |
set_location(location, new_name?, parent?, clear_parent?, description?, notes?, tags?, tags_mode?, entity_type?, asset_id?, fields?) | General location editor: rename, move (or clear_parent to root), tags, notes, entity type, assetId, custom fields. |
mark_sold(identifier, sold_price?, sold_to?, sold_date?, sold_notes?, clear=False) | Record a sale (price/buyer/date/notes) or clear=True to un-sell; pair with set_item(archived=True) to retire the item. |
| Tool | Purpose |
|---|---|
log_maintenance(identifier, name, description?, completed_date?, scheduled_date?, cost?) | Add an entry — "changed the mower oil today" (completed) or "sharpen blades in spring" (scheduled). |
list_maintenance(identifier?, status="both") | Entries for one item, or across the whole inventory ("what maintenance is due?"); status = scheduled / completed / both. |
set_maintenance(entry_id, ...) | Edit an entry — e.g. mark a scheduled one completed by setting completed_date. |
delete_maintenance(entry_id, confirm) | Delete one entry (confirm = its exact name). |
| Tool | Purpose |
|---|---|
inventory_stats(by="totals", start?, end?) | Totals (counts, total value, warranty count), value by location or tag, or purchase-price over time — the cheap way to answer "what's my inventory worth?". |
export_csv(save_to?) | Export the whole inventory as a Homebox CSV (complement of import_csv; quick backup). |
list_custom_fields(field?) | Discover the custom-field schema in use: all field names, or every distinct value of one field. |
| Tool | Purpose |
|---|---|
attach_document(identifier, source, title, doc_type="manual", primary=False) | Attach a file (local path or http(s) URL) to an item, or to a location by name as a fallback. doc_type e.g. manual/receipt/warranty/photo; primary=True makes a photo the entity's primary image (e.g. a location's "this is the spot" wayfinding shot). |
list_attachments(identifier) | List an item's or location's attachments with their ids (the handle the other attachment tools need). |
get_attachment(identifier, attachment_id, save_to) | Download one attachment to a local path so its content (e.g. a manual) can be read. |
rename_attachment(identifier, attachment_id, title?, doc_type?, primary?) | Update an attachment's title, type, or primary flag. |
Uploads auto-convert HEIC/HEIF (iPhone photos) to JPEG so Homebox always stores a browser-renderable image, and sanitize
/in titles (Homebox treats a title as a path and would otherwise truncate it).
Every delete requires confirm to equal the target's exact name and carries
the MCP destructiveHint annotation, so clients prompt before running them.
| Tool | Purpose |
|---|---|
delete_item(identifier, confirm) | Permanently delete an item and its attachments. No undo. |
delete_location(location, confirm, confirm_nonempty=False) | Delete a location. A non-empty one is refused unless confirm_nonempty=True: sub-locations cascade (are deleted); items are orphaned to the top level (not deleted). |
delete_tag(name, confirm) | Delete a tag itself (removed from every tagged item; the items survive). |
delete_attachment(identifier, attachment_id, confirm) | Delete one attachment (confirm = its exact title). |
| Tool | Purpose |
|---|---|
generate_label(identifier, kind="location", out_dir?) | Save a printable label PNG (QR + readable name) for a location, item, or asset. Saves to $HOMEBOX_LABEL_DIR (else CWD). |
qrcode(data, out_dir?) | Save a raw QR JPEG for arbitrary data (prefer generate_label for totes — it adds the name). |
finalize_photos() | Finalize after a bulk photo attach: set missing primary images, then generate missing thumbnails. |
field_index(field_name?) | {field_value: assetId} index over a custom field (default $HOMEBOX_ALIAS_FIELD) — a one-pass dedupe index before a bulk import, since q doesn't index custom fields. |
The value of this server is as much in what it works around as in what it exposes. These are hard-won behaviors of the 0.26 entities API:
/v1/entities); a location is
an entity whose entity-type has isLocation=true. A new item with no
entityTypeId lazily gets a default Item type.GET /entities returns non-location entities only. Location children never
appear in /entities results, even with parentIds — sub-locations come from
the entity tree (/entities/tree). location_contents merges both sources, and
delete_location's emptiness check counts items and sub-locations.confirm_nonempty=True) deletes its sub-locations
with it, but its items survive, re-parented to the top level. Move items
out first if you don't want them loose.fields and
have an empty assetId. The server fetches full detail (GET /entities/{id})
before returning assetId/alias, so search_items/get_item are accurate.q does not index custom fields. Resolving by an alias-field value uses a
scan fallback (one detail fetch per item). Resolving by assetId (via
/v1/assets/{id}) or name is cheaper — prefer assetId when you have it
(e.g. the value create_item returns).assetId. Every write tool rebuilds a
full body from a fresh GET (_preserve_item_body) and overrides only the
targeted keys. create_item assigns the asset ID after enrichment so it
survives./. Homebox treats a title as
path-like, so "front 3/4 view" silently stores as "4 view". The server
sanitizes /→- in attach titles.numberValue (e.g.
17000.0, which FastMCP produces from a float-typed arg) makes the API
500 "Unknown Error". The server coerces number values to int; pass whole
numbers.textValue / numberValue /
booleanValue). The server rebuilds each field by its declared type —
coercing everything to textValue (an earlier bug) silently wiped
numeric/boolean fields on any PUT.total, so search, dedupe, and
warranty sweeps stay correct on large inventories.Homebox's built-in assetId is a stable numeric handle, but it isn't something
you'd type from memory. If you want a human-meaningful stable identifier —
useful for cross-referencing items with receipts, spreadsheets, or another
system — add a custom field (say item_id) holding a slug like
makita-xdt131 and point HOMEBOX_ALIAS_FIELD at it. Then:
search_items/get_item summaries surface it, andfield_index() defaults to it, giving you a one-call dedupe map before a bulk
import.This is entirely optional — leave HOMEBOX_ALIAS_FIELD unset and items resolve
by assetId or name only. Nothing about the field name is special; any single
custom field works.
Two standalone helper scripts ship in the repo (not MCP tools):
annotate_location_photos.py turns one wide photo of a shelf bank into one
boxed/labeled image per shelf (--rows N), and can upload each straight to its
Homebox location with --upload. See its --help.heic2jpg.py converts HEIC/HEIF (iPhone photos) to JPEG for ad-hoc use
(./heic2jpg.py FILE...). Note the attach tools already auto-convert HEIC on
upload, so this is only needed to feed HEIC frames to a vision step before
upload.Both rely on pillow-heif, which bundles libheif — there's no system package to
install.
.env, which is
gitignored and should be mode 600 (chmod 600 .env). Never commit it. In the
uvx workflow it's passed through your MCP client's config — treat that file
as a secret too.HBOX_AUTH_API_KEY_PEPPER invalidates
every issued key — mint a new one and update your config.confirm to equal the
target's exact name and carry the MCP destructiveHint annotation, so a
well-behaved client prompts before any destructive call.Authorization header.git clone https://github.com/dgahagan/homebox-mcp
cd homebox-mcp
# Run the test suite (httpx mocked with respx — no live instance needed)
uv run --with pytest --with respx --with . pytest
# Lint
uv run --with ruff ruff check .
The server is a single module (homebox_mcp.py) with PEP 723 inline
dependencies. See CONTRIBUTING.md for setup and PR
expectations, and CHANGELOG.md for release notes.
MIT © Dan Gahagan
Be the first to review this server!
by Modelcontextprotocol · Developer Tools
Read, search, and manipulate Git repositories programmatically
by Toleno · Developer Tools
Toleno Network MCP Server — Manage your Toleno mining account with Claude AI using natural language.
by mcp-marketplace · Developer Tools
Create, build, and publish Python MCP servers to PyPI — conversationally.