soul.demarkus.io:6309/journal/2026-07-04.md/v6 draft reader meta

2026-07-04 — MCP client ergonomics planned (from the library session)

Discussion started as "optimize agent traversal of the knowledge graph," reframed by Fritz to the MCP-client perspective (Claude Code/Desktop as the main consumers). Key grounding: the 06-21 Leiden measurement — hub-and-spoke graph, computed graph queries mostly re-derive the index tree — so client headroom is turns and tokens, not graph algorithms.

Plan recorded + settled: /plans/mcp-client-ergonomics.md — W1 size-adaptive outline/#section fetch (8KB gate) · W3 per-session version+etag dedup · W2 mark_explore neighborhood card (doc + backlinks + outbound + siblings, 10/cap) · W4 tool-description strategy pass. D1–D4 all settled on recommendation (size-adaptive; full card; per-session; shared client/ slicing package). Deferred: client graph queries (Leiden re-test trigger), server FTS (separate lane), MCP resources/prompts (follow-up), lean tool profiles.

Pickup notes with repo pointers are in the plan (code: client/cmd/demarkus-mcp/main.go; new package beside client/{fetch,graph, links}). Next session in ~/latebit/demarkus starts at W1+W3.

(Same day, library side: v0.16.0 UX round + v0.17.0 pane-scroll-default merged and deployed — see /demarkus-library/journal/2026-07-03-ux.md and ADR 0007.)

MCP client ergonomics — W1–W4 built (feature/mcp-client-ergonomics)

Implemented the whole plan (/plans/mcp-client-ergonomics.md) in one pass on branch feature/mcp-client-ergonomics; awaiting Fritz's commit.

  • client/mdoutline (new package, D4): pure functions over markdown — Headings (goldmark parse, GitHub-style slug anchors with taken-anchor dedup: Notes/Notes/Notes-1 → notes, notes-1, notes-1-1), Section (subtree slice, lenient: accepts raw heading text re-slugged, case-insensitive), Outline, OpeningParagraph, Anchors. Edge cases tested: setext headings, # inside code fences and indented blocks, inline code in headings, duplicate anchors.
  • W1 (size-adaptive fetch): mark_fetch returns full body <8KB; above it mode: outline (heading tree with anchors + per-section line counts, opening paragraph, fetch url#<anchor> hint). url#anchor slices at any size; force=true always full body. Section-not-found errors list available anchors.
  • W3 (session dedup): per-process map (host+path → version,etag), recorded ONLY when a full body was actually returned (outline/section fetches never arm it — otherwise the outline→section flow would break). Unchanged re-fetch → status: unchanged … since vN; changed → body + note: changed since this session's earlier fetch (vX -> vY); force busts.
  • W2 (mark_explore): neighborhood card in one call — outline head + opening paragraph + outbound links (title'd, deduped) + backlinks (graph store, degrades to a note) + parent-dir siblings (LIST, degrades on failure), all capped at 10 with honest "+N more". New files explore.go / explore_test.go.
  • W4: descriptions for fetch/lookup/list/backlinks/explore now teach the strategy (lookup-first, explore to orient, #section for detail, full bodies rarely needed).

Verified live against soul.demarkus.io by driving the built binary over stdio: /demarkus-library/roadmap.md (51,939 bytes) → 5,892-char outline; #next-phases-queued slice 874 chars; guidelines.md fetched twice → second gets status: unchanged (201 chars), force re-reads; explore card on /demarkus-library/index.md orients in one turn. Acceptance met: project + phase answerable in ≤2 turns, no full roadmap fetch.

Gotcha worth remembering: mcp-go serves stdio tool calls concurrently — two same-doc fetches raced and completed out of order, which is exactly why the dedup map is mutex-guarded.

Not done (deferred per plan): broker gateway parity for outline/dedup (its mirrored formatResult contract is untouched — new behavior is client-side only), MCP resources/prompts follow-up, library librarian open adoption of mdoutline.

Merged: PR #225 (8706768) — the whole MCP client ergonomics phase. Review round fixed two real CodeRabbit findings: dedup could trivially match when a server omits both version and etag (now requires an identity field, and identity-less fetches are never recorded), and etag-only changes now carry a "content changed (still vN, etag differs)" note. Skipped the re-parse/O(n²) nitpick as deliberate (documented on mdoutline.Headings).

trail
  1. soul.demarkus.io:6309 v6