vantage-peers-mcp 2.9.0 → 2.11.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,107 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.11.0] — 2026-06-14 — Day 102 CRUD baseline PR-C-bis option B: 3-entity Convex searchIndex (mission k575kc1ryps0n8br95jw3q7d0x88m2v9)
4
+
5
+ Mission `mcp-crud-baseline-standard` PR-C-bis under T2. Pi-sequenced follow-up after PR-C (rename-only safe subset) — implements **option B SCOPED** per Pi arbitrage msg `jn7abynmghy5qdr9ga0b914wmh88n99w` ("GO option B SCOPED — démarre PR-C avec 3 entités prioritaires"): tasks + messages + briefingNotes get native Convex BM25 search via `.searchIndex()` schema additions + per-entity Convex queries + MCP tool wrappers.
6
+
7
+ Backend choice (B over A): Convex native `.searchIndex()` is BM25-only, no embeddings, no RAG per-entity pipeline. Sub-linear scan stays in the same backend without spinning up an embedding pipeline per table. Hybrid/semantic per entity remains a future RFC.
8
+
9
+ ### Added — Convex schema searchIndex (3 tables)
10
+
11
+ - `tasks` → `searchIndex("search_title", { searchField: "title", filterFields: ["assignedTo", "status", "project", "missionId", "orgId"] })`
12
+ - `messages` → `searchIndex("search_content", { searchField: "content", filterFields: ["from", "channel", "sessionDay", "tenantId"] })`
13
+ - `briefingNotes` → `searchIndex("search_content", { searchField: "content", filterFields: ["topic", "createdBy", "orgId"] })`
14
+
15
+ ### Added — Convex query functions
16
+
17
+ - `tasks:searchTasksByKeyword` — BM25 over title with assignedTo/status/project/missionId filters + scope gate via `withOrgScope` + `filterByOrgScope` + `requireScope("view-own-tasks")`. Lite/full projection.
18
+ - `messages:searchMessagesByKeyword` — BM25 over content with from/channel/sessionDay/tenantId filters.
19
+ - `briefingNotes:searchBriefingNotesByKeyword` — BM25 over content with topic/createdBy filters.
20
+
21
+ All three: 20-item default limit, 200 cap, lite projection on demand.
22
+
23
+ ### Added — MCP tool wrappers (3 canonical)
24
+
25
+ - **`search_tasks_by_keyword`** — wires to `tasks:searchTasksByKeyword`. `readOnlyHint=true`.
26
+ - **`search_messages_by_keyword`** — wires to `messages:searchMessagesByKeyword`. `readOnlyHint=true`.
27
+ - **`search_briefing_notes_by_keyword`** — wires to `briefingNotes:searchBriefingNotesByKeyword`. `readOnlyHint=true`.
28
+
29
+ ### Why option B (and why scoped to 3)
30
+
31
+ `grep searchIndex convex/schema.ts` previously returned zero hits; only `memories` had search infrastructure (via `@convex-dev/rag`). The original "13-entity cluster" full scope was deferred via PR-C (rename-only) after arbitrage timeout. Pi's arbitrage landed shortly after: **option B SCOPED to 3 priority entities** is the right blend of doctrine progress + bounded scope:
32
+ - `tasks` — largest fleet audience (Eta T13 close-issue scan, dispatch-task-find, etc.).
33
+ - `messages` — post-incident audit + messages-history skill demand.
34
+ - `briefingNotes` — daily snapshot recall narrative.
35
+
36
+ The remaining 10 entities (mission/mandate/fix_pattern/component/repo_mapping/bu/profile/deployment/diary/error/issue/recurring_task/summary) go to a per-entity RFC (mission walk-through scheduled Wed 17 June 15h with Laurent) — each entity decides B (searchIndex), A (RAG if semantic justified, e.g. episodes/diary), or N/A semantic documented (deployment/repo_mapping).
37
+
38
+ ### Version sync
39
+
40
+ - `mcp-server/server.ts:115` SERVER_VERSION 2.10.0 → 2.11.0
41
+ - `mcp-server/package.json` → 2.11.0
42
+ - README + 4 cloud docs bumped to 2.11.0 markers (`enforce-release-sync` v1.0.1 gate).
43
+
44
+ ### Test fixture catch-up
45
+
46
+ - `READ_ONLY_TOOLS` set in `mcp-server/src/__tests__/chatgpt-tool-annotations.test.ts` extended with the 3 new canonical tool names.
47
+
48
+ ### Refs
49
+
50
+ - Mission `k575kc1ryps0n8br95jw3q7d0x88m2v9`.
51
+ - Pi sequencing dispatch: msg `jn76360ckrrkwpqbfwa6tst7k588mpsh` ("(1) T2 PR-C-bis option B SCOPED 3 entities").
52
+ - Pi arbitrage that selected option B: msg `jn7abynmghy5qdr9ga0b914wmh88n99w`.
53
+ - Audit T1: `analysis/mcp-crud-baseline-vp-audit-2026-06-14.md` rows 1 (task), 3 (message), 4 (briefing_note).
54
+ - Doctrine memory: `j57dhrmkzjerjtssnr0z9ba57n88n7q7` (5 ops per entity).
55
+ - Predecessors: PR-A 2.8.0 (memories canonical), PR-B 2.9.0 (episode 5-op), PR-C 2.10.0 (rename-only safe subset).
56
+
57
+ ## [2.10.0] — 2026-06-14 — Day 102 CRUD baseline PR-C: rename-only safe subset (mission k575kc1ryps0n8br95jw3q7d0x88m2v9)
58
+
59
+ Mission `mcp-crud-baseline-standard` PR-C under T2. Third of 4 sub-PRs aligning the MCP surface with the Day 101 doctrine `j57dhrmkzjerjtssnr0z9ba57n88n7q7`. Sigma autonomous default after arbitrage timeout on the original "13-entity search_by_keyword cluster" scope — that fuller scope requires a backend search-infrastructure decision (RAG-index per entity vs Convex `.searchIndex()` per table) that is NOT a thin-wrapper PR. PR-C ships the rename-only safe subset now to keep doctrine velocity; the full cluster is deferred to a backend-RFC mission.
60
+
61
+ ### Added (2 canonical search tools)
62
+
63
+ - **`search_components_by_keyword`** — BM25 / substring search over components by name or team. Wire-identical to `search_components` (same Convex query `components:search` 1:1, same scope filter, same defaults). `readOnlyHint=true`.
64
+ - **`search_fix_patterns_by_semantic`** — semantic embedding-similarity search over fix patterns. Wire-identical to `search_fix_patterns` (same Convex action `search:searchFixPatterns` 1:1, same scope filter, same defaults). `readOnlyHint=true`. Note: the canonical suffix is `_by_semantic` (NOT `_by_keyword`) because the underlying ranker is embedding-cosine, not BM25 — naming follows behavior, not entity convention.
65
+
66
+ ### Deprecated (alias-only, removal target 2.11.0)
67
+
68
+ - **`search_components`** — alias of `search_components_by_keyword`. Description leads with `DEPRECATED ALIAS …`.
69
+ - **`search_fix_patterns`** — alias of `search_fix_patterns_by_semantic`. Description leads with `DEPRECATED ALIAS …`.
70
+
71
+ ### Re-targeted deprecations (FOLLOW-UP from Eta PR #750 review)
72
+
73
+ - **`text_search`** — 2.8.0 originally targeted removal at 2.9.0; episode-only PR-B did not include the removal. Source comments + this entry re-target removal to **2.11.0**.
74
+ - **`recall`** — same re-target rationale, removal now **2.11.0**.
75
+
76
+ Both `text_search` and `recall` remain wire-identical to their canonical successors (`search_memories_by_keyword` / `search_memories_by_semantic`). Closes FOLLOW-UP task `k1754apqtcjpre2vd5ghbkcmzn88mhwf`.
77
+
78
+ ### Scope NOT in this PR (deferred)
79
+
80
+ The original PR-C "13-entity search_by_keyword cluster" required adding BM25 / search infrastructure to 10+ entities that currently have NO backend search index (`grep searchIndex convex/schema.ts` → zero hits; only `memories` is indexed, via `@convex-dev/rag`). Two backend paths exist:
81
+
82
+ - **Path A** — extend RAG indexing to each entity (heavy: per-table embedding pipeline, RAG namespace per entity, vector cost; gets hybrid search for free).
83
+ - **Path B** — Convex native `.searchIndex()` per table (lightweight: schema migration + one query per entity, BM25-only, no embeddings).
84
+
85
+ Either path is a multi-day backend RFC, NOT a thin-wrapper PR. Sigma sent the arbitrage to Pi (msg `jn75zy4g7bhj95bhyz2zvv6n8d88na6s`) and defaulted to PR-C path C (rename-only) after 2 cron ticks of no reply. A follow-up mission to scope the backend search infrastructure is the next mission proposal.
86
+
87
+ ### Version sync
88
+
89
+ - `mcp-server/server.ts:115` SERVER_VERSION 2.9.0 → 2.10.0
90
+ - `mcp-server/package.json` → 2.10.0
91
+ - README + 4 cloud docs bumped to 2.10.0 markers (`enforce-release-sync` v1.0.1 gate).
92
+
93
+ ### Test fixture catch-up
94
+
95
+ - `READ_ONLY_TOOLS` set in `mcp-server/src/__tests__/chatgpt-tool-annotations.test.ts` extended with the 2 new canonical names.
96
+
97
+ ### Refs
98
+
99
+ - Mission `k575kc1ryps0n8br95jw3q7d0x88m2v9` (MCP CRUD Baseline Standard).
100
+ - Pi authorization msg `jn74q7twhr3s1s8dvqxbzvky9588msdd` ("next: T2-PR-C 13-entity search_by_keyword cluster post #750 merge").
101
+ - Sigma arbitrage msg to Pi: `jn75zy4g7bhj95bhyz2zvv6n8d88na6s` (3 paths A/B/C).
102
+ - FOLLOW-UP task: `k1754apqtcjpre2vd5ghbkcmzn88mhwf` (text_search/recall deprecation slipped).
103
+ - Audit T1: `analysis/mcp-crud-baseline-vp-audit-2026-06-14.md` § 4.
104
+
3
105
  ## [2.9.0] — 2026-06-14 — Day 102 CRUD baseline PR-B: episode entity 5-op surface (mission k575kc1ryps0n8br95jw3q7d0x88m2v9)
4
106
 
5
107
  Mission `mcp-crud-baseline-standard` PR-B under T2. Second of 4 sub-PRs aligning the MCP surface with the Day 101 doctrine `j57dhrmkzjerjtssnr0z9ba57n88n7q7` ("5 ops per entity"). PR-B adds the missing read/list/search facades for the `episode` entity, completing the 5-op surface (the write side `store_episode` already existed since the 8-Sins doctrine).
package/README.md CHANGED
@@ -242,7 +242,7 @@ Example:
242
242
  ### Session (1)
243
243
  `set_summary`
244
244
 
245
- ## Compact payloads and status aliases (v2.9.0 — feature since v2.3.0)
245
+ ## Compact payloads and status aliases (v2.11.0 — feature since v2.3.0)
246
246
 
247
247
  ### `fields=lite` — reduced token payloads
248
248
 
package/dist/server.js CHANGED
@@ -102,7 +102,7 @@ const convexUrl = loadConvexUrl();
102
102
  const convex = new ConvexHttpClient(convexUrl);
103
103
  const server = new McpServer({
104
104
  name: "vantage-peers",
105
- version: "2.9.0",
105
+ version: "2.11.0",
106
106
  });
107
107
  // ─────────────────────────────────────────────────────────────────────────────
108
108
  // Helper: structured error response for MCP tool handlers
package/dist/src/tools.js CHANGED
@@ -917,8 +917,9 @@ export function registerTools(server, convex, oauthCtx) {
917
917
  });
918
918
  // ── recall ──────────────────────────────────────────────────────────────────
919
919
  // DEPRECATED (Day 101 v2.8.0) — alias of search_memories_by_semantic. Retained
920
- // for one minor version as a back-compat shim. New callers should use
921
- // `search_memories_by_semantic`. To be removed in 2.9.0.
920
+ // as a back-compat shim. New callers should use `search_memories_by_semantic`.
921
+ // Removal target re-targeted to 2.11.0 (slipped past 2.9.0 — episode-only PR;
922
+ // see FOLLOW-UP task k1754apqtcjpre2vd5ghbkcmzn88mhwf for arbitrage).
922
923
  server.tool("recall", "DEPRECATED ALIAS of search_memories_by_semantic — semantic vector search over VantagePeers memories, ranked by cosine similarity. " +
923
924
  "WHEN: use at session start or before decisions — prefer over text_search for intent-based queries. New callers: use search_memories_by_semantic. " +
924
925
  "EXAMPLE: recall query='Pi feedback rules' namespace='global' type='feedback' limit=20.", {
@@ -975,8 +976,9 @@ export function registerTools(server, convex, oauthCtx) {
975
976
  });
976
977
  // ── text_search ─────────────────────────────────────────────────────────────
977
978
  // DEPRECATED (Day 101 v2.8.0) — alias of search_memories_by_keyword. Retained
978
- // for one minor version as a back-compat shim. New callers should use
979
- // `search_memories_by_keyword`. To be removed in 2.9.0.
979
+ // as a back-compat shim. New callers should use `search_memories_by_keyword`.
980
+ // Removal target re-targeted to 2.11.0 (slipped past 2.9.0 — episode-only PR;
981
+ // see FOLLOW-UP task k1754apqtcjpre2vd5ghbkcmzn88mhwf for arbitrage).
980
982
  server.tool("text_search", "DEPRECATED ALIAS of search_memories_by_keyword — BM25 full-text keyword search over VantagePeers memories for exact term matching. " +
981
983
  "WHEN: use when search_memories_by_semantic returns too-broad results and you need a specific exact phrase or ID. New callers: use search_memories_by_keyword. " +
982
984
  "EXAMPLE: text_search query='Day 92 C3 descriptions' namespace='project/vantage-peers' limit=10.", {
@@ -1025,7 +1027,7 @@ export function registerTools(server, convex, oauthCtx) {
1025
1027
  // ── search_memories_by_keyword ─────────────────────────────────────────────
1026
1028
  // Day 101 v2.8.0 — canonical name under MCP CRUD baseline doctrine.
1027
1029
  // Mirrors text_search 1:1 (same Convex action `search:textSearch`).
1028
- // text_search is retained as a deprecated alias until 2.9.0.
1030
+ // text_search is retained as a deprecated alias (removal re-targeted 2.11.0).
1029
1031
  server.tool("search_memories_by_keyword", "BM25 full-text keyword search over VantagePeers memories for exact term matching. " +
1030
1032
  "WHEN: use when search_memories_by_semantic returns too-broad results and you need a specific exact phrase or ID. " +
1031
1033
  "EXAMPLE: search_memories_by_keyword query='Day 92 C3 descriptions' namespace='project/vantage-peers' limit=10.", {
@@ -1074,7 +1076,7 @@ export function registerTools(server, convex, oauthCtx) {
1074
1076
  // ── search_memories_by_semantic ────────────────────────────────────────────
1075
1077
  // Day 101 v2.8.0 — canonical name under MCP CRUD baseline doctrine.
1076
1078
  // Mirrors recall 1:1 (same Convex action `search:recall`).
1077
- // recall is retained as a deprecated alias until 2.9.0.
1079
+ // recall is retained as a deprecated alias (removal re-targeted 2.11.0).
1078
1080
  server.tool("search_memories_by_semantic", "Semantic vector search over VantagePeers memories, ranked by cosine similarity. " +
1079
1081
  "WHEN: use at session start or before decisions — prefer over search_memories_by_keyword for intent-based queries. " +
1080
1082
  "EXAMPLE: search_memories_by_semantic query='Pi feedback rules' namespace='global' type='feedback' limit=20.", {
@@ -2109,6 +2111,60 @@ export function registerTools(server, convex, oauthCtx) {
2109
2111
  return mcpConvexError(error);
2110
2112
  }
2111
2113
  });
2114
+ // ── search_messages_by_keyword ─────────────────────────────────────────────
2115
+ // Day 102 v2.11.0 — CRUD baseline PR-C-bis option B (mission k575kc1r).
2116
+ // BM25 keyword search over message content. Backed by Convex
2117
+ // `messages:searchMessagesByKeyword` using the `search_content` searchIndex.
2118
+ server.tool("search_messages_by_keyword", "BM25 full-text keyword search over message content, ranked by relevance. " +
2119
+ "WHEN: use for post-incident audit or to find peer DMs by topic — e.g. 'find messages about deploy' across the recent sessionDay window. " +
2120
+ "EXAMPLE: search_messages_by_keyword query='convex deploy' from='pi' sessionDay=102 limit=10.", {
2121
+ query: z.string().describe("Search term to match against message content"),
2122
+ from: z.string().optional().describe("Filter by sender role"),
2123
+ channel: z
2124
+ .string()
2125
+ .optional()
2126
+ .describe("Filter by channel — e.g. 'sigma', 'broadcast'"),
2127
+ sessionDay: z
2128
+ .number()
2129
+ .int()
2130
+ .optional()
2131
+ .describe("Filter to a specific session day"),
2132
+ tenantId: z.string().optional().describe("Filter by tenant id"),
2133
+ limit: z
2134
+ .number()
2135
+ .int()
2136
+ .min(1)
2137
+ .max(200)
2138
+ .optional()
2139
+ .describe("Max items to return. Default 20 (envelope-safe). Cap 200."),
2140
+ fields: z
2141
+ .enum(["lite", "full"])
2142
+ .optional()
2143
+ .describe("'lite' returns compact payload (less tokens), 'full' is default."),
2144
+ }, {
2145
+ readOnlyHint: true,
2146
+ openWorldHint: false,
2147
+ destructiveHint: false,
2148
+ title: "Search messages by keyword (BM25)",
2149
+ }, async ({ query, from, channel, sessionDay, tenantId, limit, fields }) => {
2150
+ try {
2151
+ const results = await convex.query("messages:searchMessagesByKeyword", {
2152
+ query,
2153
+ from,
2154
+ channel,
2155
+ sessionDay,
2156
+ tenantId,
2157
+ limit: limit ?? 20,
2158
+ fields: fields ?? "lite",
2159
+ });
2160
+ return {
2161
+ content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
2162
+ };
2163
+ }
2164
+ catch (error) {
2165
+ return mcpConvexError(error);
2166
+ }
2167
+ });
2112
2168
  // ── list_broadcast_status ───────────────────────────────────────────────────
2113
2169
  // S3.3 B8 follow-up batch 3 FINAL — DOCTRINE EXCEPTION.
2114
2170
  // @cursorPagingException single-object-shape-not-list
@@ -2349,6 +2405,63 @@ export function registerTools(server, convex, oauthCtx) {
2349
2405
  return mcpConvexError(error);
2350
2406
  }
2351
2407
  });
2408
+ // ── search_tasks_by_keyword ────────────────────────────────────────────────
2409
+ // Day 102 v2.11.0 — CRUD baseline PR-C-bis option B (mission k575kc1r).
2410
+ // BM25 keyword search over task titles. Backed by Convex `tasks:searchTasksByKeyword`
2411
+ // which uses the `search_title` searchIndex. Filter axes: assignedTo, status,
2412
+ // project, missionId — all pushed into the index for sub-linear scan.
2413
+ server.tool("search_tasks_by_keyword", "BM25 full-text keyword search over task titles, ranked by relevance. " +
2414
+ "WHEN: use to find tasks by topic/keyword when list_tasks filters are too broad — e.g. 'find tasks about hook' across all assignees. " +
2415
+ "EXAMPLE: search_tasks_by_keyword query='hook PostToolUse' status='todo' limit=10.", {
2416
+ query: z.string().describe("Search term to match against task title"),
2417
+ assignedTo: z
2418
+ .string()
2419
+ .optional()
2420
+ .describe("Filter by assignee role"),
2421
+ status: z
2422
+ .enum(["todo", "in_progress", "review", "blocked", "done"])
2423
+ .optional()
2424
+ .describe("Filter by status"),
2425
+ project: z.string().optional().describe("Filter by project name"),
2426
+ missionId: z
2427
+ .string()
2428
+ .optional()
2429
+ .describe("Filter by mission Convex ID"),
2430
+ limit: z
2431
+ .number()
2432
+ .int()
2433
+ .min(1)
2434
+ .max(200)
2435
+ .optional()
2436
+ .describe("Max items to return. Default 20 (envelope-safe). Cap 200."),
2437
+ fields: z
2438
+ .enum(["lite", "full"])
2439
+ .optional()
2440
+ .describe("'lite' returns compact payload (less tokens), 'full' is default."),
2441
+ }, {
2442
+ readOnlyHint: true,
2443
+ openWorldHint: false,
2444
+ destructiveHint: false,
2445
+ title: "Search tasks by keyword (BM25)",
2446
+ }, async ({ query, assignedTo, status, project, missionId, limit, fields }) => {
2447
+ try {
2448
+ const results = await convex.query("tasks:searchTasksByKeyword", {
2449
+ query,
2450
+ assignedTo,
2451
+ status,
2452
+ project,
2453
+ missionId,
2454
+ limit: limit ?? 20,
2455
+ fields: fields ?? "lite",
2456
+ });
2457
+ return {
2458
+ content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
2459
+ };
2460
+ }
2461
+ catch (error) {
2462
+ return mcpConvexError(error);
2463
+ }
2464
+ });
2352
2465
  // ── update_task ─────────────────────────────────────────────────────────────
2353
2466
  server.tool("update_task", "Update any mutable field on a task; only provided fields are patched, updatedAt auto-set. " +
2354
2467
  "WHEN: use to reassign, reprioritize, or add context to an existing task without recreating it. " +
@@ -3475,6 +3588,51 @@ export function registerTools(server, convex, oauthCtx) {
3475
3588
  return mcpConvexError(error);
3476
3589
  }
3477
3590
  });
3591
+ // ── search_briefing_notes_by_keyword ────────────────────────────────────────
3592
+ // Day 102 v2.11.0 — CRUD baseline PR-C-bis option B (mission k575kc1r).
3593
+ // BM25 keyword search over briefing note content. Backed by Convex
3594
+ // `briefingNotes:searchBriefingNotesByKeyword` using the `search_content` searchIndex.
3595
+ server.tool("search_briefing_notes_by_keyword", "BM25 full-text keyword search over briefing note content, ranked by relevance. " +
3596
+ "WHEN: use to recall briefings about a topic/decision when list_briefing_notes filters are too coarse — e.g. 'find briefings about migration plan'. " +
3597
+ "EXAMPLE: search_briefing_notes_by_keyword query='migration plan' topic='daily' limit=10.", {
3598
+ query: z
3599
+ .string()
3600
+ .describe("Search term to match against briefing note content"),
3601
+ topic: z.string().optional().describe("Filter by topic"),
3602
+ createdBy: z.string().optional().describe("Filter by creator role"),
3603
+ limit: z
3604
+ .number()
3605
+ .int()
3606
+ .min(1)
3607
+ .max(200)
3608
+ .optional()
3609
+ .describe("Max items to return. Default 20 (envelope-safe). Cap 200."),
3610
+ fields: z
3611
+ .enum(["lite", "full"])
3612
+ .optional()
3613
+ .describe("'lite' returns compact payload (less tokens), 'full' is default."),
3614
+ }, {
3615
+ readOnlyHint: true,
3616
+ openWorldHint: false,
3617
+ destructiveHint: false,
3618
+ title: "Search briefing notes by keyword (BM25)",
3619
+ }, async ({ query, topic, createdBy, limit, fields }) => {
3620
+ try {
3621
+ const results = await convex.query("briefingNotes:searchBriefingNotesByKeyword", {
3622
+ query,
3623
+ topic,
3624
+ createdBy,
3625
+ limit: limit ?? 20,
3626
+ fields: fields ?? "lite",
3627
+ });
3628
+ return {
3629
+ content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
3630
+ };
3631
+ }
3632
+ catch (error) {
3633
+ return mcpConvexError(error);
3634
+ }
3635
+ });
3478
3636
  // ── register_component ──────────────────────────────────────────────────────
3479
3637
  server.tool("register_component", "Register or upsert a component (agent/skill/hook/plugin) in the VantagePeers registry by name+type. " +
3480
3638
  "WHEN: use when publishing a new version of an agent skill or hook so peers can discover and use it. " +
@@ -3726,6 +3884,9 @@ export function registerTools(server, convex, oauthCtx) {
3726
3884
  }
3727
3885
  });
3728
3886
  // ── search_components ───────────────────────────────────────────────────────
3887
+ // DEPRECATED (Day 102 v2.10.0) — alias of search_components_by_keyword.
3888
+ // Retained for one minor version as a back-compat shim. New callers should
3889
+ // use `search_components_by_keyword`. To be removed in 2.11.0.
3729
3890
  server.tool("search_components",
3730
3891
  // S3.3 B8 follow-up batch 3 FINAL — DOCTRINE EXCEPTION.
3731
3892
  // @cursorPagingException relevance-ranked-not-chronological
@@ -3733,8 +3894,8 @@ export function registerTools(server, convex, oauthCtx) {
3733
3894
  // anchor would skip high-relevance older matches in favor of newer
3734
3895
  // low-relevance ones, breaking the search contract. Pagination on
3735
3896
  // semantic search should be score-based (offset / topK), not time-based.
3736
- "Search components by name or team substring with optional type filter. " +
3737
- "WHEN: use before register_component to check if a similar component already exists in the registry. " +
3897
+ "DEPRECATED ALIAS of search_components_by_keyword — search components by name or team substring with optional type filter. " +
3898
+ "WHEN: use before register_component to check if a similar component already exists in the registry. New callers: use search_components_by_keyword. " +
3738
3899
  "EXAMPLE: search_components query='check-tasks' type='skill' limit=10.", {
3739
3900
  query: z
3740
3901
  .string()
@@ -3776,6 +3937,52 @@ export function registerTools(server, convex, oauthCtx) {
3776
3937
  return mcpConvexError(error);
3777
3938
  }
3778
3939
  });
3940
+ // ── search_components_by_keyword ───────────────────────────────────────────
3941
+ // Day 102 v2.10.0 — canonical name under MCP CRUD baseline doctrine (PR-C).
3942
+ // Mirrors search_components 1:1 (same Convex query `components:search`).
3943
+ // search_components is retained as a deprecated alias until 2.11.0.
3944
+ server.tool("search_components_by_keyword", "BM25 / substring keyword search over components by name or team with optional type filter. " +
3945
+ "WHEN: use before register_component to check if a similar component already exists in the registry. " +
3946
+ "EXAMPLE: search_components_by_keyword query='check-tasks' type='skill' limit=10.", {
3947
+ query: z
3948
+ .string()
3949
+ .describe("Search term to match against component name or team"),
3950
+ type: componentTypeSchema.optional().describe("Filter by component type"),
3951
+ limit: z
3952
+ .number()
3953
+ .int()
3954
+ .min(1)
3955
+ .max(200)
3956
+ .optional()
3957
+ .describe("Max items to return. Default 20 (envelope-safe). Cap 200."),
3958
+ fields: z
3959
+ .enum(["lite", "full"])
3960
+ .optional()
3961
+ .describe("'lite' returns compact payload (less tokens), 'full' is default."),
3962
+ }, {
3963
+ readOnlyHint: true,
3964
+ openWorldHint: false,
3965
+ destructiveHint: false,
3966
+ title: "Search components by keyword",
3967
+ }, async ({ query, type, limit, fields }) => {
3968
+ try {
3969
+ const results = await convex.query("components:search", {
3970
+ query,
3971
+ type,
3972
+ limit: limit ?? 20,
3973
+ fields: fields ?? "lite",
3974
+ });
3975
+ const filteredResults = scopeFilterList(oauthCtx, Array.isArray(results) ? results : []);
3976
+ return {
3977
+ content: [
3978
+ { type: "text", text: JSON.stringify(filteredResults, null, 2) },
3979
+ ],
3980
+ };
3981
+ }
3982
+ catch (error) {
3983
+ return mcpConvexError(error);
3984
+ }
3985
+ });
3779
3986
  // ── create_recurring_task ───────────────────────────────────────────────────
3780
3987
  server.tool("create_recurring_task", "Create a recurring task template that auto-generates tasks on a cron schedule. " +
3781
3988
  "WHEN: use for daily standups, weekly reviews, or any repeating work item pattern. " +
@@ -5197,14 +5404,18 @@ export function registerTools(server, convex, oauthCtx) {
5197
5404
  }
5198
5405
  });
5199
5406
  // ── search_fix_patterns ─────────────────────────────────────────────────────
5407
+ // DEPRECATED (Day 102 v2.10.0) — alias of search_fix_patterns_by_semantic.
5408
+ // Underlying behavior is embedding-similarity ranking (NOT BM25), hence
5409
+ // "_by_semantic" is the canonical suffix per the CRUD baseline doctrine.
5410
+ // To be removed in 2.11.0.
5200
5411
  server.tool("search_fix_patterns",
5201
5412
  // S3.3 B8 follow-up batch 3 FINAL — DOCTRINE EXCEPTION.
5202
5413
  // @cursorPagingException semantic-action-not-chronological
5203
5414
  // Rationale: backed by `convex.action("search:searchFixPatterns")` which
5204
5415
  // runs an embedding-similarity ranker; cursor paging by `createdBefore`
5205
5416
  // would corrupt relevance ordering. Same rationale as search_components.
5206
- "Semantic search over fix patterns by symptom description, ranked by relevance. " +
5207
- "WHEN: call BEFORE fixing any bug to check if a matching pattern exists and reuse the validated fix. " +
5417
+ "DEPRECATED ALIAS of search_fix_patterns_by_semantic — semantic search over fix patterns by symptom description, ranked by relevance. " +
5418
+ "WHEN: call BEFORE fixing any bug to check if a matching pattern exists and reuse the validated fix. New callers: use search_fix_patterns_by_semantic. " +
5208
5419
  "EXAMPLE: search_fix_patterns query='message disappears after sending on mobile' limit=5.", {
5209
5420
  query: z
5210
5421
  .string()
@@ -5247,6 +5458,53 @@ export function registerTools(server, convex, oauthCtx) {
5247
5458
  return mcpConvexError(error);
5248
5459
  }
5249
5460
  });
5461
+ // ── search_fix_patterns_by_semantic ────────────────────────────────────────
5462
+ // Day 102 v2.10.0 — canonical name under MCP CRUD baseline doctrine (PR-C).
5463
+ // Mirrors search_fix_patterns 1:1 (same Convex action `search:searchFixPatterns`).
5464
+ // search_fix_patterns is retained as a deprecated alias until 2.11.0.
5465
+ server.tool("search_fix_patterns_by_semantic", "Semantic vector search over fix patterns by symptom description, ranked by relevance. " +
5466
+ "WHEN: call BEFORE fixing any bug to check if a matching pattern exists and reuse the validated fix. " +
5467
+ "EXAMPLE: search_fix_patterns_by_semantic query='message disappears after sending on mobile' limit=5.", {
5468
+ query: z
5469
+ .string()
5470
+ .describe("Describe the problem — e.g. 'message disappears after sending'"),
5471
+ limit: z
5472
+ .number()
5473
+ .int()
5474
+ .min(1)
5475
+ .max(200)
5476
+ .optional()
5477
+ .describe("Max items to return. Default 20 (envelope-safe). Cap 200."),
5478
+ fields: z
5479
+ .enum(["lite", "full"])
5480
+ .optional()
5481
+ .describe("'lite' returns compact payload (less tokens), 'full' is default."),
5482
+ }, {
5483
+ readOnlyHint: true,
5484
+ openWorldHint: false,
5485
+ destructiveHint: false,
5486
+ title: "Search fix patterns by semantic",
5487
+ }, async ({ query, limit, fields }) => {
5488
+ try {
5489
+ const results = await convex.action("search:searchFixPatterns", {
5490
+ query,
5491
+ limit: limit ?? 20,
5492
+ fields: fields ?? "lite",
5493
+ });
5494
+ const filteredResults = scopeFilterList(oauthCtx, Array.isArray(results) ? results : []);
5495
+ return {
5496
+ content: [
5497
+ {
5498
+ type: "text",
5499
+ text: JSON.stringify(filteredResults, null, 2),
5500
+ },
5501
+ ],
5502
+ };
5503
+ }
5504
+ catch (error) {
5505
+ return mcpConvexError(error);
5506
+ }
5507
+ });
5250
5508
  // ── list_fix_patterns ───────────────────────────────────────────────────────
5251
5509
  server.tool("list_fix_patterns", "List fix patterns filtered by source project, newest first with cursor paging support. " +
5252
5510
  "WHEN: use to audit the knowledge base or review all patterns for a specific project. " +
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vantage-peers-mcp",
3
- "version": "2.9.0",
3
+ "version": "2.11.0",
4
4
  "description": "MCP server for VantagePeers — shared memory, messaging, and task coordination for AI agent teams",
5
5
  "type": "module",
6
6
  "main": "./dist/server.js",