From 0109025303742ed62ac83aa9a925952f8cd10b2b Mon Sep 17 00:00:00 2001 From: realtonyyoung <6655045+realtonyyoung@users.noreply.github.com> Date: Tue, 11 Aug 2026 22:34:10 -0400 Subject: [PATCH 1/2] Steer agents to declare work-item breakdown and relations via kcap mcp workitems Nothing told agents to declare a work item's structure, so the breakdown (parent -> parts) and dependency (blocks/blocked-by) topology stayed empty and the Home "Blockers & dependencies" view + progress figures had nothing to render. - Add a server-level MCP `instructions` preamble to the workitems MCP server (surfaced on initialize, kcap-memory style) steering agents to create part items and declare the parent->parts breakdown + blocks/blocked-by relations, and stating that these are declared, never inferred. - Add a `work-items` plugin skill teaching the create-parts -> declare-breakdown -> declare-relations flow, the same-repo / one-parent / no-self rules, and when NOT to declare. - Fix the stale "two tools" claim in help-mcp.txt and README (the workitems MCP server has exposed seven tools since the breakdown/relations surface landed). Co-Authored-By: Claude Fable 5 --- README.md | 11 ++- kcap/skills/work-items/SKILL.md | 80 +++++++++++++++++++ src/Capacitor.Cli.Core/Resources/help-mcp.txt | 18 ++++- .../Commands/McpWorkItemsServer.cs | 16 +++- .../McpWorkItemsServerTests.cs | 13 +++ 5 files changed, 131 insertions(+), 7 deletions(-) create mode 100644 kcap/skills/work-items/SKILL.md diff --git a/README.md b/README.md index f744a5fa..7266609b 100644 --- a/README.md +++ b/README.md @@ -561,14 +561,19 @@ At SessionStart (Claude Code), `kcap` also injects a compact **index** of the me kcap mcp workitems ``` -Stdio MCP server that lets coding agents correlate the current session to the SDLC work item (issue/PR) it belongs to, or list what it's already correlated to. **Claude Code:** auto-registered via the plugin's `.mcp.json`; it isn't offered for Cursor or Codex. +Stdio MCP server that lets coding agents correlate the current session to the SDLC work item (issue/PR) it belongs to, **declare that work item's structure** — its breakdown into parts and its blocks/blocked-by dependencies — and read that structure back. **Claude Code:** auto-registered via the plugin's `.mcp.json`; it isn't offered for Cursor or Codex. -It provides two tools: +It provides seven tools: - **`declare_work_item`** — attach the current session (and its continuation chain) to a work item. Pass exactly one of `issue_key` (e.g. `"AI-1234"`), `pr_number`, `work_item_id`, or `new_title` (creates a brand-new work item). - **`get_session_work_items`** — list the work items the current session is attached to. +- **`declare_work_breakdown`** — declare that a work item is broken into parts (`parent_id` + `part_ids`). Idempotent; a part has at most one parent, and all items must live in the same repository. +- **`retract_work_breakdown`** — detach the named parts from the parent. +- **`declare_work_relation`** — declare a dependency between two items (`from_id`, `to_id`, `relation_kind` `"blocks"` or `"blocked_by"`). Same repository, no self-relation. +- **`retract_work_relation`** — retract a previously declared dependency. +- **`get_work_item_topology`** — read a work item's parent, parts, and dependencies (scoped to what the caller can see). -Both tools default `session_id` to the current kcap-hooked session (`KCAP_SESSION_ID`) when omitted. This is the manual-declare path alongside the server's own mechanical and LLM-assisted correlation — use it when an agent already knows which issue or PR a session belongs to. +`declare_work_item` / `get_session_work_items` default `session_id` to the current kcap-hooked session (`KCAP_SESSION_ID`) when omitted. This is the manual path alongside the server's own mechanical and LLM-assisted correlation — use it when an agent already knows which issue or PR a session belongs to, and to record a breakdown/dependency structure the server can't infer (Home's blockers & dependencies and progress figures render only from declared parts and relations). ### Analytics MCP server (for agents) diff --git a/kcap/skills/work-items/SKILL.md b/kcap/skills/work-items/SKILL.md new file mode 100644 index 00000000..870b899d --- /dev/null +++ b/kcap/skills/work-items/SKILL.md @@ -0,0 +1,80 @@ +--- +name: work-items +description: >- + This skill should be used when you are planning or discovering the SHAPE of a + work item — that it breaks into sub-tasks (a parent and its parts), or that + one piece must land before another (a blocks / blocked-by dependency) — and + you want that structure recorded so it shows up in Kurrent Capacitor's Home + "Blockers & dependencies" view and progress figures. Use the `kcap mcp + workitems` MCP tools to DECLARE the breakdown and relations. Do NOT use this + skill for ordinary "attach this session to issue X" correlation alone (a + single `declare_work_item` call, no structure) or when the work is a single + indivisible task with no parts and no dependencies. +--- + +# Work items — declaring breakdown and dependencies + +A work item's **breakdown** (a parent broken into parts) and its **dependencies** +(one item blocks / is blocked by another) are things you **declare** through the +`kcap mcp workitems` tools. The server never infers them — if you don't declare +the structure, the work item has an empty topology and Home renders no blockers, +no dependency graph, and no `n/m parts` progress figure for it. + +Only declare structure that is **real and you are confident of**. Fabricated or +speculative parts/relations are worse than none. A single indivisible task needs +no breakdown. + +## When to use these tools + +- You've planned a work item as several sub-tasks → create the part items and + declare the parent→parts breakdown. +- You know one item must be finished before another can start → declare the + dependency. +- The structure changed (a part was dropped, a dependency no longer holds) → + retract it. +- You want to see the current structure → read the topology. + +## The flow + +1. **Attach the session to its work item** (if it isn't already). `declare_work_item` + with exactly one of `issue_key`, `pr_number`, `work_item_id`, or `new_title`. + Check `get_session_work_items` first if unsure what the session is attached to. +2. **Create the part items.** Each part is itself a work item — create one per + sub-task with `declare_work_item` (`new_title`), keeping the id each returns. +3. **Declare the breakdown.** `declare_work_breakdown` with `parent_id` and the + `part_ids`. It is idempotent — re-declaring an existing part is accepted, not + an error. +4. **Declare dependencies** where they exist. `declare_work_relation` with + `from_id`, `to_id`, and `relation_kind` `"blocks"` (from_id blocks to_id) or + `"blocked_by"` (from_id is blocked by to_id). +5. **Verify** with `get_work_item_topology` (pass the parent's `work_item_id`) — + it returns the parent, parts, and dependencies you can see. + +## Rules the server enforces + +- **Same repository.** A parent and its parts, and both ends of a relation, must + live in the same repository. Cross-repo structure is rejected. +- **One parent per part.** A part can belong to at most one parent breakdown. +- **No self-relation.** An item cannot block or be blocked by itself. +- **Idempotent declares.** Re-declaring an existing part or relation is fine. +- **Server-assigned attribution.** These tools take no `source`/`declared_by` — + the server resolves the caller. Don't look for such arguments. +- **Retract, don't delete.** Use `retract_work_breakdown` / `retract_work_relation` + when the structure changes. + +## Tool reference + +| Tool | Required args | Purpose | +|---|---|---| +| `declare_work_item` | exactly one of `issue_key` \| `pr_number` \| `work_item_id` \| `new_title` | Attach the session to a work item (or create one). `session_id` defaults to `KCAP_SESSION_ID`. | +| `get_session_work_items` | — | List what the current session is attached to. | +| `declare_work_breakdown` | `parent_id`, `part_ids` | Declare parent → parts. | +| `retract_work_breakdown` | `parent_id`, `part_ids` | Detach parts from the parent. | +| `declare_work_relation` | `from_id`, `to_id`, `relation_kind` (`blocks`\|`blocked_by`) | Declare a dependency. | +| `retract_work_relation` | `from_id`, `to_id`, `relation_kind` | Retract a dependency. | +| `get_work_item_topology` | `work_item_id` | Read parent, parts, and dependencies (visibility-scoped). | + +## Requirements + +Requires `kcap login`. The `kcap-workitems` MCP server is auto-registered for +Claude Code by `kcap setup`; it is not offered for Cursor or Codex. diff --git a/src/Capacitor.Cli.Core/Resources/help-mcp.txt b/src/Capacitor.Cli.Core/Resources/help-mcp.txt index 24767014..76250857 100644 --- a/src/Capacitor.Cli.Core/Resources/help-mcp.txt +++ b/src/Capacitor.Cli.Core/Resources/help-mcp.txt @@ -102,9 +102,11 @@ mcp memory: # needs an absolute path (e.g. /opt/homebrew/bin/kcap) mcp workitems: - Exposes two tools — declare_work_item, get_session_work_items — for agents - to attach the current session (and its continuation chain) to a work item, - or list what it's already attached to. Requires `kcap login`. + Exposes seven tools for agents to attach the current session (and its + continuation chain) to an SDLC work item and to DECLARE that work item's + structure — its breakdown (parent -> parts) and dependencies (blocks / + blocked-by). Breakdown and relations are declared, never inferred, so an item + whose structure nobody declares has an empty topology. Requires `kcap login`. Tools: declare_work_item Attach the current session to a work item by @@ -112,6 +114,16 @@ mcp workitems: title (exactly one of the four). get_session_work_items List the work items the current session is attached to. + declare_work_breakdown Declare that a work item is broken into parts + (parent_id + part_ids); idempotent, one parent + per part, all items in the same repository. + retract_work_breakdown Detach the named parts from the parent. + declare_work_relation Declare a dependency between two items (from_id, + to_id, relation_kind 'blocks' or 'blocked_by'); + same repository, no self-relation. + retract_work_relation Retract a previously declared dependency. + get_work_item_topology Read a work item's parent, parts, and + dependencies (scoped to what the caller sees). Claude Code auto-registers `kcap-workitems` via the plugin's `.mcp.json` (installed by `kcap setup`); it is not offered for Cursor or Codex. diff --git a/src/Capacitor.Cli/Commands/McpWorkItemsServer.cs b/src/Capacitor.Cli/Commands/McpWorkItemsServer.cs index 7761f119..77c10595 100644 --- a/src/Capacitor.Cli/Commands/McpWorkItemsServer.cs +++ b/src/Capacitor.Cli/Commands/McpWorkItemsServer.cs @@ -127,10 +127,24 @@ async Task TimedDispatchToolCallAsync(JsonNode callId, JsonObject callRe return 0; } + // Server-level usage preamble (MCP `instructions`) — steers agents to DECLARE a work item's + // structure, not just attach to it. A work item's breakdown (parent→parts) and dependencies + // (blocks/blocked-by) are declared facts, never inferred, so an item whose structure nobody + // declares has an empty topology and renders no blockers/dependencies or progress figure. + internal const string ServerInstructions = + "Use these tools to attach the current session to its SDLC work item AND to declare that work " + + "item's structure. When you plan or discover that a work item breaks into parts, create the part " + + "items (declare_work_item with new_title) and declare the parent→parts breakdown " + + "(declare_work_breakdown); when one item must land before another, declare the dependency " + + "(declare_work_relation — 'blocks'/'blocked_by'). Breakdown and relations are DECLARED, never " + + "inferred: if you don't declare them the work item's topology stays empty. Declare only real " + + "structure you're confident of, keep every item in the same repository, and use the retract_* " + + "tools when it changes."; + static string BuildInitializeResponse(JsonNode id, JsonObject request) => ToResponse( id, - new(McpProtocol.NegotiateVersion(request), new(new()), new("kcap-workitems", "1.0.0")), + new(McpProtocol.NegotiateVersion(request), new(new()), new("kcap-workitems", "1.0.0"), ServerInstructions), McpJsonContext.Default.McpInitResult ); diff --git a/test/Capacitor.Cli.Tests.Unit/McpWorkItemsServerTests.cs b/test/Capacitor.Cli.Tests.Unit/McpWorkItemsServerTests.cs index f5f428f9..ef15feaa 100644 --- a/test/Capacitor.Cli.Tests.Unit/McpWorkItemsServerTests.cs +++ b/test/Capacitor.Cli.Tests.Unit/McpWorkItemsServerTests.cs @@ -164,6 +164,19 @@ await Assert.That(tools.Select(t => t.Name).ToArray()).IsEquivalentTo(new[] { }); } + [Test] + public async Task Server_instructions_steer_agents_to_declare_breakdown_and_relations() { + // The MCP `instructions` preamble (surfaced on initialize) is the whole point of the steering + // work: an agent that connects the tool must be told to DECLARE structure, not just attach. Pin + // that it names both declare tools and states the declared-never-inferred rule so the guidance + // can't silently regress to a bare correlation description. + var instructions = McpWorkItemsServer.ServerInstructions; + + await Assert.That(instructions).Contains("declare_work_breakdown"); + await Assert.That(instructions).Contains("declare_work_relation"); + await Assert.That(instructions).Contains("never"); // "declared, never inferred" + } + // ── declared breakdown + relations ─────────────────────────────────────── [Test] From 92f001984f9c48382b0e9df85b791f07c1fbd179 Mon Sep 17 00:00:00 2001 From: realtonyyoung <6655045+realtonyyoung@users.noreply.github.com> Date: Tue, 11 Aug 2026 22:47:33 -0400 Subject: [PATCH 2/2] Trim the ServerInstructions + test comments to the repo's concise style qodo (Maintainability): the added multi-line comments restated what the ServerInstructions constant and the descriptive test name already convey. Shortened both to a brief "why" note, matching the other MCP servers' preamble comments. Co-Authored-By: Claude Fable 5 --- src/Capacitor.Cli/Commands/McpWorkItemsServer.cs | 4 +--- test/Capacitor.Cli.Tests.Unit/McpWorkItemsServerTests.cs | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Capacitor.Cli/Commands/McpWorkItemsServer.cs b/src/Capacitor.Cli/Commands/McpWorkItemsServer.cs index 77c10595..d597266a 100644 --- a/src/Capacitor.Cli/Commands/McpWorkItemsServer.cs +++ b/src/Capacitor.Cli/Commands/McpWorkItemsServer.cs @@ -128,9 +128,7 @@ async Task TimedDispatchToolCallAsync(JsonNode callId, JsonObject callRe } // Server-level usage preamble (MCP `instructions`) — steers agents to DECLARE a work item's - // structure, not just attach to it. A work item's breakdown (parent→parts) and dependencies - // (blocks/blocked-by) are declared facts, never inferred, so an item whose structure nobody - // declares has an empty topology and renders no blockers/dependencies or progress figure. + // structure (breakdown + relations, which the server never infers), not just attach to it. internal const string ServerInstructions = "Use these tools to attach the current session to its SDLC work item AND to declare that work " + "item's structure. When you plan or discover that a work item breaks into parts, create the part " + diff --git a/test/Capacitor.Cli.Tests.Unit/McpWorkItemsServerTests.cs b/test/Capacitor.Cli.Tests.Unit/McpWorkItemsServerTests.cs index ef15feaa..d6e38f8d 100644 --- a/test/Capacitor.Cli.Tests.Unit/McpWorkItemsServerTests.cs +++ b/test/Capacitor.Cli.Tests.Unit/McpWorkItemsServerTests.cs @@ -166,9 +166,7 @@ await Assert.That(tools.Select(t => t.Name).ToArray()).IsEquivalentTo(new[] { [Test] public async Task Server_instructions_steer_agents_to_declare_breakdown_and_relations() { - // The MCP `instructions` preamble (surfaced on initialize) is the whole point of the steering - // work: an agent that connects the tool must be told to DECLARE structure, not just attach. Pin - // that it names both declare tools and states the declared-never-inferred rule so the guidance + // Pin that the preamble names both declare tools + the declared-never-inferred rule, so it // can't silently regress to a bare correlation description. var instructions = McpWorkItemsServer.ServerInstructions;