@i4ctime/q-ring 0.16.1 → 0.17.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/dist/mcp.js CHANGED
@@ -40,7 +40,7 @@ import {
40
40
  tunnelList,
41
41
  tunnelRead,
42
42
  verifyAuditChain
43
- } from "./chunk-NCM5GHNW.js";
43
+ } from "./chunk-KFILBHOY.js";
44
44
 
45
45
  // src/mcp.ts
46
46
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
@@ -48,6 +48,82 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
48
48
  // src/mcp/server.ts
49
49
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
50
50
 
51
+ // src/mcp/tool-annotations.ts
52
+ var hints = (readOnlyHint, destructiveHint, idempotentHint, openWorldHint) => ({ readOnlyHint, destructiveHint, idempotentHint, openWorldHint });
53
+ var READ = hints(true, false, true, false);
54
+ var READ_OPEN = hints(true, false, true, true);
55
+ var TOOL_ANNOTATIONS = {
56
+ // secrets
57
+ get_secret: READ,
58
+ list_secrets: READ,
59
+ set_secret: hints(false, true, true, false),
60
+ delete_secret: hints(false, true, true, false),
61
+ has_secret: READ,
62
+ export_secrets: READ,
63
+ import_dotenv: hints(false, false, true, false),
64
+ // existing keys are skipped, not overwritten
65
+ inspect_secret: READ,
66
+ generate_secret: hints(false, true, false, false),
67
+ // saveAs overwrites; fresh value every call
68
+ entangle_secrets: hints(false, false, true, false),
69
+ disentangle_secrets: hints(false, false, true, false),
70
+ // project
71
+ check_project: READ,
72
+ env_generate: READ,
73
+ // renders text, never writes files
74
+ detect_environment: READ,
75
+ get_project_context: READ,
76
+ // tunnels (memory-only)
77
+ tunnel_create: hints(false, false, false, false),
78
+ tunnel_read: hints(false, true, false, false),
79
+ // may self-destruct on read
80
+ tunnel_list: READ,
81
+ tunnel_destroy: hints(false, true, true, false),
82
+ // teleport
83
+ teleport_pack: READ,
84
+ teleport_unpack: hints(false, true, true, false),
85
+ // imports may overwrite keys
86
+ // audit / health
87
+ audit_log: READ,
88
+ detect_anomalies: READ,
89
+ health_check: READ,
90
+ verify_audit_chain: READ,
91
+ export_audit: READ,
92
+ // validation / rotation (network)
93
+ validate_secret: READ_OPEN,
94
+ list_providers: READ,
95
+ rotate_secret: hints(false, true, false, true),
96
+ // replaces the credential upstream and locally
97
+ ci_validate_secrets: READ_OPEN,
98
+ // hooks
99
+ register_hook: hints(false, false, false, false),
100
+ list_hooks: READ,
101
+ remove_hook: hints(false, true, true, false),
102
+ // execution / scanning
103
+ exec_with_secrets: hints(false, true, false, true),
104
+ // arbitrary command
105
+ scan_codebase_for_secrets: READ,
106
+ lint_files: hints(false, true, true, false),
107
+ // fix:true rewrites source files
108
+ analyze_secrets: READ,
109
+ status_dashboard: hints(false, false, false, false),
110
+ // starts a local server, new token per launch
111
+ agent_scan: hints(false, true, false, true),
112
+ // autoRotate replaces expired credentials
113
+ // agent memory
114
+ agent_remember: hints(false, true, true, false),
115
+ agent_recall: READ,
116
+ agent_forget: hints(false, true, true, false),
117
+ // policy
118
+ check_policy: READ,
119
+ get_policy_summary: READ
120
+ };
121
+ function toolAnnotations(name) {
122
+ const a = TOOL_ANNOTATIONS[name];
123
+ if (!a) throw new Error(`q-ring: no tool annotations defined for "${name}"`);
124
+ return a;
125
+ }
126
+
51
127
  // src/mcp/tools/secrets.ts
52
128
  import { z as z2 } from "zod";
53
129
 
@@ -252,10 +328,7 @@ function opts(params) {
252
328
  function enforceToolPolicy(toolName, projectPath8) {
253
329
  const decision = checkToolPolicy(toolName, projectPath8);
254
330
  if (!decision.allowed) {
255
- return text(
256
- `Policy Denied: ${decision.reason} (source: ${decision.policySource})`,
257
- true
258
- );
331
+ return text(`Policy Denied: ${decision.reason} (source: ${decision.policySource})`, true);
259
332
  }
260
333
  return null;
261
334
  }
@@ -297,23 +370,18 @@ function registerSecretTools(server2) {
297
370
  teamId,
298
371
  orgId
299
372
  },
373
+ toolAnnotations("get_secret"),
300
374
  async (params) => {
301
375
  const toolBlock = enforceToolPolicy("get_secret", params.projectPath);
302
376
  if (toolBlock) return toolBlock;
303
377
  try {
304
- const keyBlock = checkKeyReadPolicy(
305
- params.key,
306
- void 0,
307
- params.projectPath
308
- );
378
+ const keyBlock = checkKeyReadPolicy(params.key, void 0, params.projectPath);
309
379
  if (!keyBlock.allowed) {
310
380
  return text(`Policy Denied: ${keyBlock.reason}`, true);
311
381
  }
312
382
  const value = getSecret(params.key, opts(params));
313
383
  if (value === null) return text(`Secret "${params.key}" not found`, true);
314
- return text(
315
- JSON.stringify({ ok: true, data: { key: params.key, value } }, null, 2)
316
- );
384
+ return text(JSON.stringify({ ok: true, data: { key: params.key, value } }, null, 2));
317
385
  } catch (err) {
318
386
  return text(err instanceof Error ? err.message : String(err), true);
319
387
  }
@@ -344,22 +412,19 @@ function registerSecretTools(server2) {
344
412
  teamId,
345
413
  orgId
346
414
  },
415
+ toolAnnotations("list_secrets"),
347
416
  async (params) => {
348
417
  const toolBlock = enforceToolPolicy("list_secrets", params.projectPath);
349
418
  if (toolBlock) return toolBlock;
350
419
  let entries = listSecrets(opts(params));
351
420
  if (params.tag) {
352
- entries = entries.filter(
353
- (e) => e.envelope?.meta.tags?.includes(params.tag)
354
- );
421
+ entries = entries.filter((e) => e.envelope?.meta.tags?.includes(params.tag));
355
422
  }
356
423
  if (params.expired) {
357
424
  entries = entries.filter((e) => e.decay?.isExpired);
358
425
  }
359
426
  if (params.stale) {
360
- entries = entries.filter(
361
- (e) => e.decay?.isStale && !e.decay?.isExpired
362
- );
427
+ entries = entries.filter((e) => e.decay?.isStale && !e.decay?.isExpired);
363
428
  }
364
429
  if (params.filter) {
365
430
  entries = filterSecretsByKeyGlob(entries, params.filter);
@@ -386,9 +451,7 @@ function registerSecretTools(server2) {
386
451
  "Mutates the keyring (overwrites any existing value at the same key/scope), writes a 'write' event to the audit log, and triggers any matching hooks. Subject to tool policy. Returns a short confirmation text like '[scope] KEY saved' (or '[scope] KEY set for env:NAME' when `env` is provided)."
387
452
  ].join(" "),
388
453
  {
389
- key: z2.string().describe(
390
- "Secret key name (UPPER_SNAKE_CASE recommended). Example: 'STRIPE_SECRET_KEY'."
391
- ),
454
+ key: z2.string().describe("Secret key name (UPPER_SNAKE_CASE recommended). Example: 'STRIPE_SECRET_KEY'."),
392
455
  value: z2.string().describe(
393
456
  "The secret value to store. Stored as-is; never logged or echoed. May be empty only when `env` is provided to register a new env without a default."
394
457
  ),
@@ -403,18 +466,8 @@ function registerSecretTools(server2) {
403
466
  description: z2.string().optional().describe(
404
467
  "Free-text human-readable description shown in `inspect_secret` and the dashboard."
405
468
  ),
406
- tags: z2.array(z2.string()).optional().describe(
407
- "Tag list for filtering and hook matching. Example: ['production', 'payments']."
408
- ),
409
- rotationFormat: z2.enum([
410
- "hex",
411
- "base64",
412
- "alphanumeric",
413
- "uuid",
414
- "api-key",
415
- "token",
416
- "password"
417
- ]).optional().describe(
469
+ tags: z2.array(z2.string()).optional().describe("Tag list for filtering and hook matching. Example: ['production', 'payments']."),
470
+ rotationFormat: z2.enum(["hex", "base64", "alphanumeric", "uuid", "api-key", "token", "password"]).optional().describe(
418
471
  "Format used by `agent_scan --autoRotate` and `rotate_secret` when this secret expires. Pick the format that matches the upstream service's accepted shape."
419
472
  ),
420
473
  rotationPrefix: z2.string().optional().describe(
@@ -423,6 +476,7 @@ function registerSecretTools(server2) {
423
476
  teamId,
424
477
  orgId
425
478
  },
479
+ toolAnnotations("set_secret"),
426
480
  async (params) => {
427
481
  const toolBlock = enforceToolPolicy("set_secret", params.projectPath);
428
482
  if (toolBlock) return toolBlock;
@@ -444,9 +498,7 @@ function registerSecretTools(server2) {
444
498
  rotationFormat: params.rotationFormat,
445
499
  rotationPrefix: params.rotationPrefix
446
500
  });
447
- return text(
448
- `[${params.scope ?? "global"}] ${params.key} set for env:${params.env}`
449
- );
501
+ return text(`[${params.scope ?? "global"}] ${params.key} set for env:${params.env}`);
450
502
  }
451
503
  setSecret(params.key, params.value, {
452
504
  ...o,
@@ -473,6 +525,7 @@ function registerSecretTools(server2) {
473
525
  teamId,
474
526
  orgId
475
527
  },
528
+ toolAnnotations("delete_secret"),
476
529
  async (params) => {
477
530
  const toolBlock = enforceToolPolicy("delete_secret", params.projectPath);
478
531
  if (toolBlock) return toolBlock;
@@ -497,6 +550,7 @@ function registerSecretTools(server2) {
497
550
  teamId,
498
551
  orgId
499
552
  },
553
+ toolAnnotations("has_secret"),
500
554
  async (params) => {
501
555
  const toolBlock = enforceToolPolicy("has_secret", params.projectPath);
502
556
  if (toolBlock) return toolBlock;
@@ -526,6 +580,7 @@ function registerSecretTools(server2) {
526
580
  teamId,
527
581
  orgId
528
582
  },
583
+ toolAnnotations("export_secrets"),
529
584
  async (params) => {
530
585
  const toolBlock = enforceToolPolicy("export_secrets", params.projectPath);
531
586
  if (toolBlock) return toolBlock;
@@ -559,6 +614,7 @@ function registerSecretTools(server2) {
559
614
  "If true, parse and report what would happen but do not write to the keyring. Useful for previewing imports before committing."
560
615
  )
561
616
  },
617
+ toolAnnotations("import_dotenv"),
562
618
  async (params) => {
563
619
  const toolBlock = enforceToolPolicy("import_dotenv", params.projectPath);
564
620
  if (toolBlock) return toolBlock;
@@ -595,6 +651,7 @@ function registerSecretTools(server2) {
595
651
  teamId,
596
652
  orgId
597
653
  },
654
+ toolAnnotations("inspect_secret"),
598
655
  async (params) => {
599
656
  const toolBlock = enforceToolPolicy("inspect_secret", params.projectPath);
600
657
  if (toolBlock) return toolBlock;
@@ -626,8 +683,7 @@ function registerSecretTools(server2) {
626
683
  if (envelope.meta.entangled?.length) {
627
684
  info.entangled = envelope.meta.entangled;
628
685
  }
629
- if (envelope.meta.description)
630
- info.description = envelope.meta.description;
686
+ if (envelope.meta.description) info.description = envelope.meta.description;
631
687
  if (envelope.meta.tags?.length) info.tags = envelope.meta.tags;
632
688
  return text(JSON.stringify(info, null, 2));
633
689
  }
@@ -640,15 +696,7 @@ function registerSecretTools(server2) {
640
696
  "If `saveAs` is provided this mutates the keyring (one 'write' event) and returns a summary like 'Generated and saved as \"KEY\" (FORMAT, ~N bits entropy)'. Without `saveAs` the call is read-only and returns JSON `{ ok, data: { value } }` containing the freshly generated string."
641
697
  ].join(" "),
642
698
  {
643
- format: z2.enum([
644
- "hex",
645
- "base64",
646
- "alphanumeric",
647
- "uuid",
648
- "api-key",
649
- "token",
650
- "password"
651
- ]).optional().default("api-key").describe(
699
+ format: z2.enum(["hex", "base64", "alphanumeric", "uuid", "api-key", "token", "password"]).optional().default("api-key").describe(
652
700
  "Output shape. 'hex' / 'base64' / 'alphanumeric' = raw random string of `length` characters; 'uuid' = RFC4122 v4; 'api-key' / 'token' = random alphanumeric with optional `prefix`; 'password' = mixed-case alphanumeric with symbols. Defaults to 'api-key'."
653
701
  ),
654
702
  length: z2.number().optional().describe(
@@ -665,6 +713,7 @@ function registerSecretTools(server2) {
665
713
  teamId,
666
714
  orgId
667
715
  },
716
+ toolAnnotations("generate_secret"),
668
717
  async (params) => {
669
718
  const toolBlock = enforceToolPolicy("generate_secret", params.projectPath);
670
719
  if (toolBlock) return toolBlock;
@@ -705,19 +754,14 @@ function registerSecretTools(server2) {
705
754
  "Project root for targetKey when targetScope='project'. Defaults to the server cwd."
706
755
  )
707
756
  },
757
+ toolAnnotations("entangle_secrets"),
708
758
  async (params) => {
709
- const toolBlock = enforceToolPolicy(
710
- "entangle_secrets",
711
- params.sourceProjectPath
712
- );
759
+ const toolBlock = enforceToolPolicy("entangle_secrets", params.sourceProjectPath);
713
760
  if (toolBlock) return toolBlock;
714
761
  for (const key of [params.sourceKey, params.targetKey]) {
715
762
  const decision = checkKeyReadPolicy(key, void 0, params.sourceProjectPath);
716
763
  if (!decision.allowed) {
717
- return text(
718
- `Policy Denied: ${decision.reason} (source: ${decision.policySource})`,
719
- true
720
- );
764
+ return text(`Policy Denied: ${decision.reason} (source: ${decision.policySource})`, true);
721
765
  }
722
766
  }
723
767
  entangleSecrets(
@@ -752,11 +796,9 @@ function registerSecretTools(server2) {
752
796
  sourceProjectPath: z2.string().optional().describe("Project root for sourceKey when sourceScope='project'."),
753
797
  targetProjectPath: z2.string().optional().describe("Project root for targetKey when targetScope='project'.")
754
798
  },
799
+ toolAnnotations("disentangle_secrets"),
755
800
  async (params) => {
756
- const toolBlock = enforceToolPolicy(
757
- "disentangle_secrets",
758
- params.sourceProjectPath
759
- );
801
+ const toolBlock = enforceToolPolicy("disentangle_secrets", params.sourceProjectPath);
760
802
  if (toolBlock) return toolBlock;
761
803
  disentangleSecrets(
762
804
  params.sourceKey,
@@ -789,9 +831,9 @@ var ProviderRegistry = class {
789
831
  get(name) {
790
832
  return this.providers.get(name);
791
833
  }
792
- detectProvider(value, hints) {
793
- if (hints?.provider) {
794
- return this.providers.get(hints.provider);
834
+ detectProvider(value, hints2) {
835
+ if (hints2?.provider) {
836
+ return this.providers.get(hints2.provider);
795
837
  }
796
838
  for (const provider of this.providers.values()) {
797
839
  if (provider.prefixes) {
@@ -1147,6 +1189,7 @@ function registerProjectTools(server2) {
1147
1189
  {
1148
1190
  projectPath: projectPath2
1149
1191
  },
1192
+ toolAnnotations("check_project"),
1150
1193
  async (params) => {
1151
1194
  const toolBlock = enforceToolPolicy("check_project", params.projectPath);
1152
1195
  if (toolBlock) return toolBlock;
@@ -1219,6 +1262,7 @@ function registerProjectTools(server2) {
1219
1262
  projectPath: projectPath2,
1220
1263
  env: env2
1221
1264
  },
1265
+ toolAnnotations("env_generate"),
1222
1266
  async (params) => {
1223
1267
  const toolBlock = enforceToolPolicy("env_generate", params.projectPath);
1224
1268
  if (toolBlock) return toolBlock;
@@ -1269,19 +1313,15 @@ ${warnings.map((w) => `# ${w}`).join("\n")}` : output;
1269
1313
  {
1270
1314
  projectPath: projectPath2
1271
1315
  },
1316
+ toolAnnotations("detect_environment"),
1272
1317
  async (params) => {
1273
- const toolBlock = enforceToolPolicy(
1274
- "detect_environment",
1275
- params.projectPath
1276
- );
1318
+ const toolBlock = enforceToolPolicy("detect_environment", params.projectPath);
1277
1319
  if (toolBlock) return toolBlock;
1278
1320
  const result = collapseEnvironment({
1279
1321
  projectPath: params.projectPath ?? process.cwd()
1280
1322
  });
1281
1323
  if (!result) {
1282
- return text(
1283
- "No environment detected. Set QRING_ENV, NODE_ENV, or create .q-ring.json"
1284
- );
1324
+ return text("No environment detected. Set QRING_ENV, NODE_ENV, or create .q-ring.json");
1285
1325
  }
1286
1326
  return text(JSON.stringify(result, null, 2));
1287
1327
  }
@@ -1299,11 +1339,9 @@ ${warnings.map((w) => `# ${w}`).join("\n")}` : output;
1299
1339
  teamId: teamId2,
1300
1340
  orgId: orgId2
1301
1341
  },
1342
+ toolAnnotations("get_project_context"),
1302
1343
  async (params) => {
1303
- const toolBlock = enforceToolPolicy(
1304
- "get_project_context",
1305
- params.projectPath
1306
- );
1344
+ const toolBlock = enforceToolPolicy("get_project_context", params.projectPath);
1307
1345
  if (toolBlock) return toolBlock;
1308
1346
  const context = getProjectContext(opts(params));
1309
1347
  return text(JSON.stringify(context, null, 2));
@@ -1322,9 +1360,7 @@ function registerTunnelTools(server2) {
1322
1360
  "Mutates only in-memory state \u2014 the value never touches disk and is lost on server restart. Subject to tool policy. Returns JSON `{ ok, data: { id } }` where `id` is an opaque string to pass to `tunnel_read`/`tunnel_destroy`."
1323
1361
  ].join(" "),
1324
1362
  {
1325
- value: z3.string().describe(
1326
- "The plaintext value to tunnel. Held only in process memory; never logged."
1327
- ),
1363
+ value: z3.string().describe("The plaintext value to tunnel. Held only in process memory; never logged."),
1328
1364
  ttlSeconds: z3.number().optional().describe(
1329
1365
  "Auto-destroy the tunnel after this many seconds. Omit for no time limit (then a `maxReads` is highly recommended)."
1330
1366
  ),
@@ -1332,6 +1368,7 @@ function registerTunnelTools(server2) {
1332
1368
  "Self-destruct after this many successful `tunnel_read` calls. Use 1 for true one-shot delivery."
1333
1369
  )
1334
1370
  },
1371
+ toolAnnotations("tunnel_create"),
1335
1372
  async (params) => {
1336
1373
  const toolBlock = enforceToolPolicy("tunnel_create");
1337
1374
  if (toolBlock) return toolBlock;
@@ -1350,10 +1387,9 @@ function registerTunnelTools(server2) {
1350
1387
  "Increments the read counter and may auto-destroy the tunnel if `maxReads` was set. Returns JSON `{ ok, data: { id, value } }` on success, or an error 'Tunnel \"...\" not found or expired' if the tunnel has been destroyed, hit its TTL, or never existed."
1351
1388
  ].join(" "),
1352
1389
  {
1353
- id: z3.string().describe(
1354
- "The opaque tunnel ID returned by `tunnel_create`. Case-sensitive."
1355
- )
1390
+ id: z3.string().describe("The opaque tunnel ID returned by `tunnel_create`. Case-sensitive.")
1356
1391
  },
1392
+ toolAnnotations("tunnel_read"),
1357
1393
  async (params) => {
1358
1394
  const toolBlock = enforceToolPolicy("tunnel_read");
1359
1395
  if (toolBlock) return toolBlock;
@@ -1361,9 +1397,7 @@ function registerTunnelTools(server2) {
1361
1397
  if (value === null) {
1362
1398
  return text(`Tunnel "${params.id}" not found or expired`, true);
1363
1399
  }
1364
- return text(
1365
- JSON.stringify({ ok: true, data: { id: params.id, value } }, null, 2)
1366
- );
1400
+ return text(JSON.stringify({ ok: true, data: { id: params.id, value } }, null, 2));
1367
1401
  }
1368
1402
  );
1369
1403
  server2.tool(
@@ -1374,6 +1408,7 @@ function registerTunnelTools(server2) {
1374
1408
  "Read-only. Returns one line per tunnel formatted as `id | reads:N | max:N | expires:Ns`, or the literal text 'No active tunnels' when the list is empty."
1375
1409
  ].join(" "),
1376
1410
  {},
1411
+ toolAnnotations("tunnel_list"),
1377
1412
  async () => {
1378
1413
  const toolBlock = enforceToolPolicy("tunnel_list");
1379
1414
  if (toolBlock) return toolBlock;
@@ -1384,10 +1419,7 @@ function registerTunnelTools(server2) {
1384
1419
  parts.push(`reads:${t.accessCount}`);
1385
1420
  if (t.maxReads) parts.push(`max:${t.maxReads}`);
1386
1421
  if (t.expiresAt) {
1387
- const rem = Math.max(
1388
- 0,
1389
- Math.floor((t.expiresAt - Date.now()) / 1e3)
1390
- );
1422
+ const rem = Math.max(0, Math.floor((t.expiresAt - Date.now()) / 1e3));
1391
1423
  parts.push(`expires:${rem}s`);
1392
1424
  }
1393
1425
  return parts.join(" | ");
@@ -1405,6 +1437,7 @@ function registerTunnelTools(server2) {
1405
1437
  {
1406
1438
  id: z3.string().describe("The opaque tunnel ID to destroy.")
1407
1439
  },
1440
+ toolAnnotations("tunnel_destroy"),
1408
1441
  async (params) => {
1409
1442
  const toolBlock = enforceToolPolicy("tunnel_destroy");
1410
1443
  if (toolBlock) return toolBlock;
@@ -1558,6 +1591,7 @@ function registerTeleportTools(server2) {
1558
1591
  teamId: teamId3,
1559
1592
  orgId: orgId3
1560
1593
  },
1594
+ toolAnnotations("teleport_pack"),
1561
1595
  async (params) => {
1562
1596
  const toolBlock = enforceToolPolicy("teleport_pack", params.projectPath);
1563
1597
  if (toolBlock) return toolBlock;
@@ -1598,6 +1632,7 @@ function registerTeleportTools(server2) {
1598
1632
  "If true, decrypt and report what would be written but do not mutate the keyring. Useful for verifying bundle contents before commit."
1599
1633
  )
1600
1634
  },
1635
+ toolAnnotations("teleport_unpack"),
1601
1636
  async (params) => {
1602
1637
  const toolBlock = enforceToolPolicy("teleport_unpack", params.projectPath);
1603
1638
  if (toolBlock) return toolBlock;
@@ -1605,18 +1640,14 @@ function registerTeleportTools(server2) {
1605
1640
  const payload = teleportUnpack(params.bundle, params.passphrase);
1606
1641
  if (params.dryRun) {
1607
1642
  const preview = payload.secrets.map((s) => `${s.key} [${s.scope ?? "global"}]`).join("\n");
1608
- return text(
1609
- `Would import ${payload.secrets.length} secrets:
1610
- ${preview}`
1611
- );
1643
+ return text(`Would import ${payload.secrets.length} secrets:
1644
+ ${preview}`);
1612
1645
  }
1613
1646
  const o = opts(params);
1614
1647
  for (const s of payload.secrets) {
1615
1648
  setSecret(s.key, s.value, o);
1616
1649
  }
1617
- return text(
1618
- `Imported ${payload.secrets.length} secret(s) from teleport bundle`
1619
- );
1650
+ return text(`Imported ${payload.secrets.length} secret(s) from teleport bundle`);
1620
1651
  } catch (err) {
1621
1652
  const msg = err instanceof Error ? err.message : String(err);
1622
1653
  return text(JSON.stringify({ ok: false, error: { message: msg } }), true);
@@ -1637,9 +1668,7 @@ function registerAuditTools(server2) {
1637
1668
  "Read-only. Returns one line per event in chronological order, formatted `timestamp | action | key | [scope] | env:NAME | detail`. Returns 'No audit events found' when the filter matches nothing."
1638
1669
  ].join(" "),
1639
1670
  {
1640
- key: z6.string().optional().describe(
1641
- "Limit to events touching this exact key. Omit for the full log."
1642
- ),
1671
+ key: z6.string().optional().describe("Limit to events touching this exact key. Omit for the full log."),
1643
1672
  action: z6.enum([
1644
1673
  "read",
1645
1674
  "write",
@@ -1651,29 +1680,38 @@ function registerAuditTools(server2) {
1651
1680
  "tunnel",
1652
1681
  "teleport",
1653
1682
  "collapse",
1654
- "canary",
1683
+ "approve",
1684
+ "revoke",
1685
+ "policy_deny",
1686
+ "rotate",
1687
+ "push",
1655
1688
  "wrap"
1656
1689
  ]).optional().describe(
1657
1690
  "Limit to a single action verb (e.g. 'read' to see only reads). Omit for all actions."
1658
1691
  ),
1692
+ agent: z6.string().optional().describe(
1693
+ "Limit to events stamped with this agent label (clientInfo name@version). Omit for all agents."
1694
+ ),
1659
1695
  limit: z6.number().optional().default(20).describe(
1660
1696
  "Maximum events to return, newest first. Defaults to 20. Increase for deeper investigations."
1661
1697
  )
1662
1698
  },
1699
+ toolAnnotations("audit_log"),
1663
1700
  async (params) => {
1664
1701
  const toolBlock = enforceToolPolicy("audit_log");
1665
1702
  if (toolBlock) return toolBlock;
1666
1703
  const events = queryAudit({
1667
1704
  key: params.key,
1668
1705
  action: params.action,
1669
- limit: params.limit
1670
- });
1706
+ agent: params.agent
1707
+ }).filter((e) => e.action !== "canary").slice(0, params.limit);
1671
1708
  if (events.length === 0) return text("No audit events found");
1672
1709
  const lines = events.map((e) => {
1673
1710
  const parts = [e.timestamp, e.action];
1674
1711
  if (e.key) parts.push(e.key);
1675
1712
  if (e.scope) parts.push(`[${e.scope}]`);
1676
1713
  if (e.env) parts.push(`env:${e.env}`);
1714
+ if (e.agent) parts.push(`agent:${e.agent}`);
1677
1715
  if (e.detail) parts.push(e.detail);
1678
1716
  return parts.join(" | ");
1679
1717
  });
@@ -1692,6 +1730,7 @@ function registerAuditTools(server2) {
1692
1730
  "If provided, narrow the scan to this exact key. Omit to scan across every key in the audit log."
1693
1731
  )
1694
1732
  },
1733
+ toolAnnotations("detect_anomalies"),
1695
1734
  async (params) => {
1696
1735
  const toolBlock = enforceToolPolicy("detect_anomalies");
1697
1736
  if (toolBlock) return toolBlock;
@@ -1714,6 +1753,7 @@ function registerAuditTools(server2) {
1714
1753
  teamId: teamId4,
1715
1754
  orgId: orgId4
1716
1755
  },
1756
+ toolAnnotations("health_check"),
1717
1757
  async (params) => {
1718
1758
  const toolBlock = enforceToolPolicy("health_check", params.projectPath);
1719
1759
  if (toolBlock) return toolBlock;
@@ -1750,11 +1790,7 @@ function registerAuditTools(server2) {
1750
1790
  summary.push("", "Issues:", ...issues);
1751
1791
  }
1752
1792
  if (anomalies.length > 0) {
1753
- summary.push(
1754
- "",
1755
- "Anomalies:",
1756
- ...anomalies.map((a) => `[${a.type}] ${a.description}`)
1757
- );
1793
+ summary.push("", "Anomalies:", ...anomalies.map((a) => `[${a.type}] ${a.description}`));
1758
1794
  }
1759
1795
  return text(summary.join("\n"));
1760
1796
  }
@@ -1767,6 +1803,7 @@ function registerAuditTools(server2) {
1767
1803
  "Read-only. Returns JSON `{ ok, valid, brokenAt? }` where `valid` is `true` for an intact chain and `brokenAt` (when present) names the first event whose hash did not match."
1768
1804
  ].join(" "),
1769
1805
  {},
1806
+ toolAnnotations("verify_audit_chain"),
1770
1807
  async () => {
1771
1808
  const toolBlock = enforceToolPolicy("verify_audit_chain");
1772
1809
  if (toolBlock) return toolBlock;
@@ -1792,13 +1829,16 @@ function registerAuditTools(server2) {
1792
1829
  "Output format. 'jsonl' (default) is most stream-friendly; 'json' is a single array; 'csv' is spreadsheet-friendly."
1793
1830
  )
1794
1831
  },
1832
+ toolAnnotations("export_audit"),
1795
1833
  async (params) => {
1796
1834
  const toolBlock = enforceToolPolicy("export_audit");
1797
1835
  if (toolBlock) return toolBlock;
1798
1836
  const output = exportAudit({
1799
1837
  since: params.since,
1800
1838
  until: params.until,
1801
- format: params.format
1839
+ format: params.format,
1840
+ // Same rationale as audit_log: trip records stay operator-facing.
1841
+ excludeActions: ["canary"]
1802
1842
  });
1803
1843
  return text(output);
1804
1844
  }
@@ -1828,6 +1868,7 @@ function registerValidationTools(server2) {
1828
1868
  teamId: teamId5,
1829
1869
  orgId: orgId5
1830
1870
  },
1871
+ toolAnnotations("validate_secret"),
1831
1872
  async (params) => {
1832
1873
  const toolBlock = enforceToolPolicy("validate_secret", params.projectPath);
1833
1874
  if (toolBlock) return toolBlock;
@@ -1847,6 +1888,7 @@ function registerValidationTools(server2) {
1847
1888
  "Read-only. Returns JSON array of `{ name, description, prefixes }` objects. `prefixes` are the literal key-value prefixes (e.g. 'sk-' for OpenAI) used for auto-detection."
1848
1889
  ].join(" "),
1849
1890
  {},
1891
+ toolAnnotations("list_providers"),
1850
1892
  async () => {
1851
1893
  const toolBlock = enforceToolPolicy("list_providers");
1852
1894
  if (toolBlock) return toolBlock;
@@ -1875,6 +1917,7 @@ function registerValidationTools(server2) {
1875
1917
  teamId: teamId5,
1876
1918
  orgId: orgId5
1877
1919
  },
1920
+ toolAnnotations("rotate_secret"),
1878
1921
  async (params) => {
1879
1922
  const toolBlock = enforceToolPolicy("rotate_secret", params.projectPath);
1880
1923
  if (toolBlock) return toolBlock;
@@ -1904,11 +1947,9 @@ function registerValidationTools(server2) {
1904
1947
  teamId: teamId5,
1905
1948
  orgId: orgId5
1906
1949
  },
1950
+ toolAnnotations("ci_validate_secrets"),
1907
1951
  async (params) => {
1908
- const toolBlock = enforceToolPolicy(
1909
- "ci_validate_secrets",
1910
- params.projectPath
1911
- );
1952
+ const toolBlock = enforceToolPolicy("ci_validate_secrets", params.projectPath);
1912
1953
  if (toolBlock) return toolBlock;
1913
1954
  const entries = listSecrets(opts(params));
1914
1955
  const secrets = entries.map((e) => {
@@ -1949,18 +1990,14 @@ function registerHookTools(server2) {
1949
1990
  key: z8.string().optional().describe(
1950
1991
  "Trigger only on this exact key name. Pick at most one of `key` / `keyPattern` / `tag` (or combine for stricter matching)."
1951
1992
  ),
1952
- keyPattern: z8.string().optional().describe(
1953
- "Trigger on any key matching this glob pattern. Examples: 'DB_*', 'STRIPE_*'."
1954
- ),
1993
+ keyPattern: z8.string().optional().describe("Trigger on any key matching this glob pattern. Examples: 'DB_*', 'STRIPE_*'."),
1955
1994
  tag: z8.string().optional().describe(
1956
1995
  "Trigger on any secret carrying this exact tag. Combinable with key/keyPattern as an AND filter."
1957
1996
  ),
1958
1997
  scope: z8.enum(["global", "project"]).optional().describe(
1959
1998
  "Restrict the hook to secrets in this scope. Omit to fire across both global and project secrets."
1960
1999
  ),
1961
- actions: z8.array(z8.enum(["write", "delete", "rotate"])).optional().default(["write", "delete", "rotate"]).describe(
1962
- "Which lifecycle actions trigger this hook. Defaults to all three."
1963
- ),
2000
+ actions: z8.array(z8.enum(["write", "delete", "rotate"])).optional().default(["write", "delete", "rotate"]).describe("Which lifecycle actions trigger this hook. Defaults to all three."),
1964
2001
  command: z8.string().optional().describe(
1965
2002
  "Required when type='shell'. The literal shell command to run; q-ring exposes the matching key as $QRING_HOOK_KEY and action as $QRING_HOOK_ACTION."
1966
2003
  ),
@@ -1977,14 +2014,12 @@ function registerHookTools(server2) {
1977
2014
  "Free-text human-readable description, surfaced by `list_hooks` and the dashboard."
1978
2015
  )
1979
2016
  },
2017
+ toolAnnotations("register_hook"),
1980
2018
  async (params) => {
1981
2019
  const toolBlock = enforceToolPolicy("register_hook");
1982
2020
  if (toolBlock) return toolBlock;
1983
2021
  if (!params.key && !params.keyPattern && !params.tag) {
1984
- return text(
1985
- "At least one match criterion required: key, keyPattern, or tag",
1986
- true
1987
- );
2022
+ return text("At least one match criterion required: key, keyPattern, or tag", true);
1988
2023
  }
1989
2024
  const entry = registerHook({
1990
2025
  type: params.type,
@@ -2012,6 +2047,7 @@ function registerHookTools(server2) {
2012
2047
  "Read-only. Returns pretty-printed JSON array of hook entries, or 'No hooks registered' when the registry is empty."
2013
2048
  ].join(" "),
2014
2049
  {},
2050
+ toolAnnotations("list_hooks"),
2015
2051
  async () => {
2016
2052
  const toolBlock = enforceToolPolicy("list_hooks");
2017
2053
  if (toolBlock) return toolBlock;
@@ -2032,6 +2068,7 @@ function registerHookTools(server2) {
2032
2068
  "Hook id returned by `register_hook` or visible in `list_hooks` (opaque string)."
2033
2069
  )
2034
2070
  },
2071
+ toolAnnotations("remove_hook"),
2035
2072
  async (params) => {
2036
2073
  const toolBlock = enforceToolPolicy("remove_hook");
2037
2074
  if (toolBlock) return toolBlock;
@@ -2638,6 +2675,7 @@ function registerToolingTools(server2) {
2638
2675
  teamId: teamId6,
2639
2676
  orgId: orgId6
2640
2677
  },
2678
+ toolAnnotations("exec_with_secrets"),
2641
2679
  async (params) => {
2642
2680
  const toolBlock = enforceToolPolicy("exec_with_secrets", params.projectPath);
2643
2681
  if (toolBlock) return toolBlock;
@@ -2665,10 +2703,7 @@ ${result.stdout}`);
2665
2703
  ${result.stderr}`);
2666
2704
  return text(output.join("\n\n"));
2667
2705
  } catch (err) {
2668
- return text(
2669
- `Execution failed: ${err instanceof Error ? err.message : String(err)}`,
2670
- true
2671
- );
2706
+ return text(`Execution failed: ${err instanceof Error ? err.message : String(err)}`, true);
2672
2707
  }
2673
2708
  }
2674
2709
  );
@@ -2684,6 +2719,7 @@ ${result.stderr}`);
2684
2719
  "Directory to scan, absolute or relative to the server cwd. The scan recurses into subdirectories."
2685
2720
  )
2686
2721
  },
2722
+ toolAnnotations("scan_codebase_for_secrets"),
2687
2723
  async (params) => {
2688
2724
  const toolBlock = enforceToolPolicy("scan_codebase_for_secrets");
2689
2725
  if (toolBlock) return toolBlock;
@@ -2694,10 +2730,7 @@ ${result.stderr}`);
2694
2730
  }
2695
2731
  return text(JSON.stringify(results, null, 2));
2696
2732
  } catch (err) {
2697
- return text(
2698
- `Scan failed: ${err instanceof Error ? err.message : String(err)}`,
2699
- true
2700
- );
2733
+ return text(`Scan failed: ${err instanceof Error ? err.message : String(err)}`, true);
2701
2734
  }
2702
2735
  }
2703
2736
  );
@@ -2709,9 +2742,7 @@ ${result.stderr}`);
2709
2742
  "With `fix: false` this is read-only. With `fix: true` this MUTATES the listed source files in place (review with git diff!) and writes one new secret per finding to the keyring. Returns a JSON array of `{ file, line, key, value, kind }` findings, or 'No hardcoded secrets found in the specified files.'."
2710
2743
  ].join(" "),
2711
2744
  {
2712
- files: z9.array(z9.string()).describe(
2713
- "Absolute or relative paths to lint. Non-existent paths surface as scan errors."
2714
- ),
2745
+ files: z9.array(z9.string()).describe("Absolute or relative paths to lint. Non-existent paths surface as scan errors."),
2715
2746
  fix: z9.boolean().optional().default(false).describe(
2716
2747
  "If true, rewrite the source files to read `process.env.KEY` and store the extracted value in the keyring. If false (default), only report findings."
2717
2748
  ),
@@ -2720,6 +2751,7 @@ ${result.stderr}`);
2720
2751
  teamId: teamId6,
2721
2752
  orgId: orgId6
2722
2753
  },
2754
+ toolAnnotations("lint_files"),
2723
2755
  async (params) => {
2724
2756
  const toolBlock = enforceToolPolicy("lint_files", params.projectPath);
2725
2757
  if (toolBlock) return toolBlock;
@@ -2734,10 +2766,7 @@ ${result.stderr}`);
2734
2766
  }
2735
2767
  return text(JSON.stringify(results, null, 2));
2736
2768
  } catch (err) {
2737
- return text(
2738
- `Lint failed: ${err instanceof Error ? err.message : String(err)}`,
2739
- true
2740
- );
2769
+ return text(`Lint failed: ${err instanceof Error ? err.message : String(err)}`, true);
2741
2770
  }
2742
2771
  }
2743
2772
  );
@@ -2754,6 +2783,7 @@ ${result.stderr}`);
2754
2783
  teamId: teamId6,
2755
2784
  orgId: orgId6
2756
2785
  },
2786
+ toolAnnotations("analyze_secrets"),
2757
2787
  async (params) => {
2758
2788
  const toolBlock = enforceToolPolicy("analyze_secrets", params.projectPath);
2759
2789
  if (toolBlock) return toolBlock;
@@ -2790,15 +2820,14 @@ ${result.stderr}`);
2790
2820
  "TCP port to listen on (default 9876). Pick another port if 9876 is already in use; the call fails if binding errors."
2791
2821
  )
2792
2822
  },
2823
+ toolAnnotations("status_dashboard"),
2793
2824
  async (params) => {
2794
2825
  const toolBlock = enforceToolPolicy("status_dashboard");
2795
2826
  if (toolBlock) return toolBlock;
2796
2827
  if (dashboardInstance) {
2797
- return text(
2798
- `Dashboard already running at ${dashboardInstance.url}`
2799
- );
2828
+ return text(`Dashboard already running at ${dashboardInstance.url}`);
2800
2829
  }
2801
- const { startDashboardServer } = await import("./dashboard-R3FWTFFW.js");
2830
+ const { startDashboardServer } = await import("./dashboard-WVPR5BQO.js");
2802
2831
  dashboardInstance = startDashboardServer({ port: params.port });
2803
2832
  return text(
2804
2833
  `Dashboard started at ${dashboardInstance.url}
@@ -2821,6 +2850,7 @@ Open this URL in a browser to see live quantum status. The token is required for
2821
2850
  "List of absolute project roots to scan. Defaults to `[server.cwd]` when omitted."
2822
2851
  )
2823
2852
  },
2853
+ toolAnnotations("agent_scan"),
2824
2854
  async (params) => {
2825
2855
  const toolBlock = enforceToolPolicy("agent_scan");
2826
2856
  if (toolBlock) return toolBlock;
@@ -2851,6 +2881,7 @@ function registerAgentTools(server2) {
2851
2881
  "Plain-string value to store. JSON-stringify structured data on the caller side if needed."
2852
2882
  )
2853
2883
  },
2884
+ toolAnnotations("agent_remember"),
2854
2885
  async (params) => {
2855
2886
  const toolBlock = enforceToolPolicy("agent_remember");
2856
2887
  if (toolBlock) return toolBlock;
@@ -2866,10 +2897,9 @@ function registerAgentTools(server2) {
2866
2897
  "Read-only. With a `key` argument: returns JSON `{ ok, data: { key, value } }` or a not-found error. Without `key`: returns a JSON listing of every stored key (no values), or 'Agent memory is empty'."
2867
2898
  ].join(" "),
2868
2899
  {
2869
- key: z10.string().optional().describe(
2870
- "Memory key to read. Omit to list every stored key (without values)."
2871
- )
2900
+ key: z10.string().optional().describe("Memory key to read. Omit to list every stored key (without values).")
2872
2901
  },
2902
+ toolAnnotations("agent_recall"),
2873
2903
  async (params) => {
2874
2904
  const toolBlock = enforceToolPolicy("agent_recall");
2875
2905
  if (toolBlock) return toolBlock;
@@ -2879,11 +2909,8 @@ function registerAgentTools(server2) {
2879
2909
  return text(JSON.stringify(entries, null, 2));
2880
2910
  }
2881
2911
  const value = recall(params.key);
2882
- if (value === null)
2883
- return text(`No memory found for "${params.key}"`, true);
2884
- return text(
2885
- JSON.stringify({ ok: true, data: { key: params.key, value } }, null, 2)
2886
- );
2912
+ if (value === null) return text(`No memory found for "${params.key}"`, true);
2913
+ return text(JSON.stringify({ ok: true, data: { key: params.key, value } }, null, 2));
2887
2914
  }
2888
2915
  );
2889
2916
  server2.tool(
@@ -2896,6 +2923,7 @@ function registerAgentTools(server2) {
2896
2923
  {
2897
2924
  key: z10.string().describe("Memory key to delete.")
2898
2925
  },
2926
+ toolAnnotations("agent_forget"),
2899
2927
  async (params) => {
2900
2928
  const toolBlock = enforceToolPolicy("agent_forget");
2901
2929
  if (toolBlock) return toolBlock;
@@ -2923,17 +2951,14 @@ function registerPolicyTools(server2) {
2923
2951
  action: z11.enum(["tool", "key_read", "exec"]).describe(
2924
2952
  "Which policy surface to query. 'tool' = MCP tool gate (needs `toolName`); 'key_read' = secret read gate (needs `key`); 'exec' = exec_with_secrets command gate (needs `command`)."
2925
2953
  ),
2926
- toolName: z11.string().optional().describe(
2927
- "Tool id to evaluate, e.g. 'rotate_secret'. Required when `action` is 'tool'."
2928
- ),
2929
- key: z11.string().optional().describe(
2930
- "Secret key name to evaluate. Required when `action` is 'key_read'."
2931
- ),
2954
+ toolName: z11.string().optional().describe("Tool id to evaluate, e.g. 'rotate_secret'. Required when `action` is 'tool'."),
2955
+ key: z11.string().optional().describe("Secret key name to evaluate. Required when `action` is 'key_read'."),
2932
2956
  command: z11.string().optional().describe(
2933
2957
  "Command to evaluate against the exec allowlist/denylist. Required when `action` is 'exec'."
2934
2958
  ),
2935
2959
  projectPath: projectPath7
2936
2960
  },
2961
+ toolAnnotations("check_policy"),
2937
2962
  async (params) => {
2938
2963
  if (params.action === "tool" && params.toolName) {
2939
2964
  const d = checkToolPolicy(params.toolName, params.projectPath);
@@ -2947,10 +2972,7 @@ function registerPolicyTools(server2) {
2947
2972
  const d = checkExecPolicy(params.command, params.projectPath);
2948
2973
  return text(JSON.stringify(d, null, 2));
2949
2974
  }
2950
- return text(
2951
- "Missing required parameter for the selected action type",
2952
- true
2953
- );
2975
+ return text("Missing required parameter for the selected action type", true);
2954
2976
  }
2955
2977
  );
2956
2978
  server2.tool(
@@ -2963,11 +2985,9 @@ function registerPolicyTools(server2) {
2963
2985
  {
2964
2986
  projectPath: projectPath7
2965
2987
  },
2988
+ toolAnnotations("get_policy_summary"),
2966
2989
  async (params) => {
2967
- const toolBlock = enforceToolPolicy(
2968
- "get_policy_summary",
2969
- params.projectPath
2970
- );
2990
+ const toolBlock = enforceToolPolicy("get_policy_summary", params.projectPath);
2971
2991
  if (toolBlock) return toolBlock;
2972
2992
  const summary = getPolicySummary(params.projectPath);
2973
2993
  return text(JSON.stringify(summary, null, 2));