vantage-peers-mcp 2.3.1 → 2.3.3

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,46 @@
1
1
  # Changelog
2
2
 
3
+ ## v2.3.3 — 2026-05-28
4
+
5
+ **Follow-up to v2.3.2 (Day 84 scope élargi)** — Extend list queries with `createdBy` + `updatedSince` filters + auto-clamp safeguard.
6
+
7
+ Backend (Convex) :
8
+ - `tasks.list` + `tasks.listByMission` : + `createdBy` (filter by task creator) + `updatedSince` (Unix ms window) + auto-clamp limit=30 when `fields="full"` and no explicit limit
9
+ - `missions.list` : + `updatedSince` + auto-clamp (30)
10
+ - `briefingNotes.list` : + `updatedSince` + auto-clamp (15 when fields=full)
11
+
12
+ MCP wrapper :
13
+ - 4 list tools forward the new params
14
+ - New export `updatedSinceSchema` (positive integer ms)
15
+ - `limit` `.default()` removed on the 4 list tools so absent limit flows to backend → enables auto-clamp
16
+
17
+ Tests : 15 new MCP schema cases (`src/__tests__/list-queries-v2.3.3-createdby-updatedsince.test.ts`) + 6 new Convex round-trip cases.
18
+
19
+ Pi pull cycle unblocked : `list_tasks createdBy="pi" status="review" fields="lite"` returns only Pi-dispatched tasks recently moved to review, payload 5-10× smaller.
20
+
21
+ Cap fleet : 0 overflow tolérance future (auto-clamp).
22
+
23
+ VP task: `k1796s5j6jfkvkx0tn5n926ftd87jx9p`. Successor of `k17e09ng1tf217n93z9m4tr0mx87hfe0` (v2.3.2 PR #537).
24
+
25
+ ## v2.3.2 — 2026-05-28
26
+
27
+ **Hotfix** — Expose `fields="lite"` + `status` array/aliases in MCP tool schemas (Day 82 sprint gap).
28
+
29
+ Backend support for these params shipped in v2.3.1 but the MCP wrapper Zod schemas never exposed them, so MCP clients couldn't pass them. Fixed for 4 list tools:
30
+
31
+ - `list_tasks`: + `fields`, status now accepts aliases (`"open"`, `"active"`, `"all"`) and arrays
32
+ - `list_tasks_by_mission`: same
33
+ - `list_missions`: + `fields`, status accepts aliases and arrays
34
+ - `list_briefing_notes`: + `fields`
35
+
36
+ Aliases NOT permitted inside arrays (matches backend rejection contract).
37
+
38
+ Tests: 14 new cases (`src/__tests__/list-queries-schema-v2.3.2.test.ts`), 0 regression on 295+ existing.
39
+
40
+ Fix-pattern (fleet-wide): When backend query supports a new param, ALWAYS update the MCP wrapper tool schema in the SAME PR.
41
+
42
+ VP task: `k17e09ng1tf217n93z9m4tr0mx87hfe0`.
43
+
3
44
  ## 2.3.1 — 2026-05-26
4
45
 
5
46
  ### Fixed (Eta PR #530 delta-review)
@@ -46,4 +46,96 @@ export declare const updateBriefingNoteSchema: z.ZodObject<{
46
46
  decisions: z.ZodOptional<z.ZodArray<z.ZodString>>;
47
47
  linkedMemoryIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
48
48
  }, z.core.$strip>;
49
+ export declare const taskStatusSchema: z.ZodEnum<{
50
+ todo: "todo";
51
+ in_progress: "in_progress";
52
+ review: "review";
53
+ blocked: "blocked";
54
+ done: "done";
55
+ }>;
56
+ export declare const missionStatusSchema: z.ZodEnum<{
57
+ brainstorm: "brainstorm";
58
+ plan: "plan";
59
+ execute: "execute";
60
+ validate: "validate";
61
+ complete: "complete";
62
+ }>;
63
+ export declare const taskStatusFilterSchema: z.ZodUnion<readonly [z.ZodEnum<{
64
+ todo: "todo";
65
+ in_progress: "in_progress";
66
+ review: "review";
67
+ blocked: "blocked";
68
+ done: "done";
69
+ active: "active";
70
+ open: "open";
71
+ all: "all";
72
+ }>, z.ZodArray<z.ZodEnum<{
73
+ todo: "todo";
74
+ in_progress: "in_progress";
75
+ review: "review";
76
+ blocked: "blocked";
77
+ done: "done";
78
+ }>>]>;
79
+ export declare const missionStatusFilterSchema: z.ZodUnion<readonly [z.ZodEnum<{
80
+ brainstorm: "brainstorm";
81
+ plan: "plan";
82
+ execute: "execute";
83
+ validate: "validate";
84
+ complete: "complete";
85
+ active: "active";
86
+ open: "open";
87
+ all: "all";
88
+ }>, z.ZodArray<z.ZodEnum<{
89
+ brainstorm: "brainstorm";
90
+ plan: "plan";
91
+ execute: "execute";
92
+ validate: "validate";
93
+ complete: "complete";
94
+ }>>]>;
95
+ export declare const fieldsSchema: z.ZodEnum<{
96
+ lite: "lite";
97
+ full: "full";
98
+ }>;
99
+ export declare const updatedSinceSchema: z.ZodNumber;
100
+ export interface ParsedConvexError {
101
+ code: string;
102
+ message: string;
103
+ path: string | null;
104
+ hint: string | null;
105
+ }
106
+ /**
107
+ * Parse a Convex error message string into a structured object.
108
+ *
109
+ * Input example (from ConvexHttpClient):
110
+ * "[CONVEX M(briefingNotes:create)] ArgumentValidationError: Found ID
111
+ * \"js72ewf0m...\" from table briefingNotes, which does not match the table
112
+ * name in validator v.id(\"memories\"). Path: .linkedMemoryIds[4]"
113
+ *
114
+ * Returns { code, message, path, hint } where:
115
+ * - code = "ArgumentValidationError" (or the parsed error type)
116
+ * - message = the full human-readable error description after the code prefix
117
+ * - path = e.g. ".linkedMemoryIds[4]" extracted from "Path: ..." suffix
118
+ * - hint = a concise guidance string derived from the error, or null
119
+ *
120
+ * For unrecognised error strings, code = "ServerError" and path/hint = null.
121
+ *
122
+ * Exported for unit testing.
123
+ */
124
+ export declare function parseConvexError(rawMessage: string): ParsedConvexError;
125
+ /**
126
+ * Produce a structured MCP error response for any error thrown by a Convex
127
+ * operation. For ConvexError / ArgumentValidationError the response body
128
+ * contains a JSON object with { code, message, path, hint } so the MCP client
129
+ * can display actionable diagnostics instead of a bare "Server Error" string.
130
+ *
131
+ * For unrecognised errors the response falls back to the plain text format
132
+ * used by `mcpError`.
133
+ */
134
+ export declare function mcpConvexError(error: unknown): {
135
+ content: Array<{
136
+ type: "text";
137
+ text: string;
138
+ }>;
139
+ isError: true;
140
+ };
49
141
  export declare function registerTools(server: McpServer, convex: ConvexHttpClient, oauthCtx?: OAuthContext): void;
package/dist/src/tools.js CHANGED
@@ -101,7 +101,9 @@ export const updateBriefingNoteSchema = z.object({
101
101
  linkedMemoryIds: z
102
102
  .array(memoryIdSchema)
103
103
  .optional()
104
- .describe("Optional new linkedMemoryIds array — full replace, not append. Each ID must point to memories table, NOT briefingNotes or any other table."),
104
+ .describe("Optional new linkedMemoryIds array — full replace, not append. " +
105
+ "DISCLAIMER: Memory IDs only — NOT briefingNotes IDs or any other table. " +
106
+ "Passing a briefingNotes ID causes ArgumentValidationError at path .linkedMemoryIds[N]."),
105
107
  });
106
108
  const assigneeSchema = z
107
109
  .string()
@@ -113,12 +115,61 @@ const prioritySchema = z
113
115
  const componentTypeSchema = z
114
116
  .enum(["agent", "skill", "hook", "plugin"])
115
117
  .describe("Component type");
116
- const taskStatusSchema = z
117
- .enum(["todo", "in_progress", "review", "blocked", "done"])
118
- .describe("Task status");
119
- const missionStatusSchema = z
120
- .enum(["brainstorm", "plan", "execute", "validate", "complete"])
118
+ const taskStatusValues = [
119
+ "todo",
120
+ "in_progress",
121
+ "review",
122
+ "blocked",
123
+ "done",
124
+ ];
125
+ const taskStatusAliases = ["open", "active", "all"];
126
+ export const taskStatusSchema = z.enum(taskStatusValues).describe("Task status");
127
+ const missionStatusValues = [
128
+ "brainstorm",
129
+ "plan",
130
+ "execute",
131
+ "validate",
132
+ "complete",
133
+ ];
134
+ const missionStatusAliases = ["open", "active", "all"];
135
+ export const missionStatusSchema = z
136
+ .enum(missionStatusValues)
121
137
  .describe("Mission lifecycle status");
138
+ // v2.3.2 — filter-only schemas: expose status aliases ("open"/"active"/"all")
139
+ // AND multi-status arrays to the MCP client. Backend (convex/tasks.ts +
140
+ // convex/missions.ts) handles alias expansion + array validation.
141
+ // Aliases NOT allowed inside arrays (matches backend rejection).
142
+ export const taskStatusFilterSchema = z
143
+ .union([
144
+ z.enum([...taskStatusValues, ...taskStatusAliases]),
145
+ z.array(z.enum(taskStatusValues)).min(1),
146
+ ])
147
+ .describe('Task status filter. Single status ("todo"|"in_progress"|"review"|"blocked"|"done"), ' +
148
+ 'alias ("open" = todo+in_progress+review+blocked, "active" = todo+in_progress, "all" = no filter), ' +
149
+ 'or array of direct statuses (no aliases inside array).');
150
+ export const missionStatusFilterSchema = z
151
+ .union([
152
+ z.enum([...missionStatusValues, ...missionStatusAliases]),
153
+ z.array(z.enum(missionStatusValues)).min(1),
154
+ ])
155
+ .describe('Mission status filter. Single status ("brainstorm"|"plan"|"execute"|"validate"|"complete"), ' +
156
+ 'alias ("open" = brainstorm+plan+execute+validate, "active" = plan+execute, "all" = no filter), ' +
157
+ 'or array of direct statuses (no aliases inside array).');
158
+ // v2.3.2 — fields projection toggle. "lite" returns compact projection
159
+ // (5-10× smaller payload), "full" (default) returns full doc.
160
+ export const fieldsSchema = z
161
+ .enum(["lite", "full"])
162
+ .describe('Field projection — "lite" returns compact fields only ' +
163
+ '(typical 5-10× smaller payload for large list scans), ' +
164
+ '"full" (default) returns the full document.');
165
+ // v2.3.3 — Unix timestamp ms filter for "updated since".
166
+ // Pass `Date.now() - 24*60*60*1000` for "last 24h" pattern.
167
+ export const updatedSinceSchema = z
168
+ .number()
169
+ .int()
170
+ .positive()
171
+ .describe("Unix timestamp (ms) — return only rows whose updatedAt >= this value. " +
172
+ "Typical usage: Date.now() - 24*60*60*1000 for last-24h window.");
122
173
  const mandateStatusSchema = z
123
174
  .enum(["requested", "accepted", "in_progress", "delivered", "settled"])
124
175
  .describe("Mandate lifecycle status");
@@ -180,6 +231,103 @@ function mcpError(message) {
180
231
  isError: true,
181
232
  };
182
233
  }
234
+ /**
235
+ * Parse a Convex error message string into a structured object.
236
+ *
237
+ * Input example (from ConvexHttpClient):
238
+ * "[CONVEX M(briefingNotes:create)] ArgumentValidationError: Found ID
239
+ * \"js72ewf0m...\" from table briefingNotes, which does not match the table
240
+ * name in validator v.id(\"memories\"). Path: .linkedMemoryIds[4]"
241
+ *
242
+ * Returns { code, message, path, hint } where:
243
+ * - code = "ArgumentValidationError" (or the parsed error type)
244
+ * - message = the full human-readable error description after the code prefix
245
+ * - path = e.g. ".linkedMemoryIds[4]" extracted from "Path: ..." suffix
246
+ * - hint = a concise guidance string derived from the error, or null
247
+ *
248
+ * For unrecognised error strings, code = "ServerError" and path/hint = null.
249
+ *
250
+ * Exported for unit testing.
251
+ */
252
+ export function parseConvexError(rawMessage) {
253
+ // Known Convex validation/runtime error codes surfaced as plaintext
254
+ const knownCodes = [
255
+ "ArgumentValidationError",
256
+ "AuthorizationError",
257
+ "ConvexError",
258
+ "SchemaValidationError",
259
+ "QueryError",
260
+ "MutationError",
261
+ "ActionError",
262
+ ];
263
+ // Strip the [CONVEX M(path)] / [CONVEX Q(path)] prefix if present
264
+ const stripped = rawMessage.replace(/^\[CONVEX [A-Z]+\([^\]]*\)\]\s*/, "");
265
+ // Detect the error code
266
+ let code = "ServerError";
267
+ let remainder = stripped;
268
+ for (const candidate of knownCodes) {
269
+ if (stripped.startsWith(candidate + ":") || stripped.startsWith(candidate + " ")) {
270
+ code = candidate;
271
+ remainder = stripped.slice(candidate.length).replace(/^[:\s]+/, "");
272
+ break;
273
+ }
274
+ }
275
+ // Extract "Path: .<fieldPath>" from the tail of the message
276
+ // Convex appends this as the last sentence: "Path: .linkedMemoryIds[4]"
277
+ let path = null;
278
+ const pathMatch = remainder.match(/\bPath:\s*([\w.\[\]"']+)\s*$/);
279
+ if (pathMatch) {
280
+ path = pathMatch[1];
281
+ remainder = remainder.slice(0, pathMatch.index).trim().replace(/\.\s*$/, "");
282
+ }
283
+ // Build a concise hint for common patterns
284
+ let hint = null;
285
+ if (code === "ArgumentValidationError") {
286
+ const tableMatch = remainder.match(/from table (\w+),.*validator v\.id\("(\w+)"\)/);
287
+ if (tableMatch) {
288
+ hint = `ID belongs to table "${tableMatch[1]}" but validator expects v.id("${tableMatch[2]}"). Check that you are passing the correct document ID from the "${tableMatch[2]}" table.`;
289
+ }
290
+ }
291
+ return { code, message: remainder || rawMessage, path, hint };
292
+ }
293
+ /**
294
+ * Produce a structured MCP error response for any error thrown by a Convex
295
+ * operation. For ConvexError / ArgumentValidationError the response body
296
+ * contains a JSON object with { code, message, path, hint } so the MCP client
297
+ * can display actionable diagnostics instead of a bare "Server Error" string.
298
+ *
299
+ * For unrecognised errors the response falls back to the plain text format
300
+ * used by `mcpError`.
301
+ */
302
+ export function mcpConvexError(error) {
303
+ const rawMessage = error instanceof Error ? error.message : String(error);
304
+ const parsed = parseConvexError(rawMessage);
305
+ // For ArgumentValidationError and other known Convex codes, return structured JSON
306
+ if (parsed.code !== "ServerError") {
307
+ const payload = {
308
+ code: parsed.code,
309
+ message: parsed.message,
310
+ };
311
+ if (parsed.path !== null)
312
+ payload.path = parsed.path;
313
+ if (parsed.hint !== null)
314
+ payload.hint = parsed.hint;
315
+ return {
316
+ content: [
317
+ {
318
+ type: "text",
319
+ text: JSON.stringify(payload, null, 2),
320
+ },
321
+ ],
322
+ isError: true,
323
+ };
324
+ }
325
+ // Fallback: generic error, preserve existing plain-text format
326
+ return {
327
+ content: [{ type: "text", text: `Error: ${rawMessage}` }],
328
+ isError: true,
329
+ };
330
+ }
183
331
  // ─────────────────────────────────────────────────────────────────────────────
184
332
  // Main export: register all tools against a server + convex client pair
185
333
  // ─────────────────────────────────────────────────────────────────────────────
@@ -991,7 +1139,9 @@ export function registerTools(server, convex, oauthCtx) {
991
1139
  .string()
992
1140
  .optional()
993
1141
  .describe("Filter by instance — e.g. 'pi-vps'. Returns only tasks assigned to that instance."),
994
- status: taskStatusSchema.optional().describe("Filter by status"),
1142
+ status: taskStatusFilterSchema
1143
+ .optional()
1144
+ .describe("Filter by status (single, alias, or array)"),
995
1145
  project: z.string().optional().describe("Filter by project name"),
996
1146
  limit: z
997
1147
  .number()
@@ -999,16 +1149,25 @@ export function registerTools(server, convex, oauthCtx) {
999
1149
  .min(1)
1000
1150
  .max(200)
1001
1151
  .optional()
1002
- .default(50)
1003
- .describe("Maximum number of tasks to return (default 50)"),
1004
- }, async ({ assignedTo, assignedToInstance, status, project, limit }) => {
1152
+ .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)."),
1153
+ fields: fieldsSchema
1154
+ .optional()
1155
+ .describe('Field projection ("lite"|"full")'),
1156
+ createdBy: assigneeSchema
1157
+ .optional()
1158
+ .describe("Filter by task creator (e.g. 'pi' to find Pi-dispatched tasks)"),
1159
+ updatedSince: updatedSinceSchema.optional(),
1160
+ }, async ({ assignedTo, assignedToInstance, status, project, limit, fields, createdBy, updatedSince, }) => {
1005
1161
  try {
1006
1162
  const tasks = await convex.query("tasks:list", {
1007
1163
  assignedTo,
1008
1164
  assignedToInstance,
1009
1165
  status,
1010
1166
  project,
1011
- limit: limit ?? 50,
1167
+ limit,
1168
+ fields,
1169
+ createdBy,
1170
+ updatedSince,
1012
1171
  });
1013
1172
  return {
1014
1173
  content: [
@@ -1315,21 +1474,32 @@ export function registerTools(server, convex, oauthCtx) {
1315
1474
  // ── list_tasks_by_mission ───────────────────────────────────────────────────
1316
1475
  server.tool("list_tasks_by_mission", "List all tasks linked to a specific mission. Optionally filter by status.", {
1317
1476
  missionId: z.string().describe("Convex document ID of the mission"),
1318
- status: taskStatusSchema.optional().describe("Filter by task status"),
1477
+ status: taskStatusFilterSchema
1478
+ .optional()
1479
+ .describe("Filter by task status (single, alias, or array)"),
1319
1480
  limit: z
1320
1481
  .number()
1321
1482
  .int()
1322
1483
  .min(1)
1323
1484
  .max(200)
1324
1485
  .optional()
1325
- .default(50)
1326
- .describe("Maximum number of tasks to return (default 50)"),
1327
- }, async ({ missionId, status, limit }) => {
1486
+ .describe("Maximum number of tasks to return. Default 50 with fields=lite, auto-clamped to 30 when fields=full and no explicit limit."),
1487
+ fields: fieldsSchema
1488
+ .optional()
1489
+ .describe('Field projection ("lite"|"full")'),
1490
+ createdBy: assigneeSchema
1491
+ .optional()
1492
+ .describe("Filter by task creator"),
1493
+ updatedSince: updatedSinceSchema.optional(),
1494
+ }, async ({ missionId, status, limit, fields, createdBy, updatedSince }) => {
1328
1495
  try {
1329
1496
  const tasks = await convex.query("tasks:listByMission", {
1330
1497
  missionId: missionId,
1331
1498
  status,
1332
- limit: limit ?? 50,
1499
+ limit,
1500
+ fields,
1501
+ createdBy,
1502
+ updatedSince,
1333
1503
  });
1334
1504
  return {
1335
1505
  content: [
@@ -1404,22 +1574,29 @@ export function registerTools(server, convex, oauthCtx) {
1404
1574
  "Filter by project, pilot, and/or status. Returns newest first.", {
1405
1575
  project: z.string().optional().describe("Filter by project name"),
1406
1576
  pilot: creatorSchema.optional().describe("Filter by pilot orchestrator"),
1407
- status: missionStatusSchema.optional().describe("Filter by status"),
1577
+ status: missionStatusFilterSchema
1578
+ .optional()
1579
+ .describe("Filter by status (single, alias, or array)"),
1408
1580
  limit: z
1409
1581
  .number()
1410
1582
  .int()
1411
1583
  .min(1)
1412
1584
  .max(200)
1413
1585
  .optional()
1414
- .default(50)
1415
- .describe("Maximum number of missions to return (default 50)"),
1416
- }, async ({ project, pilot, status, limit }) => {
1586
+ .describe("Maximum number of missions to return. Default 50 with fields=lite, auto-clamped to 30 when fields=full and no explicit limit."),
1587
+ fields: fieldsSchema
1588
+ .optional()
1589
+ .describe('Field projection ("lite"|"full")'),
1590
+ updatedSince: updatedSinceSchema.optional(),
1591
+ }, async ({ project, pilot, status, limit, fields, updatedSince }) => {
1417
1592
  try {
1418
1593
  const missions = await convex.query("missions:list", {
1419
1594
  project,
1420
1595
  pilot,
1421
1596
  status,
1422
- limit: limit ?? 50,
1597
+ limit,
1598
+ fields,
1599
+ updatedSince,
1423
1600
  });
1424
1601
  return {
1425
1602
  content: [
@@ -1630,7 +1807,9 @@ export function registerTools(server, convex, oauthCtx) {
1630
1807
  // ── create_briefing_note ────────────────────────────────────────────────────
1631
1808
  server.tool("create_briefing_note", "Create a briefing note — a structured record of a topic discussion, with participants, " +
1632
1809
  "content, optional decisions, and optional links to existing memories. " +
1633
- "linkedMemoryIds MUST contain IDs from the memories table only — NOT briefingNotes IDs or IDs from any other table.", {
1810
+ "linkedMemoryIds MUST contain IDs from the memories table only — NOT briefingNotes IDs or IDs from any other table. " +
1811
+ "IMPORTANT: If you need to cross-link briefing notes together, use linkedBriefingIds (not yet shipped) — " +
1812
+ "passing a briefingNotes document ID into linkedMemoryIds will produce an ArgumentValidationError at the Convex validator boundary.", {
1634
1813
  title: z.string().describe("Briefing note title"),
1635
1814
  topic: z
1636
1815
  .string()
@@ -1643,7 +1822,10 @@ export function registerTools(server, convex, oauthCtx) {
1643
1822
  linkedMemoryIds: z
1644
1823
  .array(memoryIdSchema)
1645
1824
  .optional()
1646
- .describe("Convex document IDs of related memories — each must be a 32-char ID from the memories table, NOT briefingNotes or any other table"),
1825
+ .describe("Convex document IDs of related memories — each must be a 32-char ID from the memories table, NOT briefingNotes or any other table. " +
1826
+ "DISCLAIMER: Memory IDs only. Do NOT pass briefingNotes IDs here — they share the same 32-char alphanumeric format but belong to a different table. " +
1827
+ "Passing a briefingNotes ID will fail with ArgumentValidationError at path .linkedMemoryIds[N]. " +
1828
+ "If cross-linking briefings is needed, request the linkedBriefingIds feature instead."),
1647
1829
  createdBy: creatorSchema,
1648
1830
  }, async ({ title, topic, participants, content, decisions, linkedMemoryIds, createdBy, }) => {
1649
1831
  let contentBytes = 0;
@@ -1680,7 +1862,7 @@ export function registerTools(server, convex, oauthCtx) {
1680
1862
  title,
1681
1863
  errorMessage: error?.message ?? String(error),
1682
1864
  });
1683
- return mcpError(error.message ?? String(error));
1865
+ return mcpConvexError(error);
1684
1866
  }
1685
1867
  });
1686
1868
  // ── update_briefing_note ────────────────────────────────────────────────────
@@ -1721,7 +1903,7 @@ export function registerTools(server, convex, oauthCtx) {
1721
1903
  noteId,
1722
1904
  errorMessage: error?.message ?? String(error),
1723
1905
  });
1724
- return mcpError(error.message ?? String(error));
1906
+ return mcpConvexError(error);
1725
1907
  }
1726
1908
  });
1727
1909
  // ── list_briefing_notes ─────────────────────────────────────────────────────
@@ -1736,13 +1918,18 @@ export function registerTools(server, convex, oauthCtx) {
1736
1918
  .min(1)
1737
1919
  .max(100)
1738
1920
  .optional()
1739
- .default(20)
1740
- .describe("Maximum notes to return (default 20)"),
1741
- }, async ({ topic, limit }) => {
1921
+ .describe("Maximum notes to return. Default 20 with fields=lite, auto-clamped to 15 when fields=full and no explicit limit."),
1922
+ fields: fieldsSchema
1923
+ .optional()
1924
+ .describe('Field projection ("lite"|"full")'),
1925
+ updatedSince: updatedSinceSchema.optional(),
1926
+ }, async ({ topic, limit, fields, updatedSince }) => {
1742
1927
  try {
1743
1928
  const notes = await convex.query("briefingNotes:list", {
1744
1929
  topic,
1745
- limit: limit ?? 20,
1930
+ limit,
1931
+ fields,
1932
+ updatedSince,
1746
1933
  });
1747
1934
  return {
1748
1935
  content: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vantage-peers-mcp",
3
- "version": "2.3.1",
3
+ "version": "2.3.3",
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",