wiki-vault/Sources/Homelab/2026-05-06-homelab-mcp-server-stand-up.md
Travis Herbranson 34a268d8dc migration: copy 62 notes from pbs-projects and homelab-projects
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>
2026-05-08 17:21:13 -04:00

5.3 KiB

created path project status tags type updated
2026-05-06 Sources/Homelab homelab-mcp-server active
mcp
authentik
cloudflare
obsidian
n8n
session-notes 2026-05-06

Homelab MCP Server Stand-Up

First successful end-to-end deployment of the homelab MCP server. This very note is the inaugural write through the new pipeline — vault saved via save_vault_note instead of email-to-n8n.

Outcome

Working remote MCP connector reachable from Claude (web and mobile), authenticated via Authentik OAuth, fronted by Cloudflare Tunnel. Four Phase 1 tools live: get_vault_schema, list_vault_projects, get_vault_project, save_vault_note.

What got built

Lovebug had the stack scaffolded before the session started — Python MCP server (FastMCP, Streamable HTTP), Authentik docker stack, cloudflared placeholder in compose, schema/git-writer modules. This session was the configuration and wiring pass.

  • Authentik admin account + OAuth2 provider + application created via the wizard
  • Cloudflare tunnel homelab-mcp with two public hostnames: mcp.herbylab.devhomelab-mcp:8080, auth.herbylab.devauthentik-server:9000
  • All containers on the auto-created default Docker network; cloudflared reaches services by container name, not localhost
  • .env populated with public Authentik URLs for issuer/JWKS
  • Connector registered on claude.ai with OAuth client ID/secret from Authentik

Issues hit and resolved

  • Wrong tab in Cloudflare ZT dashboard. Started in "Private Network" (which is the WARP-only feature) instead of "Public Hostname" / "Published applications." Cleared up by going into the tunnel detail page directly and using the right tab.
  • 502 from initial curl. Cloudflare tunnel URLs were set to localhost:8080 and localhost:9000, but cloudflared runs as its own container with no host networking. Fix: change tunnel URLs to use container names (homelab-mcp:8080, authentik-server:9000) since all services share the default compose network.
  • Authentik external URL "problem" was a non-problem. Authentik 2025.4 derives its issuer URL from the request Host header automatically. No brand setting change needed; the discovery URL through auth.herbylab.dev returned the correct public issuer immediately.
  • Missing OAuth Protected Resource Metadata. Claude expects /.well-known/oauth-protected-resource per RFC 9728. Initial server only served /.well-known/oauth-authorization-server. Lovebug added the protected-resource document.
  • Invalid Host header rejection. FastMCP's transport security check defaulted to localhost-only and was rejecting mcp.herbylab.dev with 421. Lovebug added mcp.herbylab.dev to the allowed hosts list.
  • list_vault_projects errors on directories ending in .md. First test surfaced a bug — tool assumes every *.md match is a file and os.open()s it. Real vault has at least one folder named pbs-membership-loe1-phase3.md. Needs os.path.isfile filter. Open for Lovebug.

Architecture decisions worth recording

  • Streamable HTTP transport, not SSE. SSE deprecation is in motion; built on the durable transport.
  • Single MCP server, organized by domain modules internally. Multiple servers were considered for trust scoping (vault read/write vs server-side execution). Decided trust separation lives between MCP and Dispatch instead — MCP for knowledge/state, Dispatch for execution. Code structured by domain so future split is mechanical, not a rewrite.
  • n8n stops being the front door but stays the integration hub. Server writes vault directly + commits + pushes; n8n becomes downstream consumer for fan-out (tasks, Drive mirroring) when those tools come online.
  • Read-only-from-Travis enforcement by absence. No edit/delete tools exist. Eliminates the concurrent-write problem class entirely. Vault repo on dev server, tower opens a local clone that pulls.
  • TLS terminates at Cloudflare's edge. Internal cloudflared → service traffic stays HTTP since both are on the same host network. Standard tunnel pattern; no internal TLS plumbing needed.

Schema additions Lovebug shipped beyond the spec

  • domains taxonomy (Dev / Content / Homelab / Reference) layered above folder paths
  • knowledge_sources populated for all three (vault, openbrain, llm_wiki) — placeholder design held up
  • frontmatter_templates with required-field declarations and concrete examples per note type

What's next

  • Save the project plan as a separate project-plan note (use the draft from the design session)
  • Fix list_vault_projects directory bug
  • Retire the email-to-n8n trigger once a few real notes have flowed through
  • Phase 2 catalog: search tools, generic task tools (n8n routes), cross-system glue, memory/OpenBrain integration when workflow patterns establish

Key learnings

  • Most "missing UI option" Authentik problems are version drift; check the discovery URL through the public domain first before hunting settings.
  • For tunnels: cloudflared container's localhost is the container, not the host. Use container names when all services share a compose network.
  • OAuth metadata for MCP requires both RFC 8414 (Authorization Server) and RFC 9728 (Protected Resource). Many implementations only ship the first.
  • "Failed" tool calls are still a successful end-to-end test — every layer below the failure had to work to surface the bug.