vantage-peers-mcp 2.2.0 → 2.3.1
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/CHANGELOG.md +61 -0
- package/README.md +85 -0
- package/dist/server-http.js +102 -32
- package/dist/server.js +128 -16
- package/dist/src/auth.js +42 -1
- package/dist/src/tools.js +6 -6
- package/package.json +7 -3
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 2.3.1 — 2026-05-26
|
|
4
|
+
|
|
5
|
+
### Fixed (Eta PR #530 delta-review)
|
|
6
|
+
- `status="all"` now actually returns every row (no filter applied). Previously advertised in 2.3.0 docs but the Convex `expandTaskStatuses` / `expandMissionStatuses` helpers rejected it as invalid.
|
|
7
|
+
- `status=["all"]` (alias inside an array) now correctly throws `ConvexError` — same conservative-rejection rule as `"open"` / `"active"`.
|
|
8
|
+
- `setPendingAliasReleases` on the Convex backend converted from `mutation` to `internalMutation`. It was a public DoS surface against the auto-IRP pipeline; it is a lifecycle operation only and must not be reachable via MCP.
|
|
9
|
+
|
|
10
|
+
## 2.3.0 — 2026-05-26
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- `list_tasks`, `list_missions`, `list_tasks_by_mission`, `list_briefing_notes` now accept `fields=lite` for compact payloads.
|
|
14
|
+
- Status filters on `list_tasks`, `list_tasks_by_mission`, and `list_missions` now accept arrays and aliases:
|
|
15
|
+
- `status=["todo","in_progress"]` — multi-value array
|
|
16
|
+
- `status="open"` — expands to non-terminal statuses (tasks: todo+in_progress+review+blocked; missions: brainstorm+plan+execute+validate)
|
|
17
|
+
- `status="active"` — in_progress only on tasks; plan+execute on missions
|
|
18
|
+
- `status="all"` — no filter applied
|
|
19
|
+
|
|
20
|
+
### Backward compat
|
|
21
|
+
- Single-string status still accepted unchanged.
|
|
22
|
+
- Omitting `fields` defaults to `"full"` — existing callers unaffected.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 2.2.0 — 2026-05-07
|
|
27
|
+
|
|
28
|
+
- 4 new fix-pattern tools: `create_fix_pattern`, `add_fix_attempt`, `validate_fix`, `link_issue_to_pattern`
|
|
29
|
+
- Detailed per-tool docs with arg tables and example calls in README
|
|
30
|
+
- New "Fix patterns cycle" section documenting the KB learning loop
|
|
31
|
+
- 41 new Zod input-validation unit tests for fix-pattern tools
|
|
32
|
+
|
|
33
|
+
## 2.1.1 — 2026-05-04
|
|
34
|
+
|
|
35
|
+
- Defense-in-depth `memoryIdSchema` validation for `create_briefing_note` and `update_briefing_note`
|
|
36
|
+
|
|
37
|
+
## 2.1.0 — 2026-04-25
|
|
38
|
+
|
|
39
|
+
- `update_briefing_note` MCP tool with RBAC
|
|
40
|
+
|
|
41
|
+
## 2.0.2 — 2026-04-14
|
|
42
|
+
|
|
43
|
+
- Added badges (npm version, downloads, license, tool count) to the published README
|
|
44
|
+
- Added Orchestrator Roles reference table including alpha, lambda, victor
|
|
45
|
+
- Added note that any custom lowercase role name is accepted
|
|
46
|
+
- Added `bugs` URL and additional keywords to `package.json`
|
|
47
|
+
|
|
48
|
+
## 2.0.1 — 2026-04-14
|
|
49
|
+
|
|
50
|
+
- Docstring fix in server.ts (minor)
|
|
51
|
+
|
|
52
|
+
## 2.0.0
|
|
53
|
+
|
|
54
|
+
- Type-safe `api.ts` export for cross-deployment calls (`vantage-peers-mcp/api`)
|
|
55
|
+
- Deploy key authentication guide
|
|
56
|
+
- Mission Templates category (1 tool: `update_mission_template`)
|
|
57
|
+
- Programmatic API section in README
|
|
58
|
+
|
|
59
|
+
## 1.x
|
|
60
|
+
|
|
61
|
+
- Initial public release with 82 MCP tools
|
package/README.md
CHANGED
|
@@ -50,6 +50,22 @@ Add to `~/.claude.json` or project `.claude/settings.json`:
|
|
|
50
50
|
}
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
+
## OAuth 2.1 DCR endpoints
|
|
54
|
+
|
|
55
|
+
VantagePeers ships a built-in OAuth 2.1 authorization server so Claude.ai web can connect via "Add custom integration" without any extra configuration.
|
|
56
|
+
|
|
57
|
+
| Method | Path | Description |
|
|
58
|
+
|--------|------|-------------|
|
|
59
|
+
| `GET` | `/.well-known/oauth-authorization-server` | Authorization Server Metadata (RFC 8414) — advertises supported grant types, endpoints, and capabilities |
|
|
60
|
+
| `GET` | `/.well-known/oauth-protected-resource` | Protected Resource Metadata (RFC 9728) — links back to the authorization server |
|
|
61
|
+
| `POST` | `/register` | Dynamic Client Registration (RFC 7591) — Claude.ai registers itself automatically on first connect |
|
|
62
|
+
| `GET` | `/authorize` | Authorization endpoint — redirects the user to grant access |
|
|
63
|
+
| `POST` | `/token` | Token endpoint — issues access tokens per OAuth 2.1 |
|
|
64
|
+
|
|
65
|
+
**RFCs implemented:** RFC 8414 (AS Metadata), RFC 9728 (Protected Resource Metadata), RFC 7591 (Dynamic Client Registration), OAuth 2.1 draft.
|
|
66
|
+
|
|
67
|
+
**Backward compatibility:** the `BEARER_SECRET_MASTER` env var still works unchanged. Claude Code and Claude Desktop users do not need to change anything — static bearer auth remains the default for those clients. OAuth 2.1 DCR is used exclusively when a client initiates the discovery flow (e.g. Claude.ai web).
|
|
68
|
+
|
|
53
69
|
## Environment variables
|
|
54
70
|
|
|
55
71
|
| Variable | Required | Description |
|
|
@@ -212,6 +228,70 @@ Example:
|
|
|
212
228
|
### Session (1)
|
|
213
229
|
`set_summary`
|
|
214
230
|
|
|
231
|
+
## Compact payloads and status aliases (v2.3.0)
|
|
232
|
+
|
|
233
|
+
### `fields=lite` — reduced token payloads
|
|
234
|
+
|
|
235
|
+
`list_tasks`, `list_tasks_by_mission`, `list_missions`, and `list_briefing_notes` accept an optional `fields` parameter:
|
|
236
|
+
|
|
237
|
+
| Value | Behaviour |
|
|
238
|
+
|-------|-----------|
|
|
239
|
+
| `"full"` | Default. Returns the complete document (backward-compatible). |
|
|
240
|
+
| `"lite"` | Returns a compact projection — significantly fewer tokens. |
|
|
241
|
+
|
|
242
|
+
Lite projections per entity:
|
|
243
|
+
|
|
244
|
+
| Tool | Lite fields |
|
|
245
|
+
|------|------------|
|
|
246
|
+
| `list_tasks` / `list_tasks_by_mission` | `_id`, `_creationTime`, `title`, `status`, `priority`, `assignedTo`, `missionId` |
|
|
247
|
+
| `list_missions` | `_id`, `_creationTime`, `name`, `status`, `pilot`, `priority`, `project` |
|
|
248
|
+
| `list_briefing_notes` | `_id`, `_creationTime`, `topic`, `title`, `participants`, `createdBy` |
|
|
249
|
+
|
|
250
|
+
Example (tasks lite):
|
|
251
|
+
```json
|
|
252
|
+
{
|
|
253
|
+
"tool": "list_tasks",
|
|
254
|
+
"arguments": { "assignedTo": "sigma", "fields": "lite", "limit": 20 }
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
Returns:
|
|
258
|
+
```json
|
|
259
|
+
[
|
|
260
|
+
{ "_id": "k17e2r...", "title": "Prepare MCP v2.3.0", "status": "in_progress", "priority": "high", "assignedTo": "sigma", "missionId": "k572a..." }
|
|
261
|
+
]
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### `status` arrays and aliases
|
|
265
|
+
|
|
266
|
+
`list_tasks`, `list_tasks_by_mission`, and `list_missions` now accept `status` as a single string, an array, or one of the aliases below.
|
|
267
|
+
|
|
268
|
+
#### Task status aliases
|
|
269
|
+
|
|
270
|
+
| Alias | Expands to |
|
|
271
|
+
|-------|-----------|
|
|
272
|
+
| `"open"` | `["todo", "in_progress", "review", "blocked"]` — everything except `done` |
|
|
273
|
+
| `"active"` | `["todo", "in_progress"]` |
|
|
274
|
+
| `"all"` | No filter — returns all statuses |
|
|
275
|
+
|
|
276
|
+
#### Mission status aliases
|
|
277
|
+
|
|
278
|
+
| Alias | Expands to |
|
|
279
|
+
|-------|-----------|
|
|
280
|
+
| `"open"` | `["brainstorm", "plan", "execute", "validate"]` — everything except `complete` |
|
|
281
|
+
| `"active"` | `["plan", "execute"]` |
|
|
282
|
+
| `"all"` | No filter — returns all statuses |
|
|
283
|
+
|
|
284
|
+
Examples:
|
|
285
|
+
|
|
286
|
+
```json
|
|
287
|
+
{ "tool": "list_tasks", "arguments": { "status": "open" } }
|
|
288
|
+
{ "tool": "list_tasks", "arguments": { "status": ["todo", "in_progress"] } }
|
|
289
|
+
{ "tool": "list_missions", "arguments": { "status": "active", "fields": "lite" } }
|
|
290
|
+
{ "tool": "list_tasks_by_mission", "arguments": { "missionId": "k572a...", "status": "all", "fields": "lite" } }
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
Single-string status values still work unchanged — fully backward-compatible.
|
|
294
|
+
|
|
215
295
|
## Fix patterns cycle
|
|
216
296
|
|
|
217
297
|
A fix pattern is a validated learning extracted from a resolved bug — symptom, root cause, and the fix that worked — stored in the VantagePeers knowledge base. Patterns accumulate across projects and agents so that the same bug is never debugged twice from scratch.
|
|
@@ -320,6 +400,11 @@ All orchestrator names are open strings — any lowercase name is accepted. The
|
|
|
320
400
|
|
|
321
401
|
## Changelog
|
|
322
402
|
|
|
403
|
+
### 2.3.0 — 2026-05-26
|
|
404
|
+
- `list_tasks`, `list_missions`, `list_tasks_by_mission`, `list_briefing_notes` now accept `fields=lite` for compact payloads (less tokens).
|
|
405
|
+
- 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).
|
|
406
|
+
- Single-string status still accepted unchanged (backward-compatible).
|
|
407
|
+
|
|
323
408
|
### 2.2.0 — 2026-05-07
|
|
324
409
|
- 4 new fix-pattern tools: `create_fix_pattern`, `add_fix_attempt`, `validate_fix`, `link_issue_to_pattern`
|
|
325
410
|
- Detailed per-tool docs with arg tables and example calls in README
|
package/dist/server-http.js
CHANGED
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
* PORT — HTTP port (default 3000)
|
|
25
25
|
* NODE_ENV — set to "production" on Railway
|
|
26
26
|
*/
|
|
27
|
+
import { readFileSync } from "node:fs";
|
|
27
28
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
28
29
|
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
|
|
29
30
|
import { ConvexHttpClient } from "convex/browser";
|
|
@@ -31,10 +32,19 @@ import { Hono } from "hono";
|
|
|
31
32
|
import { cors } from "hono/cors";
|
|
32
33
|
import { bearerAuthMiddleware, internalClient, masterOnlyMiddleware, sha256Base64Url, sha256Hex, } from "./src/auth.js";
|
|
33
34
|
import { registerTools } from "./src/tools.js";
|
|
35
|
+
let pkg;
|
|
36
|
+
try {
|
|
37
|
+
// Source mode: server-http.ts → ./package.json = mcp-server/package.json
|
|
38
|
+
pkg = JSON.parse(readFileSync(new URL("./package.json", import.meta.url), "utf-8"));
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// Dist mode: dist/server-http.js → ../package.json = mcp-server/package.json
|
|
42
|
+
pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf-8"));
|
|
43
|
+
}
|
|
34
44
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
35
45
|
// Constants
|
|
36
46
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
37
|
-
const
|
|
47
|
+
const PUBLIC_BASE_URL_FALLBACK = process.env.PUBLIC_BASE_URL ??
|
|
38
48
|
"https://vantage-peers-production.up.railway.app";
|
|
39
49
|
const ACCESS_TOKEN_TTL_SECONDS = 3600; // 1 hour
|
|
40
50
|
const REFRESH_TOKEN_TTL_SECONDS = 30 * 24 * 3600; // 30 days
|
|
@@ -46,9 +56,34 @@ const DEFAULT_PUBLIC_DCR_PROFILE = "client-generic";
|
|
|
46
56
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
47
57
|
// Helpers
|
|
48
58
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
59
|
+
/**
|
|
60
|
+
* Compute the issuer/base URL dynamically from the incoming request's Host
|
|
61
|
+
* header + protocol. Falls back to PUBLIC_BASE_URL env var when Host is absent
|
|
62
|
+
* (e.g., in curl smoke tests without a Host header).
|
|
63
|
+
*
|
|
64
|
+
* RFC 8414 §2: the issuer MUST be the URL the client uses to reach the server,
|
|
65
|
+
* so deriving it from the request is more correct than a hard-coded constant,
|
|
66
|
+
* especially when deployed behind a Railway/Cloudflare proxy that rewrites Host.
|
|
67
|
+
*/
|
|
68
|
+
function resolveIssuer(req) {
|
|
69
|
+
const host = req.headers.get("host");
|
|
70
|
+
if (host) {
|
|
71
|
+
// Use x-forwarded-proto when behind a reverse proxy; fall back to https.
|
|
72
|
+
const proto = req.headers.get("x-forwarded-proto") ??
|
|
73
|
+
(host.startsWith("localhost") || host.startsWith("127.")
|
|
74
|
+
? "http"
|
|
75
|
+
: "https");
|
|
76
|
+
return `${proto}://${host}`;
|
|
77
|
+
}
|
|
78
|
+
return PUBLIC_BASE_URL_FALLBACK;
|
|
79
|
+
}
|
|
49
80
|
function randomOpaqueToken() {
|
|
50
|
-
//
|
|
51
|
-
|
|
81
|
+
// 256-bit entropy via getRandomValues (32 bytes → 64 hex chars).
|
|
82
|
+
const bytes = new Uint8Array(32);
|
|
83
|
+
crypto.getRandomValues(bytes);
|
|
84
|
+
return Array.from(bytes)
|
|
85
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
86
|
+
.join("");
|
|
52
87
|
}
|
|
53
88
|
async function loadScopeProfile(profileId) {
|
|
54
89
|
return (await internalClient().query(
|
|
@@ -76,34 +111,65 @@ app.use("*", cors({
|
|
|
76
111
|
// OAuth 2.0 discovery (unauthenticated)
|
|
77
112
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
78
113
|
// RFC 9728 — OAuth 2.0 Protected Resource Metadata
|
|
79
|
-
app.get("/.well-known/oauth-protected-resource", (c) =>
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
114
|
+
app.get("/.well-known/oauth-protected-resource", (c) => {
|
|
115
|
+
const issuer = resolveIssuer(c.req.raw);
|
|
116
|
+
return c.json({
|
|
117
|
+
resource: issuer,
|
|
118
|
+
authorization_servers: [issuer],
|
|
119
|
+
scopes_supported: ["mcp:full"],
|
|
120
|
+
});
|
|
121
|
+
});
|
|
85
122
|
// RFC 8414 — OAuth 2.0 Authorization Server Metadata
|
|
86
|
-
app.get("/.well-known/oauth-authorization-server", (c) =>
|
|
87
|
-
issuer
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
"
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
123
|
+
app.get("/.well-known/oauth-authorization-server", (c) => {
|
|
124
|
+
const issuer = resolveIssuer(c.req.raw);
|
|
125
|
+
return c.json({
|
|
126
|
+
issuer,
|
|
127
|
+
authorization_endpoint: `${issuer}/authorize`,
|
|
128
|
+
token_endpoint: `${issuer}/token`,
|
|
129
|
+
registration_endpoint: `${issuer}/register`,
|
|
130
|
+
response_types_supported: ["code"],
|
|
131
|
+
grant_types_supported: ["authorization_code", "refresh_token"],
|
|
132
|
+
code_challenge_methods_supported: ["S256"],
|
|
133
|
+
token_endpoint_auth_methods_supported: [
|
|
134
|
+
"client_secret_basic",
|
|
135
|
+
"client_secret_post",
|
|
136
|
+
],
|
|
137
|
+
scopes_supported: ["mcp:full"],
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
const registerRateBuckets = new Map();
|
|
141
|
+
const REGISTER_RATE_LIMIT = 5;
|
|
142
|
+
const REGISTER_RATE_WINDOW_MS = 60_000;
|
|
143
|
+
function checkRegisterRateLimit(ip) {
|
|
144
|
+
const now = Date.now();
|
|
145
|
+
const bucket = registerRateBuckets.get(ip);
|
|
146
|
+
if (!bucket || now - bucket.windowStart >= REGISTER_RATE_WINDOW_MS) {
|
|
147
|
+
registerRateBuckets.set(ip, { count: 1, windowStart: now });
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
if (bucket.count < REGISTER_RATE_LIMIT) {
|
|
151
|
+
bucket.count++;
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
101
156
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
102
157
|
// RFC 7591 — Dynamic Client Registration
|
|
103
158
|
// Anonymous registrations get DEFAULT_PUBLIC_DCR_PROFILE ("client-generic").
|
|
104
159
|
// Pi must elevate the client via admin endpoint before real scopes are granted.
|
|
105
160
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
106
161
|
app.post("/register", async (c) => {
|
|
162
|
+
// S2: rate limit by IP — 5 req/min
|
|
163
|
+
const clientIp = c.req.header("x-forwarded-for")?.split(",")[0]?.trim() ??
|
|
164
|
+
c.req.header("x-real-ip") ??
|
|
165
|
+
"unknown";
|
|
166
|
+
if (!checkRegisterRateLimit(clientIp)) {
|
|
167
|
+
c.header("Retry-After", "60");
|
|
168
|
+
return c.json({
|
|
169
|
+
error: "too_many_requests",
|
|
170
|
+
error_description: "Rate limit exceeded. Max 5 registrations per minute per IP.",
|
|
171
|
+
}, 429);
|
|
172
|
+
}
|
|
107
173
|
let body = {};
|
|
108
174
|
try {
|
|
109
175
|
body = await c.req.json();
|
|
@@ -150,8 +216,8 @@ app.post("/register", async (c) => {
|
|
|
150
216
|
token_endpoint_auth_method: "client_secret_post",
|
|
151
217
|
grant_types: ["authorization_code", "refresh_token"],
|
|
152
218
|
response_types: ["code"],
|
|
153
|
-
|
|
154
|
-
|
|
219
|
+
// SC: standardized on mcp:full — consistent with well-known metadata
|
|
220
|
+
scope: "mcp:full",
|
|
155
221
|
}, 201);
|
|
156
222
|
});
|
|
157
223
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -164,7 +230,8 @@ app.get("/authorize", async (c) => {
|
|
|
164
230
|
const codeChallenge = q.code_challenge;
|
|
165
231
|
const codeChallengeMethod = q.code_challenge_method ?? "S256";
|
|
166
232
|
const state = q.state;
|
|
167
|
-
|
|
233
|
+
// SC: standardize scope — always mcp:full regardless of requested value
|
|
234
|
+
const scope = "mcp:full";
|
|
168
235
|
const responseType = q.response_type;
|
|
169
236
|
if (!clientId || !redirectUri || !codeChallenge) {
|
|
170
237
|
return c.json({
|
|
@@ -357,7 +424,8 @@ app.post("/token", async (c) => {
|
|
|
357
424
|
tokenHash: accessTokenHash,
|
|
358
425
|
clientId: record.clientId,
|
|
359
426
|
userId: record.userId,
|
|
360
|
-
|
|
427
|
+
// SC: standardized on mcp:full
|
|
428
|
+
scopes: ["mcp:full"],
|
|
361
429
|
scopeProfile: profile.profileId,
|
|
362
430
|
fromAllowList: profile.fromAllowList,
|
|
363
431
|
namespaceReadPrefixes: profile.namespaceReadPrefixes,
|
|
@@ -370,7 +438,8 @@ app.post("/token", async (c) => {
|
|
|
370
438
|
token_type: "Bearer",
|
|
371
439
|
expires_in: ACCESS_TOKEN_TTL_SECONDS,
|
|
372
440
|
refresh_token: refreshTokenRaw, // reused
|
|
373
|
-
|
|
441
|
+
// SC: standardized on mcp:full
|
|
442
|
+
scope: "mcp:full",
|
|
374
443
|
});
|
|
375
444
|
}
|
|
376
445
|
return c.json({ error: "unsupported_grant_type" }, 400);
|
|
@@ -381,9 +450,10 @@ app.post("/token", async (c) => {
|
|
|
381
450
|
app.get("/health", (c) => c.json({
|
|
382
451
|
status: "ok",
|
|
383
452
|
service: "vantage-peers-mcp-http",
|
|
384
|
-
version:
|
|
453
|
+
version: pkg.version,
|
|
385
454
|
transport: "streamable-http",
|
|
386
|
-
oauth: "
|
|
455
|
+
oauth: "supported",
|
|
456
|
+
scopes: ["mcp:full"],
|
|
387
457
|
}));
|
|
388
458
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
389
459
|
// Admin endpoints — master token only
|
|
@@ -490,7 +560,7 @@ app.all("/mcp", bearerAuthMiddleware(), async (c) => {
|
|
|
490
560
|
// Fresh McpServer per request — stateless mode, no session leakage
|
|
491
561
|
const server = new McpServer({
|
|
492
562
|
name: "vantage-peers",
|
|
493
|
-
version:
|
|
563
|
+
version: pkg.version,
|
|
494
564
|
});
|
|
495
565
|
registerTools(server, convex, oauthCtx);
|
|
496
566
|
const transport = new WebStandardStreamableHTTPServerTransport();
|
package/dist/server.js
CHANGED
|
@@ -433,7 +433,10 @@ server.tool("update_profile", "Create or update an orchestrator profile. Provide
|
|
|
433
433
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
434
434
|
server.tool("list_memories", "List active memories for a namespace, ordered newest first. " +
|
|
435
435
|
"Only returns isLatest=true memories (superseded memories are excluded by default). " +
|
|
436
|
-
"Use type to filter to a specific memory category."
|
|
436
|
+
"Use type to filter to a specific memory category. " +
|
|
437
|
+
"Returns { value: Memory[], continueCursor: string|null, isDone: boolean }. " +
|
|
438
|
+
"Pass paginationOpts.cursor from a previous response to fetch the next page. " +
|
|
439
|
+
"Without paginationOpts, returns ≤50 rows with continueCursor=null and isDone=true.", {
|
|
437
440
|
namespace: z
|
|
438
441
|
.string()
|
|
439
442
|
.describe("Namespace to list memories from — e.g. 'global', 'orchestrator/pi'"),
|
|
@@ -447,19 +450,36 @@ server.tool("list_memories", "List active memories for a namespace, ordered newe
|
|
|
447
450
|
.max(200)
|
|
448
451
|
.optional()
|
|
449
452
|
.default(20)
|
|
450
|
-
.describe("Maximum number of memories to return (default 20)"),
|
|
451
|
-
|
|
453
|
+
.describe("Maximum number of memories to return when paginationOpts is not provided (default 20, max 200)"),
|
|
454
|
+
paginationOpts: z
|
|
455
|
+
.object({
|
|
456
|
+
numItems: z
|
|
457
|
+
.number()
|
|
458
|
+
.int()
|
|
459
|
+
.min(1)
|
|
460
|
+
.max(200)
|
|
461
|
+
.describe("Number of items per page (max 200)"),
|
|
462
|
+
cursor: z
|
|
463
|
+
.union([z.string(), z.null()])
|
|
464
|
+
.describe("Cursor from a previous response continueCursor field, or null for the first page"),
|
|
465
|
+
})
|
|
466
|
+
.optional()
|
|
467
|
+
.describe("Optional cursor-based pagination. Pass { numItems, cursor: null } for the first page, " +
|
|
468
|
+
"then { numItems, cursor: <continueCursor from response> } for subsequent pages. " +
|
|
469
|
+
"When provided, isDone=false means more pages exist."),
|
|
470
|
+
}, async ({ namespace, type, limit, paginationOpts }) => {
|
|
452
471
|
try {
|
|
453
|
-
const
|
|
472
|
+
const result = await convex.query("memories:listMemories", {
|
|
454
473
|
namespace,
|
|
455
474
|
type,
|
|
456
475
|
limit: limit ?? 20,
|
|
476
|
+
paginationOpts,
|
|
457
477
|
});
|
|
458
478
|
return {
|
|
459
479
|
content: [
|
|
460
480
|
{
|
|
461
481
|
type: "text",
|
|
462
|
-
text: JSON.stringify(
|
|
482
|
+
text: JSON.stringify(result, null, 2),
|
|
463
483
|
},
|
|
464
484
|
],
|
|
465
485
|
};
|
|
@@ -831,14 +851,25 @@ server.tool("create_task", "Create a task in VantagePeers. Tasks are assigned to
|
|
|
831
851
|
// Tool: list_tasks
|
|
832
852
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
833
853
|
server.tool("list_tasks", "List tasks from VantagePeers with optional filters. " +
|
|
834
|
-
"Filter by assignee, instance, status, and/or project. Returns newest first."
|
|
854
|
+
"Filter by assignee, instance, status, and/or project. Returns newest first. " +
|
|
855
|
+
"status accepts a single value or an array, plus aliases: " +
|
|
856
|
+
"'open' (todo+in_progress+review+blocked), 'active' (todo+in_progress), 'all' (no filter). " +
|
|
857
|
+
"fields='lite' returns compact payloads ({_id,title,status,priority,assignedTo,missionId}) — fewer tokens.", {
|
|
835
858
|
assignedTo: assigneeSchema.optional().describe("Filter by assignee"),
|
|
836
859
|
assignedToInstance: z
|
|
837
860
|
.string()
|
|
838
861
|
.optional()
|
|
839
862
|
.describe("Filter by instance — e.g. 'pi-vps'. Returns only tasks assigned to that instance."),
|
|
840
|
-
status:
|
|
863
|
+
status: z
|
|
864
|
+
.union([taskStatusSchema, z.array(z.string()), z.string()])
|
|
865
|
+
.optional()
|
|
866
|
+
.describe("Filter by status. Single value, array, or alias: " +
|
|
867
|
+
"'open' (non-terminal), 'active' (in_progress only), 'all' (no filter)."),
|
|
841
868
|
project: z.string().optional().describe("Filter by project name"),
|
|
869
|
+
fields: z
|
|
870
|
+
.enum(["lite", "full"])
|
|
871
|
+
.optional()
|
|
872
|
+
.describe("'lite' = compact payload (less tokens), 'full' = default with all fields"),
|
|
842
873
|
limit: z
|
|
843
874
|
.number()
|
|
844
875
|
.int()
|
|
@@ -847,13 +878,14 @@ server.tool("list_tasks", "List tasks from VantagePeers with optional filters. "
|
|
|
847
878
|
.optional()
|
|
848
879
|
.default(50)
|
|
849
880
|
.describe("Maximum number of tasks to return (default 50)"),
|
|
850
|
-
}, async ({ assignedTo, assignedToInstance, status, project, limit }) => {
|
|
881
|
+
}, async ({ assignedTo, assignedToInstance, status, project, fields, limit }) => {
|
|
851
882
|
try {
|
|
852
883
|
const tasks = await convex.query("tasks:list", {
|
|
853
884
|
assignedTo,
|
|
854
885
|
assignedToInstance,
|
|
855
886
|
status,
|
|
856
887
|
project,
|
|
888
|
+
fields,
|
|
857
889
|
limit: limit ?? 50,
|
|
858
890
|
});
|
|
859
891
|
return {
|
|
@@ -1102,9 +1134,20 @@ server.tool("add_task_dependency", "Add a dependency to a task. The task cannot
|
|
|
1102
1134
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
1103
1135
|
// Tool: list_tasks_by_mission
|
|
1104
1136
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
1105
|
-
server.tool("list_tasks_by_mission", "List all tasks linked to a specific mission. Optionally filter by status."
|
|
1137
|
+
server.tool("list_tasks_by_mission", "List all tasks linked to a specific mission. Optionally filter by status. " +
|
|
1138
|
+
"status accepts a single value or an array, plus aliases: " +
|
|
1139
|
+
"'open' (todo+in_progress+review+blocked), 'active' (todo+in_progress), 'all' (no filter). " +
|
|
1140
|
+
"fields='lite' returns compact payloads ({_id,title,status,priority,assignedTo,missionId}) — fewer tokens.", {
|
|
1106
1141
|
missionId: z.string().describe("Convex document ID of the mission"),
|
|
1107
|
-
status:
|
|
1142
|
+
status: z
|
|
1143
|
+
.union([taskStatusSchema, z.array(z.string()), z.string()])
|
|
1144
|
+
.optional()
|
|
1145
|
+
.describe("Filter by task status. Single value, array, or alias: " +
|
|
1146
|
+
"'open' (non-terminal), 'active' (in_progress only), 'all' (no filter)."),
|
|
1147
|
+
fields: z
|
|
1148
|
+
.enum(["lite", "full"])
|
|
1149
|
+
.optional()
|
|
1150
|
+
.describe("'lite' = compact payload (less tokens), 'full' = default with all fields"),
|
|
1108
1151
|
limit: z
|
|
1109
1152
|
.number()
|
|
1110
1153
|
.int()
|
|
@@ -1113,11 +1156,12 @@ server.tool("list_tasks_by_mission", "List all tasks linked to a specific missio
|
|
|
1113
1156
|
.optional()
|
|
1114
1157
|
.default(50)
|
|
1115
1158
|
.describe("Maximum number of tasks to return (default 50)"),
|
|
1116
|
-
}, async ({ missionId, status, limit }) => {
|
|
1159
|
+
}, async ({ missionId, status, fields, limit }) => {
|
|
1117
1160
|
try {
|
|
1118
1161
|
const tasks = await convex.query("tasks:listByMission", {
|
|
1119
1162
|
missionId: missionId,
|
|
1120
1163
|
status,
|
|
1164
|
+
fields,
|
|
1121
1165
|
limit: limit ?? 50,
|
|
1122
1166
|
});
|
|
1123
1167
|
return {
|
|
@@ -1191,10 +1235,21 @@ server.tool("create_mission", "Create a mission in VantagePeers. Missions group
|
|
|
1191
1235
|
// Tool: list_missions
|
|
1192
1236
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
1193
1237
|
server.tool("list_missions", "List missions from VantagePeers with optional filters. " +
|
|
1194
|
-
"Filter by project, pilot, and/or status. Returns newest first."
|
|
1238
|
+
"Filter by project, pilot, and/or status. Returns newest first. " +
|
|
1239
|
+
"status accepts a single value or an array, plus aliases: " +
|
|
1240
|
+
"'open' (brainstorm+plan+execute+validate), 'active' (plan+execute), 'all' (no filter). " +
|
|
1241
|
+
"fields='lite' returns compact payloads ({_id,name,status,pilot,priority,project}) — fewer tokens.", {
|
|
1195
1242
|
project: z.string().optional().describe("Filter by project name"),
|
|
1196
1243
|
pilot: creatorSchema.optional().describe("Filter by pilot orchestrator"),
|
|
1197
|
-
status:
|
|
1244
|
+
status: z
|
|
1245
|
+
.union([missionStatusSchema, z.array(z.string()), z.string()])
|
|
1246
|
+
.optional()
|
|
1247
|
+
.describe("Filter by status. Single value, array, or alias: " +
|
|
1248
|
+
"'open' (non-terminal), 'active' (plan+execute), 'all' (no filter)."),
|
|
1249
|
+
fields: z
|
|
1250
|
+
.enum(["lite", "full"])
|
|
1251
|
+
.optional()
|
|
1252
|
+
.describe("'lite' = compact payload (less tokens), 'full' = default with all fields"),
|
|
1198
1253
|
limit: z
|
|
1199
1254
|
.number()
|
|
1200
1255
|
.int()
|
|
@@ -1203,12 +1258,13 @@ server.tool("list_missions", "List missions from VantagePeers with optional filt
|
|
|
1203
1258
|
.optional()
|
|
1204
1259
|
.default(50)
|
|
1205
1260
|
.describe("Maximum number of missions to return (default 50)"),
|
|
1206
|
-
}, async ({ project, pilot, status, limit }) => {
|
|
1261
|
+
}, async ({ project, pilot, status, fields, limit }) => {
|
|
1207
1262
|
try {
|
|
1208
1263
|
const missions = await convex.query("missions:list", {
|
|
1209
1264
|
project,
|
|
1210
1265
|
pilot,
|
|
1211
1266
|
status,
|
|
1267
|
+
fields,
|
|
1212
1268
|
limit: limit ?? 50,
|
|
1213
1269
|
});
|
|
1214
1270
|
return {
|
|
@@ -1413,6 +1469,31 @@ server.tool("list_diaries", "List diary entries, optionally filtered by orchestr
|
|
|
1413
1469
|
}
|
|
1414
1470
|
});
|
|
1415
1471
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
1472
|
+
// Tool: delete_diary
|
|
1473
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
1474
|
+
server.tool("delete_diary", "Permanently delete a diary entry by ID. Only the owner (or system) can delete.", {
|
|
1475
|
+
diaryId: z.string().describe("Convex document ID of the diary entry to delete"),
|
|
1476
|
+
callerOrchestrator: creatorSchema.optional().describe("Optional RBAC — must be the owner or system"),
|
|
1477
|
+
}, async ({ diaryId, callerOrchestrator }) => {
|
|
1478
|
+
try {
|
|
1479
|
+
const result = await convex.mutation("diary:deleteDiary", {
|
|
1480
|
+
diaryId: diaryId,
|
|
1481
|
+
callerOrchestrator,
|
|
1482
|
+
});
|
|
1483
|
+
return {
|
|
1484
|
+
content: [
|
|
1485
|
+
{
|
|
1486
|
+
type: "text",
|
|
1487
|
+
text: JSON.stringify(result, null, 2),
|
|
1488
|
+
},
|
|
1489
|
+
],
|
|
1490
|
+
};
|
|
1491
|
+
}
|
|
1492
|
+
catch (error) {
|
|
1493
|
+
return mcpError(error.message ?? String(error));
|
|
1494
|
+
}
|
|
1495
|
+
});
|
|
1496
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
1416
1497
|
// Tool: create_briefing_note
|
|
1417
1498
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
1418
1499
|
server.tool("create_briefing_note", "Create a briefing note — a structured record of a topic discussion, with participants, " +
|
|
@@ -1511,11 +1592,16 @@ server.tool("update_briefing_note", "Update an existing briefing note. Partial-u
|
|
|
1511
1592
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
1512
1593
|
// Tool: list_briefing_notes
|
|
1513
1594
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
1514
|
-
server.tool("list_briefing_notes", "List briefing notes, optionally filtered by topic. Returns newest first."
|
|
1595
|
+
server.tool("list_briefing_notes", "List briefing notes, optionally filtered by topic. Returns newest first. " +
|
|
1596
|
+
"fields='lite' returns compact payloads ({_id,topic,title,participants,createdBy}) — fewer tokens.", {
|
|
1515
1597
|
topic: z
|
|
1516
1598
|
.string()
|
|
1517
1599
|
.optional()
|
|
1518
1600
|
.describe("Filter to a specific topic — omit for all"),
|
|
1601
|
+
fields: z
|
|
1602
|
+
.enum(["lite", "full"])
|
|
1603
|
+
.optional()
|
|
1604
|
+
.describe("'lite' = compact payload (less tokens), 'full' = default with all fields"),
|
|
1519
1605
|
limit: z
|
|
1520
1606
|
.number()
|
|
1521
1607
|
.int()
|
|
@@ -1524,10 +1610,11 @@ server.tool("list_briefing_notes", "List briefing notes, optionally filtered by
|
|
|
1524
1610
|
.optional()
|
|
1525
1611
|
.default(20)
|
|
1526
1612
|
.describe("Maximum notes to return (default 20)"),
|
|
1527
|
-
}, async ({ topic, limit }) => {
|
|
1613
|
+
}, async ({ topic, fields, limit }) => {
|
|
1528
1614
|
try {
|
|
1529
1615
|
const notes = await convex.query("briefingNotes:list", {
|
|
1530
1616
|
topic,
|
|
1617
|
+
fields,
|
|
1531
1618
|
limit: limit ?? 20,
|
|
1532
1619
|
});
|
|
1533
1620
|
return {
|
|
@@ -1544,6 +1631,31 @@ server.tool("list_briefing_notes", "List briefing notes, optionally filtered by
|
|
|
1544
1631
|
}
|
|
1545
1632
|
});
|
|
1546
1633
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
1634
|
+
// Tool: delete_briefing_note
|
|
1635
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
1636
|
+
server.tool("delete_briefing_note", "Permanently delete a briefing note by ID. Only the creator (or system) can delete.", {
|
|
1637
|
+
noteId: z.string().describe("Convex document ID of the briefing note to delete"),
|
|
1638
|
+
callerOrchestrator: creatorSchema.optional().describe("Optional RBAC — must be creator or system"),
|
|
1639
|
+
}, async ({ noteId, callerOrchestrator }) => {
|
|
1640
|
+
try {
|
|
1641
|
+
const result = await convex.mutation("briefingNotes:deleteBriefingNote", {
|
|
1642
|
+
noteId: noteId,
|
|
1643
|
+
callerOrchestrator,
|
|
1644
|
+
});
|
|
1645
|
+
return {
|
|
1646
|
+
content: [
|
|
1647
|
+
{
|
|
1648
|
+
type: "text",
|
|
1649
|
+
text: JSON.stringify(result, null, 2),
|
|
1650
|
+
},
|
|
1651
|
+
],
|
|
1652
|
+
};
|
|
1653
|
+
}
|
|
1654
|
+
catch (error) {
|
|
1655
|
+
return mcpError(error.message ?? String(error));
|
|
1656
|
+
}
|
|
1657
|
+
});
|
|
1658
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
1547
1659
|
// Tool: register_component
|
|
1548
1660
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
1549
1661
|
server.tool("register_component", "Register or update a component (agent, skill, hook, or plugin) in the registry. " +
|
package/dist/src/auth.js
CHANGED
|
@@ -212,7 +212,48 @@ export function bearerAuthMiddleware() {
|
|
|
212
212
|
await next();
|
|
213
213
|
return;
|
|
214
214
|
}
|
|
215
|
-
// ── (3)
|
|
215
|
+
// ── (3) DCR OAuth token — check oauthTokens via oauthDcr:validateAccessToken
|
|
216
|
+
// Uses raw token (not hashed) — the DCR table stores tokens in plaintext.
|
|
217
|
+
// This path handles Claude.ai clients registered via POST /register.
|
|
218
|
+
let dcrResult = null;
|
|
219
|
+
try {
|
|
220
|
+
dcrResult = (await internalClient().query(
|
|
221
|
+
// biome-ignore lint/suspicious/noExplicitAny: Convex string API
|
|
222
|
+
"oauthDcr:validateAccessToken", { accessToken: token }));
|
|
223
|
+
}
|
|
224
|
+
catch (err) {
|
|
225
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
226
|
+
console.warn("[auth] DCR OAuth lookup skipped:", message);
|
|
227
|
+
}
|
|
228
|
+
if (dcrResult?.valid === true) {
|
|
229
|
+
const internalUrl = process.env.CONVEX_URL_INTERNAL;
|
|
230
|
+
if (!internalUrl) {
|
|
231
|
+
console.error("[auth] CONVEX_URL_INTERNAL not set — cannot route DCR OAuth token");
|
|
232
|
+
return c.json({ error: "Server misconfigured: internal deployment URL missing" }, 500);
|
|
233
|
+
}
|
|
234
|
+
// Map DCR single-scope string → OAuthContext fields.
|
|
235
|
+
// DCR tokens always carry "mcp:full" which maps to full access.
|
|
236
|
+
const scopes = dcrResult.scope.split(/\s+/).filter(Boolean);
|
|
237
|
+
const isFull = scopes.includes("mcp:full");
|
|
238
|
+
c.set("tenant", {
|
|
239
|
+
tenantName: `dcr:${dcrResult.clientId}`,
|
|
240
|
+
convexUrl: internalUrl,
|
|
241
|
+
});
|
|
242
|
+
c.set("oauthContext", {
|
|
243
|
+
clientId: dcrResult.clientId,
|
|
244
|
+
userId: dcrResult.clientId,
|
|
245
|
+
scopes,
|
|
246
|
+
scopeProfile: isFull ? "master" : "client-generic",
|
|
247
|
+
fromAllowList: isFull ? ["*"] : [],
|
|
248
|
+
namespaceReadPrefixes: isFull ? ["*"] : [],
|
|
249
|
+
namespaceWritePrefixes: isFull ? ["*"] : [],
|
|
250
|
+
expiresAt: dcrResult.expiresAt,
|
|
251
|
+
isMaster: false,
|
|
252
|
+
});
|
|
253
|
+
await next();
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
// ── (4) Legacy internal bearer — mcpTenants table ───────────────────────
|
|
216
257
|
let tenant;
|
|
217
258
|
try {
|
|
218
259
|
tenant = (await internalClient().query(
|
package/dist/src/tools.js
CHANGED
|
@@ -59,7 +59,7 @@ const memoryTypeSchema = z
|
|
|
59
59
|
.describe("Memory classification type");
|
|
60
60
|
export const creatorSchema = z
|
|
61
61
|
.string()
|
|
62
|
-
.describe("Orchestrator role name (e.g. pi, tau, phi, sigma, omega, zeta, eta, kappa, alpha, lambda, victor, laurent, or any custom client role (lowercase string)). " +
|
|
62
|
+
.describe("Orchestrator role name (e.g. pi, tau, phi, sigma, omega, zeta, eta, kappa, alpha, lambda, victor, epsilon, omicron, upsilon, laurent, or any custom client role (lowercase string)). " +
|
|
63
63
|
"New internal orchestrators use Greek letters (lowercase); external client orchestrators use free lowercase strings.");
|
|
64
64
|
export const severitySchema = z
|
|
65
65
|
.enum(["critical", "major", "minor"])
|
|
@@ -105,7 +105,7 @@ export const updateBriefingNoteSchema = z.object({
|
|
|
105
105
|
});
|
|
106
106
|
const assigneeSchema = z
|
|
107
107
|
.string()
|
|
108
|
-
.describe("Orchestrator to assign to (e.g. pi, tau, phi, sigma, omega, zeta, eta, kappa, alpha, lambda, victor, laurent, or any custom client role (lowercase string)). " +
|
|
108
|
+
.describe("Orchestrator to assign to (e.g. pi, tau, phi, sigma, omega, zeta, eta, kappa, alpha, lambda, victor, epsilon, omicron, upsilon, laurent, or any custom client role (lowercase string)). " +
|
|
109
109
|
"New internal orchestrators use Greek letters (lowercase); external client orchestrators use free lowercase strings.");
|
|
110
110
|
const prioritySchema = z
|
|
111
111
|
.enum(["urgent", "high", "medium", "low"])
|
|
@@ -615,7 +615,7 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
615
615
|
server.tool("send_message", "Send a message to one, many, or all orchestrators. " +
|
|
616
616
|
"channel: 'broadcast' = all, 'tau' = role DM, 'pi-vps' = instance DM, 'tau,phi' = multi. " +
|
|
617
617
|
"Creates message + one receipt per recipient. Replaces claude-peers send_message.", {
|
|
618
|
-
from: creatorSchema.describe("Sender role (e.g. pi, tau, phi, sigma, omega, zeta, eta, kappa, alpha, lambda, victor, or any custom role)"),
|
|
618
|
+
from: creatorSchema.describe("Sender role (e.g. pi, tau, phi, sigma, omega, zeta, eta, kappa, alpha, lambda, victor, epsilon, omicron, upsilon, or any custom role)"),
|
|
619
619
|
fromInstanceId: z
|
|
620
620
|
.string()
|
|
621
621
|
.optional()
|
|
@@ -673,7 +673,7 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
673
673
|
server.tool("check_messages", "Check for unread messages. Returns messages with receiptIds for marking as read. " +
|
|
674
674
|
"If recipientInstanceId is provided, returns instance-targeted + role-level messages. " +
|
|
675
675
|
"Replaces claude-peers check_messages.", {
|
|
676
|
-
recipient: creatorSchema.describe("Orchestrator role (e.g. pi, tau, phi, sigma, omega, zeta, eta, kappa, alpha, lambda, victor, or any custom role)"),
|
|
676
|
+
recipient: creatorSchema.describe("Orchestrator role (e.g. pi, tau, phi, sigma, omega, zeta, eta, kappa, alpha, lambda, victor, epsilon, omicron, upsilon, or any custom role)"),
|
|
677
677
|
recipientInstanceId: z
|
|
678
678
|
.string()
|
|
679
679
|
.optional()
|
|
@@ -2536,7 +2536,7 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2536
2536
|
server.tool("add_repo_mapping", "Add or update a GitHub repo → orchestrator mapping. Used by the webhook pipeline to route GitHub events to the right orchestrator.", {
|
|
2537
2537
|
repo: z
|
|
2538
2538
|
.string()
|
|
2539
|
-
.describe("Full repo name — e.g. '
|
|
2539
|
+
.describe("Full repo name — e.g. 'vantageos-agency/vantage-peers'"),
|
|
2540
2540
|
orchestrator: z
|
|
2541
2541
|
.string()
|
|
2542
2542
|
.describe("Target orchestrator — e.g. 'sigma', 'omega', 'tau'"),
|
|
@@ -2590,7 +2590,7 @@ export function registerTools(server, convex, oauthCtx) {
|
|
|
2590
2590
|
server.tool("remove_repo_mapping", "Remove a GitHub repo mapping by repo name. Stops routing webhook events for this repo.", {
|
|
2591
2591
|
repo: z
|
|
2592
2592
|
.string()
|
|
2593
|
-
.describe("Full repo name to remove — e.g. '
|
|
2593
|
+
.describe("Full repo name to remove — e.g. 'vantageos-agency/vantage-peers'"),
|
|
2594
2594
|
}, async ({ repo }) => {
|
|
2595
2595
|
try {
|
|
2596
2596
|
const result = await convex.mutation("githubRepoMapping:remove", {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vantage-peers-mcp",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.1",
|
|
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",
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
},
|
|
17
17
|
"files": [
|
|
18
18
|
"dist/",
|
|
19
|
-
"README.md"
|
|
19
|
+
"README.md",
|
|
20
|
+
"CHANGELOG.md"
|
|
20
21
|
],
|
|
21
22
|
"scripts": {
|
|
22
23
|
"generate:api": "cd .. && npx convex-helpers ts-api-spec --prod --output-file mcp-server/api.ts",
|
|
@@ -56,7 +57,7 @@
|
|
|
56
57
|
"author": "ElPi Corp",
|
|
57
58
|
"license": "FSL-1.1-Apache-2.0",
|
|
58
59
|
"dependencies": {
|
|
59
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
60
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
60
61
|
"convex": "^1.34.0",
|
|
61
62
|
"dotenv": "^17.4.2",
|
|
62
63
|
"zod": "^4.3.6"
|
|
@@ -68,6 +69,9 @@
|
|
|
68
69
|
"typescript": "^5.9.3",
|
|
69
70
|
"@types/node": "^24.12.2"
|
|
70
71
|
},
|
|
72
|
+
"overrides": {
|
|
73
|
+
"path-to-regexp": "^8.4.0"
|
|
74
|
+
},
|
|
71
75
|
"engines": {
|
|
72
76
|
"node": ">=18"
|
|
73
77
|
},
|