vantage-peers-mcp 2.4.6 → 2.4.10

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/README.md CHANGED
@@ -9,8 +9,6 @@ MCP server for [VantagePeers](https://vantagepeers.com) — shared memory, messa
9
9
 
10
10
  84 tools across 18 categories: memory, profiles, tasks, missions, mission templates, messages, diary, briefing notes, search (RAG), issues, fix patterns, error monitoring, deployments, business units, components, mandates, recurring tasks, and session. All tools ship with ChatGPT Apps SDK annotations (`readOnlyHint`, `openWorldHint`, `destructiveHint`) for native UX in ChatGPT custom connectors.
11
11
 
12
- **Companion plugin — 32 skills (target v2.7.0):** the `vantage-peers` Claude Code plugin wraps these 84 tools with 32 high-level skills split across three phases — Phase A (11 skills, v2.5.0): `dispatch-message`, `dispatch-task-create`, `dispatch-task-complete`, `dispatch-task-start`, `dispatch-subagent`, `identity-set`, `mission-bootstrap`, `check-messages` v5.1, `check-tasks` v2, `daily-start` v3, `close-day` v2 — Phase B (10 skills, v2.6.0): `memory-write`, `briefing-write`, `mission-template-apply`, `task-structure`, `component-register`, `component-discover`, `issue-triage`, `fix-pattern-cycle`, `episode-log`, `recall-deep` — Phase C (11 skills, v2.7.0): `briefing-recall`, `repo-link`, `recurring-schedule`, `deploy-track`, `profile-lookup`, `peers-discovery`, `mandate-lifecycle`, `bu-manage`, `messages-history`, `memory-edit`, `diary-discover`.
13
-
14
12
  ## Quick start
15
13
 
16
14
  ```bash
@@ -99,6 +99,11 @@ export declare const fieldsSchema: z.ZodEnum<{
99
99
  full: "full";
100
100
  }>;
101
101
  export declare const updatedSinceSchema: z.ZodNumber;
102
+ /**
103
+ * Derive the current VantagePeers day number from the server clock.
104
+ * Returns 1 on or before 2026-03-06 UTC; increments by 1 per UTC day.
105
+ */
106
+ export declare function deriveSessionDay(nowMs?: number): number;
102
107
  export interface ParsedConvexError {
103
108
  code: string;
104
109
  message: string;
package/dist/src/tools.js CHANGED
@@ -274,6 +274,30 @@ const coreTeamSchema = z
274
274
  })
275
275
  .describe("Core team composition — agents, skills, hooks, plugins");
276
276
  // ─────────────────────────────────────────────────────────────────────────────
277
+ // A.7 — project day number derivation
278
+ //
279
+ // VantagePeers uses a sequential "Day N" numbering convention where Day 1 =
280
+ // 2026-03-06 UTC. This is the confirmed epoch: Day 88 = 2026-06-01 (87 days
281
+ // after Day 1), verified from Pi VP task k178tqgzhbhzg1h4vbgn4kwdm987vntn
282
+ // Day 88 brief (2026-06-01).
283
+ //
284
+ // No Convex-side day-counter table exists. The value is purely clock-based:
285
+ // dayNumber = floor((nowUTC_midnight - epoch_midnight) / MS_PER_DAY) + 1
286
+ //
287
+ // Callers who pass explicit sessionDay always override this derivation.
288
+ // ─────────────────────────────────────────────────────────────────────────────
289
+ /** UTC midnight of Day 1 (2026-03-06). All arithmetic is in whole UTC days. */
290
+ const VP_DAY1_EPOCH_UTC_MS = Date.UTC(2026, 2, 6); // month is 0-indexed: 2 = March
291
+ /**
292
+ * Derive the current VantagePeers day number from the server clock.
293
+ * Returns 1 on or before 2026-03-06 UTC; increments by 1 per UTC day.
294
+ */
295
+ export function deriveSessionDay(nowMs = Date.now()) {
296
+ const MS_PER_DAY = 86_400_000;
297
+ const deltaDays = Math.floor((nowMs - VP_DAY1_EPOCH_UTC_MS) / MS_PER_DAY);
298
+ return Math.max(1, deltaDays + 1);
299
+ }
300
+ // ─────────────────────────────────────────────────────────────────────────────
277
301
  // Helper: normalize string|array inputs to array
278
302
  // ─────────────────────────────────────────────────────────────────────────────
279
303
  function toArray(val) {
@@ -589,16 +613,19 @@ export function registerTools(server, convex, oauthCtx) {
589
613
  .number()
590
614
  .int()
591
615
  .min(1)
592
- .max(50)
616
+ .max(200)
617
+ .optional()
618
+ .describe("Max items to return. Default 20 (envelope-safe). Cap 200."),
619
+ fields: z
620
+ .enum(["lite", "full"])
593
621
  .optional()
594
- .default(5)
595
- .describe("Maximum number of results to return (default 5)"),
622
+ .describe("'lite' returns compact payload (less tokens), 'full' is default. v2.4.9+."),
596
623
  }, {
597
624
  readOnlyHint: true,
598
625
  openWorldHint: false,
599
626
  destructiveHint: false,
600
627
  title: "Recall memories",
601
- }, async ({ query, namespace, type, limit }) => {
628
+ }, async ({ query, namespace, type, limit, fields }) => {
602
629
  try {
603
630
  const nsDenied = guardRead(namespace);
604
631
  if (nsDenied)
@@ -607,7 +634,8 @@ export function registerTools(server, convex, oauthCtx) {
607
634
  query,
608
635
  namespace,
609
636
  type,
610
- limit: limit ?? 5,
637
+ limit: limit ?? 20,
638
+ fields: fields ?? "lite",
611
639
  });
612
640
  return {
613
641
  content: [
@@ -634,16 +662,19 @@ export function registerTools(server, convex, oauthCtx) {
634
662
  .number()
635
663
  .int()
636
664
  .min(1)
637
- .max(50)
665
+ .max(200)
638
666
  .optional()
639
- .default(10)
640
- .describe("Max results"),
667
+ .describe("Max items to return. Default 20 (envelope-safe). Cap 200."),
668
+ fields: z
669
+ .enum(["lite", "full"])
670
+ .optional()
671
+ .describe("'lite' returns compact payload (less tokens), 'full' is default. v2.4.9+."),
641
672
  }, {
642
673
  readOnlyHint: true,
643
674
  openWorldHint: false,
644
675
  destructiveHint: false,
645
676
  title: "Search memories (text)",
646
- }, async ({ query, namespace, type, limit }) => {
677
+ }, async ({ query, namespace, type, limit, fields }) => {
647
678
  try {
648
679
  const nsDenied = guardRead(namespace);
649
680
  if (nsDenied)
@@ -652,7 +683,8 @@ export function registerTools(server, convex, oauthCtx) {
652
683
  query,
653
684
  namespace,
654
685
  type,
655
- limit: limit ?? 10,
686
+ limit: limit ?? 20,
687
+ fields: fields ?? "lite",
656
688
  });
657
689
  return {
658
690
  content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
@@ -671,10 +703,13 @@ export function registerTools(server, convex, oauthCtx) {
671
703
  .number()
672
704
  .int()
673
705
  .min(1)
674
- .max(50)
706
+ .max(200)
675
707
  .optional()
676
- .default(10)
677
- .describe("Max results"),
708
+ .describe("Max items to return. Default 20 (envelope-safe). Cap 200."),
709
+ fields: z
710
+ .enum(["lite", "full"])
711
+ .optional()
712
+ .describe("'lite' returns compact payload (less tokens), 'full' is default. v2.4.9+."),
678
713
  vectorWeight: z
679
714
  .number()
680
715
  .min(0)
@@ -692,7 +727,7 @@ export function registerTools(server, convex, oauthCtx) {
692
727
  openWorldHint: false,
693
728
  destructiveHint: false,
694
729
  title: "Search memories (hybrid)",
695
- }, async ({ query, namespace, type, limit, vectorWeight, textWeight }) => {
730
+ }, async ({ query, namespace, type, limit, fields, vectorWeight, textWeight }) => {
696
731
  try {
697
732
  const nsDenied = guardRead(namespace);
698
733
  if (nsDenied)
@@ -701,7 +736,8 @@ export function registerTools(server, convex, oauthCtx) {
701
736
  query,
702
737
  namespace,
703
738
  type,
704
- limit: limit ?? 10,
739
+ limit: limit ?? 20,
740
+ fields: fields ?? "lite",
705
741
  vectorWeight,
706
742
  textWeight,
707
743
  });
@@ -874,14 +910,17 @@ export function registerTools(server, convex, oauthCtx) {
874
910
  .min(1)
875
911
  .max(200)
876
912
  .optional()
877
- .default(20)
878
- .describe("Maximum number of memories to return (default 20)"),
913
+ .describe("Max items to return. Default 20 (envelope-safe). Cap 200."),
914
+ fields: z
915
+ .enum(["lite", "full"])
916
+ .optional()
917
+ .describe("'lite' returns compact payload (less tokens), 'full' is default. v2.4.9+."),
879
918
  }, {
880
919
  readOnlyHint: true,
881
920
  openWorldHint: false,
882
921
  destructiveHint: false,
883
922
  title: "List memories",
884
- }, async ({ namespace, type, createdBy, limit }) => {
923
+ }, async ({ namespace, type, createdBy, limit, fields }) => {
885
924
  try {
886
925
  const nsDenied = guardRead(namespace);
887
926
  if (nsDenied)
@@ -891,6 +930,7 @@ export function registerTools(server, convex, oauthCtx) {
891
930
  type,
892
931
  createdBy,
893
932
  limit: limit ?? 20,
933
+ fields: fields ?? "lite",
894
934
  });
895
935
  const rawList = Array.isArray(memories)
896
936
  ? memories
@@ -933,7 +973,8 @@ export function registerTools(server, convex, oauthCtx) {
933
973
  .number()
934
974
  .int()
935
975
  .optional()
936
- .describe("Day number (e.g. 19 for Day 19)"),
976
+ .describe("Day number (e.g. 88 for Day 88). If omitted, auto-derived from project epoch " +
977
+ "(Day 1 = 2026-03-06 UTC). Pass explicitly to override."),
937
978
  tenantId: z
938
979
  .string()
939
980
  .optional()
@@ -950,12 +991,18 @@ export function registerTools(server, convex, oauthCtx) {
950
991
  const fromDenied = guardFrom(from);
951
992
  if (fromDenied)
952
993
  return fromDenied;
994
+ // A.7: auto-derive sessionDay from project epoch when caller omits it.
995
+ // Day 1 = 2026-03-06 UTC (Day 88 confirmed as 2026-06-01).
996
+ // Explicit args.sessionDay always wins (backward-compat).
997
+ const derivedSessionDay = sessionDay !== undefined
998
+ ? sessionDay
999
+ : deriveSessionDay();
953
1000
  const messageId = await convex.mutation("messages:sendMessage", {
954
1001
  from,
955
1002
  fromInstanceId,
956
1003
  channel,
957
1004
  content,
958
- sessionDay,
1005
+ sessionDay: derivedSessionDay,
959
1006
  tenantId,
960
1007
  });
961
1008
  return {
@@ -1165,17 +1212,32 @@ export function registerTools(server, convex, oauthCtx) {
1165
1212
  });
1166
1213
  // ── list_peers ──────────────────────────────────────────────────────────────
1167
1214
  server.tool("list_peers", "List all orchestrator profiles with their current status and summary. " +
1168
- "Replaces claude-peers list_peers.", {}, {
1215
+ "Replaces claude-peers list_peers.", {
1216
+ limit: z
1217
+ .number()
1218
+ .int()
1219
+ .min(1)
1220
+ .max(200)
1221
+ .optional()
1222
+ .describe("Max items to return. Default 20 (envelope-safe). Cap 200."),
1223
+ fields: z
1224
+ .enum(["lite", "full"])
1225
+ .optional()
1226
+ .describe("'lite' returns compact payload (less tokens), 'full' is default. v2.4.9+."),
1227
+ }, {
1169
1228
  readOnlyHint: true,
1170
1229
  openWorldHint: false,
1171
1230
  destructiveHint: false,
1172
1231
  title: "List peers",
1173
- }, async () => {
1232
+ }, async ({ limit, fields }) => {
1174
1233
  try {
1175
1234
  const _scopeDenied = guardMasterOnly("list_peers");
1176
1235
  if (_scopeDenied)
1177
1236
  return _scopeDenied;
1178
- const profiles = await convex.query("profiles:listProfiles", {});
1237
+ const profiles = await convex.query("profiles:listProfiles", {
1238
+ limit: limit ?? 20,
1239
+ fields: fields ?? "lite",
1240
+ });
1179
1241
  const peers = profiles.map((p) => ({
1180
1242
  id: p.orchestratorId,
1181
1243
  instanceId: p.instanceId ?? p.orchestratorId,
@@ -1211,16 +1273,19 @@ export function registerTools(server, convex, oauthCtx) {
1211
1273
  .number()
1212
1274
  .int()
1213
1275
  .min(1)
1214
- .max(500)
1276
+ .max(200)
1277
+ .optional()
1278
+ .describe("Max items to return. Default 20 (envelope-safe). Cap 200."),
1279
+ fields: z
1280
+ .enum(["lite", "full"])
1215
1281
  .optional()
1216
- .default(100)
1217
- .describe("Max messages to return (default 100)"),
1282
+ .describe("'lite' returns compact payload (less tokens), 'full' is default. v2.4.9+."),
1218
1283
  }, {
1219
1284
  readOnlyHint: true,
1220
1285
  openWorldHint: false,
1221
1286
  destructiveHint: false,
1222
1287
  title: "List messages",
1223
- }, async ({ sessionDay, from, limit }) => {
1288
+ }, async ({ sessionDay, from, limit, fields }) => {
1224
1289
  try {
1225
1290
  const _scopeDenied = guardMasterOnly("list_messages");
1226
1291
  if (_scopeDenied)
@@ -1228,7 +1293,8 @@ export function registerTools(server, convex, oauthCtx) {
1228
1293
  const messages = await convex.query("messages:listMessages", {
1229
1294
  sessionDay,
1230
1295
  from,
1231
- limit: limit ?? 100,
1296
+ limit: limit ?? 20,
1297
+ fields: fields ?? "lite",
1232
1298
  });
1233
1299
  const baseText = capListResponseBytes(messages, JSON.stringify(messages, null, 2), "list_messages");
1234
1300
  const text = appendMarkerIfEnabled(baseText, () => ({
@@ -1256,18 +1322,31 @@ export function registerTools(server, convex, oauthCtx) {
1256
1322
  messageId: z
1257
1323
  .string()
1258
1324
  .describe("Convex document ID of the broadcast message"),
1325
+ limit: z
1326
+ .number()
1327
+ .int()
1328
+ .min(1)
1329
+ .max(200)
1330
+ .optional()
1331
+ .describe("Max items to return. Default 20 (envelope-safe). Cap 200."),
1332
+ fields: z
1333
+ .enum(["lite", "full"])
1334
+ .optional()
1335
+ .describe("'lite' returns compact payload (less tokens), 'full' is default. v2.4.9+."),
1259
1336
  }, {
1260
1337
  readOnlyHint: true,
1261
1338
  openWorldHint: false,
1262
1339
  destructiveHint: false,
1263
1340
  title: "List broadcast status",
1264
- }, async ({ messageId }) => {
1341
+ }, async ({ messageId, limit, fields }) => {
1265
1342
  try {
1266
1343
  const _scopeDenied = guardMasterOnly("list_broadcast_status");
1267
1344
  if (_scopeDenied)
1268
1345
  return _scopeDenied;
1269
1346
  const status = await convex.query("messages:listBroadcastStatus", {
1270
1347
  messageId,
1348
+ limit: limit ?? 20,
1349
+ fields: fields ?? "lite",
1271
1350
  });
1272
1351
  return {
1273
1352
  content: [
@@ -1375,10 +1454,10 @@ export function registerTools(server, convex, oauthCtx) {
1375
1454
  .min(1)
1376
1455
  .max(200)
1377
1456
  .optional()
1378
- .describe("Maximum number of tasks to return. Default 50 with fields=lite, auto-clamped to 30 when fields=full and no explicit limit (overflow protection)."),
1457
+ .describe("Max items to return. Default 20 (envelope-safe). Cap 200."),
1379
1458
  fields: fieldsSchema
1380
1459
  .optional()
1381
- .describe('Field projection ("lite"|"full")'),
1460
+ .describe('Field projection ("lite"|"full"). Default "lite" (v2.4.9+).'),
1382
1461
  createdBy: assigneeSchema
1383
1462
  .optional()
1384
1463
  .describe("Filter by task creator (e.g. 'pi' to find Pi-dispatched tasks)"),
@@ -1405,8 +1484,8 @@ export function registerTools(server, convex, oauthCtx) {
1405
1484
  assignedToInstance,
1406
1485
  status,
1407
1486
  project,
1408
- limit,
1409
- fields,
1487
+ limit: limit ?? 20,
1488
+ fields: fields ?? "lite",
1410
1489
  createdBy,
1411
1490
  updatedSince,
1412
1491
  });
@@ -1768,7 +1847,7 @@ export function registerTools(server, convex, oauthCtx) {
1768
1847
  .min(1)
1769
1848
  .max(200)
1770
1849
  .optional()
1771
- .describe("Maximum number of tasks to return. Default 50 with fields=lite, auto-clamped to 30 when fields=full and no explicit limit."),
1850
+ .describe("Max items to return. Default 20 (envelope-safe). Cap 200."),
1772
1851
  fields: fieldsSchema
1773
1852
  .optional()
1774
1853
  .describe('Field projection ("lite"|"full")'),
@@ -1787,8 +1866,8 @@ export function registerTools(server, convex, oauthCtx) {
1787
1866
  const tasks = await convex.query("tasks:listByMission", {
1788
1867
  missionId: missionId,
1789
1868
  status,
1790
- limit,
1791
- fields,
1869
+ limit: limit ?? 20,
1870
+ fields: fields ?? "lite",
1792
1871
  createdBy,
1793
1872
  updatedSince,
1794
1873
  });
@@ -1879,10 +1958,10 @@ export function registerTools(server, convex, oauthCtx) {
1879
1958
  .min(1)
1880
1959
  .max(200)
1881
1960
  .optional()
1882
- .describe("Maximum number of missions to return. Default 50 with fields=lite, auto-clamped to 30 when fields=full and no explicit limit."),
1961
+ .describe("Max items to return. Default 20 (envelope-safe). Cap 200."),
1883
1962
  fields: fieldsSchema
1884
1963
  .optional()
1885
- .describe('Field projection ("lite"|"full")'),
1964
+ .describe('Field projection ("lite"|"full"). Default "lite" (v2.4.9+).'),
1886
1965
  updatedSince: updatedSinceSchema.optional(),
1887
1966
  }, {
1888
1967
  readOnlyHint: true,
@@ -1902,8 +1981,8 @@ export function registerTools(server, convex, oauthCtx) {
1902
1981
  project,
1903
1982
  pilot,
1904
1983
  status,
1905
- limit,
1906
- fields,
1984
+ limit: limit ?? 20,
1985
+ fields: fields ?? "lite",
1907
1986
  updatedSince,
1908
1987
  });
1909
1988
  const baseText = capListResponseBytes(missions, JSON.stringify(missions, null, 2), "list_missions");
@@ -2062,12 +2141,19 @@ export function registerTools(server, convex, oauthCtx) {
2062
2141
  const fromDenied = guardFrom(orchestrator);
2063
2142
  if (fromDenied)
2064
2143
  return fromDenied;
2144
+ // v2.4.8: derive createdBy from auth context (oauthCtx.userId).
2145
+ // This is the anti-spoof authored-by — distinct from orchestrator
2146
+ // (writer-intent label, client-supplied). On the no-auth path
2147
+ // (master-scope bearer / local dev), oauthCtx is undefined and
2148
+ // createdBy gracefully degrades to undefined (transition period).
2149
+ const createdBy = oauthCtx?.userId;
2065
2150
  const diaryId = await convex.mutation("diary:write", {
2066
2151
  date,
2067
2152
  orchestrator,
2068
2153
  content,
2069
2154
  highlights: toArray(highlights),
2070
2155
  blockers: toArray(blockers),
2156
+ createdBy,
2071
2157
  });
2072
2158
  return {
2073
2159
  content: [
@@ -2139,33 +2225,43 @@ export function registerTools(server, convex, oauthCtx) {
2139
2225
  .describe("Filter to a specific orchestrator — omit for all"),
2140
2226
  createdBy: assigneeSchema
2141
2227
  .optional()
2142
- .describe("Filter by creator/orchestrator role alias of `orchestrator` for cross-tool consistency (mirrors list_tasks pattern). If both are passed, `createdBy` wins."),
2228
+ .describe("Filter by auth-derived author (v2.4.8+, anti-spoof). Distinct from `orchestrator` which is the writer-intent label. Pre-v2.4.8 entries are backfilled with orchestrator as best-guess."),
2143
2229
  limit: z
2144
2230
  .number()
2145
2231
  .int()
2146
2232
  .min(1)
2147
- .max(100)
2233
+ .max(200)
2148
2234
  .optional()
2149
- .default(20)
2150
- .describe("Maximum entries to return (default 20)"),
2235
+ .describe("Max items to return. Default 20 (envelope-safe). Cap 200."),
2236
+ fields: z
2237
+ .enum(["lite", "full"])
2238
+ .optional()
2239
+ .describe("'lite' returns compact payload (less tokens), 'full' is default. v2.4.9+."),
2151
2240
  }, {
2152
2241
  readOnlyHint: true,
2153
2242
  openWorldHint: false,
2154
2243
  destructiveHint: false,
2155
2244
  title: "List diary entries",
2156
- }, async ({ orchestrator, createdBy, limit }) => {
2245
+ }, async ({ orchestrator, createdBy, limit, fields }) => {
2157
2246
  try {
2158
- // createdBy is an alias of orchestrator (diary's author field). If both set, createdBy wins.
2159
- const effectiveOrchestrator = createdBy ?? orchestrator;
2160
- // Non-master: must scope to own orchestrator id.
2247
+ // v2.4.8: orchestrator (writer-intent) and createdBy (auth-derived
2248
+ // author) are separate filters — NOT aliases. Forward both independently.
2249
+ // Non-master: REQUIRE at least one explicit self-scope undefined passes
2250
+ // through are forbidden. Mirrors v2.4.7 effectiveOrchestrator shortcircuit:
2251
+ // undefined !== myId → Forbidden. No silent fleet-read for non-master callers.
2161
2252
  if (oauthCtx && !isMasterScope(oauthCtx)) {
2162
- if (effectiveOrchestrator !== oauthCtx.userId) {
2163
- return mcpError(`Forbidden: list_diaries requires orchestrator='${oauthCtx.userId}' for non-master scope (current: ${oauthCtx.scopeProfile}).`);
2253
+ const myId = oauthCtx.userId;
2254
+ const orchestratorScoped = orchestrator === myId;
2255
+ const createdByScoped = createdBy === myId;
2256
+ if (!orchestratorScoped && !createdByScoped) {
2257
+ return mcpError(`Forbidden: list_diaries requires orchestrator='${myId}' OR createdBy='${myId}' for non-master scope (current scope: ${oauthCtx.scopeProfile}).`);
2164
2258
  }
2165
2259
  }
2166
2260
  const entries = await convex.query("diary:list", {
2167
- orchestrator: effectiveOrchestrator,
2261
+ orchestrator,
2262
+ createdBy,
2168
2263
  limit: limit ?? 20,
2264
+ fields: fields ?? "lite",
2169
2265
  });
2170
2266
  return {
2171
2267
  content: [
@@ -2302,12 +2398,12 @@ export function registerTools(server, convex, oauthCtx) {
2302
2398
  .number()
2303
2399
  .int()
2304
2400
  .min(1)
2305
- .max(100)
2401
+ .max(200)
2306
2402
  .optional()
2307
- .describe("Maximum notes to return. Default 20 with fields=lite, auto-clamped to 15 when fields=full and no explicit limit."),
2403
+ .describe("Max items to return. Default 20 (envelope-safe). Cap 200."),
2308
2404
  fields: fieldsSchema
2309
2405
  .optional()
2310
- .describe('Field projection ("lite"|"full")'),
2406
+ .describe('Field projection ("lite"|"full"). Default "lite" (v2.4.9+).'),
2311
2407
  updatedSince: updatedSinceSchema.optional(),
2312
2408
  }, {
2313
2409
  readOnlyHint: true,
@@ -2321,8 +2417,8 @@ export function registerTools(server, convex, oauthCtx) {
2321
2417
  return _scopeDenied;
2322
2418
  const notes = await convex.query("briefingNotes:list", {
2323
2419
  topic,
2324
- limit,
2325
- fields,
2420
+ limit: limit ?? 20,
2421
+ fields: fields ?? "lite",
2326
2422
  updatedSince,
2327
2423
  });
2328
2424
  const baseText = capListResponseBytes(notes, JSON.stringify(notes, null, 2), "list_briefing_notes");
@@ -2421,16 +2517,19 @@ export function registerTools(server, convex, oauthCtx) {
2421
2517
  .number()
2422
2518
  .int()
2423
2519
  .min(1)
2424
- .max(500)
2520
+ .max(200)
2521
+ .optional()
2522
+ .describe("Max items to return. Default 20 (envelope-safe). Cap 200."),
2523
+ fields: z
2524
+ .enum(["lite", "full"])
2425
2525
  .optional()
2426
- .default(100)
2427
- .describe("Maximum components to return (default 100)"),
2526
+ .describe("'lite' returns compact payload (less tokens), 'full' is default. v2.4.9+."),
2428
2527
  }, {
2429
2528
  readOnlyHint: true,
2430
2529
  openWorldHint: false,
2431
2530
  destructiveHint: false,
2432
2531
  title: "List components",
2433
- }, async ({ type, team, limit }) => {
2532
+ }, async ({ type, team, limit, fields }) => {
2434
2533
  try {
2435
2534
  const _scopeDenied = guardMasterOnly("list_components");
2436
2535
  if (_scopeDenied)
@@ -2438,7 +2537,8 @@ export function registerTools(server, convex, oauthCtx) {
2438
2537
  const components = await convex.query("components:list", {
2439
2538
  type,
2440
2539
  team,
2441
- limit: limit ?? 100,
2540
+ limit: limit ?? 20,
2541
+ fields: fields ?? "lite",
2442
2542
  });
2443
2543
  return {
2444
2544
  content: [
@@ -2556,13 +2656,23 @@ export function registerTools(server, convex, oauthCtx) {
2556
2656
  .string()
2557
2657
  .describe("Search term to match against component name or team"),
2558
2658
  type: componentTypeSchema.optional().describe("Filter by component type"),
2559
- limit: z.number().int().optional().describe("Max results (default 50)"),
2659
+ limit: z
2660
+ .number()
2661
+ .int()
2662
+ .min(1)
2663
+ .max(200)
2664
+ .optional()
2665
+ .describe("Max items to return. Default 20 (envelope-safe). Cap 200."),
2666
+ fields: z
2667
+ .enum(["lite", "full"])
2668
+ .optional()
2669
+ .describe("'lite' returns compact payload (less tokens), 'full' is default. v2.4.9+."),
2560
2670
  }, {
2561
2671
  readOnlyHint: true,
2562
2672
  openWorldHint: false,
2563
2673
  destructiveHint: false,
2564
2674
  title: "Search components",
2565
- }, async ({ query, type, limit }) => {
2675
+ }, async ({ query, type, limit, fields }) => {
2566
2676
  try {
2567
2677
  const _scopeDenied = guardMasterOnly("search_components");
2568
2678
  if (_scopeDenied)
@@ -2570,7 +2680,8 @@ export function registerTools(server, convex, oauthCtx) {
2570
2680
  const results = await convex.query("components:search", {
2571
2681
  query,
2572
2682
  type,
2573
- limit,
2683
+ limit: limit ?? 20,
2684
+ fields: fields ?? "lite",
2574
2685
  });
2575
2686
  return {
2576
2687
  content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
@@ -2648,14 +2759,17 @@ export function registerTools(server, convex, oauthCtx) {
2648
2759
  .min(1)
2649
2760
  .max(200)
2650
2761
  .optional()
2651
- .default(50)
2652
- .describe("Max results"),
2762
+ .describe("Max items to return. Default 20 (envelope-safe). Cap 200."),
2763
+ fields: z
2764
+ .enum(["lite", "full"])
2765
+ .optional()
2766
+ .describe("'lite' returns compact payload (less tokens), 'full' is default. v2.4.9+."),
2653
2767
  }, {
2654
2768
  readOnlyHint: true,
2655
2769
  openWorldHint: false,
2656
2770
  destructiveHint: false,
2657
2771
  title: "List recurring tasks",
2658
- }, async ({ assignedTo, active, limit }) => {
2772
+ }, async ({ assignedTo, active, limit, fields }) => {
2659
2773
  try {
2660
2774
  const _scopeDenied = guardMasterOnly("list_recurring_tasks");
2661
2775
  if (_scopeDenied)
@@ -2663,7 +2777,8 @@ export function registerTools(server, convex, oauthCtx) {
2663
2777
  const tasks = await convex.query("recurringTasks:list", {
2664
2778
  assignedTo,
2665
2779
  active,
2666
- limit: limit ?? 50,
2780
+ limit: limit ?? 20,
2781
+ fields: fields ?? "lite",
2667
2782
  });
2668
2783
  return {
2669
2784
  content: [{ type: "text", text: capListResponseBytes(tasks, JSON.stringify(tasks, null, 2), "list_recurring_tasks") }],
@@ -2994,14 +3109,17 @@ export function registerTools(server, convex, oauthCtx) {
2994
3109
  .min(1)
2995
3110
  .max(200)
2996
3111
  .optional()
2997
- .default(50)
2998
- .describe("Maximum mandates to return (default 50)"),
3112
+ .describe("Max items to return. Default 20 (envelope-safe). Cap 200."),
3113
+ fields: z
3114
+ .enum(["lite", "full"])
3115
+ .optional()
3116
+ .describe("'lite' returns compact payload (less tokens), 'full' is default. v2.4.9+."),
2999
3117
  }, {
3000
3118
  readOnlyHint: true,
3001
3119
  openWorldHint: false,
3002
3120
  destructiveHint: false,
3003
3121
  title: "List mandates",
3004
- }, async ({ requestedBy, fulfilledBy, status, limit }) => {
3122
+ }, async ({ requestedBy, fulfilledBy, status, limit, fields }) => {
3005
3123
  try {
3006
3124
  const _scopeDenied = guardMasterOnly("list_mandates");
3007
3125
  if (_scopeDenied)
@@ -3010,7 +3128,8 @@ export function registerTools(server, convex, oauthCtx) {
3010
3128
  requestedBy,
3011
3129
  fulfilledBy,
3012
3130
  status,
3013
- limit: limit ?? 50,
3131
+ limit: limit ?? 20,
3132
+ fields: fields ?? "lite",
3014
3133
  });
3015
3134
  return {
3016
3135
  content: [
@@ -3205,14 +3324,17 @@ export function registerTools(server, convex, oauthCtx) {
3205
3324
  .min(1)
3206
3325
  .max(200)
3207
3326
  .optional()
3208
- .default(50)
3209
- .describe("Maximum BUs to return (default 50)"),
3327
+ .describe("Max items to return. Default 20 (envelope-safe). Cap 200."),
3328
+ fields: z
3329
+ .enum(["lite", "full"])
3330
+ .optional()
3331
+ .describe("'lite' returns compact payload (less tokens), 'full' is default. v2.4.9+."),
3210
3332
  }, {
3211
3333
  readOnlyHint: true,
3212
3334
  openWorldHint: false,
3213
3335
  destructiveHint: false,
3214
3336
  title: "List BUs",
3215
- }, async ({ orchestratorId, status, limit }) => {
3337
+ }, async ({ orchestratorId, status, limit, fields }) => {
3216
3338
  try {
3217
3339
  const _scopeDenied = guardMasterOnly("list_bus");
3218
3340
  if (_scopeDenied)
@@ -3220,7 +3342,8 @@ export function registerTools(server, convex, oauthCtx) {
3220
3342
  const bus = await convex.query("businessUnits:list", {
3221
3343
  orchestratorId,
3222
3344
  status,
3223
- limit: limit ?? 50,
3345
+ limit: limit ?? 20,
3346
+ fields: fields ?? "lite",
3224
3347
  });
3225
3348
  return {
3226
3349
  content: [
@@ -3306,17 +3429,32 @@ export function registerTools(server, convex, oauthCtx) {
3306
3429
  }
3307
3430
  });
3308
3431
  // ── list_repo_mappings ──────────────────────────────────────────────────────
3309
- server.tool("list_repo_mappings", "List all GitHub repo → orchestrator mappings. Shows which repos are monitored and which orchestrator handles each.", {}, {
3432
+ server.tool("list_repo_mappings", "List all GitHub repo → orchestrator mappings. Shows which repos are monitored and which orchestrator handles each.", {
3433
+ limit: z
3434
+ .number()
3435
+ .int()
3436
+ .min(1)
3437
+ .max(200)
3438
+ .optional()
3439
+ .describe("Max items to return. Default 20 (envelope-safe). Cap 200."),
3440
+ fields: z
3441
+ .enum(["lite", "full"])
3442
+ .optional()
3443
+ .describe("'lite' returns compact payload (less tokens), 'full' is default. v2.4.9+."),
3444
+ }, {
3310
3445
  readOnlyHint: true,
3311
3446
  openWorldHint: false,
3312
3447
  destructiveHint: false,
3313
3448
  title: "List repo mappings",
3314
- }, async () => {
3449
+ }, async ({ limit, fields }) => {
3315
3450
  try {
3316
3451
  const _scopeDenied = guardMasterOnly("list_repo_mappings");
3317
3452
  if (_scopeDenied)
3318
3453
  return _scopeDenied;
3319
- const mappings = await convex.query("githubRepoMapping:list", {});
3454
+ const mappings = await convex.query("githubRepoMapping:list", {
3455
+ limit: limit ?? 20,
3456
+ fields: fields ?? "lite",
3457
+ });
3320
3458
  return {
3321
3459
  content: [
3322
3460
  {
@@ -3378,14 +3516,17 @@ export function registerTools(server, convex, oauthCtx) {
3378
3516
  .min(1)
3379
3517
  .max(200)
3380
3518
  .optional()
3381
- .default(50)
3382
- .describe("Maximum number of issues to return (default 50)"),
3519
+ .describe("Max items to return. Default 20 (envelope-safe). Cap 200."),
3520
+ fields: z
3521
+ .enum(["lite", "full"])
3522
+ .optional()
3523
+ .describe("'lite' returns compact payload (less tokens), 'full' is default. v2.4.9+."),
3383
3524
  }, {
3384
3525
  readOnlyHint: true,
3385
3526
  openWorldHint: false,
3386
3527
  destructiveHint: false,
3387
3528
  title: "List issues",
3388
- }, async ({ project, status, assignedTo, limit }) => {
3529
+ }, async ({ project, status, assignedTo, limit, fields }) => {
3389
3530
  try {
3390
3531
  const _scopeDenied = guardMasterOnly("list_issues");
3391
3532
  if (_scopeDenied)
@@ -3395,26 +3536,30 @@ export function registerTools(server, convex, oauthCtx) {
3395
3536
  results = await convex.query("issues:listByOrchestrator", {
3396
3537
  assignedOrchestrator: assignedTo,
3397
3538
  status: status,
3398
- limit: limit ?? 50,
3539
+ limit: limit ?? 20,
3540
+ fields: fields ?? "lite",
3399
3541
  });
3400
3542
  }
3401
3543
  else if (project) {
3402
3544
  results = await convex.query("issues:listByProject", {
3403
3545
  project,
3404
3546
  status: status,
3405
- limit: limit ?? 50,
3547
+ limit: limit ?? 20,
3548
+ fields: fields ?? "lite",
3406
3549
  });
3407
3550
  }
3408
3551
  else if (status) {
3409
3552
  results = await convex.query("issues:listByStatus", {
3410
3553
  status: status,
3411
- limit: limit ?? 50,
3554
+ limit: limit ?? 20,
3555
+ fields: fields ?? "lite",
3412
3556
  });
3413
3557
  }
3414
3558
  else {
3415
3559
  results = await convex.query("issues:listByProject", {
3416
3560
  project: "",
3417
- limit: limit ?? 50,
3561
+ limit: limit ?? 20,
3562
+ fields: fields ?? "lite",
3418
3563
  });
3419
3564
  }
3420
3565
  return {
@@ -3730,21 +3875,28 @@ export function registerTools(server, convex, oauthCtx) {
3730
3875
  limit: z
3731
3876
  .number()
3732
3877
  .int()
3878
+ .min(1)
3879
+ .max(200)
3733
3880
  .optional()
3734
- .describe("Max results to return (default 10)"),
3881
+ .describe("Max items to return. Default 20 (envelope-safe). Cap 200."),
3882
+ fields: z
3883
+ .enum(["lite", "full"])
3884
+ .optional()
3885
+ .describe("'lite' returns compact payload (less tokens), 'full' is default. v2.4.9+."),
3735
3886
  }, {
3736
3887
  readOnlyHint: true,
3737
3888
  openWorldHint: false,
3738
3889
  destructiveHint: false,
3739
3890
  title: "Search fix patterns",
3740
- }, async ({ query, limit }) => {
3891
+ }, async ({ query, limit, fields }) => {
3741
3892
  try {
3742
3893
  const _scopeDenied = guardMasterOnly("search_fix_patterns");
3743
3894
  if (_scopeDenied)
3744
3895
  return _scopeDenied;
3745
3896
  const results = await convex.action("search:searchFixPatterns", {
3746
3897
  query,
3747
- limit,
3898
+ limit: limit ?? 20,
3899
+ fields: fields ?? "lite",
3748
3900
  });
3749
3901
  return {
3750
3902
  content: [
@@ -3765,13 +3917,23 @@ export function registerTools(server, convex, oauthCtx) {
3765
3917
  .string()
3766
3918
  .optional()
3767
3919
  .describe("Filter by source project — omit for all"),
3768
- limit: z.number().int().optional().describe("Max results (default 50)"),
3920
+ limit: z
3921
+ .number()
3922
+ .int()
3923
+ .min(1)
3924
+ .max(200)
3925
+ .optional()
3926
+ .describe("Max items to return. Default 20 (envelope-safe). Cap 200."),
3927
+ fields: z
3928
+ .enum(["lite", "full"])
3929
+ .optional()
3930
+ .describe("'lite' returns compact payload (less tokens), 'full' is default. v2.4.9+."),
3769
3931
  }, {
3770
3932
  readOnlyHint: true,
3771
3933
  openWorldHint: false,
3772
3934
  destructiveHint: false,
3773
3935
  title: "List fix patterns",
3774
- }, async ({ project, limit }) => {
3936
+ }, async ({ project, limit, fields }) => {
3775
3937
  try {
3776
3938
  const _scopeDenied = guardMasterOnly("list_fix_patterns");
3777
3939
  if (_scopeDenied)
@@ -3779,14 +3941,16 @@ export function registerTools(server, convex, oauthCtx) {
3779
3941
  if (project) {
3780
3942
  const results = await convex.query("fixPatterns:listByProject", {
3781
3943
  sourceProject: project,
3782
- limit,
3944
+ limit: limit ?? 20,
3945
+ fields: fields ?? "lite",
3783
3946
  });
3784
3947
  return {
3785
3948
  content: [{ type: "text", text: capListResponseBytes(results, JSON.stringify(results, null, 2), "list_fix_patterns") }],
3786
3949
  };
3787
3950
  }
3788
3951
  const allResults = await convex.query("fixPatterns:listAll", {
3789
- limit,
3952
+ limit: limit ?? 20,
3953
+ fields: fields ?? "lite",
3790
3954
  });
3791
3955
  return {
3792
3956
  content: [
@@ -4057,21 +4221,25 @@ export function registerTools(server, convex, oauthCtx) {
4057
4221
  .min(1)
4058
4222
  .max(200)
4059
4223
  .optional()
4060
- .default(50)
4061
- .describe("Maximum number of errors to return (default 50)"),
4224
+ .describe("Max items to return. Default 20 (envelope-safe). Cap 200."),
4225
+ fields: z
4226
+ .enum(["lite", "full"])
4227
+ .optional()
4228
+ .describe("'lite' returns compact payload (less tokens), 'full' is default. v2.4.9+."),
4062
4229
  }, {
4063
4230
  readOnlyHint: true,
4064
4231
  openWorldHint: false,
4065
4232
  destructiveHint: false,
4066
4233
  title: "List errors",
4067
- }, async ({ deployment, limit }) => {
4234
+ }, async ({ deployment, limit, fields }) => {
4068
4235
  try {
4069
4236
  const _scopeDenied = guardMasterOnly("list_errors");
4070
4237
  if (_scopeDenied)
4071
4238
  return _scopeDenied;
4072
4239
  const errors = await convex.query("errorMonitor:listErrors", {
4073
4240
  deployment,
4074
- limit: limit ?? 50,
4241
+ limit: limit ?? 20,
4242
+ fields: fields ?? "lite",
4075
4243
  });
4076
4244
  return {
4077
4245
  content: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vantage-peers-mcp",
3
- "version": "2.4.6",
3
+ "version": "2.4.10",
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",