vantage-peers-mcp 2.4.0 → 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/README.md +14 -3
- package/dist/src/auth.js +23 -5
- package/dist/src/tools.js +545 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/vantage-peers-mcp)
|
|
4
4
|
[](https://www.npmjs.com/package/vantage-peers-mcp)
|
|
5
5
|
[](https://github.com/vantageos-agency/vantage-peers/blob/main/LICENSE)
|
|
6
|
-
[]()
|
|
7
7
|
|
|
8
8
|
MCP server for [VantagePeers](https://vantagepeers.com) — shared memory, messaging, and task coordination for AI agent teams.
|
|
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
12
|
## Quick start
|
|
13
13
|
|
|
@@ -74,7 +74,7 @@ VantagePeers ships a built-in OAuth 2.1 authorization server so Claude.ai web ca
|
|
|
74
74
|
|
|
75
75
|
The server also reads `CONVEX_URL` from `.env.local` in the parent directory if not set via environment.
|
|
76
76
|
|
|
77
|
-
## Tools (
|
|
77
|
+
## Tools (84)
|
|
78
78
|
|
|
79
79
|
### Memory (6)
|
|
80
80
|
`store_memory`, `recall`, `list_memories`, `soft_delete_memory`, `get_memory`, `store_episode`
|
|
@@ -400,6 +400,17 @@ All orchestrator names are open strings — any lowercase name is accepted. The
|
|
|
400
400
|
|
|
401
401
|
## Changelog
|
|
402
402
|
|
|
403
|
+
### 2.4.1 — 2026-05-30 (Day 88)
|
|
404
|
+
- 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
|
+
- 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.
|
|
406
|
+
- feat(mcp): ChatGPT Apps SDK tool annotations on all 84 tools (`readOnlyHint`, `openWorldHint`, `destructiveHint`) — 34 read-only + 41 write + 9 destructive — PR #555.
|
|
407
|
+
- security(dcr): DCR scope isolation — new `public-readonly` profile + cross-tenant assertion tests + `scopeProfile` forced to `client-generic` for auto-discovery flow (never `master`) — PR #554.
|
|
408
|
+
- docs(cloud): dedicated `/docs/cloud/` section in vantage-peers-site for VantagePeers Cloud (multi-tenant, multi-clients MCP: Claude.ai, ChatGPT, Claude Code, Codex) — site PR #120.
|
|
409
|
+
|
|
410
|
+
### 2.4.0 — 2026-05-29 (Day 86)
|
|
411
|
+
- feat(m3): `iframeEmbedSessions` table + `__VP_TOOL_RESULT__` stream marker + ack-checklist primitive — PR #545.
|
|
412
|
+
- feat(v0.0.2-auth): `credentials:issueBearerFromClerk` httpAction + audit log + iter 2 P1 fixes — PR #546.
|
|
413
|
+
|
|
403
414
|
### 2.3.0 — 2026-05-26
|
|
404
415
|
- `list_tasks`, `list_missions`, `list_tasks_by_mission`, `list_briefing_notes` now accept `fields=lite` for compact payloads (less tokens).
|
|
405
416
|
- Status filters now accept arrays and aliases: `status=["todo","in_progress"]`, `status="open"` (expands to non-terminal), `status="active"` (in_progress only on tasks; plan+execute on missions), `status="all"` (no filter).
|
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}.`;
|
|
@@ -139,11 +151,13 @@ export function checkNamespaceWrite(ctx, namespace) {
|
|
|
139
151
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
140
152
|
export function bearerAuthMiddleware() {
|
|
141
153
|
return async (c, next) => {
|
|
142
|
-
//
|
|
143
|
-
//
|
|
154
|
+
// MCP spec §"Protected Resource Metadata Discovery Requirements" + RFC 6750 §3 —
|
|
155
|
+
// the param MUST be `resource_metadata=` (not `resource=`). Claude.ai's OAuth
|
|
156
|
+
// connector looks for `resource_metadata=` to bootstrap PRM discovery; with
|
|
157
|
+
// `resource=` the entire DCR chain breaks before any token is issued.
|
|
144
158
|
const publicBaseUrl = process.env.PUBLIC_BASE_URL ??
|
|
145
159
|
"https://vantage-peers-production.up.railway.app";
|
|
146
|
-
const wwwAuthHeader = `Bearer
|
|
160
|
+
const wwwAuthHeader = `Bearer resource_metadata="${publicBaseUrl}/.well-known/oauth-protected-resource"`;
|
|
147
161
|
const authHeader = c.req.header("Authorization");
|
|
148
162
|
if (!authHeader?.startsWith("Bearer ")) {
|
|
149
163
|
c.header("WWW-Authenticate", wwwAuthHeader);
|
|
@@ -215,6 +229,10 @@ export function bearerAuthMiddleware() {
|
|
|
215
229
|
// ── (3) DCR OAuth token — check oauthTokens via oauthDcr:validateAccessToken
|
|
216
230
|
// Uses raw token (not hashed) — the DCR table stores tokens in plaintext.
|
|
217
231
|
// This path handles Claude.ai clients registered via POST /register.
|
|
232
|
+
// NOTE: validateAccessToken is exposed as a PUBLIC query (not internalQuery)
|
|
233
|
+
// because ConvexHttpClient.query() only resolves public functions. Making it
|
|
234
|
+
// internal silently breaks the DCR path (#556). Security: lookup is keyed
|
|
235
|
+
// on the high-entropy opaque token; returns null on miss with no PII echo.
|
|
218
236
|
let dcrResult = null;
|
|
219
237
|
try {
|
|
220
238
|
dcrResult = (await internalClient().query(
|
package/dist/src/tools.js
CHANGED
|
@@ -153,7 +153,9 @@ const taskStatusValues = [
|
|
|
153
153
|
"done",
|
|
154
154
|
];
|
|
155
155
|
const taskStatusAliases = ["open", "active", "all"];
|
|
156
|
-
export const taskStatusSchema = z
|
|
156
|
+
export const taskStatusSchema = z
|
|
157
|
+
.enum(taskStatusValues)
|
|
158
|
+
.describe("Task status");
|
|
157
159
|
const missionStatusValues = [
|
|
158
160
|
"brainstorm",
|
|
159
161
|
"plan",
|
|
@@ -176,7 +178,7 @@ export const taskStatusFilterSchema = z
|
|
|
176
178
|
])
|
|
177
179
|
.describe('Task status filter. Single status ("todo"|"in_progress"|"review"|"blocked"|"done"), ' +
|
|
178
180
|
'alias ("open" = todo+in_progress+review+blocked, "active" = todo+in_progress, "all" = no filter), ' +
|
|
179
|
-
|
|
181
|
+
"or array of direct statuses (no aliases inside array).");
|
|
180
182
|
export const missionStatusFilterSchema = z
|
|
181
183
|
.union([
|
|
182
184
|
z.enum([...missionStatusValues, ...missionStatusAliases]),
|
|
@@ -184,13 +186,13 @@ export const missionStatusFilterSchema = z
|
|
|
184
186
|
])
|
|
185
187
|
.describe('Mission status filter. Single status ("brainstorm"|"plan"|"execute"|"validate"|"complete"), ' +
|
|
186
188
|
'alias ("open" = brainstorm+plan+execute+validate, "active" = plan+execute, "all" = no filter), ' +
|
|
187
|
-
|
|
189
|
+
"or array of direct statuses (no aliases inside array).");
|
|
188
190
|
// v2.3.2 — fields projection toggle. "lite" returns compact projection
|
|
189
191
|
// (5-10× smaller payload), "full" (default) returns full doc.
|
|
190
192
|
export const fieldsSchema = z
|
|
191
193
|
.enum(["lite", "full"])
|
|
192
194
|
.describe('Field projection — "lite" returns compact fields only ' +
|
|
193
|
-
|
|
195
|
+
"(typical 5-10× smaller payload for large list scans), " +
|
|
194
196
|
'"full" (default) returns the full document.');
|
|
195
197
|
// v2.3.3 — Unix timestamp ms filter for "updated since".
|
|
196
198
|
// Pass `Date.now() - 24*60*60*1000` for "last 24h" pattern.
|
|
@@ -296,7 +298,8 @@ export function parseConvexError(rawMessage) {
|
|
|
296
298
|
let code = "ServerError";
|
|
297
299
|
let remainder = stripped;
|
|
298
300
|
for (const candidate of knownCodes) {
|
|
299
|
-
if (stripped.startsWith(candidate + ":") ||
|
|
301
|
+
if (stripped.startsWith(candidate + ":") ||
|
|
302
|
+
stripped.startsWith(candidate + " ")) {
|
|
300
303
|
code = candidate;
|
|
301
304
|
remainder = stripped.slice(candidate.length).replace(/^[:\s]+/, "");
|
|
302
305
|
break;
|
|
@@ -305,10 +308,13 @@ export function parseConvexError(rawMessage) {
|
|
|
305
308
|
// Extract "Path: .<fieldPath>" from the tail of the message
|
|
306
309
|
// Convex appends this as the last sentence: "Path: .linkedMemoryIds[4]"
|
|
307
310
|
let path = null;
|
|
308
|
-
const pathMatch = remainder.match(/\bPath:\s*([\w
|
|
311
|
+
const pathMatch = remainder.match(/\bPath:\s*([\w.[\]"']+)\s*$/);
|
|
309
312
|
if (pathMatch) {
|
|
310
313
|
path = pathMatch[1];
|
|
311
|
-
remainder = remainder
|
|
314
|
+
remainder = remainder
|
|
315
|
+
.slice(0, pathMatch.index)
|
|
316
|
+
.trim()
|
|
317
|
+
.replace(/\.\s*$/, "");
|
|
312
318
|
}
|
|
313
319
|
// Build a concise hint for common patterns
|
|
314
320
|
let hint = null;
|
|
@@ -412,6 +418,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
412
418
|
.string()
|
|
413
419
|
.optional()
|
|
414
420
|
.describe("Optional expiry ISO timestamp e.g. '2026-06-01T00:00:00Z'"),
|
|
421
|
+
}, {
|
|
422
|
+
readOnlyHint: false,
|
|
423
|
+
openWorldHint: false,
|
|
424
|
+
destructiveHint: false,
|
|
425
|
+
title: "Store memory",
|
|
415
426
|
}, async ({ namespace, type, content, createdBy, relatesTo, ttl }) => {
|
|
416
427
|
let contentBytes = 0;
|
|
417
428
|
try {
|
|
@@ -461,6 +472,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
461
472
|
memoryId: z
|
|
462
473
|
.string()
|
|
463
474
|
.describe("Convex document ID of the memory to soft-delete"),
|
|
475
|
+
}, {
|
|
476
|
+
readOnlyHint: false,
|
|
477
|
+
openWorldHint: false,
|
|
478
|
+
destructiveHint: true,
|
|
479
|
+
title: "Delete memory (soft)",
|
|
464
480
|
}, async ({ memoryId }) => {
|
|
465
481
|
try {
|
|
466
482
|
const denied = guardMasterOnly("soft_delete_memory");
|
|
@@ -485,8 +501,16 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
485
501
|
// ── get_memory ──────────────────────────────────────────────────────────────
|
|
486
502
|
server.tool("get_memory", "Fetch a single memory by its ID. Returns full memory content including relations and episode data.", {
|
|
487
503
|
memoryId: z.string().describe("Memory document ID"),
|
|
504
|
+
}, {
|
|
505
|
+
readOnlyHint: true,
|
|
506
|
+
openWorldHint: false,
|
|
507
|
+
destructiveHint: false,
|
|
508
|
+
title: "Get memory",
|
|
488
509
|
}, async ({ memoryId }) => {
|
|
489
510
|
try {
|
|
511
|
+
const _scopeDenied = guardMasterOnly("get_memory");
|
|
512
|
+
if (_scopeDenied)
|
|
513
|
+
return _scopeDenied;
|
|
490
514
|
const memory = await convex.query("memories:getMemory", {
|
|
491
515
|
memoryId,
|
|
492
516
|
});
|
|
@@ -519,6 +543,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
519
543
|
.optional()
|
|
520
544
|
.default(5)
|
|
521
545
|
.describe("Maximum number of results to return (default 5)"),
|
|
546
|
+
}, {
|
|
547
|
+
readOnlyHint: true,
|
|
548
|
+
openWorldHint: false,
|
|
549
|
+
destructiveHint: false,
|
|
550
|
+
title: "Recall memories",
|
|
522
551
|
}, async ({ query, namespace, type, limit }) => {
|
|
523
552
|
try {
|
|
524
553
|
const nsDenied = guardRead(namespace);
|
|
@@ -559,6 +588,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
559
588
|
.optional()
|
|
560
589
|
.default(10)
|
|
561
590
|
.describe("Max results"),
|
|
591
|
+
}, {
|
|
592
|
+
readOnlyHint: true,
|
|
593
|
+
openWorldHint: false,
|
|
594
|
+
destructiveHint: false,
|
|
595
|
+
title: "Search memories (text)",
|
|
562
596
|
}, async ({ query, namespace, type, limit }) => {
|
|
563
597
|
try {
|
|
564
598
|
const nsDenied = guardRead(namespace);
|
|
@@ -603,6 +637,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
603
637
|
.max(1)
|
|
604
638
|
.optional()
|
|
605
639
|
.describe("Weight for text results in RRF (default: 0.5)"),
|
|
640
|
+
}, {
|
|
641
|
+
readOnlyHint: true,
|
|
642
|
+
openWorldHint: false,
|
|
643
|
+
destructiveHint: false,
|
|
644
|
+
title: "Search memories (hybrid)",
|
|
606
645
|
}, async ({ query, namespace, type, limit, vectorWeight, textWeight }) => {
|
|
607
646
|
try {
|
|
608
647
|
const nsDenied = guardRead(namespace);
|
|
@@ -642,6 +681,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
642
681
|
.string()
|
|
643
682
|
.describe("The lesson extracted — procedural memory, what to do differently"),
|
|
644
683
|
severity: severitySchema,
|
|
684
|
+
}, {
|
|
685
|
+
readOnlyHint: false,
|
|
686
|
+
openWorldHint: false,
|
|
687
|
+
destructiveHint: false,
|
|
688
|
+
title: "Store episode",
|
|
645
689
|
}, async ({ namespace, createdBy, context, goal, action, outcome, insight, severity, }) => {
|
|
646
690
|
try {
|
|
647
691
|
const fromDenied = guardFrom(createdBy);
|
|
@@ -677,8 +721,16 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
677
721
|
server.tool("get_profile", "Fetch an orchestrator profile (static identity + dynamic session state). " +
|
|
678
722
|
"Returns null if the profile does not exist yet — call update_profile to create it.", {
|
|
679
723
|
orchestratorId: z.string().describe("Orchestrator identifier"),
|
|
724
|
+
}, {
|
|
725
|
+
readOnlyHint: true,
|
|
726
|
+
openWorldHint: false,
|
|
727
|
+
destructiveHint: false,
|
|
728
|
+
title: "Get orchestrator profile",
|
|
680
729
|
}, async ({ orchestratorId }) => {
|
|
681
730
|
try {
|
|
731
|
+
const _scopeDenied = guardMasterOnly("get_profile");
|
|
732
|
+
if (_scopeDenied)
|
|
733
|
+
return _scopeDenied;
|
|
682
734
|
const profile = await convex.query("profiles:getProfile", {
|
|
683
735
|
orchestratorId,
|
|
684
736
|
});
|
|
@@ -724,6 +776,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
724
776
|
})
|
|
725
777
|
.optional()
|
|
726
778
|
.describe("Mutable session state — updated each session"),
|
|
779
|
+
}, {
|
|
780
|
+
readOnlyHint: false,
|
|
781
|
+
openWorldHint: false,
|
|
782
|
+
destructiveHint: false,
|
|
783
|
+
title: "Update orchestrator profile",
|
|
727
784
|
}, async ({ orchestratorId, name, static: staticFields, dynamic }) => {
|
|
728
785
|
try {
|
|
729
786
|
const fromDenied = guardFrom(orchestratorId);
|
|
@@ -766,6 +823,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
766
823
|
.optional()
|
|
767
824
|
.default(20)
|
|
768
825
|
.describe("Maximum number of memories to return (default 20)"),
|
|
826
|
+
}, {
|
|
827
|
+
readOnlyHint: true,
|
|
828
|
+
openWorldHint: false,
|
|
829
|
+
destructiveHint: false,
|
|
830
|
+
title: "List memories",
|
|
769
831
|
}, async ({ namespace, type, limit }) => {
|
|
770
832
|
try {
|
|
771
833
|
const nsDenied = guardRead(namespace);
|
|
@@ -822,6 +884,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
822
884
|
.string()
|
|
823
885
|
.optional()
|
|
824
886
|
.describe("Tenant identifier for multi-tenant isolation"),
|
|
887
|
+
}, {
|
|
888
|
+
readOnlyHint: false,
|
|
889
|
+
openWorldHint: false,
|
|
890
|
+
destructiveHint: false,
|
|
891
|
+
title: "Send message",
|
|
825
892
|
}, async ({ from, fromInstanceId, channel, content, sessionDay, tenantId, }) => {
|
|
826
893
|
let contentBytes = 0;
|
|
827
894
|
try {
|
|
@@ -876,8 +943,20 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
876
943
|
.int()
|
|
877
944
|
.optional()
|
|
878
945
|
.describe("Unix timestamp (ms). If provided, only messages with _creationTime > since are returned. Use for incremental polling — pass the timestamp of your last check to get only new messages. Omit for full unread backlog."),
|
|
946
|
+
}, {
|
|
947
|
+
readOnlyHint: true,
|
|
948
|
+
openWorldHint: false,
|
|
949
|
+
destructiveHint: false,
|
|
950
|
+
title: "Check messages",
|
|
879
951
|
}, async ({ recipient, recipientInstanceId, tenantId, since }) => {
|
|
880
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
|
+
}
|
|
881
960
|
const messages = await convex.query("messages:checkNewMessages", {
|
|
882
961
|
recipient,
|
|
883
962
|
recipientInstanceId,
|
|
@@ -915,6 +994,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
915
994
|
receiptIds: z
|
|
916
995
|
.union([z.array(receiptIdSchema).min(1), receiptIdSchema])
|
|
917
996
|
.describe("Receipt IDs to mark as read — array or single string"),
|
|
997
|
+
}, {
|
|
998
|
+
readOnlyHint: false,
|
|
999
|
+
openWorldHint: false,
|
|
1000
|
+
destructiveHint: false,
|
|
1001
|
+
title: "Mark messages as read",
|
|
918
1002
|
}, async ({ receiptIds }) => {
|
|
919
1003
|
try {
|
|
920
1004
|
let receiptIdsArray;
|
|
@@ -957,6 +1041,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
957
1041
|
callerOrchestrator: creatorSchema
|
|
958
1042
|
.optional()
|
|
959
1043
|
.describe("Optional RBAC — must be the sender or system"),
|
|
1044
|
+
}, {
|
|
1045
|
+
readOnlyHint: false,
|
|
1046
|
+
openWorldHint: false,
|
|
1047
|
+
destructiveHint: true,
|
|
1048
|
+
title: "Delete message",
|
|
960
1049
|
}, async ({ messageId, callerOrchestrator }) => {
|
|
961
1050
|
try {
|
|
962
1051
|
if (callerOrchestrator) {
|
|
@@ -991,6 +1080,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
991
1080
|
.optional()
|
|
992
1081
|
.describe("Instance ID — e.g. 'pi-chromebook', 'pi-vps', 'tau-vps-1'"),
|
|
993
1082
|
summary: z.string().describe("1-2 sentence summary of current work"),
|
|
1083
|
+
}, {
|
|
1084
|
+
readOnlyHint: false,
|
|
1085
|
+
openWorldHint: false,
|
|
1086
|
+
destructiveHint: false,
|
|
1087
|
+
title: "Set instance summary",
|
|
994
1088
|
}, async ({ orchestratorId, instanceId, summary }) => {
|
|
995
1089
|
try {
|
|
996
1090
|
const fromDenied = guardFrom(orchestratorId);
|
|
@@ -1017,8 +1111,16 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1017
1111
|
});
|
|
1018
1112
|
// ── list_peers ──────────────────────────────────────────────────────────────
|
|
1019
1113
|
server.tool("list_peers", "List all orchestrator profiles with their current status and summary. " +
|
|
1020
|
-
"Replaces claude-peers list_peers.", {},
|
|
1021
|
-
|
|
1114
|
+
"Replaces claude-peers list_peers.", {}, {
|
|
1115
|
+
readOnlyHint: true,
|
|
1116
|
+
openWorldHint: false,
|
|
1117
|
+
destructiveHint: false,
|
|
1118
|
+
title: "List peers",
|
|
1119
|
+
}, async () => {
|
|
1120
|
+
try {
|
|
1121
|
+
const _scopeDenied = guardMasterOnly("list_peers");
|
|
1122
|
+
if (_scopeDenied)
|
|
1123
|
+
return _scopeDenied;
|
|
1022
1124
|
const profiles = await convex.query("profiles:listProfiles", {});
|
|
1023
1125
|
const peers = profiles.map((p) => ({
|
|
1024
1126
|
id: p.orchestratorId,
|
|
@@ -1059,8 +1161,16 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1059
1161
|
.optional()
|
|
1060
1162
|
.default(100)
|
|
1061
1163
|
.describe("Max messages to return (default 100)"),
|
|
1164
|
+
}, {
|
|
1165
|
+
readOnlyHint: true,
|
|
1166
|
+
openWorldHint: false,
|
|
1167
|
+
destructiveHint: false,
|
|
1168
|
+
title: "List messages",
|
|
1062
1169
|
}, async ({ sessionDay, from, limit }) => {
|
|
1063
1170
|
try {
|
|
1171
|
+
const _scopeDenied = guardMasterOnly("list_messages");
|
|
1172
|
+
if (_scopeDenied)
|
|
1173
|
+
return _scopeDenied;
|
|
1064
1174
|
const messages = await convex.query("messages:listMessages", {
|
|
1065
1175
|
sessionDay,
|
|
1066
1176
|
from,
|
|
@@ -1092,8 +1202,16 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1092
1202
|
messageId: z
|
|
1093
1203
|
.string()
|
|
1094
1204
|
.describe("Convex document ID of the broadcast message"),
|
|
1205
|
+
}, {
|
|
1206
|
+
readOnlyHint: true,
|
|
1207
|
+
openWorldHint: false,
|
|
1208
|
+
destructiveHint: false,
|
|
1209
|
+
title: "List broadcast status",
|
|
1095
1210
|
}, async ({ messageId }) => {
|
|
1096
1211
|
try {
|
|
1212
|
+
const _scopeDenied = guardMasterOnly("list_broadcast_status");
|
|
1213
|
+
if (_scopeDenied)
|
|
1214
|
+
return _scopeDenied;
|
|
1097
1215
|
const status = await convex.query("messages:listBroadcastStatus", {
|
|
1098
1216
|
messageId,
|
|
1099
1217
|
});
|
|
@@ -1144,6 +1262,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1144
1262
|
.optional()
|
|
1145
1263
|
.describe("Optional due date as Unix timestamp (ms)"),
|
|
1146
1264
|
createdBy: creatorSchema,
|
|
1265
|
+
}, {
|
|
1266
|
+
readOnlyHint: false,
|
|
1267
|
+
openWorldHint: false,
|
|
1268
|
+
destructiveHint: false,
|
|
1269
|
+
title: "Create task",
|
|
1147
1270
|
}, async ({ title, description, project, tags, assignedTo, assignedToInstance, priority, status, dependsOn, missionId, estimatedMinutes, dueDate, createdBy, }) => {
|
|
1148
1271
|
try {
|
|
1149
1272
|
const fromDenied = guardFrom(createdBy);
|
|
@@ -1206,8 +1329,23 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1206
1329
|
.optional()
|
|
1207
1330
|
.describe("Filter by task creator (e.g. 'pi' to find Pi-dispatched tasks)"),
|
|
1208
1331
|
updatedSince: updatedSinceSchema.optional(),
|
|
1332
|
+
}, {
|
|
1333
|
+
readOnlyHint: true,
|
|
1334
|
+
openWorldHint: false,
|
|
1335
|
+
destructiveHint: false,
|
|
1336
|
+
title: "List tasks",
|
|
1209
1337
|
}, async ({ assignedTo, assignedToInstance, status, project, limit, fields, createdBy, updatedSince, }) => {
|
|
1210
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
|
+
}
|
|
1211
1349
|
const tasks = await convex.query("tasks:list", {
|
|
1212
1350
|
assignedTo,
|
|
1213
1351
|
assignedToInstance,
|
|
@@ -1276,6 +1414,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1276
1414
|
callerOrchestrator: creatorSchema
|
|
1277
1415
|
.optional()
|
|
1278
1416
|
.describe("Optional RBAC — if provided, must be creator or assignee"),
|
|
1417
|
+
}, {
|
|
1418
|
+
readOnlyHint: false,
|
|
1419
|
+
openWorldHint: false,
|
|
1420
|
+
destructiveHint: false,
|
|
1421
|
+
title: "Update task",
|
|
1279
1422
|
}, async ({ taskId, title, description, project, tags, assignedTo, priority, status, dependsOn, missionId, estimatedMinutes, actualMinutes, startedAt, completedAt, dueDate, callerOrchestrator, }) => {
|
|
1280
1423
|
try {
|
|
1281
1424
|
if (callerOrchestrator) {
|
|
@@ -1330,6 +1473,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1330
1473
|
callerOrchestrator: creatorSchema
|
|
1331
1474
|
.optional()
|
|
1332
1475
|
.describe("Optional RBAC — if provided, must be creator or assignee"),
|
|
1476
|
+
}, {
|
|
1477
|
+
readOnlyHint: false,
|
|
1478
|
+
openWorldHint: false,
|
|
1479
|
+
destructiveHint: false,
|
|
1480
|
+
title: "Complete task",
|
|
1333
1481
|
}, async ({ taskId, completionNote, callerOrchestrator }) => {
|
|
1334
1482
|
try {
|
|
1335
1483
|
if (callerOrchestrator) {
|
|
@@ -1362,6 +1510,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1362
1510
|
callerOrchestrator: creatorSchema
|
|
1363
1511
|
.optional()
|
|
1364
1512
|
.describe("Optional RBAC — if provided, must be creator or assignee"),
|
|
1513
|
+
}, {
|
|
1514
|
+
readOnlyHint: false,
|
|
1515
|
+
openWorldHint: false,
|
|
1516
|
+
destructiveHint: false,
|
|
1517
|
+
title: "Start task",
|
|
1365
1518
|
}, async ({ taskId, callerOrchestrator }) => {
|
|
1366
1519
|
try {
|
|
1367
1520
|
if (callerOrchestrator) {
|
|
@@ -1395,6 +1548,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1395
1548
|
.string()
|
|
1396
1549
|
.optional()
|
|
1397
1550
|
.describe("Instance identifier, e.g. 'sigma-vps'"),
|
|
1551
|
+
}, {
|
|
1552
|
+
readOnlyHint: false,
|
|
1553
|
+
openWorldHint: false,
|
|
1554
|
+
destructiveHint: false,
|
|
1555
|
+
title: "Checkout task",
|
|
1398
1556
|
}, async ({ taskId, callerOrchestrator, callerInstance }) => {
|
|
1399
1557
|
try {
|
|
1400
1558
|
const fromDenied = guardFrom(callerOrchestrator);
|
|
@@ -1424,6 +1582,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1424
1582
|
callerOrchestrator: creatorSchema
|
|
1425
1583
|
.optional()
|
|
1426
1584
|
.describe("Optional RBAC — must be creator or system"),
|
|
1585
|
+
}, {
|
|
1586
|
+
readOnlyHint: false,
|
|
1587
|
+
openWorldHint: false,
|
|
1588
|
+
destructiveHint: true,
|
|
1589
|
+
title: "Delete task",
|
|
1427
1590
|
}, async ({ taskId, callerOrchestrator }) => {
|
|
1428
1591
|
try {
|
|
1429
1592
|
if (callerOrchestrator) {
|
|
@@ -1459,6 +1622,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1459
1622
|
callerOrchestrator: creatorSchema
|
|
1460
1623
|
.optional()
|
|
1461
1624
|
.describe("Optional RBAC — must be creator or assignee"),
|
|
1625
|
+
}, {
|
|
1626
|
+
readOnlyHint: false,
|
|
1627
|
+
openWorldHint: false,
|
|
1628
|
+
destructiveHint: true,
|
|
1629
|
+
title: "Block task",
|
|
1462
1630
|
}, async ({ taskId, reason, blockedBy, callerOrchestrator }) => {
|
|
1463
1631
|
try {
|
|
1464
1632
|
if (callerOrchestrator) {
|
|
@@ -1502,6 +1670,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1502
1670
|
callerOrchestrator: creatorSchema
|
|
1503
1671
|
.optional()
|
|
1504
1672
|
.describe("Optional RBAC — must be creator or assignee"),
|
|
1673
|
+
}, {
|
|
1674
|
+
readOnlyHint: false,
|
|
1675
|
+
openWorldHint: false,
|
|
1676
|
+
destructiveHint: false,
|
|
1677
|
+
title: "Add task dependency",
|
|
1505
1678
|
}, async ({ taskId, dependsOn, callerOrchestrator }) => {
|
|
1506
1679
|
try {
|
|
1507
1680
|
if (callerOrchestrator) {
|
|
@@ -1545,12 +1718,18 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1545
1718
|
fields: fieldsSchema
|
|
1546
1719
|
.optional()
|
|
1547
1720
|
.describe('Field projection ("lite"|"full")'),
|
|
1548
|
-
createdBy: assigneeSchema
|
|
1549
|
-
.optional()
|
|
1550
|
-
.describe("Filter by task creator"),
|
|
1721
|
+
createdBy: assigneeSchema.optional().describe("Filter by task creator"),
|
|
1551
1722
|
updatedSince: updatedSinceSchema.optional(),
|
|
1723
|
+
}, {
|
|
1724
|
+
readOnlyHint: true,
|
|
1725
|
+
openWorldHint: false,
|
|
1726
|
+
destructiveHint: false,
|
|
1727
|
+
title: "List tasks by mission",
|
|
1552
1728
|
}, async ({ missionId, status, limit, fields, createdBy, updatedSince }) => {
|
|
1553
1729
|
try {
|
|
1730
|
+
const _scopeDenied = guardMasterOnly("list_tasks_by_mission");
|
|
1731
|
+
if (_scopeDenied)
|
|
1732
|
+
return _scopeDenied;
|
|
1554
1733
|
const tasks = await convex.query("tasks:listByMission", {
|
|
1555
1734
|
missionId: missionId,
|
|
1556
1735
|
status,
|
|
@@ -1592,6 +1771,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1592
1771
|
.describe("Target completion date (Unix ms)"),
|
|
1593
1772
|
progress: z.number().optional().describe("Progress percentage (0-100)"),
|
|
1594
1773
|
createdBy: creatorSchema,
|
|
1774
|
+
}, {
|
|
1775
|
+
readOnlyHint: false,
|
|
1776
|
+
openWorldHint: false,
|
|
1777
|
+
destructiveHint: false,
|
|
1778
|
+
title: "Create mission",
|
|
1595
1779
|
}, async ({ name, description, project, status, priority, pilot, agents, brief, startDate, targetDate, progress, createdBy, }) => {
|
|
1596
1780
|
try {
|
|
1597
1781
|
const fromDenied = guardFrom(createdBy);
|
|
@@ -1646,8 +1830,20 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1646
1830
|
.optional()
|
|
1647
1831
|
.describe('Field projection ("lite"|"full")'),
|
|
1648
1832
|
updatedSince: updatedSinceSchema.optional(),
|
|
1833
|
+
}, {
|
|
1834
|
+
readOnlyHint: true,
|
|
1835
|
+
openWorldHint: false,
|
|
1836
|
+
destructiveHint: false,
|
|
1837
|
+
title: "List missions",
|
|
1649
1838
|
}, async ({ project, pilot, status, limit, fields, updatedSince }) => {
|
|
1650
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
|
+
}
|
|
1651
1847
|
const missions = await convex.query("missions:list", {
|
|
1652
1848
|
project,
|
|
1653
1849
|
pilot,
|
|
@@ -1682,8 +1878,16 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1682
1878
|
// ── get_mission ─────────────────────────────────────────────────────────────
|
|
1683
1879
|
server.tool("get_mission", "Fetch a single mission by ID. Returns full mission details including status, pilot, agents, progress, and dates.", {
|
|
1684
1880
|
missionId: z.string().describe("Convex document ID of the mission"),
|
|
1881
|
+
}, {
|
|
1882
|
+
readOnlyHint: true,
|
|
1883
|
+
openWorldHint: false,
|
|
1884
|
+
destructiveHint: false,
|
|
1885
|
+
title: "Get mission",
|
|
1685
1886
|
}, async ({ missionId }) => {
|
|
1686
1887
|
try {
|
|
1888
|
+
const _scopeDenied = guardMasterOnly("get_mission");
|
|
1889
|
+
if (_scopeDenied)
|
|
1890
|
+
return _scopeDenied;
|
|
1687
1891
|
const mission = await convex.query("missions:get", {
|
|
1688
1892
|
missionId: missionId,
|
|
1689
1893
|
});
|
|
@@ -1717,6 +1921,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1717
1921
|
startDate: z.number().optional().describe("New start date (Unix ms)"),
|
|
1718
1922
|
targetDate: z.number().optional().describe("New target date (Unix ms)"),
|
|
1719
1923
|
progress: z.number().optional().describe("New progress (0-100)"),
|
|
1924
|
+
}, {
|
|
1925
|
+
readOnlyHint: false,
|
|
1926
|
+
openWorldHint: false,
|
|
1927
|
+
destructiveHint: false,
|
|
1928
|
+
title: "Update mission",
|
|
1720
1929
|
}, async ({ missionId, name, description, project, status, priority, pilot, agents, brief, startDate, targetDate, progress, }) => {
|
|
1721
1930
|
try {
|
|
1722
1931
|
if (pilot) {
|
|
@@ -1755,6 +1964,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1755
1964
|
server.tool("update_mission_status", "Change a mission's status. Shortcut for updating only the status field.", {
|
|
1756
1965
|
missionId: z.string().describe("Convex document ID of the mission"),
|
|
1757
1966
|
status: missionStatusSchema.describe("New status"),
|
|
1967
|
+
}, {
|
|
1968
|
+
readOnlyHint: false,
|
|
1969
|
+
openWorldHint: false,
|
|
1970
|
+
destructiveHint: false,
|
|
1971
|
+
title: "Update mission status",
|
|
1758
1972
|
}, async ({ missionId, status }) => {
|
|
1759
1973
|
try {
|
|
1760
1974
|
await convex.mutation("missions:updateStatus", {
|
|
@@ -1782,6 +1996,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1782
1996
|
content: z.string().describe("Full diary entry content"),
|
|
1783
1997
|
highlights: flexArrayOptional.describe("Key highlights of the day"),
|
|
1784
1998
|
blockers: flexArrayOptional.describe("Blockers encountered"),
|
|
1999
|
+
}, {
|
|
2000
|
+
readOnlyHint: false,
|
|
2001
|
+
openWorldHint: false,
|
|
2002
|
+
destructiveHint: false,
|
|
2003
|
+
title: "Write diary entry",
|
|
1785
2004
|
}, async ({ date, orchestrator, content, highlights, blockers }) => {
|
|
1786
2005
|
let contentBytes = 0;
|
|
1787
2006
|
try {
|
|
@@ -1821,8 +2040,16 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1821
2040
|
server.tool("get_diary", "Fetch a diary entry for a specific date and orchestrator. Returns null if no entry exists.", {
|
|
1822
2041
|
date: z.string().describe("ISO date string — e.g. '2026-03-25'"),
|
|
1823
2042
|
orchestrator: creatorSchema.describe("Which orchestrator's diary to fetch"),
|
|
2043
|
+
}, {
|
|
2044
|
+
readOnlyHint: true,
|
|
2045
|
+
openWorldHint: false,
|
|
2046
|
+
destructiveHint: false,
|
|
2047
|
+
title: "Get diary entry",
|
|
1824
2048
|
}, async ({ date, orchestrator }) => {
|
|
1825
2049
|
try {
|
|
2050
|
+
const _scopeDenied = guardMasterOnly("get_diary");
|
|
2051
|
+
if (_scopeDenied)
|
|
2052
|
+
return _scopeDenied;
|
|
1826
2053
|
const entry = await convex.query("diary:get", {
|
|
1827
2054
|
date,
|
|
1828
2055
|
orchestrator,
|
|
@@ -1864,8 +2091,19 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1864
2091
|
.optional()
|
|
1865
2092
|
.default(20)
|
|
1866
2093
|
.describe("Maximum entries to return (default 20)"),
|
|
2094
|
+
}, {
|
|
2095
|
+
readOnlyHint: true,
|
|
2096
|
+
openWorldHint: false,
|
|
2097
|
+
destructiveHint: false,
|
|
2098
|
+
title: "List diary entries",
|
|
1867
2099
|
}, async ({ orchestrator, limit }) => {
|
|
1868
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
|
+
}
|
|
1869
2107
|
const entries = await convex.query("diary:list", {
|
|
1870
2108
|
orchestrator,
|
|
1871
2109
|
limit: limit ?? 20,
|
|
@@ -1906,6 +2144,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1906
2144
|
"Passing a briefingNotes ID will fail with ArgumentValidationError at path .linkedMemoryIds[N]. " +
|
|
1907
2145
|
"If cross-linking briefings is needed, request the linkedBriefingIds feature instead."),
|
|
1908
2146
|
createdBy: creatorSchema,
|
|
2147
|
+
}, {
|
|
2148
|
+
readOnlyHint: false,
|
|
2149
|
+
openWorldHint: false,
|
|
2150
|
+
destructiveHint: false,
|
|
2151
|
+
title: "Create briefing note",
|
|
1909
2152
|
}, async ({ title, topic, participants, content, decisions, linkedMemoryIds, createdBy, }) => {
|
|
1910
2153
|
let contentBytes = 0;
|
|
1911
2154
|
try {
|
|
@@ -1945,7 +2188,12 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
1945
2188
|
}
|
|
1946
2189
|
});
|
|
1947
2190
|
// ── update_briefing_note ────────────────────────────────────────────────────
|
|
1948
|
-
server.tool("update_briefing_note", updateBriefingNoteDescription, updateBriefingNoteSchema.shape,
|
|
2191
|
+
server.tool("update_briefing_note", updateBriefingNoteDescription, updateBriefingNoteSchema.shape, {
|
|
2192
|
+
readOnlyHint: false,
|
|
2193
|
+
openWorldHint: false,
|
|
2194
|
+
destructiveHint: false,
|
|
2195
|
+
title: "Update briefing note",
|
|
2196
|
+
}, async ({ noteId, callerOrchestrator, title, topic, participants, content, decisions, linkedMemoryIds, }) => {
|
|
1949
2197
|
let contentBytes = 0;
|
|
1950
2198
|
try {
|
|
1951
2199
|
if (content !== undefined) {
|
|
@@ -2002,8 +2250,16 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2002
2250
|
.optional()
|
|
2003
2251
|
.describe('Field projection ("lite"|"full")'),
|
|
2004
2252
|
updatedSince: updatedSinceSchema.optional(),
|
|
2253
|
+
}, {
|
|
2254
|
+
readOnlyHint: true,
|
|
2255
|
+
openWorldHint: false,
|
|
2256
|
+
destructiveHint: false,
|
|
2257
|
+
title: "List briefing notes",
|
|
2005
2258
|
}, async ({ topic, limit, fields, updatedSince }) => {
|
|
2006
2259
|
try {
|
|
2260
|
+
const _scopeDenied = guardMasterOnly("list_briefing_notes");
|
|
2261
|
+
if (_scopeDenied)
|
|
2262
|
+
return _scopeDenied;
|
|
2007
2263
|
const notes = await convex.query("briefingNotes:list", {
|
|
2008
2264
|
topic,
|
|
2009
2265
|
limit,
|
|
@@ -2055,6 +2311,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2055
2311
|
.optional()
|
|
2056
2312
|
.describe("Project this component belongs to"),
|
|
2057
2313
|
createdBy: creatorSchema,
|
|
2314
|
+
}, {
|
|
2315
|
+
readOnlyHint: false,
|
|
2316
|
+
openWorldHint: false,
|
|
2317
|
+
destructiveHint: false,
|
|
2318
|
+
title: "Register component",
|
|
2058
2319
|
}, async ({ name, type, team, content, version, project, createdBy }) => {
|
|
2059
2320
|
let contentBytes = 0;
|
|
2060
2321
|
try {
|
|
@@ -2105,8 +2366,16 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2105
2366
|
.optional()
|
|
2106
2367
|
.default(100)
|
|
2107
2368
|
.describe("Maximum components to return (default 100)"),
|
|
2369
|
+
}, {
|
|
2370
|
+
readOnlyHint: true,
|
|
2371
|
+
openWorldHint: false,
|
|
2372
|
+
destructiveHint: false,
|
|
2373
|
+
title: "List components",
|
|
2108
2374
|
}, async ({ type, team, limit }) => {
|
|
2109
2375
|
try {
|
|
2376
|
+
const _scopeDenied = guardMasterOnly("list_components");
|
|
2377
|
+
if (_scopeDenied)
|
|
2378
|
+
return _scopeDenied;
|
|
2110
2379
|
const components = await convex.query("components:list", {
|
|
2111
2380
|
type,
|
|
2112
2381
|
team,
|
|
@@ -2129,8 +2398,16 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2129
2398
|
server.tool("get_component", "Fetch a single component by name and type. Returns the full content.", {
|
|
2130
2399
|
name: z.string().describe("Component name"),
|
|
2131
2400
|
type: componentTypeSchema,
|
|
2401
|
+
}, {
|
|
2402
|
+
readOnlyHint: true,
|
|
2403
|
+
openWorldHint: false,
|
|
2404
|
+
destructiveHint: false,
|
|
2405
|
+
title: "Get component",
|
|
2132
2406
|
}, async ({ name, type }) => {
|
|
2133
2407
|
try {
|
|
2408
|
+
const _scopeDenied = guardMasterOnly("get_component");
|
|
2409
|
+
if (_scopeDenied)
|
|
2410
|
+
return _scopeDenied;
|
|
2134
2411
|
const component = await convex.query("components:get", {
|
|
2135
2412
|
name,
|
|
2136
2413
|
type,
|
|
@@ -2156,6 +2433,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2156
2433
|
content: z.string().optional().describe("New content/source code"),
|
|
2157
2434
|
version: z.string().optional().describe("New version string"),
|
|
2158
2435
|
project: z.string().optional().describe("New project name"),
|
|
2436
|
+
}, {
|
|
2437
|
+
readOnlyHint: false,
|
|
2438
|
+
openWorldHint: false,
|
|
2439
|
+
destructiveHint: false,
|
|
2440
|
+
title: "Update component",
|
|
2159
2441
|
}, async ({ componentId, ...fields }) => {
|
|
2160
2442
|
let contentBytes = 0;
|
|
2161
2443
|
try {
|
|
@@ -2191,6 +2473,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2191
2473
|
componentId: z
|
|
2192
2474
|
.string()
|
|
2193
2475
|
.describe("Convex document ID of the component to delete"),
|
|
2476
|
+
}, {
|
|
2477
|
+
readOnlyHint: false,
|
|
2478
|
+
openWorldHint: false,
|
|
2479
|
+
destructiveHint: true,
|
|
2480
|
+
title: "Delete component",
|
|
2194
2481
|
}, async ({ componentId }) => {
|
|
2195
2482
|
try {
|
|
2196
2483
|
const result = await convex.mutation("components:remove", {
|
|
@@ -2211,8 +2498,16 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2211
2498
|
.describe("Search term to match against component name or team"),
|
|
2212
2499
|
type: componentTypeSchema.optional().describe("Filter by component type"),
|
|
2213
2500
|
limit: z.number().int().optional().describe("Max results (default 50)"),
|
|
2501
|
+
}, {
|
|
2502
|
+
readOnlyHint: true,
|
|
2503
|
+
openWorldHint: false,
|
|
2504
|
+
destructiveHint: false,
|
|
2505
|
+
title: "Search components",
|
|
2214
2506
|
}, async ({ query, type, limit }) => {
|
|
2215
2507
|
try {
|
|
2508
|
+
const _scopeDenied = guardMasterOnly("search_components");
|
|
2509
|
+
if (_scopeDenied)
|
|
2510
|
+
return _scopeDenied;
|
|
2216
2511
|
const results = await convex.query("components:search", {
|
|
2217
2512
|
query,
|
|
2218
2513
|
type,
|
|
@@ -2243,6 +2538,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2243
2538
|
.string()
|
|
2244
2539
|
.describe("5-field cron: minute hour day-of-month month day-of-week"),
|
|
2245
2540
|
createdBy: creatorSchema,
|
|
2541
|
+
}, {
|
|
2542
|
+
readOnlyHint: false,
|
|
2543
|
+
openWorldHint: false,
|
|
2544
|
+
destructiveHint: false,
|
|
2545
|
+
title: "Create recurring task",
|
|
2246
2546
|
}, async ({ title, description, assignedTo, priority, project, tags, cronExpression, createdBy, }) => {
|
|
2247
2547
|
try {
|
|
2248
2548
|
const fromDenied = guardFrom(createdBy);
|
|
@@ -2291,8 +2591,16 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2291
2591
|
.optional()
|
|
2292
2592
|
.default(50)
|
|
2293
2593
|
.describe("Max results"),
|
|
2594
|
+
}, {
|
|
2595
|
+
readOnlyHint: true,
|
|
2596
|
+
openWorldHint: false,
|
|
2597
|
+
destructiveHint: false,
|
|
2598
|
+
title: "List recurring tasks",
|
|
2294
2599
|
}, async ({ assignedTo, active, limit }) => {
|
|
2295
2600
|
try {
|
|
2601
|
+
const _scopeDenied = guardMasterOnly("list_recurring_tasks");
|
|
2602
|
+
if (_scopeDenied)
|
|
2603
|
+
return _scopeDenied;
|
|
2296
2604
|
const tasks = await convex.query("recurringTasks:list", {
|
|
2297
2605
|
assignedTo,
|
|
2298
2606
|
active,
|
|
@@ -2309,6 +2617,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2309
2617
|
// ── pause_recurring_task ────────────────────────────────────────────────────
|
|
2310
2618
|
server.tool("pause_recurring_task", "Pause a recurring task — stops auto-creating tasks until resumed.", {
|
|
2311
2619
|
taskId: z.string().describe("Recurring task ID"),
|
|
2620
|
+
}, {
|
|
2621
|
+
readOnlyHint: false,
|
|
2622
|
+
openWorldHint: false,
|
|
2623
|
+
destructiveHint: false,
|
|
2624
|
+
title: "Pause recurring task",
|
|
2312
2625
|
}, async ({ taskId }) => {
|
|
2313
2626
|
try {
|
|
2314
2627
|
const result = await convex.mutation("recurringTasks:pause", {
|
|
@@ -2325,6 +2638,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2325
2638
|
// ── resume_recurring_task ───────────────────────────────────────────────────
|
|
2326
2639
|
server.tool("resume_recurring_task", "Resume a paused recurring task — recalculates next run time.", {
|
|
2327
2640
|
taskId: z.string().describe("Recurring task ID"),
|
|
2641
|
+
}, {
|
|
2642
|
+
readOnlyHint: false,
|
|
2643
|
+
openWorldHint: false,
|
|
2644
|
+
destructiveHint: false,
|
|
2645
|
+
title: "Resume recurring task",
|
|
2328
2646
|
}, async ({ taskId }) => {
|
|
2329
2647
|
try {
|
|
2330
2648
|
const result = await convex.mutation("recurringTasks:resume", {
|
|
@@ -2341,6 +2659,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2341
2659
|
// ── delete_recurring_task ───────────────────────────────────────────────────
|
|
2342
2660
|
server.tool("delete_recurring_task", "Permanently delete a recurring task template.", {
|
|
2343
2661
|
taskId: z.string().describe("Recurring task ID"),
|
|
2662
|
+
}, {
|
|
2663
|
+
readOnlyHint: false,
|
|
2664
|
+
openWorldHint: false,
|
|
2665
|
+
destructiveHint: true,
|
|
2666
|
+
title: "Delete recurring task",
|
|
2344
2667
|
}, async ({ taskId }) => {
|
|
2345
2668
|
try {
|
|
2346
2669
|
const result = await convex.mutation("recurringTasks:remove", {
|
|
@@ -2370,6 +2693,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2370
2693
|
.string()
|
|
2371
2694
|
.optional()
|
|
2372
2695
|
.describe("New cron expression (5-field)"),
|
|
2696
|
+
}, {
|
|
2697
|
+
readOnlyHint: false,
|
|
2698
|
+
openWorldHint: false,
|
|
2699
|
+
destructiveHint: false,
|
|
2700
|
+
title: "Update recurring task",
|
|
2373
2701
|
}, async ({ recurringTaskId, ...fields }) => {
|
|
2374
2702
|
try {
|
|
2375
2703
|
if (fields.assignedTo) {
|
|
@@ -2417,6 +2745,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2417
2745
|
.string()
|
|
2418
2746
|
.optional()
|
|
2419
2747
|
.describe("Signed authorization document or reference"),
|
|
2748
|
+
}, {
|
|
2749
|
+
readOnlyHint: false,
|
|
2750
|
+
openWorldHint: false,
|
|
2751
|
+
destructiveHint: false,
|
|
2752
|
+
title: "Create mandate",
|
|
2420
2753
|
}, async ({ requestedBy, fulfilledBy, service, budget, spendingLimits, approvedCategories, mandateDocument, }) => {
|
|
2421
2754
|
try {
|
|
2422
2755
|
const fromDenied = guardFrom(requestedBy);
|
|
@@ -2453,6 +2786,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2453
2786
|
.string()
|
|
2454
2787
|
.describe("Convex document ID of the mandate to accept"),
|
|
2455
2788
|
callerOrchestrator: creatorSchema.describe("Must be the fulfilledBy orchestrator or system"),
|
|
2789
|
+
}, {
|
|
2790
|
+
readOnlyHint: false,
|
|
2791
|
+
openWorldHint: false,
|
|
2792
|
+
destructiveHint: false,
|
|
2793
|
+
title: "Accept mandate",
|
|
2456
2794
|
}, async ({ mandateId, callerOrchestrator }) => {
|
|
2457
2795
|
try {
|
|
2458
2796
|
const fromDenied = guardFrom(callerOrchestrator);
|
|
@@ -2488,6 +2826,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2488
2826
|
.array(z.string())
|
|
2489
2827
|
.optional()
|
|
2490
2828
|
.describe("Task IDs created to fulfill this mandate"),
|
|
2829
|
+
}, {
|
|
2830
|
+
readOnlyHint: false,
|
|
2831
|
+
openWorldHint: false,
|
|
2832
|
+
destructiveHint: false,
|
|
2833
|
+
title: "Update mandate",
|
|
2491
2834
|
}, async ({ mandateId, callerOrchestrator, status, tokensCost, linkedTaskIds, }) => {
|
|
2492
2835
|
try {
|
|
2493
2836
|
const fromDenied = guardFrom(callerOrchestrator);
|
|
@@ -2521,6 +2864,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2521
2864
|
.describe("Convex document ID of the mandate to settle"),
|
|
2522
2865
|
callerOrchestrator: creatorSchema.describe("Must be the requestedBy orchestrator or system"),
|
|
2523
2866
|
finalCost: z.number().describe("Final actual token cost to record"),
|
|
2867
|
+
}, {
|
|
2868
|
+
readOnlyHint: false,
|
|
2869
|
+
openWorldHint: false,
|
|
2870
|
+
destructiveHint: false,
|
|
2871
|
+
title: "Settle mandate",
|
|
2524
2872
|
}, async ({ mandateId, callerOrchestrator, finalCost }) => {
|
|
2525
2873
|
try {
|
|
2526
2874
|
const fromDenied = guardFrom(callerOrchestrator);
|
|
@@ -2550,6 +2898,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2550
2898
|
proposedAmount: z
|
|
2551
2899
|
.number()
|
|
2552
2900
|
.describe("Proposed token spend amount to validate"),
|
|
2901
|
+
}, {
|
|
2902
|
+
readOnlyHint: true,
|
|
2903
|
+
openWorldHint: false,
|
|
2904
|
+
destructiveHint: false,
|
|
2905
|
+
title: "Validate mandate spending",
|
|
2553
2906
|
}, async ({ mandateId, proposedAmount }) => {
|
|
2554
2907
|
try {
|
|
2555
2908
|
const result = await convex.query("mandates:validateSpending", {
|
|
@@ -2584,8 +2937,16 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2584
2937
|
.optional()
|
|
2585
2938
|
.default(50)
|
|
2586
2939
|
.describe("Maximum mandates to return (default 50)"),
|
|
2940
|
+
}, {
|
|
2941
|
+
readOnlyHint: true,
|
|
2942
|
+
openWorldHint: false,
|
|
2943
|
+
destructiveHint: false,
|
|
2944
|
+
title: "List mandates",
|
|
2587
2945
|
}, async ({ requestedBy, fulfilledBy, status, limit }) => {
|
|
2588
2946
|
try {
|
|
2947
|
+
const _scopeDenied = guardMasterOnly("list_mandates");
|
|
2948
|
+
if (_scopeDenied)
|
|
2949
|
+
return _scopeDenied;
|
|
2589
2950
|
const mandates = await convex.query("mandates:list", {
|
|
2590
2951
|
requestedBy,
|
|
2591
2952
|
fulfilledBy,
|
|
@@ -2633,6 +2994,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2633
2994
|
.optional()
|
|
2634
2995
|
.default(10)
|
|
2635
2996
|
.describe("Management fee percentage (default 10)"),
|
|
2997
|
+
}, {
|
|
2998
|
+
readOnlyHint: false,
|
|
2999
|
+
openWorldHint: false,
|
|
3000
|
+
destructiveHint: false,
|
|
3001
|
+
title: "Create BU",
|
|
2636
3002
|
}, async ({ name, description, purpose, domain, orchestratorId, status, businessModel, targetCustomers, services, pricing, revenueProjections, coreTeam, coreProcesses, dependencies, kpis, managementFee, }) => {
|
|
2637
3003
|
try {
|
|
2638
3004
|
const fromDenied = guardFrom(orchestratorId);
|
|
@@ -2693,6 +3059,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2693
3059
|
dependencies: flexArrayOptional.describe("New dependencies"),
|
|
2694
3060
|
kpis: flexArrayOptional.describe("New KPIs"),
|
|
2695
3061
|
managementFee: z.number().optional().describe("New management fee %"),
|
|
3062
|
+
}, {
|
|
3063
|
+
readOnlyHint: false,
|
|
3064
|
+
openWorldHint: false,
|
|
3065
|
+
destructiveHint: false,
|
|
3066
|
+
title: "Update BU",
|
|
2696
3067
|
}, async ({ buId, name, description, purpose, domain, orchestratorId, status, businessModel, targetCustomers, services, pricing, revenueProjections, coreTeam, coreProcesses, dependencies, kpis, managementFee, }) => {
|
|
2697
3068
|
try {
|
|
2698
3069
|
if (orchestratorId) {
|
|
@@ -2735,8 +3106,16 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2735
3106
|
// ── get_bu ──────────────────────────────────────────────────────────────────
|
|
2736
3107
|
server.tool("get_bu", "Fetch a single business unit by its Convex document ID. Returns null if not found.", {
|
|
2737
3108
|
buId: z.string().describe("Convex document ID of the business unit"),
|
|
3109
|
+
}, {
|
|
3110
|
+
readOnlyHint: true,
|
|
3111
|
+
openWorldHint: false,
|
|
3112
|
+
destructiveHint: false,
|
|
3113
|
+
title: "Get BU",
|
|
2738
3114
|
}, async ({ buId }) => {
|
|
2739
3115
|
try {
|
|
3116
|
+
const _scopeDenied = guardMasterOnly("get_bu");
|
|
3117
|
+
if (_scopeDenied)
|
|
3118
|
+
return _scopeDenied;
|
|
2740
3119
|
const bu = await convex.query("businessUnits:get", {
|
|
2741
3120
|
buId: buId,
|
|
2742
3121
|
});
|
|
@@ -2769,8 +3148,16 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2769
3148
|
.optional()
|
|
2770
3149
|
.default(50)
|
|
2771
3150
|
.describe("Maximum BUs to return (default 50)"),
|
|
3151
|
+
}, {
|
|
3152
|
+
readOnlyHint: true,
|
|
3153
|
+
openWorldHint: false,
|
|
3154
|
+
destructiveHint: false,
|
|
3155
|
+
title: "List BUs",
|
|
2772
3156
|
}, async ({ orchestratorId, status, limit }) => {
|
|
2773
3157
|
try {
|
|
3158
|
+
const _scopeDenied = guardMasterOnly("list_bus");
|
|
3159
|
+
if (_scopeDenied)
|
|
3160
|
+
return _scopeDenied;
|
|
2774
3161
|
const bus = await convex.query("businessUnits:list", {
|
|
2775
3162
|
orchestratorId,
|
|
2776
3163
|
status,
|
|
@@ -2794,6 +3181,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2794
3181
|
buId: z
|
|
2795
3182
|
.string()
|
|
2796
3183
|
.describe("Convex document ID of the business unit to delete"),
|
|
3184
|
+
}, {
|
|
3185
|
+
readOnlyHint: false,
|
|
3186
|
+
openWorldHint: false,
|
|
3187
|
+
destructiveHint: true,
|
|
3188
|
+
title: "Delete BU",
|
|
2797
3189
|
}, async ({ buId }) => {
|
|
2798
3190
|
try {
|
|
2799
3191
|
const result = await convex.mutation("businessUnits:remove", {
|
|
@@ -2828,6 +3220,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2828
3220
|
.optional()
|
|
2829
3221
|
.default(true)
|
|
2830
3222
|
.describe("Whether this mapping is active (default true)"),
|
|
3223
|
+
}, {
|
|
3224
|
+
readOnlyHint: false,
|
|
3225
|
+
openWorldHint: false,
|
|
3226
|
+
destructiveHint: false,
|
|
3227
|
+
title: "Add repo mapping",
|
|
2831
3228
|
}, async ({ repo, orchestrator, project, active }) => {
|
|
2832
3229
|
try {
|
|
2833
3230
|
const id = await convex.mutation("githubRepoMapping:add", {
|
|
@@ -2850,8 +3247,16 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2850
3247
|
}
|
|
2851
3248
|
});
|
|
2852
3249
|
// ── list_repo_mappings ──────────────────────────────────────────────────────
|
|
2853
|
-
server.tool("list_repo_mappings", "List all GitHub repo → orchestrator mappings. Shows which repos are monitored and which orchestrator handles each.", {},
|
|
2854
|
-
|
|
3250
|
+
server.tool("list_repo_mappings", "List all GitHub repo → orchestrator mappings. Shows which repos are monitored and which orchestrator handles each.", {}, {
|
|
3251
|
+
readOnlyHint: true,
|
|
3252
|
+
openWorldHint: false,
|
|
3253
|
+
destructiveHint: false,
|
|
3254
|
+
title: "List repo mappings",
|
|
3255
|
+
}, async () => {
|
|
3256
|
+
try {
|
|
3257
|
+
const _scopeDenied = guardMasterOnly("list_repo_mappings");
|
|
3258
|
+
if (_scopeDenied)
|
|
3259
|
+
return _scopeDenied;
|
|
2855
3260
|
const mappings = await convex.query("githubRepoMapping:list", {});
|
|
2856
3261
|
return {
|
|
2857
3262
|
content: [
|
|
@@ -2871,6 +3276,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2871
3276
|
repo: z
|
|
2872
3277
|
.string()
|
|
2873
3278
|
.describe("Full repo name to remove — e.g. 'vantageos-agency/vantage-peers'"),
|
|
3279
|
+
}, {
|
|
3280
|
+
readOnlyHint: false,
|
|
3281
|
+
openWorldHint: false,
|
|
3282
|
+
destructiveHint: true,
|
|
3283
|
+
title: "Remove repo mapping",
|
|
2874
3284
|
}, async ({ repo }) => {
|
|
2875
3285
|
try {
|
|
2876
3286
|
const result = await convex.mutation("githubRepoMapping:remove", {
|
|
@@ -2911,8 +3321,16 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2911
3321
|
.optional()
|
|
2912
3322
|
.default(50)
|
|
2913
3323
|
.describe("Maximum number of issues to return (default 50)"),
|
|
3324
|
+
}, {
|
|
3325
|
+
readOnlyHint: true,
|
|
3326
|
+
openWorldHint: false,
|
|
3327
|
+
destructiveHint: false,
|
|
3328
|
+
title: "List issues",
|
|
2914
3329
|
}, async ({ project, status, assignedTo, limit }) => {
|
|
2915
3330
|
try {
|
|
3331
|
+
const _scopeDenied = guardMasterOnly("list_issues");
|
|
3332
|
+
if (_scopeDenied)
|
|
3333
|
+
return _scopeDenied;
|
|
2916
3334
|
let results;
|
|
2917
3335
|
if (assignedTo) {
|
|
2918
3336
|
results = await convex.query("issues:listByOrchestrator", {
|
|
@@ -2959,8 +3377,16 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2959
3377
|
.string()
|
|
2960
3378
|
.describe("Full repo name — e.g. 'myreeldream-ai/MyShortReel-beta'"),
|
|
2961
3379
|
issueNumber: z.number().int().describe("GitHub issue number"),
|
|
3380
|
+
}, {
|
|
3381
|
+
readOnlyHint: true,
|
|
3382
|
+
openWorldHint: false,
|
|
3383
|
+
destructiveHint: false,
|
|
3384
|
+
title: "Get issue",
|
|
2962
3385
|
}, async ({ repo, issueNumber }) => {
|
|
2963
3386
|
try {
|
|
3387
|
+
const _scopeDenied = guardMasterOnly("get_issue");
|
|
3388
|
+
if (_scopeDenied)
|
|
3389
|
+
return _scopeDenied;
|
|
2964
3390
|
const issue = await convex.query("issues:getByRepoNumber", {
|
|
2965
3391
|
repo,
|
|
2966
3392
|
issueNumber,
|
|
@@ -2987,6 +3413,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2987
3413
|
status: z
|
|
2988
3414
|
.enum(["open", "in_progress", "fixed", "verified", "closed"])
|
|
2989
3415
|
.describe("New status for the issue"),
|
|
3416
|
+
}, {
|
|
3417
|
+
readOnlyHint: false,
|
|
3418
|
+
openWorldHint: false,
|
|
3419
|
+
destructiveHint: false,
|
|
3420
|
+
title: "Update issue status",
|
|
2990
3421
|
}, async ({ repo, issueNumber, status }) => {
|
|
2991
3422
|
try {
|
|
2992
3423
|
await convex.mutation("issues:updateStatus", {
|
|
@@ -3017,6 +3448,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3017
3448
|
fixedBy: z
|
|
3018
3449
|
.string()
|
|
3019
3450
|
.describe("Who fixed it — orchestrator name or person"),
|
|
3451
|
+
}, {
|
|
3452
|
+
readOnlyHint: false,
|
|
3453
|
+
openWorldHint: false,
|
|
3454
|
+
destructiveHint: false,
|
|
3455
|
+
title: "Link commit to issue",
|
|
3020
3456
|
}, async ({ repo, issueNumber, commitSha, fixedBy }) => {
|
|
3021
3457
|
try {
|
|
3022
3458
|
await convex.mutation("issues:linkCommit", {
|
|
@@ -3047,6 +3483,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3047
3483
|
verifiedBy: z
|
|
3048
3484
|
.string()
|
|
3049
3485
|
.describe("Who verified the fix — orchestrator name or person"),
|
|
3486
|
+
}, {
|
|
3487
|
+
readOnlyHint: false,
|
|
3488
|
+
openWorldHint: false,
|
|
3489
|
+
destructiveHint: false,
|
|
3490
|
+
title: "Verify issue",
|
|
3050
3491
|
}, async ({ repo, issueNumber, verifiedBy }) => {
|
|
3051
3492
|
try {
|
|
3052
3493
|
await convex.mutation("issues:verify", {
|
|
@@ -3073,8 +3514,16 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3073
3514
|
.string()
|
|
3074
3515
|
.optional()
|
|
3075
3516
|
.describe("Filter stats to a specific project — omit for all projects"),
|
|
3517
|
+
}, {
|
|
3518
|
+
readOnlyHint: true,
|
|
3519
|
+
openWorldHint: false,
|
|
3520
|
+
destructiveHint: false,
|
|
3521
|
+
title: "Issue statistics",
|
|
3076
3522
|
}, async ({ project }) => {
|
|
3077
3523
|
try {
|
|
3524
|
+
const _scopeDenied = guardMasterOnly("issue_stats");
|
|
3525
|
+
if (_scopeDenied)
|
|
3526
|
+
return _scopeDenied;
|
|
3078
3527
|
const stats = await convex.query("issues:getStats", {
|
|
3079
3528
|
project,
|
|
3080
3529
|
});
|
|
@@ -3112,6 +3561,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3112
3561
|
.describe("The fix that worked — set later if not known yet"),
|
|
3113
3562
|
files: flexArrayOptional.describe("Files involved in the fix"),
|
|
3114
3563
|
linkedIssueIds: flexArrayOptional.describe("VantagePeers issue IDs linked to this pattern"),
|
|
3564
|
+
}, {
|
|
3565
|
+
readOnlyHint: false,
|
|
3566
|
+
openWorldHint: false,
|
|
3567
|
+
destructiveHint: false,
|
|
3568
|
+
title: "Create fix pattern",
|
|
3115
3569
|
}, async ({ symptom, rootCause, tags, stack, sourceProject, createdBy, severity, validatedFix, files, linkedIssueIds, }) => {
|
|
3116
3570
|
try {
|
|
3117
3571
|
const fromDenied = guardFrom(createdBy);
|
|
@@ -3150,6 +3604,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3150
3604
|
why: z.string().describe("Why it worked or didn't — the reasoning"),
|
|
3151
3605
|
createdBy: creatorSchema,
|
|
3152
3606
|
commit: z.string().optional().describe("Git commit hash of this attempt"),
|
|
3607
|
+
}, {
|
|
3608
|
+
readOnlyHint: false,
|
|
3609
|
+
openWorldHint: false,
|
|
3610
|
+
destructiveHint: false,
|
|
3611
|
+
title: "Add fix attempt",
|
|
3153
3612
|
}, async ({ patternId, description, worked, why, createdBy, commit }) => {
|
|
3154
3613
|
try {
|
|
3155
3614
|
const fromDenied = guardFrom(createdBy);
|
|
@@ -3180,6 +3639,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3180
3639
|
server.tool("validate_fix", "Set or update the validated fix on a pattern. Use after confirming a fix works.", {
|
|
3181
3640
|
patternId: z.string().describe("ID of the fix pattern"),
|
|
3182
3641
|
validatedFix: z.string().describe("Description of the validated fix"),
|
|
3642
|
+
}, {
|
|
3643
|
+
readOnlyHint: false,
|
|
3644
|
+
openWorldHint: false,
|
|
3645
|
+
destructiveHint: false,
|
|
3646
|
+
title: "Validate fix",
|
|
3183
3647
|
}, async ({ patternId, validatedFix }) => {
|
|
3184
3648
|
try {
|
|
3185
3649
|
await convex.mutation("fixPatterns:validate", {
|
|
@@ -3209,8 +3673,16 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3209
3673
|
.int()
|
|
3210
3674
|
.optional()
|
|
3211
3675
|
.describe("Max results to return (default 10)"),
|
|
3676
|
+
}, {
|
|
3677
|
+
readOnlyHint: true,
|
|
3678
|
+
openWorldHint: false,
|
|
3679
|
+
destructiveHint: false,
|
|
3680
|
+
title: "Search fix patterns",
|
|
3212
3681
|
}, async ({ query, limit }) => {
|
|
3213
3682
|
try {
|
|
3683
|
+
const _scopeDenied = guardMasterOnly("search_fix_patterns");
|
|
3684
|
+
if (_scopeDenied)
|
|
3685
|
+
return _scopeDenied;
|
|
3214
3686
|
const results = await convex.action("search:searchFixPatterns", {
|
|
3215
3687
|
query,
|
|
3216
3688
|
limit,
|
|
@@ -3235,8 +3707,16 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3235
3707
|
.optional()
|
|
3236
3708
|
.describe("Filter by source project — omit for all"),
|
|
3237
3709
|
limit: z.number().int().optional().describe("Max results (default 50)"),
|
|
3710
|
+
}, {
|
|
3711
|
+
readOnlyHint: true,
|
|
3712
|
+
openWorldHint: false,
|
|
3713
|
+
destructiveHint: false,
|
|
3714
|
+
title: "List fix patterns",
|
|
3238
3715
|
}, async ({ project, limit }) => {
|
|
3239
3716
|
try {
|
|
3717
|
+
const _scopeDenied = guardMasterOnly("list_fix_patterns");
|
|
3718
|
+
if (_scopeDenied)
|
|
3719
|
+
return _scopeDenied;
|
|
3240
3720
|
if (project) {
|
|
3241
3721
|
const results = await convex.query("fixPatterns:listByProject", {
|
|
3242
3722
|
sourceProject: project,
|
|
@@ -3263,6 +3743,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3263
3743
|
server.tool("link_issue_to_pattern", "Link a VantagePeers issue to a fix pattern. Creates a bidirectional reference.", {
|
|
3264
3744
|
patternId: z.string().describe("ID of the fix pattern"),
|
|
3265
3745
|
issueId: z.string().describe("VantagePeers issue ID to link"),
|
|
3746
|
+
}, {
|
|
3747
|
+
readOnlyHint: false,
|
|
3748
|
+
openWorldHint: false,
|
|
3749
|
+
destructiveHint: false,
|
|
3750
|
+
title: "Link issue to fix pattern",
|
|
3266
3751
|
}, async ({ patternId, issueId }) => {
|
|
3267
3752
|
try {
|
|
3268
3753
|
await convex.mutation("fixPatterns:linkIssue", {
|
|
@@ -3286,8 +3771,16 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3286
3771
|
server.tool("get_mission_template", "Fetch a mission template by name. Returns the template with all steps, or null if not found. " +
|
|
3287
3772
|
"Use 'issue-resolution-v2' for the default Issue Resolution Protocol.", {
|
|
3288
3773
|
name: z.string().describe("Template name — e.g. 'issue-resolution-v2'"),
|
|
3774
|
+
}, {
|
|
3775
|
+
readOnlyHint: true,
|
|
3776
|
+
openWorldHint: false,
|
|
3777
|
+
destructiveHint: false,
|
|
3778
|
+
title: "Get mission template",
|
|
3289
3779
|
}, async ({ name }) => {
|
|
3290
3780
|
try {
|
|
3781
|
+
const _scopeDenied = guardMasterOnly("get_mission_template");
|
|
3782
|
+
if (_scopeDenied)
|
|
3783
|
+
return _scopeDenied;
|
|
3291
3784
|
const template = await convex.query("missionTemplates:getByName", { name });
|
|
3292
3785
|
return {
|
|
3293
3786
|
content: [
|
|
@@ -3340,6 +3833,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3340
3833
|
.boolean()
|
|
3341
3834
|
.optional()
|
|
3342
3835
|
.describe("Mark as the default template for its type"),
|
|
3836
|
+
}, {
|
|
3837
|
+
readOnlyHint: false,
|
|
3838
|
+
openWorldHint: false,
|
|
3839
|
+
destructiveHint: false,
|
|
3840
|
+
title: "Update mission template",
|
|
3343
3841
|
}, async ({ name, description, steps, createdBy, isDefault }) => {
|
|
3344
3842
|
try {
|
|
3345
3843
|
const fromDenied = guardFrom(createdBy);
|
|
@@ -3385,7 +3883,12 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3385
3883
|
.string()
|
|
3386
3884
|
.optional()
|
|
3387
3885
|
.describe("Orchestrator making this call — used as createdBy on tasks. Defaults to 'system'."),
|
|
3388
|
-
},
|
|
3886
|
+
}, {
|
|
3887
|
+
readOnlyHint: false,
|
|
3888
|
+
openWorldHint: false,
|
|
3889
|
+
destructiveHint: false,
|
|
3890
|
+
title: "Instantiate mission template",
|
|
3891
|
+
}, async ({ templateName, missionId, context, titlePrefix, callerOrchestrator, }) => {
|
|
3389
3892
|
try {
|
|
3390
3893
|
const denied = guardMasterOnly("instantiate_template_into_mission");
|
|
3391
3894
|
if (denied)
|
|
@@ -3429,6 +3932,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3429
3932
|
orchestrator: z
|
|
3430
3933
|
.string()
|
|
3431
3934
|
.describe("Orchestrator responsible for this deployment — e.g. 'sigma'"),
|
|
3935
|
+
}, {
|
|
3936
|
+
readOnlyHint: false,
|
|
3937
|
+
openWorldHint: false,
|
|
3938
|
+
destructiveHint: false,
|
|
3939
|
+
title: "Add deployment",
|
|
3432
3940
|
}, async ({ name, deploymentUrl, deployKeyEnvVar, githubRepo, orchestrator, }) => {
|
|
3433
3941
|
try {
|
|
3434
3942
|
const id = await convex.mutation("errorMonitor:addDeployment", {
|
|
@@ -3456,6 +3964,11 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3456
3964
|
name: z
|
|
3457
3965
|
.string()
|
|
3458
3966
|
.describe("Name of the deployment to deactivate — e.g. 'your-deployment-123'"),
|
|
3967
|
+
}, {
|
|
3968
|
+
readOnlyHint: false,
|
|
3969
|
+
openWorldHint: false,
|
|
3970
|
+
destructiveHint: true,
|
|
3971
|
+
title: "Remove deployment",
|
|
3459
3972
|
}, async ({ name }) => {
|
|
3460
3973
|
try {
|
|
3461
3974
|
await convex.mutation("errorMonitor:removeDeployment", { name });
|
|
@@ -3487,8 +4000,16 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3487
4000
|
.optional()
|
|
3488
4001
|
.default(50)
|
|
3489
4002
|
.describe("Maximum number of errors to return (default 50)"),
|
|
4003
|
+
}, {
|
|
4004
|
+
readOnlyHint: true,
|
|
4005
|
+
openWorldHint: false,
|
|
4006
|
+
destructiveHint: false,
|
|
4007
|
+
title: "List errors",
|
|
3490
4008
|
}, async ({ deployment, limit }) => {
|
|
3491
4009
|
try {
|
|
4010
|
+
const _scopeDenied = guardMasterOnly("list_errors");
|
|
4011
|
+
if (_scopeDenied)
|
|
4012
|
+
return _scopeDenied;
|
|
3492
4013
|
const errors = await convex.query("errorMonitor:listErrors", {
|
|
3493
4014
|
deployment,
|
|
3494
4015
|
limit: limit ?? 50,
|
|
@@ -3509,8 +4030,16 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
3509
4030
|
// ── get_error ───────────────────────────────────────────────────────────────
|
|
3510
4031
|
server.tool("get_error", "Fetch a single error log entry by its Convex document ID, including stack trace and issue linkage.", {
|
|
3511
4032
|
errorId: z.string().describe("Convex document ID of the errorLogs entry"),
|
|
4033
|
+
}, {
|
|
4034
|
+
readOnlyHint: true,
|
|
4035
|
+
openWorldHint: false,
|
|
4036
|
+
destructiveHint: false,
|
|
4037
|
+
title: "Get error",
|
|
3512
4038
|
}, async ({ errorId }) => {
|
|
3513
4039
|
try {
|
|
4040
|
+
const _scopeDenied = guardMasterOnly("get_error");
|
|
4041
|
+
if (_scopeDenied)
|
|
4042
|
+
return _scopeDenied;
|
|
3514
4043
|
const error = await convex.query("errorMonitor:getError", {
|
|
3515
4044
|
errorId: errorId,
|
|
3516
4045
|
});
|
package/package.json
CHANGED