vantage-peers-mcp 2.4.2 → 2.4.6
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 +5 -0
- package/dist/src/tools.d.ts +2 -0
- package/dist/src/tools.js +81 -22
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,6 +9,8 @@ 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
|
+
|
|
12
14
|
## Quick start
|
|
13
15
|
|
|
14
16
|
```bash
|
|
@@ -400,6 +402,9 @@ All orchestrator names are open strings — any lowercase name is accepted. The
|
|
|
400
402
|
|
|
401
403
|
## Changelog
|
|
402
404
|
|
|
405
|
+
### 2.4.3 — 2026-05-31 (Day 89)
|
|
406
|
+
- fix(overflow): defensive byte-cap on all 17 `list_*` tools — `capListResponseBytes` truncates any list response above 60 KB and wraps the result in a `_meta` envelope (`_truncated`, `_showing`, `_total`, `_advice`) so MCP clients (Claude.ai, ChatGPT, Claude Code) never reject a list result for exceeding their token budget. Day 89 Pi 75,003-char `list_tasks` overflow incident reproduced and capped in regression test. PR #565.
|
|
407
|
+
|
|
403
408
|
### 2.4.1 — 2026-05-30 (Day 88)
|
|
404
409
|
- fix(dcr): `oauthDcr:validateAccessToken` exposed as PUBLIC `query` (was `internalQuery`, unreachable via `ConvexHttpClient.query()` → Path 3 DCR returned 401 even with valid token) — issue #556 / PR #557.
|
|
405
410
|
- fix(dcr): `WWW-Authenticate` header now emits `Bearer resource_metadata="..."` per MCP spec §Protected Resource Metadata Discovery (was `resource="..."` — broke Claude.ai PRM discovery bootstrap on 401) — PR #557.
|
package/dist/src/tools.d.ts
CHANGED
|
@@ -21,6 +21,8 @@ export declare const MAX_CONTENT_BYTES = 900000;
|
|
|
21
21
|
* @param toolName Caller tool name (used only in the error message).
|
|
22
22
|
*/
|
|
23
23
|
export declare function assertContentSize(content: string, toolName: string): number;
|
|
24
|
+
export declare const MAX_LIST_RESPONSE_BYTES = 60000;
|
|
25
|
+
export declare function capListResponseBytes(items: unknown, rawText: string, toolName: string, maxBytes?: number): string;
|
|
24
26
|
/**
|
|
25
27
|
* Convex document IDs are 32 lowercase alphanumeric characters (a-z0-9).
|
|
26
28
|
* Exported so tests can validate the schema independently of the MCP server.
|
package/dist/src/tools.js
CHANGED
|
@@ -71,6 +71,56 @@ export function assertContentSize(content, toolName) {
|
|
|
71
71
|
return contentBytes;
|
|
72
72
|
}
|
|
73
73
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
74
|
+
// List response byte cap (overflow protection for MCP clients)
|
|
75
|
+
//
|
|
76
|
+
// MCP clients (Claude.ai, ChatGPT, Claude Code) reject tool results that
|
|
77
|
+
// exceed their token budget — typical ceiling ~25k tokens ≈ 75 KB JSON.
|
|
78
|
+
// When that happens the entire response is lost to a downstream truncation
|
|
79
|
+
// error and the user must fall back to reading the on-disk overflow file.
|
|
80
|
+
//
|
|
81
|
+
// `capListResponseBytes` guards every bulk list_* tool: if the serialized
|
|
82
|
+
// payload exceeds MAX_LIST_RESPONSE_BYTES (60 KB), it truncates the items
|
|
83
|
+
// array (halving until it fits) and wraps the result in a _meta envelope
|
|
84
|
+
// that tells the caller exactly how to refine the query.
|
|
85
|
+
//
|
|
86
|
+
// 60 KB leaves headroom for tool-call JSON framing and the UI stream marker
|
|
87
|
+
// before any MCP client hits its own ceiling. The cap is byte-counted on the
|
|
88
|
+
// raw JSON string, not on item count, because content-heavy rows
|
|
89
|
+
// (memories / diaries / briefing notes) blow past 30 items easily.
|
|
90
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
91
|
+
export const MAX_LIST_RESPONSE_BYTES = 60_000;
|
|
92
|
+
export function capListResponseBytes(items, rawText, toolName, maxBytes = MAX_LIST_RESPONSE_BYTES) {
|
|
93
|
+
const byteLen = Buffer.byteLength(rawText, "utf8");
|
|
94
|
+
if (byteLen <= maxBytes)
|
|
95
|
+
return rawText;
|
|
96
|
+
if (!Array.isArray(items) || items.length === 0)
|
|
97
|
+
return rawText;
|
|
98
|
+
let n = items.length;
|
|
99
|
+
let truncated = items;
|
|
100
|
+
let truncatedText = rawText;
|
|
101
|
+
while (n > 1) {
|
|
102
|
+
n = Math.max(1, Math.floor(n / 2));
|
|
103
|
+
truncated = items.slice(0, n);
|
|
104
|
+
truncatedText = JSON.stringify(truncated, null, 2);
|
|
105
|
+
if (Buffer.byteLength(truncatedText, "utf8") <= maxBytes - 600)
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
const envelope = {
|
|
109
|
+
_meta: {
|
|
110
|
+
_truncated: true,
|
|
111
|
+
_showing: truncated.length,
|
|
112
|
+
_total: items.length,
|
|
113
|
+
_bytesOriginal: byteLen,
|
|
114
|
+
_bytesCap: maxBytes,
|
|
115
|
+
_tool: toolName,
|
|
116
|
+
_advice: `Response exceeded ${maxBytes} bytes. Showing first ${truncated.length}/${items.length}. ` +
|
|
117
|
+
`Pass fields="lite", a smaller limit, stricter filters (status, assignedTo, project, namespace, updatedSince), or paginate.`,
|
|
118
|
+
},
|
|
119
|
+
items: truncated,
|
|
120
|
+
};
|
|
121
|
+
return JSON.stringify(envelope, null, 2);
|
|
122
|
+
}
|
|
123
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
74
124
|
// Shared Zod schemas
|
|
75
125
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
76
126
|
/**
|
|
@@ -815,6 +865,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
815
865
|
type: memoryTypeSchema
|
|
816
866
|
.optional()
|
|
817
867
|
.describe("Filter to a specific type — omit to return all types"),
|
|
868
|
+
createdBy: assigneeSchema
|
|
869
|
+
.optional()
|
|
870
|
+
.describe("Filter by creator/orchestrator role — mirrors list_tasks pattern for cross-tool consistency."),
|
|
818
871
|
limit: z
|
|
819
872
|
.number()
|
|
820
873
|
.int()
|
|
@@ -828,7 +881,7 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
828
881
|
openWorldHint: false,
|
|
829
882
|
destructiveHint: false,
|
|
830
883
|
title: "List memories",
|
|
831
|
-
}, async ({ namespace, type, limit }) => {
|
|
884
|
+
}, async ({ namespace, type, createdBy, limit }) => {
|
|
832
885
|
try {
|
|
833
886
|
const nsDenied = guardRead(namespace);
|
|
834
887
|
if (nsDenied)
|
|
@@ -836,6 +889,7 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
836
889
|
const memories = await convex.query("memories:listMemories", {
|
|
837
890
|
namespace,
|
|
838
891
|
type,
|
|
892
|
+
createdBy,
|
|
839
893
|
limit: limit ?? 20,
|
|
840
894
|
});
|
|
841
895
|
const rawList = Array.isArray(memories)
|
|
@@ -843,7 +897,7 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
843
897
|
: Array.isArray(memories?.page)
|
|
844
898
|
? memories.page
|
|
845
899
|
: [];
|
|
846
|
-
const baseText = JSON.stringify(memories, null, 2);
|
|
900
|
+
const baseText = capListResponseBytes(memories, JSON.stringify(memories, null, 2), "list_memories");
|
|
847
901
|
const text = appendMarkerIfEnabled(baseText, () => ({
|
|
848
902
|
kind: "memory-quote",
|
|
849
903
|
items: rawList.map((m) => ({
|
|
@@ -1136,7 +1190,7 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1136
1190
|
content: [
|
|
1137
1191
|
{
|
|
1138
1192
|
type: "text",
|
|
1139
|
-
text: JSON.stringify(peers, null, 2),
|
|
1193
|
+
text: capListResponseBytes(peers, JSON.stringify(peers, null, 2), "list_peers"),
|
|
1140
1194
|
},
|
|
1141
1195
|
],
|
|
1142
1196
|
};
|
|
@@ -1176,7 +1230,7 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1176
1230
|
from,
|
|
1177
1231
|
limit: limit ?? 100,
|
|
1178
1232
|
});
|
|
1179
|
-
const baseText = JSON.stringify(messages, null, 2);
|
|
1233
|
+
const baseText = capListResponseBytes(messages, JSON.stringify(messages, null, 2), "list_messages");
|
|
1180
1234
|
const text = appendMarkerIfEnabled(baseText, () => ({
|
|
1181
1235
|
kind: "messages-feed",
|
|
1182
1236
|
items: Array.isArray(messages)
|
|
@@ -1219,7 +1273,7 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1219
1273
|
content: [
|
|
1220
1274
|
{
|
|
1221
1275
|
type: "text",
|
|
1222
|
-
text: JSON.stringify(status, null, 2),
|
|
1276
|
+
text: capListResponseBytes(status, JSON.stringify(status, null, 2), "list_broadcast_status"),
|
|
1223
1277
|
},
|
|
1224
1278
|
],
|
|
1225
1279
|
};
|
|
@@ -1356,7 +1410,7 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1356
1410
|
createdBy,
|
|
1357
1411
|
updatedSince,
|
|
1358
1412
|
});
|
|
1359
|
-
const baseText = JSON.stringify(tasks, null, 2);
|
|
1413
|
+
const baseText = capListResponseBytes(tasks, JSON.stringify(tasks, null, 2), "list_tasks");
|
|
1360
1414
|
const text = appendMarkerIfEnabled(baseText, () => ({
|
|
1361
1415
|
kind: "tasks-table",
|
|
1362
1416
|
items: Array.isArray(tasks)
|
|
@@ -1742,7 +1796,7 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1742
1796
|
content: [
|
|
1743
1797
|
{
|
|
1744
1798
|
type: "text",
|
|
1745
|
-
text: JSON.stringify(tasks, null, 2),
|
|
1799
|
+
text: capListResponseBytes(tasks, JSON.stringify(tasks, null, 2), "list_tasks_by_mission"),
|
|
1746
1800
|
},
|
|
1747
1801
|
],
|
|
1748
1802
|
};
|
|
@@ -1852,7 +1906,7 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1852
1906
|
fields,
|
|
1853
1907
|
updatedSince,
|
|
1854
1908
|
});
|
|
1855
|
-
const baseText = JSON.stringify(missions, null, 2);
|
|
1909
|
+
const baseText = capListResponseBytes(missions, JSON.stringify(missions, null, 2), "list_missions");
|
|
1856
1910
|
const text = appendMarkerIfEnabled(baseText, () => ({
|
|
1857
1911
|
kind: "mission-timeline",
|
|
1858
1912
|
items: Array.isArray(missions)
|
|
@@ -2083,6 +2137,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2083
2137
|
orchestrator: creatorSchema
|
|
2084
2138
|
.optional()
|
|
2085
2139
|
.describe("Filter to a specific orchestrator — omit for all"),
|
|
2140
|
+
createdBy: assigneeSchema
|
|
2141
|
+
.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."),
|
|
2086
2143
|
limit: z
|
|
2087
2144
|
.number()
|
|
2088
2145
|
.int()
|
|
@@ -2096,23 +2153,25 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2096
2153
|
openWorldHint: false,
|
|
2097
2154
|
destructiveHint: false,
|
|
2098
2155
|
title: "List diary entries",
|
|
2099
|
-
}, async ({ orchestrator, limit }) => {
|
|
2156
|
+
}, async ({ orchestrator, createdBy, limit }) => {
|
|
2100
2157
|
try {
|
|
2158
|
+
// createdBy is an alias of orchestrator (diary's author field). If both set, createdBy wins.
|
|
2159
|
+
const effectiveOrchestrator = createdBy ?? orchestrator;
|
|
2101
2160
|
// Non-master: must scope to own orchestrator id.
|
|
2102
2161
|
if (oauthCtx && !isMasterScope(oauthCtx)) {
|
|
2103
|
-
if (
|
|
2162
|
+
if (effectiveOrchestrator !== oauthCtx.userId) {
|
|
2104
2163
|
return mcpError(`Forbidden: list_diaries requires orchestrator='${oauthCtx.userId}' for non-master scope (current: ${oauthCtx.scopeProfile}).`);
|
|
2105
2164
|
}
|
|
2106
2165
|
}
|
|
2107
2166
|
const entries = await convex.query("diary:list", {
|
|
2108
|
-
orchestrator,
|
|
2167
|
+
orchestrator: effectiveOrchestrator,
|
|
2109
2168
|
limit: limit ?? 20,
|
|
2110
2169
|
});
|
|
2111
2170
|
return {
|
|
2112
2171
|
content: [
|
|
2113
2172
|
{
|
|
2114
2173
|
type: "text",
|
|
2115
|
-
text: JSON.stringify(entries, null, 2),
|
|
2174
|
+
text: capListResponseBytes(entries, JSON.stringify(entries, null, 2), "list_diaries"),
|
|
2116
2175
|
},
|
|
2117
2176
|
],
|
|
2118
2177
|
};
|
|
@@ -2266,7 +2325,7 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2266
2325
|
fields,
|
|
2267
2326
|
updatedSince,
|
|
2268
2327
|
});
|
|
2269
|
-
const baseText = JSON.stringify(notes, null, 2);
|
|
2328
|
+
const baseText = capListResponseBytes(notes, JSON.stringify(notes, null, 2), "list_briefing_notes");
|
|
2270
2329
|
const text = appendMarkerIfEnabled(baseText, () => {
|
|
2271
2330
|
const items = Array.isArray(notes) ? notes : [];
|
|
2272
2331
|
if (items.length === 0)
|
|
@@ -2385,7 +2444,7 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2385
2444
|
content: [
|
|
2386
2445
|
{
|
|
2387
2446
|
type: "text",
|
|
2388
|
-
text: JSON.stringify(components, null, 2),
|
|
2447
|
+
text: capListResponseBytes(components, JSON.stringify(components, null, 2), "list_components"),
|
|
2389
2448
|
},
|
|
2390
2449
|
],
|
|
2391
2450
|
};
|
|
@@ -2607,7 +2666,7 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2607
2666
|
limit: limit ?? 50,
|
|
2608
2667
|
});
|
|
2609
2668
|
return {
|
|
2610
|
-
content: [{ type: "text", text: JSON.stringify(tasks, null, 2) }],
|
|
2669
|
+
content: [{ type: "text", text: capListResponseBytes(tasks, JSON.stringify(tasks, null, 2), "list_recurring_tasks") }],
|
|
2611
2670
|
};
|
|
2612
2671
|
}
|
|
2613
2672
|
catch (error) {
|
|
@@ -2957,7 +3016,7 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2957
3016
|
content: [
|
|
2958
3017
|
{
|
|
2959
3018
|
type: "text",
|
|
2960
|
-
text: JSON.stringify(mandates, null, 2),
|
|
3019
|
+
text: capListResponseBytes(mandates, JSON.stringify(mandates, null, 2), "list_mandates"),
|
|
2961
3020
|
},
|
|
2962
3021
|
],
|
|
2963
3022
|
};
|
|
@@ -3167,7 +3226,7 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3167
3226
|
content: [
|
|
3168
3227
|
{
|
|
3169
3228
|
type: "text",
|
|
3170
|
-
text: JSON.stringify(bus, null, 2),
|
|
3229
|
+
text: capListResponseBytes(bus, JSON.stringify(bus, null, 2), "list_bus"),
|
|
3171
3230
|
},
|
|
3172
3231
|
],
|
|
3173
3232
|
};
|
|
@@ -3262,7 +3321,7 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3262
3321
|
content: [
|
|
3263
3322
|
{
|
|
3264
3323
|
type: "text",
|
|
3265
|
-
text: JSON.stringify(mappings, null, 2),
|
|
3324
|
+
text: capListResponseBytes(mappings, JSON.stringify(mappings, null, 2), "list_repo_mappings"),
|
|
3266
3325
|
},
|
|
3267
3326
|
],
|
|
3268
3327
|
};
|
|
@@ -3362,7 +3421,7 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3362
3421
|
content: [
|
|
3363
3422
|
{
|
|
3364
3423
|
type: "text",
|
|
3365
|
-
text: JSON.stringify({ count: results.length, issues: results }, null, 2),
|
|
3424
|
+
text: capListResponseBytes(results, JSON.stringify({ count: results.length, issues: results }, null, 2), "list_issues"),
|
|
3366
3425
|
},
|
|
3367
3426
|
],
|
|
3368
3427
|
};
|
|
@@ -3723,7 +3782,7 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3723
3782
|
limit,
|
|
3724
3783
|
});
|
|
3725
3784
|
return {
|
|
3726
|
-
content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
|
|
3785
|
+
content: [{ type: "text", text: capListResponseBytes(results, JSON.stringify(results, null, 2), "list_fix_patterns") }],
|
|
3727
3786
|
};
|
|
3728
3787
|
}
|
|
3729
3788
|
const allResults = await convex.query("fixPatterns:listAll", {
|
|
@@ -3731,7 +3790,7 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3731
3790
|
});
|
|
3732
3791
|
return {
|
|
3733
3792
|
content: [
|
|
3734
|
-
{ type: "text", text: JSON.stringify(allResults, null, 2) },
|
|
3793
|
+
{ type: "text", text: capListResponseBytes(allResults, JSON.stringify(allResults, null, 2), "list_fix_patterns") },
|
|
3735
3794
|
],
|
|
3736
3795
|
};
|
|
3737
3796
|
}
|
|
@@ -4018,7 +4077,7 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
4018
4077
|
content: [
|
|
4019
4078
|
{
|
|
4020
4079
|
type: "text",
|
|
4021
|
-
text: JSON.stringify(errors, null, 2),
|
|
4080
|
+
text: capListResponseBytes(errors, JSON.stringify(errors, null, 2), "list_errors"),
|
|
4022
4081
|
},
|
|
4023
4082
|
],
|
|
4024
4083
|
};
|
package/package.json
CHANGED