@roadmapperai/mcp 0.9.3 → 0.9.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/AGENTS.md +112 -16
  2. package/README.md +42 -16
  3. package/package.json +1 -1
  4. package/server.mjs +1647 -125
package/AGENTS.md CHANGED
@@ -7,6 +7,23 @@ The roadmap data model is the source of truth — TypeScript types in
7
7
  [`src/types.ts`](https://github.com/vsgro/roadmapper/blob/main/src/types.ts) are the canonical schema. Everything
8
8
  in this doc is downstream of that file.
9
9
 
10
+ ## How to call operations (dispatch surface)
11
+
12
+ The `roadmapper` MCP server advertises **one** action tool: `roadmap({ op, args })`.
13
+ Every operation named in this document — `get_roadmap_snapshot`, `suggest_capability_for`,
14
+ `propose_task`, `propose_tasks`, `propose_capability`, `get_agents_md`, `link_repo`, etc. — is
15
+ an `op` **value**, not a separately-advertised tool. Invoke it as
16
+ `roadmap({ op: "<name>", args: { ... } })`. Two helpers round out the surface:
17
+
18
+ - `roadmap_search({ intent })` — list/rank the available operations by what you're trying to do.
19
+ - `roadmap_describe({ op })` — return one operation's exact input schema (its arguments).
20
+ - `roadmap({ op, args })` — execute the operation. `args` may also be passed flat (top-level), but the documented shape is nested under `args`.
21
+
22
+ So when a section below says "call `propose_tasks`" or shows `suggest_theme_for({ description })`,
23
+ read it as `roadmap({ op: "propose_tasks", args: {...} })` /
24
+ `roadmap({ op: "suggest_theme_for", args: { description } })`. The server enforces the
25
+ rubric/discovery gates and per-op validation identically however you call.
26
+
10
27
  ## TL;DR — what an agent must do
11
28
 
12
29
  - Reference Themes / Capabilities / Tasks / Sprints by **stable ID**,
@@ -85,6 +102,13 @@ that's almost always what you actually want.
85
102
  CANDIDATE for `propose_capability` (human-confirmed) followed by
86
103
  `move_tasks`. Tune `minClusterSize` (default 3) / `fitThreshold`
87
104
  (default 0.2) to control sensitivity.
105
+ - `detect_theme_sprawl` — the theme-level companion. Scores every
106
+ active theme against every other and flags pairs that overlap enough
107
+ to be consolidation candidates, each with a suggested merge
108
+ (`move_capabilities` the lighter theme's bets into the heavier, then
109
+ `archive_theme`). This is how you keep theme count in check now that
110
+ agents create themes autonomously (see `propose_theme`). Run it at
111
+ quarterly review. Tune `threshold` (default 0.34).
88
112
  - `get_agents_md` — re-read this contract on demand.
89
113
 
90
114
  **Multi-workspace addressing.** A single MCP install can talk to any
@@ -137,18 +161,37 @@ to "tell the human what I'd do"):
137
161
  succeeds (this is a nudge, not a gate — unlike `propose_capability`,
138
162
  which hard-blocks until you've done discovery). Surface the warning
139
163
  to the user.
164
+ - `propose_tasks` — **bulk** create many tasks under ONE capability in
165
+ a single call. **This is the token-efficient path: prefer it over N
166
+ separate `propose_task` calls when filing a plan** — one request, one
167
+ compact `{id,title}` array back. Each task spec needs `title` +
168
+ `effort`; intra-batch dependencies work by giving a task a `ref` and
169
+ listing that ref in a sibling's `dependsOn` (refs are rewritten to
170
+ real ids after minting). A validation error fails the whole batch
171
+ before writing; once valid, per-row RPC failures are reported in
172
+ `tasks[].error` without sinking the rest.
140
173
  - `propose_capability` — create a new capability under an **existing**
141
174
  theme. Required: `name`, `pillarId`. Sensible defaults are applied
142
175
  (`reach: 100`, `impact: 1`, `confidence: 70`). Pass `outcome` and
143
176
  `specRef` whenever you have them — capabilities without an outcome
144
177
  rarely survive review. Pass `idempotencyKey`.
145
- - `propose_theme` — create a new theme. **Use sparingly.** Themes are
146
- years-stable strategic categories; almost every plan fits under an
147
- existing one. Only call this when the human explicitly asks for a
148
- new theme or when the work genuinely doesn't fit any of the existing
149
- ones from `list_themes`. Default rule: if you're tempted to create
150
- a theme, file a capability under the closest existing theme instead
151
- and let the human reorganize later.
178
+ - `propose_theme` — create a new theme. **Theme-autonomy is ON by
179
+ default**, so you MAY create a theme without human confirmation when
180
+ the work genuinely needs a new years-stable pillar. You don't police
181
+ sprawl by asking a human every time the server does it for you:
182
+ `propose_theme` returns `error: "too_similar"` (naming the match) if
183
+ your theme overlaps an existing one above the block bar, so you reuse
184
+ or `update_theme` that one instead. Still prefer the deepest existing
185
+ parent (new task > new capability > new theme); a theme is the rare
186
+ case. If a workspace turned autonomy OFF (Settings → Agent
187
+ automation), `propose_theme` returns `error: "confirmation_required"`
188
+ until you surface the new theme to the user and retry with
189
+ `confirm: true`. Use `force: true` only to override a `too_similar`
190
+ block that's a genuine false positive. Run `detect_theme_sprawl`
191
+ periodically to catch themes that have drifted into overlap.
192
+ - `submit_acceptance_grades` — stamp `{ status: pass | fail, note? }`
193
+ per criterion index, after you've actually verified the work. The
194
+ server stamps `gradedAt` and `gradedBy: "mcp:agent"`.
152
195
  - `submit_acceptance_grades` — stamp `{ status: pass | fail, note? }`
153
196
  per criterion index, after you've actually verified the work. The
154
197
  server stamps `gradedAt` and `gradedBy: "mcp:agent"`.
@@ -223,6 +266,45 @@ disagrees with the cwd snapshot are refused — set
223
266
  `ROADMAPPER_ALLOW_CROSS_WORKSPACE=1` in the env to override.
224
267
  Reads can target any workspace freely.
225
268
 
269
+ **Repo-link gate (write path).** If you're in a git repo that
270
+ isn't mapped to a workspace, a mutator would silently land on the
271
+ install's env-default workspace — almost never what you want. So
272
+ the first such write is **refused** with a structured error:
273
+
274
+ ```json
275
+ {
276
+ "error": "repo_unmapped",
277
+ "message": "\"owner/name\" isn't mapped to a workspace ...",
278
+ "fix": "link_repo()",
279
+ "alt": "<tool>({ workspaceId: \"<target>\", ... })"
280
+ }
281
+ ```
282
+
283
+ Resolve it one of two ways, then retry:
284
+ - **`link_repo()`** — maps the repo you're in to your key's
285
+ workspace, so every future session resolves silently. This is
286
+ the right move when the repo *should* feed this workspace.
287
+ - **Pass `workspaceId` explicitly** on the call — proceeds without
288
+ mapping the repo. This is the escape hatch when you're working
289
+ across **several repos in one session** and just want this write
290
+ to land on a specific existing workspace.
291
+
292
+ The gate is repo-aware and only fires for a genuinely unmapped
293
+ repo: an explicit `workspaceId`, an already-mapped repo (resolves
294
+ via `repo_workspace_map`), or not being in a git repo at all
295
+ pass straight through — a multi-repo chat is never bricked.
296
+ Operators can disable the gate entirely with
297
+ `ROADMAPPER_ALLOW_UNMAPPED_REPO=1`.
298
+
299
+ This gate and the **seed-workspace guard** below are complementary,
300
+ not redundant: the repo-link gate covers the *in a git repo but
301
+ unmapped* case (and gives the more actionable `link_repo` fix),
302
+ while the seed-workspace guard still catches a write that fell
303
+ through to the bundled `"default"` workspace when you're **not** in
304
+ any repo (nothing to link). A write can be refused by at most one of
305
+ them; both protect the same env-default footgun from different
306
+ angles.
307
+
226
308
  Authoring discipline:
227
309
  - Read first (`list_themes`, `list_capabilities`, `list_tasks`) before
228
310
  proposing anything, so you don't invent a new theme/capability that
@@ -402,12 +484,17 @@ Before writing anything:
402
484
  - **Top score 0.2–0.4**: weak overlap. The top match is still
403
485
  usually the right home; re-using a "close-enough" theme is
404
486
  almost always better than creating a duplicate.
405
- - **Empty matches or top < 0.2**: no existing theme fits. Stop
406
- and ask the user explicitly whether this represents a new
407
- strategic direction worth a years-stable theme. Only call
408
- `propose_theme` after the user confirms themes are
409
- years-stable, not per-feature.
410
- The propose_theme tool enforces this discovery: skipping
487
+ - **Empty matches or top < 0.2**: no existing theme fits. With
488
+ theme-autonomy ON (the default), you may call `propose_theme`
489
+ directly when this is a genuinely new years-stable pillar you
490
+ don't need to stop and ask. The server is the sprawl guard: it
491
+ refuses a near-duplicate (`error: "too_similar"`), so a theme that
492
+ gets created is one that doesn't already exist. With autonomy OFF,
493
+ `propose_theme` returns `error: "confirmation_required"` — surface
494
+ the theme to the user and retry with `confirm: true`. Either way,
495
+ prefer a capability under the closest theme when one is even
496
+ plausibly a fit; a new theme is the rare case.
497
+ The propose_theme tool enforces discovery: skipping
411
498
  `suggest_theme_for` (or `list_themes` / `get_roadmap_snapshot`)
412
499
  returns a `discovery_missing` error.
413
500
  2. **Decompose into Capabilities.** A Capability is "a quarterly
@@ -427,9 +514,18 @@ Before writing anything:
427
514
 
428
515
  ### What to emit (template)
429
516
 
430
- Return a single JSON block. The user will paste it into Roadmapper's
431
- import or read it manually; either way the field names must match
432
- exactly. IDs use the `__NEW__` placeholder prefix when you're
517
+ **If the write tools are live (you can call `propose_capability` /
518
+ `propose_tasks`), FILE DIRECTLY do not also emit this JSON block.**
519
+ Dumping the full plan as JSON *and* filing it via tools pays for the
520
+ plan twice in tokens, and the tools are the canonical path anyway. Use
521
+ `propose_capability` for the bet, then ONE `propose_tasks` call for all
522
+ its tasks (not N `propose_task` calls), and report back a short summary
523
+ with the returned ids — not the whole record set. The JSON block below
524
+ is only for the **no-write-tools** case (seed/live-read tier), where
525
+ the user pastes it into Roadmapper's import manually.
526
+
527
+ When you do emit it: return a single JSON block. The field names must
528
+ match exactly. IDs use the `__NEW__` placeholder prefix when you're
433
529
  proposing a new record — Roadmapper assigns the real `TH-NNNNNN` /
434
530
  `CAP-NNN` / `TK-NNNNNN` ID at import time.
435
531
 
package/README.md CHANGED
@@ -57,7 +57,22 @@ JSON config works, dropped into Cursor's `mcp` field.
57
57
 
58
58
  ## What this server exposes
59
59
 
60
- Read tools (always available):
60
+ **The wire surface is three dispatch tools.** As of 0.9.5 the server advertises
61
+ only:
62
+
63
+ - `roadmap_search({ intent })` — list/rank the available operations by intent
64
+ - `roadmap_describe({ op })` — return one operation's exact input schema
65
+ - `roadmap({ op, args })` — execute an operation
66
+
67
+ Everything below is an **`op` value** passed to `roadmap`, e.g.
68
+ `roadmap({ op: "get_roadmap_snapshot" })` or
69
+ `roadmap({ op: "propose_tasks", args: { capabilityId, tasks: [...] } })`. This
70
+ keeps `tools/list` tiny (one schema instead of ~34) while the per-op schemas
71
+ load on demand via `roadmap_describe`. The full planning contract ships in the
72
+ server's `instructions` (sent at connect) and the `get_agents_md` op /
73
+ `roadmapper://rubric` resource.
74
+
75
+ ### Read operations (always available)
61
76
 
62
77
  - `list_themes` — top-level strategic themes
63
78
  - `list_capabilities` — capabilities (optionally filtered by theme)
@@ -86,7 +101,7 @@ Read tools (always available):
86
101
  > for full rows and `limit` (max 200) to raise the cap. This keeps a
87
102
  > cold-start read from blowing the token budget on a large workspace.
88
103
 
89
- Write tools (require workspace-scoped write auth — set `ROADMAPPER_API_KEY`
104
+ ### Write operations (require workspace-scoped write auth — set `ROADMAPPER_API_KEY`
90
105
  to an `rmpr_…` key from the dashboard; writes then route through the
91
106
  mcp-broker so the service-role key never lives on your machine):
92
107
 
@@ -100,25 +115,26 @@ mcp-broker so the service-role key never lives on your machine):
100
115
 
101
116
  ## How agents are meant to use this
102
117
 
103
- The intended loop:
118
+ The intended loop (every step is `roadmap({ op, args })`):
104
119
 
105
- 1. Agent calls `get_agents_md` to load the rubric.
106
- 2. Agent reads the current roadmap state via `list_themes` /
107
- `list_capabilities` / `list_tasks`.
108
- 3. Before proposing anything new, agent calls `suggest_capability_for`
120
+ 1. `roadmap({ op: "get_agents_md" })` to load the rubric.
121
+ 2. Read the current roadmap state: `roadmap({ op: "get_roadmap_snapshot" })`
122
+ (or `list_themes` / `list_capabilities` / `list_tasks` ops).
123
+ 3. Before proposing anything new, `roadmap({ op: "suggest_capability_for", args: { description } })`
109
124
  (or `suggest_theme_for`) to check if a matching parent already exists.
110
- Skipping this is allowed for `propose_task` but the response will carry
111
- a warn-on-skip nudge; `propose_capability` hard-requires it.
112
- 4. Agent proposes new rows through the `propose_*` tools, including all
113
- the rubric-required fields (RICE, acceptance criteria, etc.).
114
- 5. Once a PR is merged, the agent calls `link_pr` to attach it; the
125
+ Skipping is allowed for `propose_task` but the response carries a
126
+ warn-on-skip nudge; `propose_capability` hard-requires it.
127
+ 4. Propose new rows via `roadmap({ op: "propose_tasks", args })` /
128
+ `roadmap({ op: "propose_capability", args })`, including all the
129
+ rubric-required fields (RICE, acceptance criteria, etc.).
130
+ 5. Once a PR is merged, `roadmap({ op: "link_pr", args })` to attach it; the
115
131
  roadmap delivery stats update automatically.
116
- 6. After delivery, the agent self-grades against the acceptance criteria
117
- with `submit_acceptance_grades`.
132
+ 6. After delivery, self-grade against the acceptance criteria with
133
+ `roadmap({ op: "submit_acceptance_grades", args })`.
118
134
 
119
135
  The server enforces the rubric: proposals filed without first fetching
120
- `get_agents_md` are rejected with a remediation hint pointing the agent
121
- back to the rubric.
136
+ `get_agents_md` are rejected with a structured remediation hint whose `fix`
137
+ field names the exact dispatch call to make next.
122
138
 
123
139
  ## Versioning
124
140
 
@@ -134,6 +150,16 @@ the check with `ROADMAPPER_DISABLE_UPDATE_CHECK=1`.
134
150
 
135
151
  ### Recent changes
136
152
 
153
+ - **0.9.5** — **tool-surface collapse for token efficiency.** `tools/list` now
154
+ advertises three dispatch tools (`roadmap_search` / `roadmap_describe` /
155
+ `roadmap`) instead of ~34, cutting the always-loaded tool definitions ~97%
156
+ (~15k → ~0.5k tokens) and the per-session planning footprint ~95%. The ~34
157
+ operations are unchanged — they're reached as `roadmap({ op, args })`, with
158
+ schemas served on demand via `roadmap_describe`. Per-tool methodology moved
159
+ into the server `instructions` (now correctly top-level) plus the
160
+ `roadmapper://rubric` resource. Already-linked repos should re-run
161
+ `npm run mcp:setup -- --link <path>` to refresh the permission allow-list and
162
+ the CLAUDE.md block for the dispatch surface.
137
163
  - **0.9.3** — new `link_repo` tool: when `get_active_workspace` reports
138
164
  `env_default`/`unresolved` and you're in a git repo, one call persists
139
165
  the repo → workspace mapping so future sessions resolve silently (the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@roadmapperai/mcp",
3
- "version": "0.9.3",
3
+ "version": "0.9.5",
4
4
  "description": "Roadmapper AI MCP server — exposes a planning surface (themes, capabilities, tasks, sprints, PRs) to coding agents via stdio JSON-RPC. Pairs with the Roadmapper AI workspace at dashboard.roadmapperai.com.",
5
5
  "keywords": [
6
6
  "mcp",