vantage-peers-mcp 2.4.1 → 2.4.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/README.md +3 -0
- package/dist/src/auth.js +14 -2
- package/dist/src/tools.d.ts +2 -0
- package/dist/src/tools.js +173 -18
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -400,6 +400,9 @@ All orchestrator names are open strings — any lowercase name is accepted. The
|
|
|
400
400
|
|
|
401
401
|
## Changelog
|
|
402
402
|
|
|
403
|
+
### 2.4.3 — 2026-05-31 (Day 89)
|
|
404
|
+
- 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.
|
|
405
|
+
|
|
403
406
|
### 2.4.1 — 2026-05-30 (Day 88)
|
|
404
407
|
- 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
408
|
- 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/auth.js
CHANGED
|
@@ -119,8 +119,20 @@ export function checkNamespaceRead(ctx, namespace) {
|
|
|
119
119
|
return null;
|
|
120
120
|
if (isMasterScope(ctx))
|
|
121
121
|
return null;
|
|
122
|
-
if (!namespace)
|
|
123
|
-
|
|
122
|
+
if (!namespace) {
|
|
123
|
+
// Day 88 P0 fix: a list-across (namespace undefined) call from a
|
|
124
|
+
// non-master scope cannot be served safely — the underlying query
|
|
125
|
+
// returns rows across every tenant. Reject with an explicit message
|
|
126
|
+
// telling the caller to pass a namespace they own. Previously this
|
|
127
|
+
// returned null and leaked the whole memories/profiles/etc. table
|
|
128
|
+
// to any DCR-issued client.
|
|
129
|
+
const allowed = ctx.namespaceReadPrefixes.length > 0
|
|
130
|
+
? ctx.namespaceReadPrefixes.join(", ")
|
|
131
|
+
: "(none — your client has no read scope)";
|
|
132
|
+
return ("Forbidden: this tool requires an explicit namespace argument when " +
|
|
133
|
+
`called with a non-master scope (current: ${ctx.scopeProfile}). ` +
|
|
134
|
+
`Pass namespace= one of: ${allowed}.`);
|
|
135
|
+
}
|
|
124
136
|
if (checkNamespacePrefix(ctx.namespaceReadPrefixes, namespace))
|
|
125
137
|
return null;
|
|
126
138
|
return `Forbidden: namespace='${namespace}' is not readable by scope_profile=${ctx.scopeProfile}.`;
|
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
|
/**
|
|
@@ -508,6 +558,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
508
558
|
title: "Get memory",
|
|
509
559
|
}, async ({ memoryId }) => {
|
|
510
560
|
try {
|
|
561
|
+
const _scopeDenied = guardMasterOnly("get_memory");
|
|
562
|
+
if (_scopeDenied)
|
|
563
|
+
return _scopeDenied;
|
|
511
564
|
const memory = await convex.query("memories:getMemory", {
|
|
512
565
|
memoryId,
|
|
513
566
|
});
|
|
@@ -725,6 +778,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
725
778
|
title: "Get orchestrator profile",
|
|
726
779
|
}, async ({ orchestratorId }) => {
|
|
727
780
|
try {
|
|
781
|
+
const _scopeDenied = guardMasterOnly("get_profile");
|
|
782
|
+
if (_scopeDenied)
|
|
783
|
+
return _scopeDenied;
|
|
728
784
|
const profile = await convex.query("profiles:getProfile", {
|
|
729
785
|
orchestratorId,
|
|
730
786
|
});
|
|
@@ -837,7 +893,7 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
837
893
|
: Array.isArray(memories?.page)
|
|
838
894
|
? memories.page
|
|
839
895
|
: [];
|
|
840
|
-
const baseText = JSON.stringify(memories, null, 2);
|
|
896
|
+
const baseText = capListResponseBytes(memories, JSON.stringify(memories, null, 2), "list_memories");
|
|
841
897
|
const text = appendMarkerIfEnabled(baseText, () => ({
|
|
842
898
|
kind: "memory-quote",
|
|
843
899
|
items: rawList.map((m) => ({
|
|
@@ -944,6 +1000,13 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
944
1000
|
title: "Check messages",
|
|
945
1001
|
}, async ({ recipient, recipientInstanceId, tenantId, since }) => {
|
|
946
1002
|
try {
|
|
1003
|
+
// Non-master: force recipient to caller's own userId. Anything else
|
|
1004
|
+
// would let the client read another tenant's inbox.
|
|
1005
|
+
if (oauthCtx && !isMasterScope(oauthCtx)) {
|
|
1006
|
+
if (recipient !== oauthCtx.userId) {
|
|
1007
|
+
return mcpError(`Forbidden: check_messages can only read messages for your own identity ('${oauthCtx.userId}'), not '${recipient}'.`);
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
947
1010
|
const messages = await convex.query("messages:checkNewMessages", {
|
|
948
1011
|
recipient,
|
|
949
1012
|
recipientInstanceId,
|
|
@@ -1105,6 +1168,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1105
1168
|
title: "List peers",
|
|
1106
1169
|
}, async () => {
|
|
1107
1170
|
try {
|
|
1171
|
+
const _scopeDenied = guardMasterOnly("list_peers");
|
|
1172
|
+
if (_scopeDenied)
|
|
1173
|
+
return _scopeDenied;
|
|
1108
1174
|
const profiles = await convex.query("profiles:listProfiles", {});
|
|
1109
1175
|
const peers = profiles.map((p) => ({
|
|
1110
1176
|
id: p.orchestratorId,
|
|
@@ -1120,7 +1186,7 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1120
1186
|
content: [
|
|
1121
1187
|
{
|
|
1122
1188
|
type: "text",
|
|
1123
|
-
text: JSON.stringify(peers, null, 2),
|
|
1189
|
+
text: capListResponseBytes(peers, JSON.stringify(peers, null, 2), "list_peers"),
|
|
1124
1190
|
},
|
|
1125
1191
|
],
|
|
1126
1192
|
};
|
|
@@ -1152,12 +1218,15 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1152
1218
|
title: "List messages",
|
|
1153
1219
|
}, async ({ sessionDay, from, limit }) => {
|
|
1154
1220
|
try {
|
|
1221
|
+
const _scopeDenied = guardMasterOnly("list_messages");
|
|
1222
|
+
if (_scopeDenied)
|
|
1223
|
+
return _scopeDenied;
|
|
1155
1224
|
const messages = await convex.query("messages:listMessages", {
|
|
1156
1225
|
sessionDay,
|
|
1157
1226
|
from,
|
|
1158
1227
|
limit: limit ?? 100,
|
|
1159
1228
|
});
|
|
1160
|
-
const baseText = JSON.stringify(messages, null, 2);
|
|
1229
|
+
const baseText = capListResponseBytes(messages, JSON.stringify(messages, null, 2), "list_messages");
|
|
1161
1230
|
const text = appendMarkerIfEnabled(baseText, () => ({
|
|
1162
1231
|
kind: "messages-feed",
|
|
1163
1232
|
items: Array.isArray(messages)
|
|
@@ -1190,6 +1259,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1190
1259
|
title: "List broadcast status",
|
|
1191
1260
|
}, async ({ messageId }) => {
|
|
1192
1261
|
try {
|
|
1262
|
+
const _scopeDenied = guardMasterOnly("list_broadcast_status");
|
|
1263
|
+
if (_scopeDenied)
|
|
1264
|
+
return _scopeDenied;
|
|
1193
1265
|
const status = await convex.query("messages:listBroadcastStatus", {
|
|
1194
1266
|
messageId,
|
|
1195
1267
|
});
|
|
@@ -1197,7 +1269,7 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1197
1269
|
content: [
|
|
1198
1270
|
{
|
|
1199
1271
|
type: "text",
|
|
1200
|
-
text: JSON.stringify(status, null, 2),
|
|
1272
|
+
text: capListResponseBytes(status, JSON.stringify(status, null, 2), "list_broadcast_status"),
|
|
1201
1273
|
},
|
|
1202
1274
|
],
|
|
1203
1275
|
};
|
|
@@ -1314,6 +1386,16 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1314
1386
|
title: "List tasks",
|
|
1315
1387
|
}, async ({ assignedTo, assignedToInstance, status, project, limit, fields, createdBy, updatedSince, }) => {
|
|
1316
1388
|
try {
|
|
1389
|
+
// Non-master: must scope to own identity. If neither assignedTo
|
|
1390
|
+
// nor createdBy matches the caller's userId, reject — otherwise
|
|
1391
|
+
// the query would span the whole tenant table.
|
|
1392
|
+
if (oauthCtx && !isMasterScope(oauthCtx)) {
|
|
1393
|
+
const myId = oauthCtx.userId;
|
|
1394
|
+
const scopedToSelf = assignedTo === myId || createdBy === myId;
|
|
1395
|
+
if (!scopedToSelf) {
|
|
1396
|
+
return mcpError(`Forbidden: list_tasks requires assignedTo='${myId}' or createdBy='${myId}' for non-master scope (current: ${oauthCtx.scopeProfile}).`);
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1317
1399
|
const tasks = await convex.query("tasks:list", {
|
|
1318
1400
|
assignedTo,
|
|
1319
1401
|
assignedToInstance,
|
|
@@ -1324,7 +1406,7 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1324
1406
|
createdBy,
|
|
1325
1407
|
updatedSince,
|
|
1326
1408
|
});
|
|
1327
|
-
const baseText = JSON.stringify(tasks, null, 2);
|
|
1409
|
+
const baseText = capListResponseBytes(tasks, JSON.stringify(tasks, null, 2), "list_tasks");
|
|
1328
1410
|
const text = appendMarkerIfEnabled(baseText, () => ({
|
|
1329
1411
|
kind: "tasks-table",
|
|
1330
1412
|
items: Array.isArray(tasks)
|
|
@@ -1695,6 +1777,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1695
1777
|
title: "List tasks by mission",
|
|
1696
1778
|
}, async ({ missionId, status, limit, fields, createdBy, updatedSince }) => {
|
|
1697
1779
|
try {
|
|
1780
|
+
const _scopeDenied = guardMasterOnly("list_tasks_by_mission");
|
|
1781
|
+
if (_scopeDenied)
|
|
1782
|
+
return _scopeDenied;
|
|
1698
1783
|
const tasks = await convex.query("tasks:listByMission", {
|
|
1699
1784
|
missionId: missionId,
|
|
1700
1785
|
status,
|
|
@@ -1707,7 +1792,7 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1707
1792
|
content: [
|
|
1708
1793
|
{
|
|
1709
1794
|
type: "text",
|
|
1710
|
-
text: JSON.stringify(tasks, null, 2),
|
|
1795
|
+
text: capListResponseBytes(tasks, JSON.stringify(tasks, null, 2), "list_tasks_by_mission"),
|
|
1711
1796
|
},
|
|
1712
1797
|
],
|
|
1713
1798
|
};
|
|
@@ -1802,6 +1887,13 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1802
1887
|
title: "List missions",
|
|
1803
1888
|
}, async ({ project, pilot, status, limit, fields, updatedSince }) => {
|
|
1804
1889
|
try {
|
|
1890
|
+
// Non-master: must pilot=<own-userId>. Otherwise the query spans
|
|
1891
|
+
// every tenant's missions.
|
|
1892
|
+
if (oauthCtx && !isMasterScope(oauthCtx)) {
|
|
1893
|
+
if (pilot !== oauthCtx.userId) {
|
|
1894
|
+
return mcpError(`Forbidden: list_missions requires pilot='${oauthCtx.userId}' for non-master scope (current: ${oauthCtx.scopeProfile}).`);
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1805
1897
|
const missions = await convex.query("missions:list", {
|
|
1806
1898
|
project,
|
|
1807
1899
|
pilot,
|
|
@@ -1810,7 +1902,7 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1810
1902
|
fields,
|
|
1811
1903
|
updatedSince,
|
|
1812
1904
|
});
|
|
1813
|
-
const baseText = JSON.stringify(missions, null, 2);
|
|
1905
|
+
const baseText = capListResponseBytes(missions, JSON.stringify(missions, null, 2), "list_missions");
|
|
1814
1906
|
const text = appendMarkerIfEnabled(baseText, () => ({
|
|
1815
1907
|
kind: "mission-timeline",
|
|
1816
1908
|
items: Array.isArray(missions)
|
|
@@ -1843,6 +1935,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1843
1935
|
title: "Get mission",
|
|
1844
1936
|
}, async ({ missionId }) => {
|
|
1845
1937
|
try {
|
|
1938
|
+
const _scopeDenied = guardMasterOnly("get_mission");
|
|
1939
|
+
if (_scopeDenied)
|
|
1940
|
+
return _scopeDenied;
|
|
1846
1941
|
const mission = await convex.query("missions:get", {
|
|
1847
1942
|
missionId: missionId,
|
|
1848
1943
|
});
|
|
@@ -2002,6 +2097,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2002
2097
|
title: "Get diary entry",
|
|
2003
2098
|
}, async ({ date, orchestrator }) => {
|
|
2004
2099
|
try {
|
|
2100
|
+
const _scopeDenied = guardMasterOnly("get_diary");
|
|
2101
|
+
if (_scopeDenied)
|
|
2102
|
+
return _scopeDenied;
|
|
2005
2103
|
const entry = await convex.query("diary:get", {
|
|
2006
2104
|
date,
|
|
2007
2105
|
orchestrator,
|
|
@@ -2050,6 +2148,12 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2050
2148
|
title: "List diary entries",
|
|
2051
2149
|
}, async ({ orchestrator, limit }) => {
|
|
2052
2150
|
try {
|
|
2151
|
+
// Non-master: must scope to own orchestrator id.
|
|
2152
|
+
if (oauthCtx && !isMasterScope(oauthCtx)) {
|
|
2153
|
+
if (orchestrator !== oauthCtx.userId) {
|
|
2154
|
+
return mcpError(`Forbidden: list_diaries requires orchestrator='${oauthCtx.userId}' for non-master scope (current: ${oauthCtx.scopeProfile}).`);
|
|
2155
|
+
}
|
|
2156
|
+
}
|
|
2053
2157
|
const entries = await convex.query("diary:list", {
|
|
2054
2158
|
orchestrator,
|
|
2055
2159
|
limit: limit ?? 20,
|
|
@@ -2058,7 +2162,7 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2058
2162
|
content: [
|
|
2059
2163
|
{
|
|
2060
2164
|
type: "text",
|
|
2061
|
-
text: JSON.stringify(entries, null, 2),
|
|
2165
|
+
text: capListResponseBytes(entries, JSON.stringify(entries, null, 2), "list_diaries"),
|
|
2062
2166
|
},
|
|
2063
2167
|
],
|
|
2064
2168
|
};
|
|
@@ -2203,13 +2307,16 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2203
2307
|
title: "List briefing notes",
|
|
2204
2308
|
}, async ({ topic, limit, fields, updatedSince }) => {
|
|
2205
2309
|
try {
|
|
2310
|
+
const _scopeDenied = guardMasterOnly("list_briefing_notes");
|
|
2311
|
+
if (_scopeDenied)
|
|
2312
|
+
return _scopeDenied;
|
|
2206
2313
|
const notes = await convex.query("briefingNotes:list", {
|
|
2207
2314
|
topic,
|
|
2208
2315
|
limit,
|
|
2209
2316
|
fields,
|
|
2210
2317
|
updatedSince,
|
|
2211
2318
|
});
|
|
2212
|
-
const baseText = JSON.stringify(notes, null, 2);
|
|
2319
|
+
const baseText = capListResponseBytes(notes, JSON.stringify(notes, null, 2), "list_briefing_notes");
|
|
2213
2320
|
const text = appendMarkerIfEnabled(baseText, () => {
|
|
2214
2321
|
const items = Array.isArray(notes) ? notes : [];
|
|
2215
2322
|
if (items.length === 0)
|
|
@@ -2316,6 +2423,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2316
2423
|
title: "List components",
|
|
2317
2424
|
}, async ({ type, team, limit }) => {
|
|
2318
2425
|
try {
|
|
2426
|
+
const _scopeDenied = guardMasterOnly("list_components");
|
|
2427
|
+
if (_scopeDenied)
|
|
2428
|
+
return _scopeDenied;
|
|
2319
2429
|
const components = await convex.query("components:list", {
|
|
2320
2430
|
type,
|
|
2321
2431
|
team,
|
|
@@ -2325,7 +2435,7 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2325
2435
|
content: [
|
|
2326
2436
|
{
|
|
2327
2437
|
type: "text",
|
|
2328
|
-
text: JSON.stringify(components, null, 2),
|
|
2438
|
+
text: capListResponseBytes(components, JSON.stringify(components, null, 2), "list_components"),
|
|
2329
2439
|
},
|
|
2330
2440
|
],
|
|
2331
2441
|
};
|
|
@@ -2345,6 +2455,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2345
2455
|
title: "Get component",
|
|
2346
2456
|
}, async ({ name, type }) => {
|
|
2347
2457
|
try {
|
|
2458
|
+
const _scopeDenied = guardMasterOnly("get_component");
|
|
2459
|
+
if (_scopeDenied)
|
|
2460
|
+
return _scopeDenied;
|
|
2348
2461
|
const component = await convex.query("components:get", {
|
|
2349
2462
|
name,
|
|
2350
2463
|
type,
|
|
@@ -2442,6 +2555,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2442
2555
|
title: "Search components",
|
|
2443
2556
|
}, async ({ query, type, limit }) => {
|
|
2444
2557
|
try {
|
|
2558
|
+
const _scopeDenied = guardMasterOnly("search_components");
|
|
2559
|
+
if (_scopeDenied)
|
|
2560
|
+
return _scopeDenied;
|
|
2445
2561
|
const results = await convex.query("components:search", {
|
|
2446
2562
|
query,
|
|
2447
2563
|
type,
|
|
@@ -2532,13 +2648,16 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2532
2648
|
title: "List recurring tasks",
|
|
2533
2649
|
}, async ({ assignedTo, active, limit }) => {
|
|
2534
2650
|
try {
|
|
2651
|
+
const _scopeDenied = guardMasterOnly("list_recurring_tasks");
|
|
2652
|
+
if (_scopeDenied)
|
|
2653
|
+
return _scopeDenied;
|
|
2535
2654
|
const tasks = await convex.query("recurringTasks:list", {
|
|
2536
2655
|
assignedTo,
|
|
2537
2656
|
active,
|
|
2538
2657
|
limit: limit ?? 50,
|
|
2539
2658
|
});
|
|
2540
2659
|
return {
|
|
2541
|
-
content: [{ type: "text", text: JSON.stringify(tasks, null, 2) }],
|
|
2660
|
+
content: [{ type: "text", text: capListResponseBytes(tasks, JSON.stringify(tasks, null, 2), "list_recurring_tasks") }],
|
|
2542
2661
|
};
|
|
2543
2662
|
}
|
|
2544
2663
|
catch (error) {
|
|
@@ -2875,6 +2994,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2875
2994
|
title: "List mandates",
|
|
2876
2995
|
}, async ({ requestedBy, fulfilledBy, status, limit }) => {
|
|
2877
2996
|
try {
|
|
2997
|
+
const _scopeDenied = guardMasterOnly("list_mandates");
|
|
2998
|
+
if (_scopeDenied)
|
|
2999
|
+
return _scopeDenied;
|
|
2878
3000
|
const mandates = await convex.query("mandates:list", {
|
|
2879
3001
|
requestedBy,
|
|
2880
3002
|
fulfilledBy,
|
|
@@ -2885,7 +3007,7 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2885
3007
|
content: [
|
|
2886
3008
|
{
|
|
2887
3009
|
type: "text",
|
|
2888
|
-
text: JSON.stringify(mandates, null, 2),
|
|
3010
|
+
text: capListResponseBytes(mandates, JSON.stringify(mandates, null, 2), "list_mandates"),
|
|
2889
3011
|
},
|
|
2890
3012
|
],
|
|
2891
3013
|
};
|
|
@@ -3041,6 +3163,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3041
3163
|
title: "Get BU",
|
|
3042
3164
|
}, async ({ buId }) => {
|
|
3043
3165
|
try {
|
|
3166
|
+
const _scopeDenied = guardMasterOnly("get_bu");
|
|
3167
|
+
if (_scopeDenied)
|
|
3168
|
+
return _scopeDenied;
|
|
3044
3169
|
const bu = await convex.query("businessUnits:get", {
|
|
3045
3170
|
buId: buId,
|
|
3046
3171
|
});
|
|
@@ -3080,6 +3205,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3080
3205
|
title: "List BUs",
|
|
3081
3206
|
}, async ({ orchestratorId, status, limit }) => {
|
|
3082
3207
|
try {
|
|
3208
|
+
const _scopeDenied = guardMasterOnly("list_bus");
|
|
3209
|
+
if (_scopeDenied)
|
|
3210
|
+
return _scopeDenied;
|
|
3083
3211
|
const bus = await convex.query("businessUnits:list", {
|
|
3084
3212
|
orchestratorId,
|
|
3085
3213
|
status,
|
|
@@ -3089,7 +3217,7 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3089
3217
|
content: [
|
|
3090
3218
|
{
|
|
3091
3219
|
type: "text",
|
|
3092
|
-
text: JSON.stringify(bus, null, 2),
|
|
3220
|
+
text: capListResponseBytes(bus, JSON.stringify(bus, null, 2), "list_bus"),
|
|
3093
3221
|
},
|
|
3094
3222
|
],
|
|
3095
3223
|
};
|
|
@@ -3176,12 +3304,15 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3176
3304
|
title: "List repo mappings",
|
|
3177
3305
|
}, async () => {
|
|
3178
3306
|
try {
|
|
3307
|
+
const _scopeDenied = guardMasterOnly("list_repo_mappings");
|
|
3308
|
+
if (_scopeDenied)
|
|
3309
|
+
return _scopeDenied;
|
|
3179
3310
|
const mappings = await convex.query("githubRepoMapping:list", {});
|
|
3180
3311
|
return {
|
|
3181
3312
|
content: [
|
|
3182
3313
|
{
|
|
3183
3314
|
type: "text",
|
|
3184
|
-
text: JSON.stringify(mappings, null, 2),
|
|
3315
|
+
text: capListResponseBytes(mappings, JSON.stringify(mappings, null, 2), "list_repo_mappings"),
|
|
3185
3316
|
},
|
|
3186
3317
|
],
|
|
3187
3318
|
};
|
|
@@ -3247,6 +3378,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3247
3378
|
title: "List issues",
|
|
3248
3379
|
}, async ({ project, status, assignedTo, limit }) => {
|
|
3249
3380
|
try {
|
|
3381
|
+
const _scopeDenied = guardMasterOnly("list_issues");
|
|
3382
|
+
if (_scopeDenied)
|
|
3383
|
+
return _scopeDenied;
|
|
3250
3384
|
let results;
|
|
3251
3385
|
if (assignedTo) {
|
|
3252
3386
|
results = await convex.query("issues:listByOrchestrator", {
|
|
@@ -3278,7 +3412,7 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3278
3412
|
content: [
|
|
3279
3413
|
{
|
|
3280
3414
|
type: "text",
|
|
3281
|
-
text: JSON.stringify({ count: results.length, issues: results }, null, 2),
|
|
3415
|
+
text: capListResponseBytes(results, JSON.stringify({ count: results.length, issues: results }, null, 2), "list_issues"),
|
|
3282
3416
|
},
|
|
3283
3417
|
],
|
|
3284
3418
|
};
|
|
@@ -3300,6 +3434,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3300
3434
|
title: "Get issue",
|
|
3301
3435
|
}, async ({ repo, issueNumber }) => {
|
|
3302
3436
|
try {
|
|
3437
|
+
const _scopeDenied = guardMasterOnly("get_issue");
|
|
3438
|
+
if (_scopeDenied)
|
|
3439
|
+
return _scopeDenied;
|
|
3303
3440
|
const issue = await convex.query("issues:getByRepoNumber", {
|
|
3304
3441
|
repo,
|
|
3305
3442
|
issueNumber,
|
|
@@ -3434,6 +3571,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3434
3571
|
title: "Issue statistics",
|
|
3435
3572
|
}, async ({ project }) => {
|
|
3436
3573
|
try {
|
|
3574
|
+
const _scopeDenied = guardMasterOnly("issue_stats");
|
|
3575
|
+
if (_scopeDenied)
|
|
3576
|
+
return _scopeDenied;
|
|
3437
3577
|
const stats = await convex.query("issues:getStats", {
|
|
3438
3578
|
project,
|
|
3439
3579
|
});
|
|
@@ -3590,6 +3730,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3590
3730
|
title: "Search fix patterns",
|
|
3591
3731
|
}, async ({ query, limit }) => {
|
|
3592
3732
|
try {
|
|
3733
|
+
const _scopeDenied = guardMasterOnly("search_fix_patterns");
|
|
3734
|
+
if (_scopeDenied)
|
|
3735
|
+
return _scopeDenied;
|
|
3593
3736
|
const results = await convex.action("search:searchFixPatterns", {
|
|
3594
3737
|
query,
|
|
3595
3738
|
limit,
|
|
@@ -3621,13 +3764,16 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3621
3764
|
title: "List fix patterns",
|
|
3622
3765
|
}, async ({ project, limit }) => {
|
|
3623
3766
|
try {
|
|
3767
|
+
const _scopeDenied = guardMasterOnly("list_fix_patterns");
|
|
3768
|
+
if (_scopeDenied)
|
|
3769
|
+
return _scopeDenied;
|
|
3624
3770
|
if (project) {
|
|
3625
3771
|
const results = await convex.query("fixPatterns:listByProject", {
|
|
3626
3772
|
sourceProject: project,
|
|
3627
3773
|
limit,
|
|
3628
3774
|
});
|
|
3629
3775
|
return {
|
|
3630
|
-
content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
|
|
3776
|
+
content: [{ type: "text", text: capListResponseBytes(results, JSON.stringify(results, null, 2), "list_fix_patterns") }],
|
|
3631
3777
|
};
|
|
3632
3778
|
}
|
|
3633
3779
|
const allResults = await convex.query("fixPatterns:listAll", {
|
|
@@ -3635,7 +3781,7 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3635
3781
|
});
|
|
3636
3782
|
return {
|
|
3637
3783
|
content: [
|
|
3638
|
-
{ type: "text", text: JSON.stringify(allResults, null, 2) },
|
|
3784
|
+
{ type: "text", text: capListResponseBytes(allResults, JSON.stringify(allResults, null, 2), "list_fix_patterns") },
|
|
3639
3785
|
],
|
|
3640
3786
|
};
|
|
3641
3787
|
}
|
|
@@ -3682,6 +3828,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3682
3828
|
title: "Get mission template",
|
|
3683
3829
|
}, async ({ name }) => {
|
|
3684
3830
|
try {
|
|
3831
|
+
const _scopeDenied = guardMasterOnly("get_mission_template");
|
|
3832
|
+
if (_scopeDenied)
|
|
3833
|
+
return _scopeDenied;
|
|
3685
3834
|
const template = await convex.query("missionTemplates:getByName", { name });
|
|
3686
3835
|
return {
|
|
3687
3836
|
content: [
|
|
@@ -3908,6 +4057,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3908
4057
|
title: "List errors",
|
|
3909
4058
|
}, async ({ deployment, limit }) => {
|
|
3910
4059
|
try {
|
|
4060
|
+
const _scopeDenied = guardMasterOnly("list_errors");
|
|
4061
|
+
if (_scopeDenied)
|
|
4062
|
+
return _scopeDenied;
|
|
3911
4063
|
const errors = await convex.query("errorMonitor:listErrors", {
|
|
3912
4064
|
deployment,
|
|
3913
4065
|
limit: limit ?? 50,
|
|
@@ -3916,7 +4068,7 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3916
4068
|
content: [
|
|
3917
4069
|
{
|
|
3918
4070
|
type: "text",
|
|
3919
|
-
text: JSON.stringify(errors, null, 2),
|
|
4071
|
+
text: capListResponseBytes(errors, JSON.stringify(errors, null, 2), "list_errors"),
|
|
3920
4072
|
},
|
|
3921
4073
|
],
|
|
3922
4074
|
};
|
|
@@ -3935,6 +4087,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3935
4087
|
title: "Get error",
|
|
3936
4088
|
}, async ({ errorId }) => {
|
|
3937
4089
|
try {
|
|
4090
|
+
const _scopeDenied = guardMasterOnly("get_error");
|
|
4091
|
+
if (_scopeDenied)
|
|
4092
|
+
return _scopeDenied;
|
|
3938
4093
|
const error = await convex.query("errorMonitor:getError", {
|
|
3939
4094
|
errorId: errorId,
|
|
3940
4095
|
});
|
package/package.json
CHANGED