# Plan: MCP client ergonomics — cheaper turns, cheaper tokens **Status:** drafted 2026-07-04 · **Repo:** demarkus (`demarkus-mcp` client) + demarkus-memory plugin · **Decisions D1–D4 open (recommendations inline)** ## Problem The main consumers of the soul and the knowledge system are MCP agents (Claude Code, Claude Desktop). For them the unit of cost is not the QUIC round-trip — it is the **model turn** per tool call and the **context tokens** per result. Today: - Orientation costs a chain of turns: lookup → fetch → backlinks → fetch… - `mark_fetch` always returns the whole body. Append-heavy status docs grow unbounded (the library roadmap is the live example — one fetch is a five- figure token spend), and agents usually need one section. - Agents re-fetch unchanged docs across a long session; every re-read pays full price. - 14 tools per joined surface × several surfaces = schema budget + verb choice-paralysis. Measured constraint (journal 2026-06-21): the link graph is hub-and-spoke (73% leaves) — computed graph queries (paths, centrality, communities) mostly re-derive the index tree, so the pre-made graph is NOT where client headroom is. Turns and tokens are. ## Workstreams (priority order) ### W1 — `mark_fetch` outline/section mode (token economy) - `url#` returns just that section (client slices the fetched markdown; no protocol change). - Outline mode returns the heading tree + opening paragraph + doc stats (version, modified, size, tags) with a "fetch url#section for detail" hint. - Client caches the full body per session, so section follow-ups are free. ### W2 — `mark_explore` composite (turn economy) One tool call returns a neighborhood card for a URL: outline-mode head of the doc + backlinks (titles) + outbound links (titles) + sibling listing, each entry one line. Composes FETCH/LOOKUP/graph client-side. Caps per section (~10) with honest "+N more". Replaces the common 3–4-turn orientation chain. ### W3 — session etag/version dedup Re-fetch of an unchanged doc returns `unchanged since v (fetched earlier this session)` instead of the body. Changed docs return the body plus a version delta note. Per-process cache; a `force` param overrides. ### W4 — polish (cheap, alongside) - Tool descriptions teach the strategy: lookup-first, filters, explore for orientation, `#section` fetches, when NOT to fetch full bodies. - Plugin session-start injection stays the primary orientation channel (the index hubs ARE the map — Leiden finding); keep hub injection current. ## Deferred / non-goals - **Client graph queries** (path, common-neighbors, centrality): re-test when the knowledge system develops reciprocal cross-reference density (journal 2026-06-21's trigger). Not now. - **Server-side FTS / semantic search**: real, but a server lane — separate plan when picked up. - **MCP resources + prompts** (Desktop attach-without-turns): nice follow-up after W1–W3; needs demarkus-mcp resource support end-to-end. - **Lean tool profile** (consumer vs authoring subsets): only if schema budget shows up as real pain; Claude Code already defers tool loading. ## Decisions to settle - **D1 — outline default.** Options: (a) opt-in param only; (b) outline by default for all; (c) **size-adaptive: full body under ~8KB, outline above, always overridable** (recommended — small docs keep today's behavior, monsters stop flooding context, no consumer breaks silently: the outline says how to get the rest). - **D2 — explore card contents.** Recommended: outline head + backlinks + outbound + siblings, 10/section cap. Open: include lookup-by-shared-tags? (lean no for v1 — tag noise.) - **D3 — dedup scope.** Recommended: per-process session, version+etag keyed, `force` override; no cross-session persistence (staleness risk > saving). - **D4 — where W1 slicing lives.** Recommended: demarkus-mcp only (client slices); the library's `open` librarian tool adopts the same convention separately if wanted. Alternative: a shared client/markdown package in demarkus both consume. ## Acceptance A scripted orientation scenario ("what is project X, what phase, who decided") run before/after: tool turns and result tokens both drop materially (target: orientation ≤2 turns; no full-body fetch of an append-heavy status doc unless forced). Existing skills/hook flows (session-start, /promote, journal appends) unchanged. ## Order W1 → W3 (both inside mark_fetch's path, ship together) → W2 → W4 tool- description pass → then MCP resources/prompts as the follow-up phase. --- ## SETTLED (2026-07-04, Fritz — all four on recommendation) - **D1: size-adaptive** — full body under ~8KB, outline above, always overridable. - **D2: doc + backlinks + outbound + siblings**, 10/section cap (no same-tag section in v1). - **D3: per-session dedup** — version+etag keyed, `force` override, no cross-session persistence. - **D4: shared package** in demarkus (client-side markdown outline/section slicing) — demarkus-mcp consumes now, the library's librarian `open` adopts later. Build order stands: W1 (+W3 together) → W2 → W4. Work lives in the demarkus repo (`demarkus-mcp` + a shared client markdown package). ## Pickup notes (for the fresh session in ~/latebit/demarkus) - **Where the code lives:** `client/cmd/demarkus-mcp/main.go` — a single ~1.3k-line file; all 14 tools registered in one block (`s.AddTool(...)` around line 61) with per-tool `markXTool()` definition + `h.markX` handler pairs. W1/W3 modify the `mark_fetch` definition + handler; W2 adds a new tool pair. Expect to split the file if it gets unwieldy — mind the repo's own conventions (read `~/latebit/demarkus/CLAUDE.md` first). - **Shared slicing package (D4):** siblings live at `client/` — fetch, graph, links, index, merge are separate packages there. New package (e.g. `client/mdoutline`) for heading-tree parse + `#section` slice + size gate; pure functions over markdown bytes, unit-test heavy (heading edge cases: setext headings, code fences containing `#`, duplicate headings → anchor dedup). - **W3 dedup state:** per-process map in the mcp handler struct keyed by (host, path) → (version, etag); consult VERSIONS or the fetch response metadata. `force` boolean param on mark_fetch busts it. - **W1 outline shape:** doc stats line (version/modified/tags/size) + heading tree with per-section line counts + opening paragraph + hint line (`fetch # for a section; force=true for the full body`). 8KB threshold on the BODY size; `#section` and `force` always available regardless of size. - **W2 explore composition:** FETCH (outline head) + BACKLINKS + outbound links (parse the fetched body's mark:// + relative links — `client/links` may already do this) + LIST of the parent dir. 10/section, "+N more". - **Anchor convention:** match what the library's goldmark produces (GitHub-style slugs) so `url#section` means the same thing everywhere. - **Gate:** repo `pre-commit.sh`. Never commit — Fritz commits. - **Acceptance scenario:** in a Claude Code session against the soul: "what is the demarkus-library project and what phase is it in?" should orient in ≤2 tool turns without a full-body fetch of `/demarkus-library/roadmap.md` (the append-heavy monster that motivated W1). ## BUILT (2026-07-04, branch feature/mcp-client-ergonomics — awaiting commit) W1–W4 implemented and verified live against soul.demarkus.io; acceptance met (orientation ≤2 turns, roadmap.md 51.9KB → 5.9KB outline, no forced full-body fetch). New shared package `client/mdoutline` (D4); mark_fetch gained `#anchor` + `force` + outline mode + session dedup; new `mark_explore` tool (explore.go). Details in /journal/2026-07-04.md. Follow-ups deferred: MCP resources/prompts, librarian `open` adoption, broker gateway parity for the new fetch modes. **MERGED 2026-07-04 — PR #225 (`8706768`).** W1–W4 + CodeRabbit round (empty-identity dedup guard, etag-only change note) all on main. This plan is complete except the deferred follow-ups listed above. **SHIPPED TO USERS 2026-07-05** — full release chain: #225 (client/v0.15.0) → #226 (provision pin, tools/v0.4.1) → #227 (bootstrap TOOLS_VERSION repin + plugin bumps: memory 0.12.4, knowledge 0.5.4, pi-memory 0.12.5, pi-knowledge 0.5.5). Plan closed; only the deferred follow-ups remain (MCP resources/prompts, librarian `open` adoption, broker gateway parity). **BROKER PARITY BUILT 2026-07-05** (was a deferred follow-up): the broker MCP gateway now carries W1–W4 too — shared `client/mdoutline/render.go` helpers, session-ID-keyed dedup (multi-tenant safe: no session → no dedup; OnUnregisterSession eviction + caps), `mark_explore` as the 16th tool, and the description pass. Branch `feature/broker-ergonomics-parity`, awaiting commit. Remaining deferred: MCP resources/prompts, library librarian `open` adoption. **BROKER PARITY MERGED 2026-07-05 — #230 (`2d4dab7`).** Both MCP surfaces now at W1–W4 parity; identity/notice logic shared via `client/fetchdedup` (hoisted after review caught mirror drift twice). Reaches the live knowledge system with the next broker release/deploy. Still deferred: MCP resources/prompts, librarian `open` adoption.