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 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
- return null; // no namespace filter list-across, which master-only in practice
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vantage-peers-mcp",
3
- "version": "2.4.1",
3
+ "version": "2.4.2",
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",