mcp: project-plan — Trellis MCP — Tool Surface Followups

This commit is contained in:
Lovebug MCP 2026-05-23 10:58:17 +00:00
parent d65d95bd04
commit 1b5ee52b98

View File

@ -0,0 +1,96 @@
---
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 <pid>`.
- **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)