@roadmapperai/mcp 0.6.0 → 0.7.0

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 (2) hide show
  1. package/package.json +1 -1
  2. package/server.mjs +34 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@roadmapperai/mcp",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
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",
package/server.mjs CHANGED
@@ -1943,7 +1943,16 @@ async function proposeTask(args, projected, wsId) {
1943
1943
  if (!cap) return errorResult(`Capability ${args.capabilityId} not found.`);
1944
1944
  const titleErr = validateName(args.title, 5);
1945
1945
  if (titleErr) return errorResult(titleErr);
1946
- if (args.effort && !VALID_EFFORTS.has(args.effort))
1946
+ // Effort is required end-to-end: the schema flags it, and the
1947
+ // Postgres RPC refuses without it. We also raise here so older
1948
+ // clients that ignore the schema get a clear error message before
1949
+ // a round-trip.
1950
+ if (!args.effort) {
1951
+ return errorResult(
1952
+ "effort is required (one of XS, S, M, L, XL). Always size the task — XS (≤2h), S (≤1d), M (~1-3d), L (~1-2w), XL (>2w)."
1953
+ );
1954
+ }
1955
+ if (!VALID_EFFORTS.has(args.effort))
1947
1956
  return errorResult(`Invalid effort ${args.effort}.`);
1948
1957
  if (args.priority && !VALID_PRIORITIES.has(args.priority))
1949
1958
  return errorResult(`Invalid priority ${args.priority}.`);
@@ -1960,7 +1969,7 @@ async function proposeTask(args, projected, wsId) {
1960
1969
  )
1961
1970
  return errorResult(`expectedScope must be a positive number, got ${args.expectedScope}.`);
1962
1971
 
1963
- const effort = args.effort ?? "M";
1972
+ const effort = args.effort;
1964
1973
  const start = todayISO();
1965
1974
  // Target dates are day-resolution; round up so sub-day estimates
1966
1975
  // (XS=0.25, S=0.5) still nudge the target at least one day out.
@@ -2269,10 +2278,32 @@ function suggestCapabilityFor(args, projected) {
2269
2278
  (c) => effectiveCapabilityStatus(c, projected.tasks) !== "delivered"
2270
2279
  );
2271
2280
  const query = tokenize(desc);
2281
+ // Build a richer haystack per capability: include the parent
2282
+ // theme's name+description (vocabulary often overlaps with the
2283
+ // query) and the titles of attached tasks (the *implementation*
2284
+ // language, which is closer to how engineers describe new work
2285
+ // than the bet-style capability outcome statement). This makes
2286
+ // matches that previously scored near zero hit the 0.2+ band
2287
+ // where the model takes them seriously.
2288
+ const themeById = new Map(
2289
+ (projected.themes ?? []).map((t) => [t.id, t])
2290
+ );
2272
2291
  const ranked = activeCaps
2273
2292
  .map((c) => {
2293
+ const theme = themeById.get(c.pillarId);
2294
+ const taskTitles = (projected.tasks ?? [])
2295
+ .filter((t) => t.capabilityId === c.id)
2296
+ .map((t) => t.title)
2297
+ .join(" ");
2274
2298
  const hay = tokenize(
2275
- `${c.name} ${c.description ?? ""} ${c.outcome ?? ""}`
2299
+ [
2300
+ c.name,
2301
+ c.description ?? "",
2302
+ c.outcome ?? "",
2303
+ theme?.name ?? "",
2304
+ theme?.description ?? "",
2305
+ taskTitles,
2306
+ ].join(" ")
2276
2307
  );
2277
2308
  return { capability: c, score: jaccardScore(query, hay) };
2278
2309
  })