vantage-peers-mcp 2.4.1 → 2.4.2
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/dist/src/auth.js +14 -2
- package/dist/src/tools.js +105 -0
- package/package.json +1 -1
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.js
CHANGED
|
@@ -508,6 +508,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
508
508
|
title: "Get memory",
|
|
509
509
|
}, async ({ memoryId }) => {
|
|
510
510
|
try {
|
|
511
|
+
const _scopeDenied = guardMasterOnly("get_memory");
|
|
512
|
+
if (_scopeDenied)
|
|
513
|
+
return _scopeDenied;
|
|
511
514
|
const memory = await convex.query("memories:getMemory", {
|
|
512
515
|
memoryId,
|
|
513
516
|
});
|
|
@@ -725,6 +728,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
725
728
|
title: "Get orchestrator profile",
|
|
726
729
|
}, async ({ orchestratorId }) => {
|
|
727
730
|
try {
|
|
731
|
+
const _scopeDenied = guardMasterOnly("get_profile");
|
|
732
|
+
if (_scopeDenied)
|
|
733
|
+
return _scopeDenied;
|
|
728
734
|
const profile = await convex.query("profiles:getProfile", {
|
|
729
735
|
orchestratorId,
|
|
730
736
|
});
|
|
@@ -944,6 +950,13 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
944
950
|
title: "Check messages",
|
|
945
951
|
}, async ({ recipient, recipientInstanceId, tenantId, since }) => {
|
|
946
952
|
try {
|
|
953
|
+
// Non-master: force recipient to caller's own userId. Anything else
|
|
954
|
+
// would let the client read another tenant's inbox.
|
|
955
|
+
if (oauthCtx && !isMasterScope(oauthCtx)) {
|
|
956
|
+
if (recipient !== oauthCtx.userId) {
|
|
957
|
+
return mcpError(`Forbidden: check_messages can only read messages for your own identity ('${oauthCtx.userId}'), not '${recipient}'.`);
|
|
958
|
+
}
|
|
959
|
+
}
|
|
947
960
|
const messages = await convex.query("messages:checkNewMessages", {
|
|
948
961
|
recipient,
|
|
949
962
|
recipientInstanceId,
|
|
@@ -1105,6 +1118,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1105
1118
|
title: "List peers",
|
|
1106
1119
|
}, async () => {
|
|
1107
1120
|
try {
|
|
1121
|
+
const _scopeDenied = guardMasterOnly("list_peers");
|
|
1122
|
+
if (_scopeDenied)
|
|
1123
|
+
return _scopeDenied;
|
|
1108
1124
|
const profiles = await convex.query("profiles:listProfiles", {});
|
|
1109
1125
|
const peers = profiles.map((p) => ({
|
|
1110
1126
|
id: p.orchestratorId,
|
|
@@ -1152,6 +1168,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1152
1168
|
title: "List messages",
|
|
1153
1169
|
}, async ({ sessionDay, from, limit }) => {
|
|
1154
1170
|
try {
|
|
1171
|
+
const _scopeDenied = guardMasterOnly("list_messages");
|
|
1172
|
+
if (_scopeDenied)
|
|
1173
|
+
return _scopeDenied;
|
|
1155
1174
|
const messages = await convex.query("messages:listMessages", {
|
|
1156
1175
|
sessionDay,
|
|
1157
1176
|
from,
|
|
@@ -1190,6 +1209,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1190
1209
|
title: "List broadcast status",
|
|
1191
1210
|
}, async ({ messageId }) => {
|
|
1192
1211
|
try {
|
|
1212
|
+
const _scopeDenied = guardMasterOnly("list_broadcast_status");
|
|
1213
|
+
if (_scopeDenied)
|
|
1214
|
+
return _scopeDenied;
|
|
1193
1215
|
const status = await convex.query("messages:listBroadcastStatus", {
|
|
1194
1216
|
messageId,
|
|
1195
1217
|
});
|
|
@@ -1314,6 +1336,16 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1314
1336
|
title: "List tasks",
|
|
1315
1337
|
}, async ({ assignedTo, assignedToInstance, status, project, limit, fields, createdBy, updatedSince, }) => {
|
|
1316
1338
|
try {
|
|
1339
|
+
// Non-master: must scope to own identity. If neither assignedTo
|
|
1340
|
+
// nor createdBy matches the caller's userId, reject — otherwise
|
|
1341
|
+
// the query would span the whole tenant table.
|
|
1342
|
+
if (oauthCtx && !isMasterScope(oauthCtx)) {
|
|
1343
|
+
const myId = oauthCtx.userId;
|
|
1344
|
+
const scopedToSelf = assignedTo === myId || createdBy === myId;
|
|
1345
|
+
if (!scopedToSelf) {
|
|
1346
|
+
return mcpError(`Forbidden: list_tasks requires assignedTo='${myId}' or createdBy='${myId}' for non-master scope (current: ${oauthCtx.scopeProfile}).`);
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1317
1349
|
const tasks = await convex.query("tasks:list", {
|
|
1318
1350
|
assignedTo,
|
|
1319
1351
|
assignedToInstance,
|
|
@@ -1695,6 +1727,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1695
1727
|
title: "List tasks by mission",
|
|
1696
1728
|
}, async ({ missionId, status, limit, fields, createdBy, updatedSince }) => {
|
|
1697
1729
|
try {
|
|
1730
|
+
const _scopeDenied = guardMasterOnly("list_tasks_by_mission");
|
|
1731
|
+
if (_scopeDenied)
|
|
1732
|
+
return _scopeDenied;
|
|
1698
1733
|
const tasks = await convex.query("tasks:listByMission", {
|
|
1699
1734
|
missionId: missionId,
|
|
1700
1735
|
status,
|
|
@@ -1802,6 +1837,13 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1802
1837
|
title: "List missions",
|
|
1803
1838
|
}, async ({ project, pilot, status, limit, fields, updatedSince }) => {
|
|
1804
1839
|
try {
|
|
1840
|
+
// Non-master: must pilot=<own-userId>. Otherwise the query spans
|
|
1841
|
+
// every tenant's missions.
|
|
1842
|
+
if (oauthCtx && !isMasterScope(oauthCtx)) {
|
|
1843
|
+
if (pilot !== oauthCtx.userId) {
|
|
1844
|
+
return mcpError(`Forbidden: list_missions requires pilot='${oauthCtx.userId}' for non-master scope (current: ${oauthCtx.scopeProfile}).`);
|
|
1845
|
+
}
|
|
1846
|
+
}
|
|
1805
1847
|
const missions = await convex.query("missions:list", {
|
|
1806
1848
|
project,
|
|
1807
1849
|
pilot,
|
|
@@ -1843,6 +1885,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1843
1885
|
title: "Get mission",
|
|
1844
1886
|
}, async ({ missionId }) => {
|
|
1845
1887
|
try {
|
|
1888
|
+
const _scopeDenied = guardMasterOnly("get_mission");
|
|
1889
|
+
if (_scopeDenied)
|
|
1890
|
+
return _scopeDenied;
|
|
1846
1891
|
const mission = await convex.query("missions:get", {
|
|
1847
1892
|
missionId: missionId,
|
|
1848
1893
|
});
|
|
@@ -2002,6 +2047,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2002
2047
|
title: "Get diary entry",
|
|
2003
2048
|
}, async ({ date, orchestrator }) => {
|
|
2004
2049
|
try {
|
|
2050
|
+
const _scopeDenied = guardMasterOnly("get_diary");
|
|
2051
|
+
if (_scopeDenied)
|
|
2052
|
+
return _scopeDenied;
|
|
2005
2053
|
const entry = await convex.query("diary:get", {
|
|
2006
2054
|
date,
|
|
2007
2055
|
orchestrator,
|
|
@@ -2050,6 +2098,12 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2050
2098
|
title: "List diary entries",
|
|
2051
2099
|
}, async ({ orchestrator, limit }) => {
|
|
2052
2100
|
try {
|
|
2101
|
+
// Non-master: must scope to own orchestrator id.
|
|
2102
|
+
if (oauthCtx && !isMasterScope(oauthCtx)) {
|
|
2103
|
+
if (orchestrator !== oauthCtx.userId) {
|
|
2104
|
+
return mcpError(`Forbidden: list_diaries requires orchestrator='${oauthCtx.userId}' for non-master scope (current: ${oauthCtx.scopeProfile}).`);
|
|
2105
|
+
}
|
|
2106
|
+
}
|
|
2053
2107
|
const entries = await convex.query("diary:list", {
|
|
2054
2108
|
orchestrator,
|
|
2055
2109
|
limit: limit ?? 20,
|
|
@@ -2203,6 +2257,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2203
2257
|
title: "List briefing notes",
|
|
2204
2258
|
}, async ({ topic, limit, fields, updatedSince }) => {
|
|
2205
2259
|
try {
|
|
2260
|
+
const _scopeDenied = guardMasterOnly("list_briefing_notes");
|
|
2261
|
+
if (_scopeDenied)
|
|
2262
|
+
return _scopeDenied;
|
|
2206
2263
|
const notes = await convex.query("briefingNotes:list", {
|
|
2207
2264
|
topic,
|
|
2208
2265
|
limit,
|
|
@@ -2316,6 +2373,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2316
2373
|
title: "List components",
|
|
2317
2374
|
}, async ({ type, team, limit }) => {
|
|
2318
2375
|
try {
|
|
2376
|
+
const _scopeDenied = guardMasterOnly("list_components");
|
|
2377
|
+
if (_scopeDenied)
|
|
2378
|
+
return _scopeDenied;
|
|
2319
2379
|
const components = await convex.query("components:list", {
|
|
2320
2380
|
type,
|
|
2321
2381
|
team,
|
|
@@ -2345,6 +2405,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2345
2405
|
title: "Get component",
|
|
2346
2406
|
}, async ({ name, type }) => {
|
|
2347
2407
|
try {
|
|
2408
|
+
const _scopeDenied = guardMasterOnly("get_component");
|
|
2409
|
+
if (_scopeDenied)
|
|
2410
|
+
return _scopeDenied;
|
|
2348
2411
|
const component = await convex.query("components:get", {
|
|
2349
2412
|
name,
|
|
2350
2413
|
type,
|
|
@@ -2442,6 +2505,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2442
2505
|
title: "Search components",
|
|
2443
2506
|
}, async ({ query, type, limit }) => {
|
|
2444
2507
|
try {
|
|
2508
|
+
const _scopeDenied = guardMasterOnly("search_components");
|
|
2509
|
+
if (_scopeDenied)
|
|
2510
|
+
return _scopeDenied;
|
|
2445
2511
|
const results = await convex.query("components:search", {
|
|
2446
2512
|
query,
|
|
2447
2513
|
type,
|
|
@@ -2532,6 +2598,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2532
2598
|
title: "List recurring tasks",
|
|
2533
2599
|
}, async ({ assignedTo, active, limit }) => {
|
|
2534
2600
|
try {
|
|
2601
|
+
const _scopeDenied = guardMasterOnly("list_recurring_tasks");
|
|
2602
|
+
if (_scopeDenied)
|
|
2603
|
+
return _scopeDenied;
|
|
2535
2604
|
const tasks = await convex.query("recurringTasks:list", {
|
|
2536
2605
|
assignedTo,
|
|
2537
2606
|
active,
|
|
@@ -2875,6 +2944,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2875
2944
|
title: "List mandates",
|
|
2876
2945
|
}, async ({ requestedBy, fulfilledBy, status, limit }) => {
|
|
2877
2946
|
try {
|
|
2947
|
+
const _scopeDenied = guardMasterOnly("list_mandates");
|
|
2948
|
+
if (_scopeDenied)
|
|
2949
|
+
return _scopeDenied;
|
|
2878
2950
|
const mandates = await convex.query("mandates:list", {
|
|
2879
2951
|
requestedBy,
|
|
2880
2952
|
fulfilledBy,
|
|
@@ -3041,6 +3113,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3041
3113
|
title: "Get BU",
|
|
3042
3114
|
}, async ({ buId }) => {
|
|
3043
3115
|
try {
|
|
3116
|
+
const _scopeDenied = guardMasterOnly("get_bu");
|
|
3117
|
+
if (_scopeDenied)
|
|
3118
|
+
return _scopeDenied;
|
|
3044
3119
|
const bu = await convex.query("businessUnits:get", {
|
|
3045
3120
|
buId: buId,
|
|
3046
3121
|
});
|
|
@@ -3080,6 +3155,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3080
3155
|
title: "List BUs",
|
|
3081
3156
|
}, async ({ orchestratorId, status, limit }) => {
|
|
3082
3157
|
try {
|
|
3158
|
+
const _scopeDenied = guardMasterOnly("list_bus");
|
|
3159
|
+
if (_scopeDenied)
|
|
3160
|
+
return _scopeDenied;
|
|
3083
3161
|
const bus = await convex.query("businessUnits:list", {
|
|
3084
3162
|
orchestratorId,
|
|
3085
3163
|
status,
|
|
@@ -3176,6 +3254,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3176
3254
|
title: "List repo mappings",
|
|
3177
3255
|
}, async () => {
|
|
3178
3256
|
try {
|
|
3257
|
+
const _scopeDenied = guardMasterOnly("list_repo_mappings");
|
|
3258
|
+
if (_scopeDenied)
|
|
3259
|
+
return _scopeDenied;
|
|
3179
3260
|
const mappings = await convex.query("githubRepoMapping:list", {});
|
|
3180
3261
|
return {
|
|
3181
3262
|
content: [
|
|
@@ -3247,6 +3328,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3247
3328
|
title: "List issues",
|
|
3248
3329
|
}, async ({ project, status, assignedTo, limit }) => {
|
|
3249
3330
|
try {
|
|
3331
|
+
const _scopeDenied = guardMasterOnly("list_issues");
|
|
3332
|
+
if (_scopeDenied)
|
|
3333
|
+
return _scopeDenied;
|
|
3250
3334
|
let results;
|
|
3251
3335
|
if (assignedTo) {
|
|
3252
3336
|
results = await convex.query("issues:listByOrchestrator", {
|
|
@@ -3300,6 +3384,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3300
3384
|
title: "Get issue",
|
|
3301
3385
|
}, async ({ repo, issueNumber }) => {
|
|
3302
3386
|
try {
|
|
3387
|
+
const _scopeDenied = guardMasterOnly("get_issue");
|
|
3388
|
+
if (_scopeDenied)
|
|
3389
|
+
return _scopeDenied;
|
|
3303
3390
|
const issue = await convex.query("issues:getByRepoNumber", {
|
|
3304
3391
|
repo,
|
|
3305
3392
|
issueNumber,
|
|
@@ -3434,6 +3521,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3434
3521
|
title: "Issue statistics",
|
|
3435
3522
|
}, async ({ project }) => {
|
|
3436
3523
|
try {
|
|
3524
|
+
const _scopeDenied = guardMasterOnly("issue_stats");
|
|
3525
|
+
if (_scopeDenied)
|
|
3526
|
+
return _scopeDenied;
|
|
3437
3527
|
const stats = await convex.query("issues:getStats", {
|
|
3438
3528
|
project,
|
|
3439
3529
|
});
|
|
@@ -3590,6 +3680,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3590
3680
|
title: "Search fix patterns",
|
|
3591
3681
|
}, async ({ query, limit }) => {
|
|
3592
3682
|
try {
|
|
3683
|
+
const _scopeDenied = guardMasterOnly("search_fix_patterns");
|
|
3684
|
+
if (_scopeDenied)
|
|
3685
|
+
return _scopeDenied;
|
|
3593
3686
|
const results = await convex.action("search:searchFixPatterns", {
|
|
3594
3687
|
query,
|
|
3595
3688
|
limit,
|
|
@@ -3621,6 +3714,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3621
3714
|
title: "List fix patterns",
|
|
3622
3715
|
}, async ({ project, limit }) => {
|
|
3623
3716
|
try {
|
|
3717
|
+
const _scopeDenied = guardMasterOnly("list_fix_patterns");
|
|
3718
|
+
if (_scopeDenied)
|
|
3719
|
+
return _scopeDenied;
|
|
3624
3720
|
if (project) {
|
|
3625
3721
|
const results = await convex.query("fixPatterns:listByProject", {
|
|
3626
3722
|
sourceProject: project,
|
|
@@ -3682,6 +3778,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3682
3778
|
title: "Get mission template",
|
|
3683
3779
|
}, async ({ name }) => {
|
|
3684
3780
|
try {
|
|
3781
|
+
const _scopeDenied = guardMasterOnly("get_mission_template");
|
|
3782
|
+
if (_scopeDenied)
|
|
3783
|
+
return _scopeDenied;
|
|
3685
3784
|
const template = await convex.query("missionTemplates:getByName", { name });
|
|
3686
3785
|
return {
|
|
3687
3786
|
content: [
|
|
@@ -3908,6 +4007,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3908
4007
|
title: "List errors",
|
|
3909
4008
|
}, async ({ deployment, limit }) => {
|
|
3910
4009
|
try {
|
|
4010
|
+
const _scopeDenied = guardMasterOnly("list_errors");
|
|
4011
|
+
if (_scopeDenied)
|
|
4012
|
+
return _scopeDenied;
|
|
3911
4013
|
const errors = await convex.query("errorMonitor:listErrors", {
|
|
3912
4014
|
deployment,
|
|
3913
4015
|
limit: limit ?? 50,
|
|
@@ -3935,6 +4037,9 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3935
4037
|
title: "Get error",
|
|
3936
4038
|
}, async ({ errorId }) => {
|
|
3937
4039
|
try {
|
|
4040
|
+
const _scopeDenied = guardMasterOnly("get_error");
|
|
4041
|
+
if (_scopeDenied)
|
|
4042
|
+
return _scopeDenied;
|
|
3938
4043
|
const error = await convex.query("errorMonitor:getError", {
|
|
3939
4044
|
errorId: errorId,
|
|
3940
4045
|
});
|
package/package.json
CHANGED