Two-layer structure: Sources (raw notes) + Wiki (compile output) Four domains: Dev (40), Venture (3), Homelab (23), Reference (0) Includes CLAUDE.md spec, index pages at all levels, compile log Co-Authored-By: Lovebug <lovebug@herbylab.dev>
6.5 KiB
| created | path | project | status | tags | type | updated | ||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2026-05-05 | Sources/Homelab | homelab-mcp-server | active |
|
project-plan | 2026-05-05 |
Homelab MCP Server
Goal
Replace the email → n8n → Obsidian pipeline for project and session notes with a custom MCP server that LLMs can call directly. The MCP server becomes the canonical surface for "things I want LLMs to know and record" across the homelab.
Why
- The email-as-transport pattern works but feels old-fashioned and forces schema/template duplication across every LLM project.
- Schema changes (e.g., flattening folder structure) currently require editing every project's instructions. With an MCP server, the contract lives in one place — change the server, every LLM picks it up.
- Establishes the auth and deployment pattern for future homelab tooling exposed to LLMs.
- Mobile-friendly: once added on claude.ai, the connector works from the phone app.
Architecture
Claude (web/mobile/desktop) ↓ HTTPS Cloudflare Tunnel → mcp.herbylab.dev ↓ Authentik (OAuth) ↓ validated token MCP server (Python, us-test-authy) ↓ ┌────┴────┐ ↓ ↓ Vault git n8n webhook (commit + (downstream fan-out: push) tasks, Drive mirror, etc.)
Trust separation: MCP = read + scoped write. Dispatch and future harnesses handle execution. The two surfaces never merge.
Locked Decisions
- Single MCP server, organized by domain modules internally
- Streamable HTTP transport (not SSE — being deprecated)
- Authentik for OAuth
- Cloudflare tunnel →
mcp.herbylab.dev→us-test-authy - Python implementation
- Vault writes go direct (filesystem + git commit/push); n8n becomes downstream consumer for fan-out
- Read-only on Travis's side, enforced by not building edit/delete tools
- Execution stays out of MCP entirely — Dispatch's job
- Vault repo lives on
us-test-authy; tower opens a local clone that pulls - Gitea remote may move; not a blocker (one-line
git remote set-url)
Code Structure
mcp_server/ tools/ vault.py # save_note, list_projects, get_project, get_schema memory.py # placeholder for OpenBrain / LLM wiki tasks.py # generic task tools, n8n forwards core/ auth.py # Authentik validation git_writer.py # commit + push logic schema.py # canonical schema dict server.py # registers tools from each module
Phase 1 Tool Surface
save_note(title, content, project, type, tags, status?)— write markdown, assemble frontmatter, commit, push. Returns file path + git SHA.list_projects()— enumerate existing project slugs with metadata. Prevents slug collisions.get_project(slug)— fetch a project's notes for context loading.get_schema()— return canonical valid values fortype,status,tags, folder paths. Solves the folder-flattening problem. Also includesknowledge_sourcesfield describing what each connected system is for (vault, openbrain, llm_wiki).
Phase 2 Catalog (mapped, not built)
Vault depth:
search_notes(query, filters)— full-text + tag/type/date filterslist_recent_notes(days, filters)get_note(path)list_tags()summarize_project(slug)
Tasks (generic, n8n routes):
create_task(project, title, description?, frontmatter?, due?)— n8n decides destination based on frontmatterlist_tasks(project?, status?, filters?)modify_task(task_id, updates)— generic update covers complete/reopen/edit/reassign- Task IDs opaque to MCP server; n8n owns the ID space
Cross-system glue:
mirror_to_drive(note_path)— Obsidian → Google Docs via n8nrecord_session_artifact(session_id, type, location)— track session-generated files without putting them in the vault
Memory & knowledge (placeholder, design pending):
OpenBrain and LLM wiki integration deferred until workflow patterns establish. Open questions:
- Unit of retrieval (note? fact? synthesized answer?)
- Authoritative source (vault → memory, memory → vault, or peers?)
- Read-only or read-write from LLM's side?
- API-shaped or markdown-on-disk?
Likely tool shapes (names will change):
query_knowledge(question, sources?)— federated semantic queryget_context(topic, depth?)— background assembly before starting workrecord_to_memory(content, system, metadata)
Architectural commitment: tools/memory.py exists from day one as empty
module. Boundary stays.
Implementation Order
- Hello-world MCP server in Python (Streamable HTTP, single dummy tool)
- Cloudflare tunnel + DNS for
mcp.herbylab.dev - Verify end-to-end: connector added on claude.ai, tool callable from chat
- Authentik OAuth integration
- Phase 1 tools:
save_note,list_projects,get_project,get_schema - Git commit/push integration with deploy key
- Migrate from email flow; retire email trigger in n8n
Sequencing principle: working transport before auth before real tools. Don't debug "is it the auth, the network, or my code?" all at once.
Key Learnings From Design Session
- MCP is one of multiple LLM-facing surfaces. MCP for knowledge/state, Dispatch for execution. Don't conflate trust classes.
- n8n stops being the front door but stays the integration hub. Server calls n8n for fan-out (tasks, Drive mirroring); n8n keeps Google/Trello tight integration without owning the entry point.
- Schema-as-API is the unlock.
get_schema()makes the MCP server the canonical contract. Schema changes propagate without touching project instructions. - Read-only-from-Travis eliminates the concurrent-write problem class.
MCP server is sole writer; Travis is consumer. No merge conflicts, no
.obsidian/workspace.jsonthrash. - Single server is correct until proven otherwise. Code structured by domain so future split is mechanical, not a rewrite.
- Streamable HTTP, not SSE. SSE deprecation is in motion — build on the durable transport from day one.
- Anthropic connects from their cloud, not from the user's device. Tailscale alone won't work — server needs public HTTPS reachability. Cloudflare tunnel handles this.
Open Items / Future Travis
- Sharing model (collaborator access to specific tools) — defer until needed
- Decision on Gitea hosting location
- OpenBrain and LLM wiki workflow patterns before Phase 2 memory tools
- Whether
save_noteauto-fans-out tasks from## Taskssections, or stays explicit (current lean: explicit)
...sent from Jenny & Travis