@agentvalet/mcp-server 1.8.0 → 1.10.0
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 +12 -0
- package/dist/auth.js +1 -1
- package/dist/index.js +14 -1
- package/dist/tools/handlers.js +11 -0
- package/dist/tools/schemas.js +29 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -59,6 +59,18 @@ Add to your `.mcp.json` or equivalent:
|
|
|
59
59
|
| `report_self_diagnostic` | JWT | Lodge a self-report (error/warning/info) with the owner |
|
|
60
60
|
| `agent_register` | None | Self-register a new agent with an owner |
|
|
61
61
|
| `agent_status` | None | Poll registration approval status |
|
|
62
|
+
| `issue_child_agent` | JWT | Mint a short-lived child identity for a subagent: its own agent id, an attenuated scope slice (child ⊆ parent), its own audit trail, individually revocable. Returns a bearer token for the subagent's `Authorization` header. |
|
|
63
|
+
|
|
64
|
+
## Session attribution
|
|
65
|
+
|
|
66
|
+
Every assertion this server signs carries a stable per-process `session_id`
|
|
67
|
+
claim, which the proxy stamps on each audit row. It distinguishes *runs* of
|
|
68
|
+
the server, not subagents: hosts that share one server process (for example
|
|
69
|
+
Claude Code Task-tool subagents) share the id. HTTP callers without this
|
|
70
|
+
server can supply the same discriminator via an `X-AV-Session` header
|
|
71
|
+
(`[A-Za-z0-9._:-]{1,128}`). Both are caller-asserted forensics — the proxy
|
|
72
|
+
never makes an authorization decision on them. For verified per-subagent
|
|
73
|
+
identity, mint a child with `issue_child_agent` instead.
|
|
62
74
|
|
|
63
75
|
## Transports
|
|
64
76
|
|
package/dist/auth.js
CHANGED
|
@@ -9,7 +9,7 @@ import { fetchWithTimeout } from "./net.js";
|
|
|
9
9
|
export async function signJWT(ctx) {
|
|
10
10
|
if (!ctx.privateKey)
|
|
11
11
|
throw new Error("Private key not loaded");
|
|
12
|
-
return new SignJWT({ agent_id: ctx.AGENT_ID, owner_id: ctx.OWNER_ID })
|
|
12
|
+
return new SignJWT({ agent_id: ctx.AGENT_ID, owner_id: ctx.OWNER_ID, session_id: ctx.SESSION_ID })
|
|
13
13
|
.setProtectedHeader({ alg: "RS256" })
|
|
14
14
|
.setIssuedAt()
|
|
15
15
|
.setExpirationTime("60s")
|
package/dist/index.js
CHANGED
|
@@ -15,6 +15,7 @@ process.on("unhandledRejection", (err) => {
|
|
|
15
15
|
process.stderr.write(`[mcp-server] FATAL unhandledRejection: ${err instanceof Error ? err.stack ?? err.message : String(err)}\n`);
|
|
16
16
|
process.exit(1);
|
|
17
17
|
});
|
|
18
|
+
import { randomBytes } from "node:crypto";
|
|
18
19
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
19
20
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
20
21
|
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
@@ -22,7 +23,7 @@ import { validateConfig } from "./config.js";
|
|
|
22
23
|
import { renderInstructions } from "./instructions.js";
|
|
23
24
|
import { signJWT } from "./auth.js";
|
|
24
25
|
import { ALLOWED_METHODS, ALL_TOOLS } from "./tools/schemas.js";
|
|
25
|
-
import { handleListPlatforms, handleUsePlatform, handleAgentRegister, handleAgentStatus, handleAuthzenEvaluate, handleListMyPendingActions, handleReportSelfDiagnostic, handleRequestPlatformAccess, } from "./tools/handlers.js";
|
|
26
|
+
import { handleListPlatforms, handleUsePlatform, handleAgentRegister, handleAgentStatus, handleAuthzenEvaluate, handleListMyPendingActions, handleReportSelfDiagnostic, handleRequestPlatformAccess, handleIssueChildAgent, } from "./tools/handlers.js";
|
|
26
27
|
import { errorContent } from "./net.js";
|
|
27
28
|
import { getPermissionsVersion, recordPermissionsVersion, startPermissionsWatcher, } from "./permissions-watcher.js";
|
|
28
29
|
// ---------------------------------------------------------------------------
|
|
@@ -47,12 +48,18 @@ const server = new Server({ name: "agentvalet", version: "1.0.0" },
|
|
|
47
48
|
// permissions-watcher.ts). Declaring the capability is what makes the SDK
|
|
48
49
|
// allow that notification.
|
|
49
50
|
{ capabilities: { tools: { listChanged: true } }, instructions: renderInstructions(undefined) });
|
|
51
|
+
// One id per server process. Discriminates runs in audit_log.session_id;
|
|
52
|
+
// forensics only — the proxy never branches enforcement on it. Note: Claude
|
|
53
|
+
// Code Task-tool subagents share the parent process, so this discriminates
|
|
54
|
+
// processes/runs, not subagents.
|
|
55
|
+
const SESSION_ID = "sess_" + randomBytes(8).toString("hex");
|
|
50
56
|
// The config + server bundle threaded into auth + handlers (see context.ts) —
|
|
51
57
|
// keeps those modules free of globals.
|
|
52
58
|
const ctx = {
|
|
53
59
|
AGENT_ID,
|
|
54
60
|
OWNER_ID,
|
|
55
61
|
PROXY_URL,
|
|
62
|
+
SESSION_ID,
|
|
56
63
|
AGENT_PRIVATE_KEY_RAW,
|
|
57
64
|
privateKey,
|
|
58
65
|
server,
|
|
@@ -213,6 +220,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
213
220
|
}
|
|
214
221
|
return await handleRequestPlatformAccess(ctx, args);
|
|
215
222
|
}
|
|
223
|
+
if (name === "issue_child_agent") {
|
|
224
|
+
if (!args || !Array.isArray(args.grants)) {
|
|
225
|
+
return errorContent("Invalid or missing argument: grants is required");
|
|
226
|
+
}
|
|
227
|
+
return await handleIssueChildAgent(ctx, args);
|
|
228
|
+
}
|
|
216
229
|
return {
|
|
217
230
|
content: [{ type: "text", text: `Unknown tool: ${name}` }],
|
|
218
231
|
isError: true,
|
package/dist/tools/handlers.js
CHANGED
|
@@ -347,6 +347,17 @@ export async function handleRequestPlatformAccess(ctx, args) {
|
|
|
347
347
|
message: "Request submitted and awaiting admin approval. Retry your call later.",
|
|
348
348
|
}));
|
|
349
349
|
}
|
|
350
|
+
export async function handleIssueChildAgent(ctx, args) {
|
|
351
|
+
const gate = await requireCredentials(ctx);
|
|
352
|
+
if (gate)
|
|
353
|
+
return gate;
|
|
354
|
+
const resp = await fetchWithAuth(ctx, `${ctx.PROXY_URL}/v1/agents/children`, {
|
|
355
|
+
method: "POST",
|
|
356
|
+
body: JSON.stringify(args ?? {}),
|
|
357
|
+
});
|
|
358
|
+
const json = await resp.json().catch(() => ({ error: `HTTP ${resp.status}` }));
|
|
359
|
+
return { content: [{ type: "text", text: JSON.stringify(json) }], isError: !resp.ok };
|
|
360
|
+
}
|
|
350
361
|
export async function handleReportSelfDiagnostic(ctx, args) {
|
|
351
362
|
const gate = await requireCredentials(ctx);
|
|
352
363
|
if (gate)
|
package/dist/tools/schemas.js
CHANGED
|
@@ -375,6 +375,34 @@ export const REQUEST_PLATFORM_ACCESS_TOOL = {
|
|
|
375
375
|
required: ["status"],
|
|
376
376
|
},
|
|
377
377
|
};
|
|
378
|
+
export const ISSUE_CHILD_AGENT_TOOL = {
|
|
379
|
+
name: "issue_child_agent",
|
|
380
|
+
description: "Issue a short-lived child agent identity for a subagent you are about to spawn. The child gets its own agent id, its own attenuated permission slice (child scopes must be a subset of yours), and its own audit trail; it can be revoked alone without touching you or its siblings. Returns a bearer token the subagent must send as its Authorization header. TTL 60-3600s (default 900). Inject ONLY the bearer_token into the subagent's context — never your own credentials.",
|
|
381
|
+
inputSchema: {
|
|
382
|
+
type: "object",
|
|
383
|
+
properties: {
|
|
384
|
+
name: { type: "string", description: "Optional human-readable name for the child agent (1-200 chars)." },
|
|
385
|
+
ttl_seconds: {
|
|
386
|
+
type: "number",
|
|
387
|
+
description: "Optional lifetime in seconds for the child's bearer token. 60-3600, default 900.",
|
|
388
|
+
},
|
|
389
|
+
grants: {
|
|
390
|
+
type: "array",
|
|
391
|
+
description: "Array of platform grants to attenuate onto the child. Each scope must be a subset of your own granted scopes.",
|
|
392
|
+
items: {
|
|
393
|
+
type: "object",
|
|
394
|
+
properties: {
|
|
395
|
+
platform: { type: "string", description: "Platform ID (e.g. github, slack)" },
|
|
396
|
+
scopes: { type: "array", items: { type: "string" }, description: "Scopes to grant the child for this platform." },
|
|
397
|
+
connection_id: { type: "string", description: "Optional. Target a specific connection when a platform has more than one connected." },
|
|
398
|
+
},
|
|
399
|
+
required: ["platform", "scopes"],
|
|
400
|
+
},
|
|
401
|
+
},
|
|
402
|
+
},
|
|
403
|
+
required: ["grants"],
|
|
404
|
+
},
|
|
405
|
+
};
|
|
378
406
|
export const ALL_TOOLS = [
|
|
379
407
|
LIST_PLATFORMS_TOOL,
|
|
380
408
|
USE_PLATFORM_TOOL,
|
|
@@ -387,4 +415,5 @@ export const ALL_TOOLS = [
|
|
|
387
415
|
REPORT_SELF_DIAGNOSTIC_TOOL,
|
|
388
416
|
LIST_MY_PENDING_ACTIONS_TOOL,
|
|
389
417
|
REQUEST_PLATFORM_ACCESS_TOOL,
|
|
418
|
+
ISSUE_CHILD_AGENT_TOOL,
|
|
390
419
|
];
|
package/package.json
CHANGED