loading…
Search for a command to run...
loading…
Self-hosted AI orchestrator that monitors and manages homelab services, exposing them as MCP tools for LLMs to drive.
Self-hosted AI orchestrator that monitors and manages homelab services, exposing them as MCP tools for LLMs to drive.
Self-hosted AI orchestrator for your homelab — monitors your services, self-heals when they break, and exposes everything you run as MCP tools any LLM can drive.
CI PyPI Docker Python 3.11+ License: MIT Self-hosted
A FastAPI + Ollama-powered control plane that sits in front of your *arr stack, Jellyfin, qBittorrent, Paperless, Immich, Home Assistant — whatever you run — and gives you:
Status: beta. The core architecture is stable and battle-tested in a production homelab; expect rough edges around new-service plugins and the PWA polish.
The selfhosted scene has great individual tools — Sonarr knows about TV, Immich knows about photos, Paperless knows about scans — but nothing that lets an LLM drive all of them coherently. You can ask ChatGPT for a recipe; you can't ask your homelab "is anything broken, and if so fix it" or "find me an audiobook by Brandon Sanderson and download it" without writing custom glue per service.
homelab-ai is the glue: a config-driven plugin system where every service you run becomes both a monitored entity and a callable tool, with an AI agent that does the routing and an auto-repair loop that fixes the boring stuff while you sleep.
Recommended — Docker:
# 1) Generate config.yaml interactively (detects Ollama, scans for services, makes an API key)
docker run --rm -it --network host -v "$PWD/data:/data" \
ghcr.io/jeremiahm37/homelab-ai:latest --config /data/config.yaml init
# 2) Run for real
docker run -d --name homelab-ai \
-p 9105:9105 \
-v "$PWD/data:/data" \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
ghcr.io/jeremiahm37/homelab-ai:latest
Open http://<your-host>:9105/app. The PWA asks for the API key on first load — paste it from data/config.yaml.
Or use docker-compose.example.yml.
Try with zero setup — demo mode:
docker run --rm -p 9105:9105 ghcr.io/jeremiahm37/homelab-ai:latest demo
Open http://localhost:9105/app — five mock services, working chat with tool-call cards, real history, no Ollama needed.
Python install (for development or running as a systemd service):
pip install homelab-ai
homelab-ai --config config.yaml init
homelab-ai --config config.yaml run
The pip path is mainly for contributors and people running homelab-ai as a host-level systemd service. For day-to-day self-hosting, use Docker — dependency isolation, single-command upgrades, and the only thing you need to back up is ./data.
Works with anything that speaks the Ollama or OpenAI API:
llm:
backend: openai_compat # or "ollama", or "auto"
url: https://api.openai.com/v1 # or http://localhost:11434 for Ollama
api_key: ${OPENAI_API_KEY}
small_model: gpt-4o-mini
smart_model: gpt-4o
embed_model: text-embedding-3-small
Tested with Ollama, vLLM, LiteLLM proxy, OpenAI, Anthropic-via-LiteLLM, OpenRouter, Groq, LM Studio.
Set auth.enabled: true in config.yaml (the wizard does this for you) and every request needs an X-Api-Key header or a session cookie. Optional username/password users go in auth.users with bcrypt hashes (or PBKDF2 if bcrypt isn't installed).
auth:
enabled: true
api_key: hk_<generated-32-char-token>
users:
admin: "$2b$12$..."
Everything beyond the agent/AI core is opt-in. Toggle features in config.yaml:
features:
metrics: {enabled: true} # /metrics endpoint
ntfy: {enabled: true, url: "..."} # push notifications
scheduler: {enabled: true, schedules: [...]}
webhooks: {enabled: true, receivers: {...}}
multi_llm: {enabled: true, ...} # cheap local + paid for hard problems
history: {enabled: true} # /api/history/{scans,ai,fixes}
rag: {enabled: true} # ChromaDB-backed document search
mcp_http: {enabled: true} # /mcp JSON-RPC for HTTP MCP clients
A feature that's off has zero cost — its module is never imported, its routes are never registered, its deps don't need to be installed. Heavy deps live behind pip extras:
pip install homelab-ai[metrics] # adds prometheus-client
pip install homelab-ai[rag] # adds chromadb
pip install homelab-ai[scheduler] # adds croniter
pip install homelab-ai[all] # everything
See docs/features.md for the full menu.
┌─────────────────────────────────────────────────────────┐
│ Mobile PWA / AI Chat / MCP Client (Claude/etc.) │
└─────────────────────┬───────────────────────────────────┘
│ HTTP + tool calls
┌─────────────────────▼───────────────────────────────────┐
│ FastAPI core (REST, MCP, WebUI, Settings) │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ Tool router Service plugins Settings store │
│ (semantic) (Sonarr, Radarr, (YAML + secrets) │
│ Jellyfin, ...) │
└─────────────────────┬───────────────────────────────────┘
│
┌─────────────────────▼───────────────────────────────────┐
│ Agent scan loop ──► Tier-1 rules ──► Tier-2 LLM │
│ │ │
│ Failure memory (SQLite) ◄─────────────────┘ │
│ │ │
│ ▼ │
│ Tier-3 smart │
│ fixer + audit │
└─────────────────────────────────────────────────────────┘
agent/modules/, done.backups/ and logged in audit_log.md for human review./api/ai/agent) that picks tools semantically and streams responses.mcp/custom_tools/ to add your own.health(), restart(), and optional tools() methods.health() for monitoring; the AI uses tools() for actions.python -m homelab_ai.verify runs a flow-test suite against your live config.fix-request.md file the AI agent or you can pick up.All configuration lives in one config.yaml. Environment variables override file values, so secrets can stay out of the file. See config.example.yaml for the annotated reference.
Minimal example:
ollama:
url: http://localhost:11434
small_model: qwen3.5:4b # tool-calling / intent
smart_model: qwen3.6:35b # repair / chat
agent:
scan_interval: 300 # seconds
notify:
discord_webhook: "" # optional
services:
sonarr:
url: http://sonarr:8989
api_key: ${SONARR_API_KEY}
radarr:
url: http://radarr:7878
api_key: ${RADARR_API_KEY}
jellyfin:
url: http://jellyfin:8096
api_key: ${JELLYFIN_API_KEY}
Service entries map to plugins by name. Unknown names are loaded from homelab_ai/services/ or the user ~/.config/homelab-ai/services/ directory.
For any service that speaks HTTP-JSON, you can add support without writing Python — just a config block:
services:
my_thing:
plugin: generic_http
url: http://my-thing:8080
auth:
type: bearer
key: ${MY_THING_TOKEN}
health:
path: /healthz
tools:
- name: list_widgets
description: List widgets from My Thing.
path: /api/widgets
params:
limit: {type: integer, default: 10}
- name: get_widget
description: Get one widget by ID.
path: /api/widgets/{id}
params:
id: {type: string, required: true, in: path}
Restart, and the agent monitors my_thing health every scan while the AI gains two new tools. See docs/declarative-services.md for the full schema.
The 30 built-in Python plugins (sonarr, radarr, jellyfin, nut, etc.) remain the finished build — they handle wire protocols, multi-step auth, and complex transforms. generic_http is the blueprint for everything else.
# ~/.config/homelab-ai/services/my_thing.py
from homelab_ai.services.base import Service, ToolSpec
class MyThing(Service):
name = "my_thing"
async def health(self) -> dict:
r = await self.http.get(f"{self.config['url']}/health")
return {"ok": r.status == 200}
def tools(self) -> list[ToolSpec]:
return [
ToolSpec(
name="my_thing_status",
description="Get current status of My Thing.",
handler=self._status,
params={},
),
]
async def _status(self) -> dict:
r = await self.http.get(f"{self.config['url']}/status")
return await r.json()
Add to config.yaml:
services:
my_thing:
url: http://my-thing:8080
Restart. The agent will start monitoring it; the AI will gain a my_thing_status tool.
Full guide: docs/adding-services.md.
If you just want to expose a function to the AI without wiring up a whole service:
# ~/.config/homelab-ai/tools/weather.py
from homelab_ai.mcp.decorators import tool
@tool(description="Get the current temperature for a city.")
async def get_weather(city: str) -> dict:
...
Full guide: docs/adding-tools.md.
| Endpoint | Purpose |
|---|---|
GET /api/health |
Liveness probe |
GET /api/overview |
All-services snapshot for dashboards |
GET /api/services |
List of configured services and their health |
POST /api/ai/agent |
One-shot agent call: prompt in, tool calls + answer out |
POST /api/ai/agent/stream |
Same, server-sent-events streaming |
GET /api/agent/status |
Last scan, recent fixes, queued escalations |
POST /api/agent/scan |
Trigger an immediate scan |
GET /api/settings / PUT /api/settings |
Read / update config (mirrored to disk) |
GET /mcp |
MCP server endpoint (for Claude Desktop, Open WebUI, etc.) |
GET /app |
Mobile PWA |
GET /docs |
OpenAPI Swagger UI |
The full OpenAPI spec is at /openapi.json. The MCP tool catalog is at /mcp/tools.
| homelab-ai | Home Assistant + LLM Vision | n8n + Ollama | Open WebUI alone | |
|---|---|---|---|---|
| Native *arr / media plugins | ✅ | ❌ | manual | ❌ |
| Auto-repair / self-healing | ✅ | partial | manual | ❌ |
| MCP server (Claude/Cursor) | ✅ | ❌ | ❌ | partial |
| Mobile PWA out of the box | ✅ | ✅ | ❌ | ✅ |
| Local-first / no cloud calls | ✅ | ✅ | ✅ | ✅ |
| Selfhosted-LLM-friendly | ✅ | ✅ | ✅ | ✅ |
Pick the one whose primitives match what you do most. homelab-ai's primitive is "a service that's monitored and AI-callable", which is the right shape for media/storage/scan workflows. Home Assistant's primitive is "a device with state and triggers", which is the right shape for IoT.
Tracked in GitHub issues. Near-term focuses:
See CONTRIBUTING.md. TL;DR: discuss large changes in an issue first; new services and tools should be plugins, not core patches; no personal IPs or secrets in code.
MIT.
Run in your terminal:
claude mcp add homelab-ai -- npx