@agentvalet/mcp-server 1.8.0 → 1.12.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 +19 -2
- package/dist/permissions-watcher.js +86 -1
- package/dist/tools/handlers.js +11 -0
- package/dist/tools/schemas.js +33 -4
- 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,9 +23,9 @@ 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
|
-
import { getPermissionsVersion, recordPermissionsVersion, startPermissionsWatcher, } from "./permissions-watcher.js";
|
|
28
|
+
import { getPermissionsVersion, noteAgentActivity, recordPermissionsVersion, startPermissionsWatcher, } from "./permissions-watcher.js";
|
|
28
29
|
// ---------------------------------------------------------------------------
|
|
29
30
|
// Startup env validation
|
|
30
31
|
// ---------------------------------------------------------------------------
|
|
@@ -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,
|
|
@@ -110,6 +117,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
110
117
|
});
|
|
111
118
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
112
119
|
const { name, arguments: args } = request.params;
|
|
120
|
+
// The agent is doing something, so its view of the spec is about to matter.
|
|
121
|
+
// This is what un-idles the permissions watcher (and resets its backoff); an
|
|
122
|
+
// editor window nobody is using never reaches here and never revalidates.
|
|
123
|
+
noteAgentActivity();
|
|
113
124
|
if (name === "list_platforms") {
|
|
114
125
|
return await handleListPlatforms(ctx);
|
|
115
126
|
}
|
|
@@ -213,6 +224,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
213
224
|
}
|
|
214
225
|
return await handleRequestPlatformAccess(ctx, args);
|
|
215
226
|
}
|
|
227
|
+
if (name === "issue_child_agent") {
|
|
228
|
+
if (!args || !Array.isArray(args.grants)) {
|
|
229
|
+
return errorContent("Invalid or missing argument: grants is required");
|
|
230
|
+
}
|
|
231
|
+
return await handleIssueChildAgent(ctx, args);
|
|
232
|
+
}
|
|
216
233
|
return {
|
|
217
234
|
content: [{ type: "text", text: `Unknown tool: ${name}` }],
|
|
218
235
|
isError: true,
|
|
@@ -22,14 +22,66 @@
|
|
|
22
22
|
// (apps/proxy/src/routes/mcp.ts) is deliberately stateless — a fresh Server per
|
|
23
23
|
// request — so it has no server→client channel and cannot push at all; there,
|
|
24
24
|
// cheap revalidation via the 304 path is the whole story.
|
|
25
|
+
//
|
|
26
|
+
// WHEN we revalidate, and why it is not simply "every 60 seconds":
|
|
27
|
+
//
|
|
28
|
+
// A fixed timer polls a resource that almost never changes, on behalf of a
|
|
29
|
+
// session that is usually doing nothing. On 2026-09-01 that shape turned 210
|
|
30
|
+
// real governed calls into ~7,000 revalidations in a day — mostly from editor
|
|
31
|
+
// windows left open overnight — and each one cost the proxy a full spec
|
|
32
|
+
// resolution. The spec only matters when the agent is about to ACT on it, so
|
|
33
|
+
// the timer now defers to two signals:
|
|
34
|
+
//
|
|
35
|
+
// 1. ACTIVITY. noteAgentActivity() is called from the tool dispatch on every
|
|
36
|
+
// tool call. A server that has not served one in IDLE_AFTER_MS stops
|
|
37
|
+
// revalidating entirely — an idle window costs nothing — and resumes the
|
|
38
|
+
// moment the agent does anything.
|
|
39
|
+
// 2. STABILITY. Each consecutive 304 backs the interval off (doubling, to a
|
|
40
|
+
// cap). A spec that has not moved in twenty minutes is not about to be
|
|
41
|
+
// checked every minute. Any observed change, and any tool call, resets it.
|
|
42
|
+
//
|
|
43
|
+
// What this trades away: after an idle stretch, the FIRST tool call may still
|
|
44
|
+
// reason from a spec up to one poll old. That is the pre-watcher behaviour and
|
|
45
|
+
// it is safe by construction — the proxy authorises every call against the live
|
|
46
|
+
// database regardless, so the worst case is one clean 403 rather than a wrongly
|
|
47
|
+
// permitted action. An agent that wants certainty calls list_platforms, which
|
|
48
|
+
// revalidates synchronously.
|
|
25
49
|
import { signJWT } from "./auth.js";
|
|
26
50
|
import { fetchWithTimeout } from "./net.js";
|
|
27
51
|
const DEFAULT_POLL_MS = 60_000;
|
|
28
52
|
// Floor so a typo in the env var can't turn an agent into a polling hammer
|
|
29
53
|
// against the proxy.
|
|
30
54
|
const MIN_POLL_MS = 10_000;
|
|
55
|
+
// No tool call in this long ⇒ nobody is relying on the spec, so stop asking for
|
|
56
|
+
// it. Generous on purpose: the point is to catch the window left open all
|
|
57
|
+
// night, not to punish a user who paused to read something.
|
|
58
|
+
const IDLE_AFTER_MS = 15 * 60 * 1000;
|
|
59
|
+
// Ceiling on the stability backoff, counted in base intervals. At the 60s
|
|
60
|
+
// default this settles an unchanging spec at one revalidation every ~16 min.
|
|
61
|
+
const MAX_BACKOFF_TICKS = 15;
|
|
31
62
|
let currentVersion = null;
|
|
32
63
|
let timer = null;
|
|
64
|
+
// Boot counts as activity: a server that has just started is about to be used.
|
|
65
|
+
let lastActivityAt = Date.now();
|
|
66
|
+
// Base intervals still to be skipped before the next revalidation.
|
|
67
|
+
let ticksToSkip = 0;
|
|
68
|
+
// Consecutive revalidations that found nothing changed; drives the backoff.
|
|
69
|
+
let unchangedStreak = 0;
|
|
70
|
+
/**
|
|
71
|
+
* Record that the agent just did something. Called from the tool dispatch on
|
|
72
|
+
* EVERY tool call — the cheapest possible signal that the spec is about to
|
|
73
|
+
* matter again. Resets both gates, so a session waking up revalidates on the
|
|
74
|
+
* next tick rather than at the end of a long backoff.
|
|
75
|
+
*/
|
|
76
|
+
export function noteAgentActivity() {
|
|
77
|
+
lastActivityAt = Date.now();
|
|
78
|
+
ticksToSkip = 0;
|
|
79
|
+
unchangedStreak = 0;
|
|
80
|
+
}
|
|
81
|
+
/** Whether the watcher would skip a tick right now. Exported for tests. */
|
|
82
|
+
export function isIdle(now = Date.now()) {
|
|
83
|
+
return now - lastActivityAt > IDLE_AFTER_MS;
|
|
84
|
+
}
|
|
33
85
|
export function getPermissionsVersion() {
|
|
34
86
|
return currentVersion;
|
|
35
87
|
}
|
|
@@ -49,6 +101,9 @@ export function recordPermissionsVersion(version) {
|
|
|
49
101
|
/** Test seam: forget everything the watcher has learned. */
|
|
50
102
|
export function resetPermissionsVersion() {
|
|
51
103
|
currentVersion = null;
|
|
104
|
+
lastActivityAt = Date.now();
|
|
105
|
+
ticksToSkip = 0;
|
|
106
|
+
unchangedStreak = 0;
|
|
52
107
|
}
|
|
53
108
|
export function pollIntervalMs(raw = process.env.AV_PERMISSIONS_POLL_MS) {
|
|
54
109
|
if (raw === undefined || raw === "")
|
|
@@ -107,6 +162,36 @@ export async function checkPermissionsVersion(ctx) {
|
|
|
107
162
|
}
|
|
108
163
|
return true;
|
|
109
164
|
}
|
|
165
|
+
/**
|
|
166
|
+
* One timer tick: decide whether this is a moment worth spending a request on,
|
|
167
|
+
* and revalidate if so. Exported for tests. Returns true when it actually went
|
|
168
|
+
* to the network.
|
|
169
|
+
*/
|
|
170
|
+
export async function tick(ctx) {
|
|
171
|
+
// Nobody is using this session. The spec can drift freely — the next tool
|
|
172
|
+
// call resets the gate, and the proxy authorises against the live database
|
|
173
|
+
// whatever this server happens to believe.
|
|
174
|
+
if (isIdle())
|
|
175
|
+
return false;
|
|
176
|
+
// Backing off after a run of unchanged reads.
|
|
177
|
+
if (ticksToSkip > 0) {
|
|
178
|
+
ticksToSkip -= 1;
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
const changed = await checkPermissionsVersion(ctx);
|
|
182
|
+
if (changed) {
|
|
183
|
+
// Something moved. Watch closely again — an owner mid-edit often makes
|
|
184
|
+
// several changes in a row.
|
|
185
|
+
unchangedStreak = 0;
|
|
186
|
+
ticksToSkip = 0;
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
// 1, 3, 7, 15, 15, … base intervals of silence.
|
|
190
|
+
unchangedStreak += 1;
|
|
191
|
+
ticksToSkip = Math.min(2 ** unchangedStreak - 1, MAX_BACKOFF_TICKS);
|
|
192
|
+
}
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
110
195
|
/**
|
|
111
196
|
* Start background revalidation. No-op without credentials or when polling is
|
|
112
197
|
* disabled. The timer is unref'd so it can never hold the process open.
|
|
@@ -120,7 +205,7 @@ export function startPermissionsWatcher(ctx) {
|
|
|
120
205
|
if (interval === 0)
|
|
121
206
|
return;
|
|
122
207
|
timer = setInterval(() => {
|
|
123
|
-
void
|
|
208
|
+
void tick(ctx).catch(() => { });
|
|
124
209
|
}, interval);
|
|
125
210
|
// `unref` is absent in some non-Node runtimes; guard rather than assume.
|
|
126
211
|
timer.unref?.();
|
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
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
export const ALLOWED_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE"];
|
|
8
8
|
export const LIST_PLATFORMS_TOOL = {
|
|
9
9
|
name: "list_platforms",
|
|
10
|
-
description: "list_platforms: List the platforms and permission scopes this agent has access to.\nInput: None.\nReturns: { platforms: [{ platformId, platformName, scopes, requireApproval, approvalScopes?, connections? }], version: \"<hex>\" }. A platform includes `connections` only when it has 2+ usable accounts; pass the chosen connection_id to use_platform (omit for the default).\nApproval: a call needs human approval if the platform's requireApproval is true (grant-level: every call prompts), if the chosen connection's requireApproval is true, or if the scope is listed in approvalScopes (policy-level: just those scopes prompt). approvalScopes is omitted when no scope is policy-gated.\nVersion: a deterministic hash over the effective spec — it changes whenever the platform set, the scopes, the approval requirements (grant or policy), or the connections change. Compare it against the `Current permissions version` stamped on this description: if they differ, your cached result is stale and you must call list_platforms again before trusting it.\nAuth: Bearer JWT.",
|
|
10
|
+
description: "list_platforms: List the platforms and permission scopes this agent has access to.\nInput: None.\nReturns: { platforms: [{ platformId, platformName, scopes, requireApproval, approvalScopes?, connections?, callStyle? }], version: \"<hex>\" }. A platform includes `connections` only when it has 2+ usable accounts; pass the chosen connection_id to use_platform (omit for the default).\nCall style: `callStyle` is present only as \"graphql\", and says how to ADDRESS the platform rather than what you may do there: that platform has ONE endpoint taking {query, variables} via POST, so pass \"/\" as the endpoint and put the operation in the body. Its path and HTTP method carry no meaning, read_platform cannot be used on it at all, and a GET returns an upstream error naming neither the platform nor the cause. Absent means a conventional REST API.\nApproval: a call needs human approval if the platform's requireApproval is true (grant-level: every call prompts), if the chosen connection's requireApproval is true, or if the scope is listed in approvalScopes (policy-level: just those scopes prompt). approvalScopes is omitted when no scope is policy-gated.\nVersion: a deterministic hash over the effective spec — it changes whenever the platform set, the scopes, the approval requirements (grant or policy), or the connections change. Compare it against the `Current permissions version` stamped on this description: if they differ, your cached result is stale and you must call list_platforms again before trusting it.\nAuth: Bearer JWT.",
|
|
11
11
|
inputSchema: { type: "object", properties: {} },
|
|
12
12
|
outputSchema: {
|
|
13
13
|
type: "object",
|
|
@@ -101,7 +101,7 @@ export const USE_PLATFORM_TOOL = {
|
|
|
101
101
|
// readOnlyHint annotation enforced rather than merely promised.
|
|
102
102
|
export const READ_PLATFORM_TOOL = {
|
|
103
103
|
name: "read_platform",
|
|
104
|
-
description: "read_platform: Read data from a platform the owner has connected, through the AgentValet proxy. Calls the platform's own public REST API (for example the GitHub REST API, the Slack Web API, the Stripe API); `endpoint` is a path in that API exactly as its documentation defines it. Always issues GET — use write_platform to create or update, and delete_platform to remove. Input: platform, endpoint, scope, connection_id (optional), reason (optional). Returns: the upstream API response body. Auth: Bearer JWT.",
|
|
104
|
+
description: "read_platform: Read data from a platform the owner has connected, through the AgentValet proxy. Calls the platform's own public REST API (for example the GitHub REST API, the Slack Web API, the Stripe API); `endpoint` is a path in that API exactly as its documentation defines it. A platform that list_platforms reports as callStyle 'graphql' cannot be read with this tool at all: it exposes a SINGLE endpoint that accepts only POST {query, variables}, so send the read query through write_platform (or use_platform with method POST) even though it is a read. Issuing a GET there returns an upstream error that names neither the platform nor the cause — it is not a broker fault. Always issues GET — use write_platform to create or update, and delete_platform to remove. Input: platform, endpoint, scope, connection_id (optional), reason (optional). Returns: the upstream API response body. Auth: Bearer JWT.",
|
|
105
105
|
inputSchema: {
|
|
106
106
|
type: "object",
|
|
107
107
|
properties: {
|
|
@@ -122,7 +122,7 @@ export const READ_PLATFORM_TOOL = {
|
|
|
122
122
|
};
|
|
123
123
|
export const WRITE_PLATFORM_TOOL = {
|
|
124
124
|
name: "write_platform",
|
|
125
|
-
description: "write_platform: Create or update data on a platform the owner has connected, through the AgentValet proxy. Calls the platform's own public REST API (for example the GitHub REST API, the Slack Web API, the Stripe API); `endpoint` is a path in that API exactly as its documentation defines it. Accepts POST, PUT or PATCH only — use read_platform for GET and delete_platform for DELETE. May block up to 50s for owner approval, then returns the approved result; otherwise returns a pending_approval envelope. Input: platform, endpoint, scope, method (optional, default POST), body (optional), connection_id (optional), reason (optional). Auth: Bearer JWT.",
|
|
125
|
+
description: "write_platform: Create or update data on a platform the owner has connected, through the AgentValet proxy. Calls the platform's own public REST API (for example the GitHub REST API, the Slack Web API, the Stripe API); `endpoint` is a path in that API exactly as its documentation defines it. For a platform whose callStyle is 'graphql', pass \"/\" as the endpoint and put the operation in body as {query, variables}: the path and the HTTP method carry no meaning on that platform. Accepts POST, PUT or PATCH only — use read_platform for GET and delete_platform for DELETE. May block up to 50s for owner approval, then returns the approved result; otherwise returns a pending_approval envelope. Input: platform, endpoint, scope, method (optional, default POST), body (optional), connection_id (optional), reason (optional). Auth: Bearer JWT.",
|
|
126
126
|
inputSchema: {
|
|
127
127
|
type: "object",
|
|
128
128
|
properties: {
|
|
@@ -153,7 +153,7 @@ export const WRITE_PLATFORM_TOOL = {
|
|
|
153
153
|
};
|
|
154
154
|
export const DELETE_PLATFORM_TOOL = {
|
|
155
155
|
name: "delete_platform",
|
|
156
|
-
description: "delete_platform: Delete data on a platform the owner has connected, through the AgentValet proxy. Calls the platform's own public REST API (for example the GitHub REST API, the Slack Web API, the Stripe API); `endpoint` is a path in that API exactly as its documentation defines it. Always issues DELETE. This removes data and is not reversible by AgentValet. May block up to 50s for owner approval. Input: platform, endpoint, scope, body (optional), connection_id (optional), reason (optional). Auth: Bearer JWT.",
|
|
156
|
+
description: "delete_platform: Delete data on a platform the owner has connected, through the AgentValet proxy. Calls the platform's own public REST API (for example the GitHub REST API, the Slack Web API, the Stripe API); `endpoint` is a path in that API exactly as its documentation defines it. For a platform whose callStyle is 'graphql', pass \"/\" as the endpoint and put the operation in body as {query, variables}: the path and the HTTP method carry no meaning on that platform. Always issues DELETE. This removes data and is not reversible by AgentValet. May block up to 50s for owner approval. Input: platform, endpoint, scope, body (optional), connection_id (optional), reason (optional). Auth: Bearer JWT.",
|
|
157
157
|
inputSchema: {
|
|
158
158
|
type: "object",
|
|
159
159
|
properties: {
|
|
@@ -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