wiki-vault/Sources/Homelab/trellis-mcp.md
2026-05-10 15:32:17 +00:00

6.6 KiB

created path project status tags type updated
2026-05-10 Sources/Homelab trellis-mcp active
mcp
claude-code
lovebug
project-plan 2026-05-10

Goal

Build an MCP server (trellis) that captures and resumes long-running threads of work across artificial conversation boundaries. The unit is a thread — a logical work effort that may span multiple chat sessions over days or weeks. Pause/resume verbs let you switch contexts without losing state.

Solves the "where exactly did we leave the Traefik debugging" problem. Memory is too lossy; chat history finds old conversations but doesn't restore working state. Trellis sits between them: synthesized state per thread, on demand.

Locked Decisions

Three-layer model — keep these separate, none should swallow another's job:

  • CLAUDE.md / LOVEBUG.md = instructions (how to behave)
  • Trellis threads = record (where we are in the doing)
  • Memory / vault = lessons and outcomes (what we learned, what we built)

Namingtrellis. Garden-themed, fits the brand world, metaphor maps cleanly (persistent structure things grow on, you keep returning to it).

Deployment — separate MCP server, not folded into herby-dev. Different scope: herby-dev is your knowledge management, trellis is cross-cutting infrastructure usable from any agent context. Same deployment pattern as herby-dev: Cloudflare Tunnel, FastMCP, Authentik in front. Hostname trellis.herbylab.dev.

Storage — Postgres on the existing instance. Database trellis, schema trellis. Schema namespace makes future consolidation into a single database trivial.

Schema shape — four tables, append-only events log:

  • threads — one row per named thread (slug, topic, goal, status, parent_id for forks)
  • tags — separate table with DB-level normalization (lowercase, dash-separated, regex-checked)
  • thread_tags — join table
  • events — append-only log (started, checkpoint, status_change, fork, metadata_change)

Checkpoint payload is freeform markdown. The agent synthesizes whatever shape fits the thread (current state, decisions, open questions, next steps) — structure not enforced at the DB level.

Tool surface (v1) — seven tools, all suffixed with _thread:

  • start_thread — creates row, writes started event
  • pause_thread — writes checkpoint event, requires state payload
  • resume_thread — reads thread + latest checkpoint (no write)
  • get_thread — read-only inspection, optional full event history
  • list_threads — filtered summary view
  • fork_thread — new thread row with parent_id, fork event on parent
  • update_thread — mutates thread fields, writes metadata_change/status_change event

Lifecycle is prompt-driven — system does nothing autonomously. No background jobs, no auto-abandon, no reminders. Threads exist and persist until you explicitly act on them.

Active context lives in the harness, not the DB — pause is a signal to the harness ("save and switch"); the DB just stores the trail. One active thread at a time in any given chat session is the harness's concern; the DB doesn't care how many threads are in flight.

Open Items

  • Postgres deployment shape — LXC vs Docker, fresh instance vs reusing existing
  • Migration tooling — Alembic, plain SQL files, or simpler
  • Tunnel + Authentik setup for trellis.herbylab.dev (parallels herby-dev work)
  • Server framework — presumably FastMCP + Python to match herby-dev, confirm at build time
  • First-run question: when trellis is operational, the trellis build itself becomes the first thread; paste design state from this project plan as the initial checkpoint

Phases

Phase 1 — Infrastructure

  • Postgres instance available with trellis database and trellis schema
  • Cloudflare Tunnel hostname trellis.herbylab.dev
  • Authentik application + provider for trellis
  • Repo trellis-mcp scaffolded

Phase 2 — Schema and migrations

  • Apply v1 schema (threads, tags, thread_tags, events)
  • Verify CHECK constraints (status enum, tag normalization regex)
  • Verify GIN/btree indexes resolve the resume-by-slug hot path

Phase 3 — Tool implementation

  • start_thread, pause_thread, resume_thread (the core loop)
  • get_thread, list_threads (read paths)
  • fork_thread, update_thread (mutation paths)
  • Tag normalization in tool layer with clear errors before DB constraint fires

Phase 4 — Wire-up and first use

  • Connect from desktop Claude, mobile Claude, Claude Code, Lovebug
  • Bootstrap: file the trellis build itself as the first thread
  • Iterate based on real use — payload shape, missing affordances, etc.

Notes

Deferred to v2:

  • Multi-agent / non-Claude support
  • Auto-thread for unnamed conversations
  • Auto-status changes (abandon after N days, surfacing stale threads, etc.)
  • Read patterns beyond resume-by-name: date ranges, full-text search, decision timelines, fork trees
  • Fork merging
  • Soft delete / archival
  • Tag metadata (description, color)
  • Transcript path as a real column (Claude Code transcripts at ~/.claude/projects/)
  • A resumed event kind if telemetry on resumes proves useful

Design principles that drove the shape:

  • Append-only in spirit — historical information isn't adjusted, only added to. Even fields that look mutable (current state, next steps) are superseded by newer events rather than rewritten. The thread row reflects current state; events tell you how it got there.
  • Coarse event grain — about session-level state ("we paused here, this is what we knew"), not per-interaction ("decision added"). The unit is the checkpoint, not the keystroke.
  • Pause = save-and-switch action, not a status — every thread that isn't actively being worked on is implicitly paused. No separate "paused" status needed.
  • Trigger is naming — short topical Q&A doesn't need a thread. Lifting to trellis happens when you explicitly say "start a thread called X."

Schema-namespacing rationale — putting trellis tables in a trellis schema (even inside its own database) costs nothing now and makes any future merge into another database a no-op. Connection sets search_path so queries don't qualify table names.

Why two MCPs instead of one — herby-dev is your knowledge management, scoped to your homelab. Trellis is cross-cutting infrastructure usable from any agent context. Folding them couples unrelated concerns; deployment overhead of a second MCP is low given herby-dev established the pattern.

Why separate tags table — preference for normalization upfront. Array column would work for v1 but you know you'll end up at a join table eventually, so start there.