vantage-peers-mcp 2.8.0 → 2.10.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 +80 -0
- package/README.md +1 -1
- package/dist/server.js +1 -1
- package/dist/src/tools.js +331 -10
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,85 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.10.0] — 2026-06-14 — Day 102 CRUD baseline PR-C: rename-only safe subset (mission k575kc1ryps0n8br95jw3q7d0x88m2v9)
|
|
4
|
+
|
|
5
|
+
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.
|
|
6
|
+
|
|
7
|
+
### Added (2 canonical search tools)
|
|
8
|
+
|
|
9
|
+
- **`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`.
|
|
10
|
+
- **`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.
|
|
11
|
+
|
|
12
|
+
### Deprecated (alias-only, removal target 2.11.0)
|
|
13
|
+
|
|
14
|
+
- **`search_components`** — alias of `search_components_by_keyword`. Description leads with `DEPRECATED ALIAS …`.
|
|
15
|
+
- **`search_fix_patterns`** — alias of `search_fix_patterns_by_semantic`. Description leads with `DEPRECATED ALIAS …`.
|
|
16
|
+
|
|
17
|
+
### Re-targeted deprecations (FOLLOW-UP from Eta PR #750 review)
|
|
18
|
+
|
|
19
|
+
- **`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**.
|
|
20
|
+
- **`recall`** — same re-target rationale, removal now **2.11.0**.
|
|
21
|
+
|
|
22
|
+
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`.
|
|
23
|
+
|
|
24
|
+
### Scope NOT in this PR (deferred)
|
|
25
|
+
|
|
26
|
+
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:
|
|
27
|
+
|
|
28
|
+
- **Path A** — extend RAG indexing to each entity (heavy: per-table embedding pipeline, RAG namespace per entity, vector cost; gets hybrid search for free).
|
|
29
|
+
- **Path B** — Convex native `.searchIndex()` per table (lightweight: schema migration + one query per entity, BM25-only, no embeddings).
|
|
30
|
+
|
|
31
|
+
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.
|
|
32
|
+
|
|
33
|
+
### Version sync
|
|
34
|
+
|
|
35
|
+
- `mcp-server/server.ts:115` SERVER_VERSION 2.9.0 → 2.10.0
|
|
36
|
+
- `mcp-server/package.json` → 2.10.0
|
|
37
|
+
- README + 4 cloud docs bumped to 2.10.0 markers (`enforce-release-sync` v1.0.1 gate).
|
|
38
|
+
|
|
39
|
+
### Test fixture catch-up
|
|
40
|
+
|
|
41
|
+
- `READ_ONLY_TOOLS` set in `mcp-server/src/__tests__/chatgpt-tool-annotations.test.ts` extended with the 2 new canonical names.
|
|
42
|
+
|
|
43
|
+
### Refs
|
|
44
|
+
|
|
45
|
+
- Mission `k575kc1ryps0n8br95jw3q7d0x88m2v9` (MCP CRUD Baseline Standard).
|
|
46
|
+
- Pi authorization msg `jn74q7twhr3s1s8dvqxbzvky9588msdd` ("next: T2-PR-C 13-entity search_by_keyword cluster post #750 merge").
|
|
47
|
+
- Sigma arbitrage msg to Pi: `jn75zy4g7bhj95bhyz2zvv6n8d88na6s` (3 paths A/B/C).
|
|
48
|
+
- FOLLOW-UP task: `k1754apqtcjpre2vd5ghbkcmzn88mhwf` (text_search/recall deprecation slipped).
|
|
49
|
+
- Audit T1: `analysis/mcp-crud-baseline-vp-audit-2026-06-14.md` § 4.
|
|
50
|
+
|
|
51
|
+
## [2.9.0] — 2026-06-14 — Day 102 CRUD baseline PR-B: episode entity 5-op surface (mission k575kc1ryps0n8br95jw3q7d0x88m2v9)
|
|
52
|
+
|
|
53
|
+
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).
|
|
54
|
+
|
|
55
|
+
Architectural note: episodes are NOT a separate Convex table — per hotfix `7f958d0`, episodes are stored as memories with `type='episode'` (context/goal/action/outcome/insight + severity). The 4 new tools are thin wrappers that force `type='episode'` on the existing `memories:*` / `search:*` actions, so callers get an ergonomic episode-scoped surface without introducing a new backend table or duplicating index logic.
|
|
56
|
+
|
|
57
|
+
### Added (4 canonical episode tools)
|
|
58
|
+
|
|
59
|
+
- **`get_episode`** — fetch a single episode by memory ID. Calls `memories:getMemory` then asserts the returned row has `type='episode'`; otherwise returns a non-leaky "Episode not found" so wrong-type IDs do not leak existence of non-episode memories. Scope-aware via `scopeFilterGet`. Annotations: `readOnlyHint=true`, `openWorldHint=false`, `destructiveHint=false`.
|
|
60
|
+
- **`list_episodes`** — list episodes ordered newest first. Calls `memories:listMemories` with `type='episode'` forced. Accepts `namespace?`, `createdBy?`, `limit?`, `fields?`, `cursor?` — same paging semantics as `list_memories`. Scope-aware via `scopeFilterList`. Envelope-capped via `capListResponseBytes("list_episodes")`.
|
|
61
|
+
- **`search_episodes_by_keyword`** — BM25 search restricted to episodes. Calls `search:textSearch` with `type='episode'` forced. Same `guardRead` namespace gate, same 20-default / 200-cap limits.
|
|
62
|
+
- **`search_episodes_by_semantic`** — semantic vector cosine search restricted to episodes. Calls `search:recall` with `type='episode'` forced. Same gate, same limits.
|
|
63
|
+
|
|
64
|
+
All four are pure MCP-layer additions: no Convex schema change, no new index, no new action. tsc clean.
|
|
65
|
+
|
|
66
|
+
### Why
|
|
67
|
+
|
|
68
|
+
Episode = the 8-Sins / orchestrator-introspection memory type (severity + context/goal/action/outcome/insight). Until 2.9.0, recalling past episodes required `recall query='...' type='episode'` — discoverable only to callers who already knew the memory-side filter trick. Surfacing dedicated wrappers makes the episode lifecycle (write via `store_episode`, read via `get_episode`, browse via `list_episodes`, recall via the two search ops) consistent with the doctrine and self-documenting in any MCP client's tool list.
|
|
69
|
+
|
|
70
|
+
`hybrid_search` remains entity-agnostic and is NOT mirrored for episodes (cross-cutting RRF tool per audit § 4).
|
|
71
|
+
|
|
72
|
+
### Test fixture catch-up
|
|
73
|
+
|
|
74
|
+
- `READ_ONLY_TOOLS` set in `mcp-server/src/__tests__/chatgpt-tool-annotations.test.ts` extended with the 4 new tool names.
|
|
75
|
+
|
|
76
|
+
### Refs
|
|
77
|
+
|
|
78
|
+
- Mission `k575kc1ryps0n8br95jw3q7d0x88m2v9` (MCP CRUD Baseline Standard, pilot Sigma + agents Sigma + Eta).
|
|
79
|
+
- Pi authorization msg `jn74v2pkfz08agex4nfm2yfvfd88nzdw` — "chain T2 PR-B episode entity en autonomie scope mission".
|
|
80
|
+
- Audit T1 deliverable: `analysis/mcp-crud-baseline-vp-audit-2026-06-14.md` row 7 (episode entity recommendation: add façade wrappers).
|
|
81
|
+
- Architecture: hotfix `7f958d0` — episodes are memories with metadata, not a separate table.
|
|
82
|
+
|
|
3
83
|
## [2.8.0] — 2026-06-14 — Day 101 CRUD baseline PR-A: search_memories_by_keyword + search_memories_by_semantic (mission k575kc1ryps0n8br95jw3q7d0x88m2v9, task k1735qk9kx6agjjyt3e38rdvvh88mk0p)
|
|
4
84
|
|
|
5
85
|
Mission `mcp-crud-baseline-standard` PR-A under T2 `[CRUD-T2] Implémentation gaps VP MCP`. First of 4 sub-PRs aligning the MCP surface with the Day 101 doctrine `j57dhrmkzjerjtssnr0z9ba57n88n7q7` ("5 ops per entity: get / list / search_by_keyword / search_by_semantic / create-or-upsert"). PR-A handles the convention drift on the `memories` entity — the only entity that already had both keyword + semantic search wired, but under non-canonical names.
|
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.
|
|
245
|
+
## Compact payloads and status aliases (v2.10.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.
|
|
105
|
+
version: "2.10.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
|
-
//
|
|
921
|
-
//
|
|
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
|
-
//
|
|
979
|
-
//
|
|
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
|
|
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
|
|
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.", {
|
|
@@ -1240,6 +1242,225 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1240
1242
|
return mcpConvexError(error);
|
|
1241
1243
|
}
|
|
1242
1244
|
});
|
|
1245
|
+
// ── get_episode ────────────────────────────────────────────────────────────
|
|
1246
|
+
// Day 102 v2.9.0 — episode entity 5-op surface (PR-B).
|
|
1247
|
+
// Thin wrapper: episodes are stored as memories with type='episode'
|
|
1248
|
+
// (no separate table — see hotfix 7f958d0). This calls memories:getMemory
|
|
1249
|
+
// and asserts type='episode' so callers get a non-leaky 404 on wrong-type IDs.
|
|
1250
|
+
server.tool("get_episode", "Fetch a single episode by its memory document ID. Episodes are memories with type='episode' carrying context/goal/action/outcome/insight + severity. " +
|
|
1251
|
+
"WHEN: use when you have an episodeId from store_episode or a prior search and need the full record. " +
|
|
1252
|
+
"EXAMPLE: get_episode episodeId='j57dy3049btafda9m2f5d2ggk987ph3f'.", {
|
|
1253
|
+
episodeId: z.string().describe("Episode (memory) document ID"),
|
|
1254
|
+
}, {
|
|
1255
|
+
readOnlyHint: true,
|
|
1256
|
+
openWorldHint: false,
|
|
1257
|
+
destructiveHint: false,
|
|
1258
|
+
title: "Get episode",
|
|
1259
|
+
}, async ({ episodeId }) => {
|
|
1260
|
+
try {
|
|
1261
|
+
const memory = await convex.query("memories:getMemory", {
|
|
1262
|
+
memoryId: episodeId,
|
|
1263
|
+
});
|
|
1264
|
+
const filtered = scopeFilterGet(oauthCtx, memory);
|
|
1265
|
+
if (filtered === null) {
|
|
1266
|
+
return mcpError(`Episode not found: ${episodeId}`);
|
|
1267
|
+
}
|
|
1268
|
+
if (filtered?.type !== "episode") {
|
|
1269
|
+
return mcpError(`Episode not found: ${episodeId}`);
|
|
1270
|
+
}
|
|
1271
|
+
return {
|
|
1272
|
+
content: [{ type: "text", text: JSON.stringify(filtered, null, 2) }],
|
|
1273
|
+
};
|
|
1274
|
+
}
|
|
1275
|
+
catch (error) {
|
|
1276
|
+
return mcpConvexError(error);
|
|
1277
|
+
}
|
|
1278
|
+
});
|
|
1279
|
+
// ── list_episodes ──────────────────────────────────────────────────────────
|
|
1280
|
+
// Day 102 v2.9.0 — episode entity 5-op surface (PR-B).
|
|
1281
|
+
// Thin wrapper on memories:listMemories with type='episode' forced.
|
|
1282
|
+
server.tool("list_episodes", "List episodes (memories with type='episode') ordered newest first. " +
|
|
1283
|
+
"WHEN: use to enumerate episodes by namespace or creator before recall/audit. " +
|
|
1284
|
+
"EXAMPLE: list_episodes namespace='orchestrator/sigma' limit=20.", {
|
|
1285
|
+
namespace: z
|
|
1286
|
+
.string()
|
|
1287
|
+
.optional()
|
|
1288
|
+
.describe("Filter to a specific namespace — omit to list across all"),
|
|
1289
|
+
createdBy: z
|
|
1290
|
+
.string()
|
|
1291
|
+
.optional()
|
|
1292
|
+
.describe("Filter by creator role (e.g. 'sigma', 'pi')"),
|
|
1293
|
+
limit: z
|
|
1294
|
+
.number()
|
|
1295
|
+
.int()
|
|
1296
|
+
.min(1)
|
|
1297
|
+
.max(200)
|
|
1298
|
+
.optional()
|
|
1299
|
+
.describe("Max items to return. Default 20 (envelope-safe). Cap 200."),
|
|
1300
|
+
fields: z
|
|
1301
|
+
.enum(["lite", "full"])
|
|
1302
|
+
.optional()
|
|
1303
|
+
.describe("'lite' returns compact payload (less tokens), 'full' is default."),
|
|
1304
|
+
cursor: z
|
|
1305
|
+
.string()
|
|
1306
|
+
.optional()
|
|
1307
|
+
.describe("S3.3 B8 — opaque pagination cursor from a prior call's `nextCursor`."),
|
|
1308
|
+
}, {
|
|
1309
|
+
readOnlyHint: true,
|
|
1310
|
+
openWorldHint: false,
|
|
1311
|
+
destructiveHint: false,
|
|
1312
|
+
title: "List episodes",
|
|
1313
|
+
}, async ({ namespace, createdBy, limit, fields, cursor }) => {
|
|
1314
|
+
try {
|
|
1315
|
+
const nsDenied = guardRead(namespace);
|
|
1316
|
+
if (nsDenied)
|
|
1317
|
+
return nsDenied;
|
|
1318
|
+
let backendCursor;
|
|
1319
|
+
if (cursor !== undefined && cursor !== "") {
|
|
1320
|
+
try {
|
|
1321
|
+
const decoded = decodeCursor(cursor);
|
|
1322
|
+
if (decoded && "backendCursor" in decoded) {
|
|
1323
|
+
backendCursor = decoded.backendCursor;
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
catch (err) {
|
|
1327
|
+
return mcpError(err?.message ?? "invalid cursor");
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
const effectiveLimit = limit === undefined ? undefined : clampLimit(limit);
|
|
1331
|
+
const queryArgs = {
|
|
1332
|
+
namespace,
|
|
1333
|
+
type: "episode",
|
|
1334
|
+
createdBy,
|
|
1335
|
+
limit: effectiveLimit ?? 20,
|
|
1336
|
+
fields: fields ?? "lite",
|
|
1337
|
+
};
|
|
1338
|
+
if (backendCursor !== undefined) {
|
|
1339
|
+
queryArgs.paginationOpts = {
|
|
1340
|
+
numItems: effectiveLimit ?? 50,
|
|
1341
|
+
cursor: backendCursor,
|
|
1342
|
+
};
|
|
1343
|
+
}
|
|
1344
|
+
const memories = await convex.query("memories:listMemories", queryArgs);
|
|
1345
|
+
const rawList = Array.isArray(memories)
|
|
1346
|
+
? memories
|
|
1347
|
+
: Array.isArray(memories?.page)
|
|
1348
|
+
? memories.page
|
|
1349
|
+
: [];
|
|
1350
|
+
const filteredList = scopeFilterList(oauthCtx, rawList);
|
|
1351
|
+
const filteredEnvelope = Array.isArray(memories)
|
|
1352
|
+
? filteredList
|
|
1353
|
+
: { ...memories, page: filteredList };
|
|
1354
|
+
const text = capListResponseBytes(filteredEnvelope, JSON.stringify(filteredEnvelope, null, 2), "list_episodes");
|
|
1355
|
+
return {
|
|
1356
|
+
content: [{ type: "text", text }],
|
|
1357
|
+
};
|
|
1358
|
+
}
|
|
1359
|
+
catch (error) {
|
|
1360
|
+
return mcpConvexError(error);
|
|
1361
|
+
}
|
|
1362
|
+
});
|
|
1363
|
+
// ── search_episodes_by_keyword ─────────────────────────────────────────────
|
|
1364
|
+
// Day 102 v2.9.0 — episode entity 5-op surface (PR-B).
|
|
1365
|
+
// Thin wrapper on search:textSearch with type='episode' forced.
|
|
1366
|
+
server.tool("search_episodes_by_keyword", "BM25 full-text keyword search restricted to episodes (memories with type='episode'). " +
|
|
1367
|
+
"WHEN: use when search_episodes_by_semantic returns too-broad results and you need an exact phrase or ID inside an episode field. " +
|
|
1368
|
+
"EXAMPLE: search_episodes_by_keyword query='convex deploy schema' namespace='orchestrator/sigma' limit=10.", {
|
|
1369
|
+
query: z.string().describe("Search query text"),
|
|
1370
|
+
namespace: z
|
|
1371
|
+
.string()
|
|
1372
|
+
.optional()
|
|
1373
|
+
.describe("Namespace filter (e.g. 'orchestrator/sigma')"),
|
|
1374
|
+
limit: z
|
|
1375
|
+
.number()
|
|
1376
|
+
.int()
|
|
1377
|
+
.min(1)
|
|
1378
|
+
.max(200)
|
|
1379
|
+
.optional()
|
|
1380
|
+
.describe("Max items to return. Default 20 (envelope-safe). Cap 200."),
|
|
1381
|
+
fields: z
|
|
1382
|
+
.enum(["lite", "full"])
|
|
1383
|
+
.optional()
|
|
1384
|
+
.describe("'lite' returns compact payload (less tokens), 'full' is default."),
|
|
1385
|
+
}, {
|
|
1386
|
+
readOnlyHint: true,
|
|
1387
|
+
openWorldHint: false,
|
|
1388
|
+
destructiveHint: false,
|
|
1389
|
+
title: "Search episodes by keyword (BM25)",
|
|
1390
|
+
}, async ({ query, namespace, limit, fields }) => {
|
|
1391
|
+
try {
|
|
1392
|
+
const nsDenied = guardRead(namespace);
|
|
1393
|
+
if (nsDenied)
|
|
1394
|
+
return nsDenied;
|
|
1395
|
+
const results = await convex.action("search:textSearch", {
|
|
1396
|
+
query,
|
|
1397
|
+
namespace,
|
|
1398
|
+
type: "episode",
|
|
1399
|
+
limit: limit ?? 20,
|
|
1400
|
+
fields: fields ?? "lite",
|
|
1401
|
+
});
|
|
1402
|
+
return {
|
|
1403
|
+
content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
|
|
1404
|
+
};
|
|
1405
|
+
}
|
|
1406
|
+
catch (error) {
|
|
1407
|
+
return mcpConvexError(error);
|
|
1408
|
+
}
|
|
1409
|
+
});
|
|
1410
|
+
// ── search_episodes_by_semantic ────────────────────────────────────────────
|
|
1411
|
+
// Day 102 v2.9.0 — episode entity 5-op surface (PR-B).
|
|
1412
|
+
// Thin wrapper on search:recall with type='episode' forced.
|
|
1413
|
+
server.tool("search_episodes_by_semantic", "Semantic vector search restricted to episodes (memories with type='episode'), ranked by cosine similarity. " +
|
|
1414
|
+
"WHEN: use to recall structured past events by intent — failure modes, lessons, similar contexts. " +
|
|
1415
|
+
"EXAMPLE: search_episodes_by_semantic query='hook false positive blocked publish' namespace='orchestrator/sigma' limit=20.", {
|
|
1416
|
+
query: z
|
|
1417
|
+
.string()
|
|
1418
|
+
.describe("Natural language query to search for relevant episodes"),
|
|
1419
|
+
namespace: z
|
|
1420
|
+
.string()
|
|
1421
|
+
.optional()
|
|
1422
|
+
.describe("Filter to a specific namespace — omit to search all"),
|
|
1423
|
+
limit: z
|
|
1424
|
+
.number()
|
|
1425
|
+
.int()
|
|
1426
|
+
.min(1)
|
|
1427
|
+
.max(200)
|
|
1428
|
+
.optional()
|
|
1429
|
+
.describe("Max items to return. Default 20 (envelope-safe). Cap 200."),
|
|
1430
|
+
fields: z
|
|
1431
|
+
.enum(["lite", "full"])
|
|
1432
|
+
.optional()
|
|
1433
|
+
.describe("'lite' returns compact payload (less tokens), 'full' is default."),
|
|
1434
|
+
}, {
|
|
1435
|
+
readOnlyHint: true,
|
|
1436
|
+
openWorldHint: false,
|
|
1437
|
+
destructiveHint: false,
|
|
1438
|
+
title: "Search episodes by semantic (vector cosine)",
|
|
1439
|
+
}, async ({ query, namespace, limit, fields }) => {
|
|
1440
|
+
try {
|
|
1441
|
+
const nsDenied = guardRead(namespace);
|
|
1442
|
+
if (nsDenied)
|
|
1443
|
+
return nsDenied;
|
|
1444
|
+
const results = await convex.action("search:recall", {
|
|
1445
|
+
query,
|
|
1446
|
+
namespace,
|
|
1447
|
+
type: "episode",
|
|
1448
|
+
limit: limit ?? 20,
|
|
1449
|
+
fields: fields ?? "lite",
|
|
1450
|
+
});
|
|
1451
|
+
return {
|
|
1452
|
+
content: [
|
|
1453
|
+
{
|
|
1454
|
+
type: "text",
|
|
1455
|
+
text: JSON.stringify(results, null, 2),
|
|
1456
|
+
},
|
|
1457
|
+
],
|
|
1458
|
+
};
|
|
1459
|
+
}
|
|
1460
|
+
catch (error) {
|
|
1461
|
+
return mcpConvexError(error);
|
|
1462
|
+
}
|
|
1463
|
+
});
|
|
1243
1464
|
// ── get_profile ─────────────────────────────────────────────────────────────
|
|
1244
1465
|
server.tool("get_profile", "Fetch an orchestrator profile with static identity and dynamic session state fields. " +
|
|
1245
1466
|
"WHEN: use to check peer status, capabilities, or current task before assigning work. " +
|
|
@@ -3507,6 +3728,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3507
3728
|
}
|
|
3508
3729
|
});
|
|
3509
3730
|
// ── search_components ───────────────────────────────────────────────────────
|
|
3731
|
+
// DEPRECATED (Day 102 v2.10.0) — alias of search_components_by_keyword.
|
|
3732
|
+
// Retained for one minor version as a back-compat shim. New callers should
|
|
3733
|
+
// use `search_components_by_keyword`. To be removed in 2.11.0.
|
|
3510
3734
|
server.tool("search_components",
|
|
3511
3735
|
// S3.3 B8 follow-up batch 3 FINAL — DOCTRINE EXCEPTION.
|
|
3512
3736
|
// @cursorPagingException relevance-ranked-not-chronological
|
|
@@ -3514,8 +3738,8 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3514
3738
|
// anchor would skip high-relevance older matches in favor of newer
|
|
3515
3739
|
// low-relevance ones, breaking the search contract. Pagination on
|
|
3516
3740
|
// semantic search should be score-based (offset / topK), not time-based.
|
|
3517
|
-
"
|
|
3518
|
-
"WHEN: use before register_component to check if a similar component already exists in the registry. " +
|
|
3741
|
+
"DEPRECATED ALIAS of search_components_by_keyword — search components by name or team substring with optional type filter. " +
|
|
3742
|
+
"WHEN: use before register_component to check if a similar component already exists in the registry. New callers: use search_components_by_keyword. " +
|
|
3519
3743
|
"EXAMPLE: search_components query='check-tasks' type='skill' limit=10.", {
|
|
3520
3744
|
query: z
|
|
3521
3745
|
.string()
|
|
@@ -3557,6 +3781,52 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3557
3781
|
return mcpConvexError(error);
|
|
3558
3782
|
}
|
|
3559
3783
|
});
|
|
3784
|
+
// ── search_components_by_keyword ───────────────────────────────────────────
|
|
3785
|
+
// Day 102 v2.10.0 — canonical name under MCP CRUD baseline doctrine (PR-C).
|
|
3786
|
+
// Mirrors search_components 1:1 (same Convex query `components:search`).
|
|
3787
|
+
// search_components is retained as a deprecated alias until 2.11.0.
|
|
3788
|
+
server.tool("search_components_by_keyword", "BM25 / substring keyword search over components by name or team with optional type filter. " +
|
|
3789
|
+
"WHEN: use before register_component to check if a similar component already exists in the registry. " +
|
|
3790
|
+
"EXAMPLE: search_components_by_keyword query='check-tasks' type='skill' limit=10.", {
|
|
3791
|
+
query: z
|
|
3792
|
+
.string()
|
|
3793
|
+
.describe("Search term to match against component name or team"),
|
|
3794
|
+
type: componentTypeSchema.optional().describe("Filter by component type"),
|
|
3795
|
+
limit: z
|
|
3796
|
+
.number()
|
|
3797
|
+
.int()
|
|
3798
|
+
.min(1)
|
|
3799
|
+
.max(200)
|
|
3800
|
+
.optional()
|
|
3801
|
+
.describe("Max items to return. Default 20 (envelope-safe). Cap 200."),
|
|
3802
|
+
fields: z
|
|
3803
|
+
.enum(["lite", "full"])
|
|
3804
|
+
.optional()
|
|
3805
|
+
.describe("'lite' returns compact payload (less tokens), 'full' is default."),
|
|
3806
|
+
}, {
|
|
3807
|
+
readOnlyHint: true,
|
|
3808
|
+
openWorldHint: false,
|
|
3809
|
+
destructiveHint: false,
|
|
3810
|
+
title: "Search components by keyword",
|
|
3811
|
+
}, async ({ query, type, limit, fields }) => {
|
|
3812
|
+
try {
|
|
3813
|
+
const results = await convex.query("components:search", {
|
|
3814
|
+
query,
|
|
3815
|
+
type,
|
|
3816
|
+
limit: limit ?? 20,
|
|
3817
|
+
fields: fields ?? "lite",
|
|
3818
|
+
});
|
|
3819
|
+
const filteredResults = scopeFilterList(oauthCtx, Array.isArray(results) ? results : []);
|
|
3820
|
+
return {
|
|
3821
|
+
content: [
|
|
3822
|
+
{ type: "text", text: JSON.stringify(filteredResults, null, 2) },
|
|
3823
|
+
],
|
|
3824
|
+
};
|
|
3825
|
+
}
|
|
3826
|
+
catch (error) {
|
|
3827
|
+
return mcpConvexError(error);
|
|
3828
|
+
}
|
|
3829
|
+
});
|
|
3560
3830
|
// ── create_recurring_task ───────────────────────────────────────────────────
|
|
3561
3831
|
server.tool("create_recurring_task", "Create a recurring task template that auto-generates tasks on a cron schedule. " +
|
|
3562
3832
|
"WHEN: use for daily standups, weekly reviews, or any repeating work item pattern. " +
|
|
@@ -4978,14 +5248,18 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
4978
5248
|
}
|
|
4979
5249
|
});
|
|
4980
5250
|
// ── search_fix_patterns ─────────────────────────────────────────────────────
|
|
5251
|
+
// DEPRECATED (Day 102 v2.10.0) — alias of search_fix_patterns_by_semantic.
|
|
5252
|
+
// Underlying behavior is embedding-similarity ranking (NOT BM25), hence
|
|
5253
|
+
// "_by_semantic" is the canonical suffix per the CRUD baseline doctrine.
|
|
5254
|
+
// To be removed in 2.11.0.
|
|
4981
5255
|
server.tool("search_fix_patterns",
|
|
4982
5256
|
// S3.3 B8 follow-up batch 3 FINAL — DOCTRINE EXCEPTION.
|
|
4983
5257
|
// @cursorPagingException semantic-action-not-chronological
|
|
4984
5258
|
// Rationale: backed by `convex.action("search:searchFixPatterns")` which
|
|
4985
5259
|
// runs an embedding-similarity ranker; cursor paging by `createdBefore`
|
|
4986
5260
|
// would corrupt relevance ordering. Same rationale as search_components.
|
|
4987
|
-
"
|
|
4988
|
-
"WHEN: call BEFORE fixing any bug to check if a matching pattern exists and reuse the validated fix. " +
|
|
5261
|
+
"DEPRECATED ALIAS of search_fix_patterns_by_semantic — semantic search over fix patterns by symptom description, ranked by relevance. " +
|
|
5262
|
+
"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. " +
|
|
4989
5263
|
"EXAMPLE: search_fix_patterns query='message disappears after sending on mobile' limit=5.", {
|
|
4990
5264
|
query: z
|
|
4991
5265
|
.string()
|
|
@@ -5028,6 +5302,53 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
5028
5302
|
return mcpConvexError(error);
|
|
5029
5303
|
}
|
|
5030
5304
|
});
|
|
5305
|
+
// ── search_fix_patterns_by_semantic ────────────────────────────────────────
|
|
5306
|
+
// Day 102 v2.10.0 — canonical name under MCP CRUD baseline doctrine (PR-C).
|
|
5307
|
+
// Mirrors search_fix_patterns 1:1 (same Convex action `search:searchFixPatterns`).
|
|
5308
|
+
// search_fix_patterns is retained as a deprecated alias until 2.11.0.
|
|
5309
|
+
server.tool("search_fix_patterns_by_semantic", "Semantic vector search over fix patterns by symptom description, ranked by relevance. " +
|
|
5310
|
+
"WHEN: call BEFORE fixing any bug to check if a matching pattern exists and reuse the validated fix. " +
|
|
5311
|
+
"EXAMPLE: search_fix_patterns_by_semantic query='message disappears after sending on mobile' limit=5.", {
|
|
5312
|
+
query: z
|
|
5313
|
+
.string()
|
|
5314
|
+
.describe("Describe the problem — e.g. 'message disappears after sending'"),
|
|
5315
|
+
limit: z
|
|
5316
|
+
.number()
|
|
5317
|
+
.int()
|
|
5318
|
+
.min(1)
|
|
5319
|
+
.max(200)
|
|
5320
|
+
.optional()
|
|
5321
|
+
.describe("Max items to return. Default 20 (envelope-safe). Cap 200."),
|
|
5322
|
+
fields: z
|
|
5323
|
+
.enum(["lite", "full"])
|
|
5324
|
+
.optional()
|
|
5325
|
+
.describe("'lite' returns compact payload (less tokens), 'full' is default."),
|
|
5326
|
+
}, {
|
|
5327
|
+
readOnlyHint: true,
|
|
5328
|
+
openWorldHint: false,
|
|
5329
|
+
destructiveHint: false,
|
|
5330
|
+
title: "Search fix patterns by semantic",
|
|
5331
|
+
}, async ({ query, limit, fields }) => {
|
|
5332
|
+
try {
|
|
5333
|
+
const results = await convex.action("search:searchFixPatterns", {
|
|
5334
|
+
query,
|
|
5335
|
+
limit: limit ?? 20,
|
|
5336
|
+
fields: fields ?? "lite",
|
|
5337
|
+
});
|
|
5338
|
+
const filteredResults = scopeFilterList(oauthCtx, Array.isArray(results) ? results : []);
|
|
5339
|
+
return {
|
|
5340
|
+
content: [
|
|
5341
|
+
{
|
|
5342
|
+
type: "text",
|
|
5343
|
+
text: JSON.stringify(filteredResults, null, 2),
|
|
5344
|
+
},
|
|
5345
|
+
],
|
|
5346
|
+
};
|
|
5347
|
+
}
|
|
5348
|
+
catch (error) {
|
|
5349
|
+
return mcpConvexError(error);
|
|
5350
|
+
}
|
|
5351
|
+
});
|
|
5031
5352
|
// ── list_fix_patterns ───────────────────────────────────────────────────────
|
|
5032
5353
|
server.tool("list_fix_patterns", "List fix patterns filtered by source project, newest first with cursor paging support. " +
|
|
5033
5354
|
"WHEN: use to audit the knowledge base or review all patterns for a specific project. " +
|
package/package.json
CHANGED