--- created: '2026-05-23' path: Sources/Homelab project: trellis-mcp-tool-surface-followups tags: - mcp - claude-code - lovebug type: project-plan --- ## Goal Make the Trellis MCP tool surface more ergonomic for in-session use without changing the underlying data model. Two changes: 1. Rename `pause_thread` to `checkpoint_thread` so the verb matches the event it writes (and stops colliding with the `paused` status value). 2. Add optional `id` parameter to all tools that take `slug`, so threads can be addressed by their integer ID during a session. Slug remains canonical; ID is a convenience handle. Both changes are small, scoped server-side edits. No data migrations, no breaking changes if done with aliases. ## Locked Decisions - **Slug stays canonical.** Names are durable, meaningful, survive migrations. IDs are database-implementation artifacts. The model is: slug for discovery and persistence, ID for session-scoped operation. Same pattern as `ps` vs `kill `. - **ID stability is explicitly not a goal.** IDs may not survive a restore or migration. That's fine — you're not going to remember `232` next week either. You'll discover the thread again via `list_threads`, see whatever ID it has now, and use that. - **Rename via alias, not hard cutover.** Add `checkpoint_thread` as the new canonical name, leave `pause_thread` as a deprecated alias that calls through. Drop the alias after a grace period (TBD — could be one trellis release, could be a calendar deadline). - **No `id`-only tools.** Every tool that gains `id` keeps `slug`. If both are passed, prefer `id` and warn on mismatch. If neither, error. This preserves slug-first workflows and adds id as additive. ## Open Items - [ ] Decide grace period for `pause_thread` alias before removal - [ ] Confirm whether `parent_id` field in `list_threads` already returns the integer (it filters by it, but does it return it?) — affects whether `list_threads` needs response-shape changes - [ ] Decide whether `start_thread` and `fork_thread` should return the new thread's ID in their response payload (probably yes — otherwise you have to immediately `get_thread` to learn the ID) - [ ] Audit the database schema to confirm `threads.id` is the right field to expose (vs some other internal identifier) ## Phases ### Phase 1 — Rename pause_thread → checkpoint_thread - [ ] Add `checkpoint_thread` tool that takes the same args as `pause_thread` (`slug`, `state`) and writes the same `checkpoint` event - [ ] Leave `pause_thread` registered as a deprecated alias — same handler, optionally logs a deprecation warning - [ ] Update tool descriptions to reflect the rename - [ ] Update any internal documentation / README references ### Phase 2 — Add id parameter to read/write tools Tools to modify: - `get_thread(slug, include_events)` → `get_thread(slug?, id?, include_events)` - `checkpoint_thread(slug, state)` → `checkpoint_thread(slug?, id?, state)` - `resume_thread(slug)` → `resume_thread(slug?, id?)` - `update_thread(slug, ...)` → `update_thread(slug?, id?, ...)` - `fork_thread(parent_slug, new_slug, ...)` → `fork_thread(parent_slug?, parent_id?, new_slug, ...)` For each: - [ ] Make `slug` optional in the schema - [ ] Add optional `id` (integer) - [ ] Server-side: branch on which was provided; error if neither; warn if both disagree - [ ] Update tool descriptions ### Phase 3 — Expose id in responses - [ ] `start_thread` response includes `id` - [ ] `fork_thread` response includes `id` of new child - [ ] `get_thread` response includes `id` - [ ] `list_threads` response includes `id` per row (confirm whether already true) ### Phase 4 — Verification - [ ] In-session test: address a thread by integer ID end-to-end (start → checkpoint → resume) without typing the slug - [ ] In-session test: cross-chat handoff using ID ("can you pull thread 232") - [ ] Confirm `pause_thread` alias still works for any prior tooling that calls it ## Notes **Naming context — why pause is wrong for the action:** The existing `pause_thread` tool writes a `checkpoint` event, not a status change. Per its own docstring: "the thread row is not changed except for `updated_at`." So the verb doesn't describe what's happening — checkpointing is happening; pausing is not. Worse, there's a `paused` value in the status enum (`active | paused | done | abandoned`). So you have: - A `paused` status that means "this thread is on hold" - A `pause_thread` action that does *not* set that status `checkpoint_thread` resolves the collision. Pausing-the-status remains a separate decision via `update_thread(slug, status='paused')`. **ID lookup context — why this matters ergonomically:** In session usage, slugs are friction: long, must be re-typed or re-quoted, easy to mistype. Integers are zero-friction: `pull thread 232`, `checkpoint 232`, `resume 232`. The slug stays useful for first-discovery ("which thread was the traefik debugging one?") but once you're operating on it, the integer wins. Pattern parallel: Linux processes. `ps` shows names for discovery; `kill 4729` for operation. Names for finding, numbers for doing. **What's explicitly out of scope:** - Changing slug semantics (still required at creation, still kebab-case, still the URL-safe identifier) - Migrating existing data - Changing the event log structure - Adding any new event types - The three other trellis follow-ups already tracked (retry/cleanup on embedding failure, semantic dedup before insert, revisit enricher pause-during-reflector)