@tenova/swt3-mcp 0.1.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/README.md +162 -33
  2. package/dist/bin/swt3-mcp.js +3 -2
  3. package/dist/bin/swt3-mcp.js.map +1 -1
  4. package/dist/chain-verifier.d.ts +46 -0
  5. package/dist/chain-verifier.d.ts.map +1 -0
  6. package/dist/chain-verifier.js +210 -0
  7. package/dist/chain-verifier.js.map +1 -0
  8. package/dist/client.d.ts +4 -0
  9. package/dist/client.d.ts.map +1 -1
  10. package/dist/client.js.map +1 -1
  11. package/dist/config.d.ts +56 -6
  12. package/dist/config.d.ts.map +1 -1
  13. package/dist/config.js +151 -14
  14. package/dist/config.js.map +1 -1
  15. package/dist/density-policy.d.ts +55 -0
  16. package/dist/density-policy.d.ts.map +1 -0
  17. package/dist/density-policy.js +128 -0
  18. package/dist/density-policy.js.map +1 -0
  19. package/dist/redis-reader.d.ts +57 -0
  20. package/dist/redis-reader.d.ts.map +1 -0
  21. package/dist/redis-reader.js +254 -0
  22. package/dist/redis-reader.js.map +1 -0
  23. package/dist/server.d.ts +2 -2
  24. package/dist/server.d.ts.map +1 -1
  25. package/dist/server.js +555 -1
  26. package/dist/server.js.map +1 -1
  27. package/dist/state.d.ts +29 -0
  28. package/dist/state.d.ts.map +1 -0
  29. package/dist/state.js +20 -0
  30. package/dist/state.js.map +1 -0
  31. package/dist/tools/audit.d.ts +20 -0
  32. package/dist/tools/audit.d.ts.map +1 -0
  33. package/dist/tools/audit.js +102 -0
  34. package/dist/tools/audit.js.map +1 -0
  35. package/dist/tools/authorize.d.ts +19 -0
  36. package/dist/tools/authorize.d.ts.map +1 -0
  37. package/dist/tools/authorize.js +96 -0
  38. package/dist/tools/authorize.js.map +1 -0
  39. package/dist/tools/chain.d.ts +24 -0
  40. package/dist/tools/chain.d.ts.map +1 -0
  41. package/dist/tools/chain.js +114 -0
  42. package/dist/tools/chain.js.map +1 -0
  43. package/dist/tools/model.d.ts +33 -0
  44. package/dist/tools/model.d.ts.map +1 -0
  45. package/dist/tools/model.js +162 -0
  46. package/dist/tools/model.js.map +1 -0
  47. package/dist/tools/signup.d.ts +2 -2
  48. package/dist/tools/signup.d.ts.map +1 -1
  49. package/dist/tools/signup.js +10 -3
  50. package/dist/tools/signup.js.map +1 -1
  51. package/dist/tools/skill.d.ts +33 -0
  52. package/dist/tools/skill.d.ts.map +1 -0
  53. package/dist/tools/skill.js +168 -0
  54. package/dist/tools/skill.js.map +1 -0
  55. package/dist/tools/suggest.d.ts +15 -0
  56. package/dist/tools/suggest.d.ts.map +1 -0
  57. package/dist/tools/suggest.js +174 -0
  58. package/dist/tools/suggest.js.map +1 -0
  59. package/dist/tools/trust.d.ts +33 -0
  60. package/dist/tools/trust.d.ts.map +1 -0
  61. package/dist/tools/trust.js +213 -0
  62. package/dist/tools/trust.js.map +1 -0
  63. package/dist/tools/violation.d.ts +20 -0
  64. package/dist/tools/violation.d.ts.map +1 -0
  65. package/dist/tools/violation.js +97 -0
  66. package/dist/tools/violation.js.map +1 -0
  67. package/dist/tools/witness.d.ts +6 -1
  68. package/dist/tools/witness.d.ts.map +1 -1
  69. package/dist/tools/witness.js +19 -5
  70. package/dist/tools/witness.js.map +1 -1
  71. package/package.json +33 -3
package/dist/server.js CHANGED
@@ -8,17 +8,203 @@
8
8
  import { z } from "zod";
9
9
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
10
10
  import { AxiomClient } from "./client.js";
11
+ import { createSessionState } from "./state.js";
11
12
  import { handleWitness } from "./tools/witness.js";
12
13
  import { handleVerify } from "./tools/verify.js";
13
14
  import { handleProcedures } from "./tools/procedures.js";
14
15
  import { handlePosture } from "./tools/posture.js";
15
16
  import { handleSignup } from "./tools/signup.js";
17
+ import { handleAuthorize } from "./tools/authorize.js";
18
+ import { handleStartAudit, handleEndAudit, trackProcedure } from "./tools/audit.js";
19
+ import { handleSuggest } from "./tools/suggest.js";
20
+ import { handleStartChain, handleChainHandoff } from "./tools/chain.js";
21
+ import { handleReportViolation } from "./tools/violation.js";
22
+ import { handleWitnessModelIntegrity, handleWitnessAdapterStack } from "./tools/model.js";
23
+ import { handleAttestSkillManifest, handleAttestMemoryContext } from "./tools/skill.js";
24
+ import { handleVerifyAgentTrust, handlePresentCredential } from "./tools/trust.js";
16
25
  import { readRegistry } from "./resources/registry.js";
17
26
  import { readHealth } from "./resources/health.js";
18
27
  import { REGISTRY_RESOURCE } from "./resources/registry.js";
19
28
  import { HEALTH_RESOURCE } from "./resources/health.js";
20
- export function createServer(config) {
29
+ import { verifyAnchorChain } from "./chain-verifier.js";
30
+ import { loadDensityPolicy } from "./density-policy.js";
31
+ import { startRedisReader, stopRedisReader, getReaderState } from "./redis-reader.js";
32
+ import { mintFingerprint, timestampMs, signPayload } from "./fingerprint.js";
33
+ /**
34
+ * Match a tool name against a glob pattern list.
35
+ * Supports * (any chars) and ? (single char).
36
+ */
37
+ function matchesToolPattern(toolName, patterns) {
38
+ for (const pattern of patterns) {
39
+ const regex = new RegExp("^" + pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&")
40
+ .replace(/\*/g, ".*")
41
+ .replace(/\?/g, ".") + "$");
42
+ if (regex.test(toolName))
43
+ return true;
44
+ }
45
+ return false;
46
+ }
47
+ function initChainDensity(policy) {
48
+ if (!policy.maxVelocity && policy.maxChainDepth === undefined &&
49
+ !policy.toolAllowlist?.length && !policy.toolBlocklist?.length) {
50
+ return null;
51
+ }
52
+ let velocityLimit = 0, velocityWindowMs = 0;
53
+ if (policy.maxVelocity) {
54
+ const parts = policy.maxVelocity.split("/");
55
+ velocityLimit = parseInt(parts[0], 10);
56
+ velocityWindowMs = parseInt(parts[1].replace("s", ""), 10) * 1000;
57
+ }
58
+ const toRegex = (p) => new RegExp("^" + p.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".") + "$");
59
+ return {
60
+ velocityWindow: [],
61
+ velocityLimit,
62
+ velocityWindowMs,
63
+ chainDepth: 0,
64
+ maxChainDepth: policy.maxChainDepth ?? Infinity,
65
+ lastToolName: null,
66
+ blockPatterns: (policy.toolBlocklist ?? []).map(toRegex),
67
+ allowPatterns: policy.toolAllowlist?.length ? policy.toolAllowlist.map(toRegex) : null,
68
+ failSecure: policy.failSecure ?? true,
69
+ };
70
+ }
71
+ function checkChainDensity(state, toolName) {
72
+ const now = Date.now();
73
+ // Blocklist
74
+ for (const p of state.blockPatterns) {
75
+ if (p.test(toolName))
76
+ return `Tool "${toolName}" is on the blocklist`;
77
+ }
78
+ // Allowlist
79
+ if (state.allowPatterns && !state.allowPatterns.some((p) => p.test(toolName))) {
80
+ return `Tool "${toolName}" is not on the allowlist`;
81
+ }
82
+ // Velocity
83
+ if (state.velocityLimit > 0) {
84
+ const cutoff = now - state.velocityWindowMs;
85
+ while (state.velocityWindow.length > 0 && state.velocityWindow[0] <= cutoff) {
86
+ state.velocityWindow.shift();
87
+ }
88
+ if (state.velocityWindow.length >= state.velocityLimit) {
89
+ if (state.failSecure)
90
+ return `Rate limit exceeded: ${state.velocityLimit} calls per ${state.velocityWindowMs / 1000}s`;
91
+ }
92
+ state.velocityWindow.push(now);
93
+ }
94
+ // Depth
95
+ if (state.maxChainDepth < Infinity) {
96
+ if (toolName !== state.lastToolName && state.lastToolName !== null)
97
+ state.chainDepth = 0;
98
+ state.chainDepth++;
99
+ state.lastToolName = toolName;
100
+ if (state.chainDepth > state.maxChainDepth) {
101
+ if (state.failSecure)
102
+ return `Chain depth ${state.chainDepth} exceeds max ${state.maxChainDepth}`;
103
+ }
104
+ }
105
+ return null;
106
+ }
107
+ export function createServer(config, bundle) {
21
108
  const client = new AxiomClient(config);
109
+ const sessionState = createSessionState(bundle?.trustMesh?.trustedTenants, bundle?.trustMesh?.deniedAgents);
110
+ const densityPolicy = bundle?.densityPolicy ?? loadDensityPolicy();
111
+ const mcpPolicy = bundle?.mcpPolicy ?? null;
112
+ const chainDensity = mcpPolicy ? initChainDensity(mcpPolicy) : null;
113
+ // Start Redis reader if chain verification is enabled
114
+ if (config.chainVerify) {
115
+ startRedisReader({ redisUrl: config.redisUrl, streamName: config.redisStream })
116
+ .then((ok) => {
117
+ sessionState.redisReader = getReaderState();
118
+ if (!ok) {
119
+ // Redis unavailable -- will fall back to ledger queries
120
+ }
121
+ })
122
+ .catch(() => { });
123
+ }
124
+ /**
125
+ * Chain verification gate. Returns null if chain is valid (or gate disabled).
126
+ * Returns denial message string if chain verification fails.
127
+ */
128
+ async function chainGate(args) {
129
+ if (!config.chainVerify || config.demo)
130
+ return null;
131
+ const agentId = args.agent_id || config.agentId;
132
+ const cycleId = args.cycle_id || sessionState.activeChain?.cycleId;
133
+ // No identity context -- cannot verify, deny
134
+ if (!agentId && !cycleId) {
135
+ return "Chain verification failed: no agent_id or cycle_id provided. " +
136
+ "Set SWT3_AGENT_ID or pass agent_id/cycle_id to enable chain verification.";
137
+ }
138
+ const result = await verifyAnchorChain(agentId, cycleId, config, client, densityPolicy, args.input_tokens);
139
+ if (result.valid)
140
+ return null;
141
+ // Mint AI-TRUST.1 FAIL anchor for the denial
142
+ const [ts, epoch] = timestampMs();
143
+ const fp = mintFingerprint(config.tenantId, "AI-TRUST.1", 1, 0, 0, ts);
144
+ if (!config.demo) {
145
+ client.postWitness({
146
+ procedure_id: "AI-TRUST.1",
147
+ factor_a: 1, factor_b: 0, factor_c: 0,
148
+ clearing_level: config.clearingLevel,
149
+ anchor_fingerprint: fp,
150
+ anchor_epoch: epoch,
151
+ fingerprint_timestamp_ms: ts,
152
+ ai_model_id: "chain-gate",
153
+ witness_source: "mcp",
154
+ ...(agentId ? { agent_id: agentId } : {}),
155
+ ...(cycleId ? { cycle_id: cycleId } : {}),
156
+ ...(config.signingKey ? { payload_signature: signPayload(config.signingKey, fp, agentId) } : {}),
157
+ }).catch(() => { });
158
+ }
159
+ const violations = result.policyViolations.map((v) => ` - ${v.message}`).join("\n");
160
+ return [
161
+ `Chain Verification DENIED`,
162
+ `Reason: ${result.reason || "unknown"}`,
163
+ `Anchors found: ${result.anchorCount} (source: ${result.source})`,
164
+ ...(result.gaps.length > 0 ? [`Gaps: ${result.gaps.length} (max gap: ${Math.max(...result.gaps.map((g) => g.gapSeconds))}s)`] : []),
165
+ ...(result.revoked.length > 0 ? [`Revoked anchors: ${result.revoked.join(", ")}`] : []),
166
+ ...(violations ? [`Policy violations:\n${violations}`] : []),
167
+ ``,
168
+ `To pass chain verification, ensure your agent has recent SWT3 anchors`,
169
+ `linked by agent_id or cycle_id with no gaps exceeding ${config.maxChainGapSeconds}s.`,
170
+ ].join("\n");
171
+ }
172
+ /**
173
+ * MCP tool policy gate. Checks if a tool name requires witnessing per the
174
+ * mcp_policy section of the YAML config. Returns:
175
+ * - "witness": tool must be witnessed (auto_witness or matched pattern)
176
+ * - "exempt": tool is explicitly exempt
177
+ * - "block": tool blocked due to trust level or failure policy
178
+ * - null: no mcp_policy configured, pass through
179
+ */
180
+ function toolPolicyGate(toolName) {
181
+ if (!mcpPolicy)
182
+ return null;
183
+ // Exempt tools always pass through unwatched
184
+ if (mcpPolicy.exemptTools.length > 0 && matchesToolPattern(toolName, mcpPolicy.exemptTools)) {
185
+ return "exempt";
186
+ }
187
+ // Check if tool matches witnessed patterns
188
+ const isWitnessed = mcpPolicy.witnessedTools.length === 0
189
+ || matchesToolPattern(toolName, mcpPolicy.witnessedTools);
190
+ if (!isWitnessed)
191
+ return "exempt";
192
+ // Trust level gate: use real verified trust level, fall back to heuristic
193
+ if (mcpPolicy.requireTrustLevel > 0) {
194
+ const sessionTrust = sessionState.verifiedTrustLevel
195
+ ?? (sessionState.activeAuditSession ? 2 : (config.signingKey ? 1 : 0));
196
+ if (sessionTrust < mcpPolicy.requireTrustLevel) {
197
+ return "block";
198
+ }
199
+ }
200
+ // Chain density enforcement
201
+ if (chainDensity) {
202
+ const violation = checkChainDensity(chainDensity, toolName);
203
+ if (violation)
204
+ return "block";
205
+ }
206
+ return mcpPolicy.autoWitness ? "witness" : "exempt";
207
+ }
22
208
  const server = new McpServer({
23
209
  name: "swt3-mcp",
24
210
  version: "0.1.0",
@@ -42,11 +228,17 @@ export function createServer(config) {
42
228
  .describe("Data clearing level (0=analytics, 1=standard, 2=sensitive, 3=classified)"),
43
229
  procedure: z.string().optional().describe("UCT procedure ID (default: AI-INF.1)"),
44
230
  provider: z.string().optional().describe("AI provider name (openai, anthropic, bedrock, etc.)"),
231
+ agent_id: z.string().optional().describe("Agent identity for this inference (AI-ID.1)"),
232
+ cycle_id: z.string().optional().describe("Multi-agent chain link identifier"),
233
+ jurisdiction: z.string().optional().describe("ISO 3166-1 jurisdiction code (e.g., 'DE', 'US-VA')"),
234
+ legal_basis: z.string().optional().describe("GDPR legal basis (e.g., 'consent', 'legitimate_interest', 'contract')"),
235
+ purpose_class: z.string().optional().describe("Processing purpose classification (e.g., 'clinical_decision_support')"),
45
236
  },
46
237
  annotations: { readOnlyHint: false },
47
238
  }, async (args) => {
48
239
  try {
49
240
  const text = await handleWitness(args, config, client);
241
+ trackProcedure(sessionState, args.procedure || "AI-INF.1");
50
242
  return { content: [{ type: "text", text }] };
51
243
  }
52
244
  catch (err) {
@@ -139,6 +331,365 @@ export function createServer(config) {
139
331
  };
140
332
  }
141
333
  });
334
+ server.registerTool("witness_authorization", {
335
+ description: "Witness an authorization decision as an AI-ACC.1 anchor. " +
336
+ "Records whether a resource access was granted or denied. " +
337
+ "FAIL anchors trigger alerts but never block execution." +
338
+ (config.demo ? " Currently in DEMO mode — anchors are minted locally. Use the signup tool to persist them." : ""),
339
+ inputSchema: {
340
+ resource: z.string().describe("Resource being accessed (e.g., 'prod-database', 'user-pii-store')"),
341
+ scope: z.string().optional().describe("Authorization scope (e.g., 'read-only', 'write', 'admin')"),
342
+ granted: z.boolean().describe("Whether access was granted (true) or denied (false)"),
343
+ agent_id: z.string().optional().describe("Agent identity requesting access (AI-ID.1)"),
344
+ cycle_id: z.string().optional().describe("Multi-agent chain link identifier"),
345
+ clearing_level: z.union([z.literal(0), z.literal(1), z.literal(2), z.literal(3)]).optional()
346
+ .describe("Data clearing level (0=analytics, 1=standard, 2=sensitive, 3=classified)"),
347
+ },
348
+ annotations: { readOnlyHint: false },
349
+ }, async (args) => {
350
+ try {
351
+ const denial = await chainGate(args);
352
+ if (denial)
353
+ return { content: [{ type: "text", text: denial }], isError: true };
354
+ const text = await handleAuthorize(args, config, client);
355
+ trackProcedure(sessionState, "AI-ACC.1");
356
+ return { content: [{ type: "text", text }] };
357
+ }
358
+ catch (err) {
359
+ return {
360
+ content: [{ type: "text", text: `Error: ${err.message}` }],
361
+ isError: true,
362
+ };
363
+ }
364
+ });
365
+ // --- Compliance Discovery Tools ---
366
+ server.registerTool("start_audit_session", {
367
+ description: "Begin passive compliance tracking for this conversation. " +
368
+ "Records which procedures are witnessed. Call end_audit_session for a gap report. " +
369
+ "Purely observational -- never blocks execution.",
370
+ annotations: { readOnlyHint: true },
371
+ }, async () => {
372
+ try {
373
+ const text = handleStartAudit(sessionState);
374
+ return { content: [{ type: "text", text }] };
375
+ }
376
+ catch (err) {
377
+ return {
378
+ content: [{ type: "text", text: `Error: ${err.message}` }],
379
+ isError: true,
380
+ };
381
+ }
382
+ });
383
+ server.registerTool("end_audit_session", {
384
+ description: "End the audit session and produce a compliance gap report. " +
385
+ "Shows which AI procedures were witnessed and which were missed.",
386
+ inputSchema: {
387
+ session_id: z.string().optional().describe("Session ID (uses active session if omitted)"),
388
+ },
389
+ annotations: { readOnlyHint: true },
390
+ }, async (args) => {
391
+ try {
392
+ const text = await handleEndAudit(args, sessionState, client);
393
+ return { content: [{ type: "text", text }] };
394
+ }
395
+ catch (err) {
396
+ return {
397
+ content: [{ type: "text", text: `Error: ${err.message}` }],
398
+ isError: true,
399
+ };
400
+ }
401
+ });
402
+ server.registerTool("suggest_procedures", {
403
+ description: "Get advisory suggestions for which SWT3 procedures to witness based on context. " +
404
+ "Returns a ranked list of applicable procedures. Advisory only -- never enforced. " +
405
+ "No network call required.",
406
+ inputSchema: {
407
+ context: z.string().describe("What the agent is doing (e.g., 'calling GPT-4o to summarize a contract')"),
408
+ model_id: z.string().optional().describe("AI model being used"),
409
+ data_classification: z.string().optional().describe("Data sensitivity (public, internal, sensitive, classified)"),
410
+ tools_used: z.array(z.string()).optional().describe("Tools or functions being called"),
411
+ },
412
+ annotations: { readOnlyHint: true },
413
+ }, async (args) => {
414
+ try {
415
+ const text = handleSuggest(args);
416
+ return { content: [{ type: "text", text }] };
417
+ }
418
+ catch (err) {
419
+ return {
420
+ content: [{ type: "text", text: `Error: ${err.message}` }],
421
+ isError: true,
422
+ };
423
+ }
424
+ });
425
+ // --- Multi-Agent Chain Tools ---
426
+ server.registerTool("start_chain", {
427
+ description: "Generate a cycle_id for a multi-agent chain. " +
428
+ "Pass the returned cycle_id to subsequent witness calls to link all anchors in the chain. " +
429
+ "Metadata only -- never blocks execution.",
430
+ inputSchema: {
431
+ description: z.string().optional().describe("Chain description (e.g., 'contract review pipeline')"),
432
+ },
433
+ annotations: { readOnlyHint: true },
434
+ }, async (args) => {
435
+ try {
436
+ const text = handleStartChain(args, sessionState);
437
+ return { content: [{ type: "text", text }] };
438
+ }
439
+ catch (err) {
440
+ return {
441
+ content: [{ type: "text", text: `Error: ${err.message}` }],
442
+ isError: true,
443
+ };
444
+ }
445
+ });
446
+ server.registerTool("chain_handoff", {
447
+ description: "Witness a handoff between agents in a multi-agent chain. " +
448
+ "Mints an AI-CHAIN.1 anchor recording custody transfer. " +
449
+ "Evidence only -- never blocks execution." +
450
+ (config.demo ? " Currently in DEMO mode -- anchors are minted locally." : ""),
451
+ inputSchema: {
452
+ cycle_id: z.string().describe("Chain cycle_id from start_chain"),
453
+ from_agent: z.string().describe("Agent handing off (e.g., 'summarizer-agent')"),
454
+ to_agent: z.string().describe("Agent receiving handoff (e.g., 'reviewer-agent')"),
455
+ context: z.string().optional().describe("What is being handed off"),
456
+ clearing_level: z.union([z.literal(0), z.literal(1), z.literal(2), z.literal(3)]).optional()
457
+ .describe("Data clearing level (0=analytics, 1=standard, 2=sensitive, 3=classified)"),
458
+ },
459
+ annotations: { readOnlyHint: false },
460
+ }, async (args) => {
461
+ try {
462
+ const text = await handleChainHandoff(args, config, client);
463
+ trackProcedure(sessionState, "AI-CHAIN.1");
464
+ return { content: [{ type: "text", text }] };
465
+ }
466
+ catch (err) {
467
+ return {
468
+ content: [{ type: "text", text: `Error: ${err.message}` }],
469
+ isError: true,
470
+ };
471
+ }
472
+ });
473
+ // --- Self-Attestation Tools ---
474
+ server.registerTool("report_violation", {
475
+ description: "Voluntarily self-report a policy violation. " +
476
+ "Mints a FAIL anchor as evidence. Never blocks execution. " +
477
+ "FAIL anchors trigger downstream alerts via the existing pipeline." +
478
+ (config.demo ? " Currently in DEMO mode -- anchors are minted locally." : ""),
479
+ inputSchema: {
480
+ violation_type: z.string().describe("Type of violation (e.g., 'unauthorized_model', 'data_leak', 'jurisdiction_mismatch')"),
481
+ description: z.string().describe("Description of what happened"),
482
+ severity: z.string().optional().describe("Severity level (low, medium, high, critical). Default: medium"),
483
+ agent_id: z.string().optional().describe("Agent identity reporting the violation"),
484
+ cycle_id: z.string().optional().describe("Multi-agent chain link identifier"),
485
+ clearing_level: z.union([z.literal(0), z.literal(1), z.literal(2), z.literal(3)]).optional()
486
+ .describe("Data clearing level (0=analytics, 1=standard, 2=sensitive, 3=classified)"),
487
+ },
488
+ annotations: { readOnlyHint: false },
489
+ }, async (args) => {
490
+ try {
491
+ const text = await handleReportViolation(args, config, client);
492
+ trackProcedure(sessionState, "AI-VIO.1");
493
+ return { content: [{ type: "text", text }] };
494
+ }
495
+ catch (err) {
496
+ return {
497
+ content: [{ type: "text", text: `Error: ${err.message}` }],
498
+ isError: true,
499
+ };
500
+ }
501
+ });
502
+ // --- Model Weight & Adapter Tools ---
503
+ server.registerTool("witness_model_integrity", {
504
+ description: "Witness model weight file integrity (AI-MDL.5). " +
505
+ "Verifies the SHA-256 hash of model weights against an expected value. " +
506
+ "Evidence only -- never blocks execution." +
507
+ (config.demo ? " Currently in DEMO mode -- anchors are minted locally." : ""),
508
+ inputSchema: {
509
+ model_id: z.string().describe("Model identifier (e.g., 'llama-3.1-70b-instruct')"),
510
+ weight_hash: z.string().describe("SHA-256 hash of the model weight file"),
511
+ expected_hash: z.string().optional().describe("Expected hash for verification. Omit to attest without verification."),
512
+ format: z.string().optional().describe("Weight file format (safetensors, gguf, bin, pt)"),
513
+ file_size_bytes: z.number().optional().describe("Weight file size in bytes"),
514
+ agent_id: z.string().optional().describe("Agent identity"),
515
+ cycle_id: z.string().optional().describe("Multi-agent chain link identifier"),
516
+ clearing_level: z.union([z.literal(0), z.literal(1), z.literal(2), z.literal(3)]).optional()
517
+ .describe("Data clearing level (0=analytics, 1=standard, 2=sensitive, 3=classified)"),
518
+ },
519
+ annotations: { readOnlyHint: false },
520
+ }, async (args) => {
521
+ try {
522
+ const denial = await chainGate(args);
523
+ if (denial)
524
+ return { content: [{ type: "text", text: denial }], isError: true };
525
+ const text = await handleWitnessModelIntegrity(args, config, client);
526
+ trackProcedure(sessionState, "AI-MDL.5");
527
+ return { content: [{ type: "text", text }] };
528
+ }
529
+ catch (err) {
530
+ return {
531
+ content: [{ type: "text", text: `Error: ${err.message}` }],
532
+ isError: true,
533
+ };
534
+ }
535
+ });
536
+ server.registerTool("witness_adapter_stack", {
537
+ description: "Witness active LoRA/QLoRA/PEFT adapter stack (AI-MDL.6). " +
538
+ "Records which adapters are loaded on top of a base model. " +
539
+ "Evidence only -- never blocks execution." +
540
+ (config.demo ? " Currently in DEMO mode -- anchors are minted locally." : ""),
541
+ inputSchema: {
542
+ base_model: z.string().describe("Base model identifier (e.g., 'llama-3.1-70b')"),
543
+ adapters: z.array(z.object({
544
+ name: z.string().describe("Adapter name"),
545
+ hash: z.string().describe("SHA-256 hash of adapter weights"),
546
+ base_model: z.string().optional().describe("Base model this adapter was trained on"),
547
+ })).describe("List of active adapters"),
548
+ agent_id: z.string().optional().describe("Agent identity"),
549
+ cycle_id: z.string().optional().describe("Multi-agent chain link identifier"),
550
+ clearing_level: z.union([z.literal(0), z.literal(1), z.literal(2), z.literal(3)]).optional()
551
+ .describe("Data clearing level"),
552
+ },
553
+ annotations: { readOnlyHint: false },
554
+ }, async (args) => {
555
+ try {
556
+ const denial = await chainGate(args);
557
+ if (denial)
558
+ return { content: [{ type: "text", text: denial }], isError: true };
559
+ const text = await handleWitnessAdapterStack(args, config, client);
560
+ trackProcedure(sessionState, "AI-MDL.6");
561
+ return { content: [{ type: "text", text }] };
562
+ }
563
+ catch (err) {
564
+ return {
565
+ content: [{ type: "text", text: `Error: ${err.message}` }],
566
+ isError: true,
567
+ };
568
+ }
569
+ });
570
+ // --- Skill & Memory Attestation Tools ---
571
+ server.registerTool("attest_skill_manifest", {
572
+ description: "Attest the active skill/tool/plugin manifest (AI-SKILL.1). " +
573
+ "Records which capabilities are loaded. " +
574
+ "Evidence only -- never blocks execution." +
575
+ (config.demo ? " Currently in DEMO mode -- anchors are minted locally." : ""),
576
+ inputSchema: {
577
+ skills: z.array(z.object({
578
+ name: z.string().describe("Skill name"),
579
+ version: z.string().optional().describe("Skill version"),
580
+ hash: z.string().optional().describe("SHA-256 hash of skill definition"),
581
+ })).describe("List of active skills/tools/plugins"),
582
+ expected_manifest_hash: z.string().optional().describe("Expected manifest hash for verification"),
583
+ agent_id: z.string().optional().describe("Agent identity"),
584
+ cycle_id: z.string().optional().describe("Multi-agent chain link identifier"),
585
+ clearing_level: z.union([z.literal(0), z.literal(1), z.literal(2), z.literal(3)]).optional()
586
+ .describe("Data clearing level"),
587
+ },
588
+ annotations: { readOnlyHint: false },
589
+ }, async (args) => {
590
+ try {
591
+ const denial = await chainGate(args);
592
+ if (denial)
593
+ return { content: [{ type: "text", text: denial }], isError: true };
594
+ const text = await handleAttestSkillManifest(args, config, client);
595
+ trackProcedure(sessionState, "AI-SKILL.1");
596
+ return { content: [{ type: "text", text }] };
597
+ }
598
+ catch (err) {
599
+ return {
600
+ content: [{ type: "text", text: `Error: ${err.message}` }],
601
+ isError: true,
602
+ };
603
+ }
604
+ });
605
+ server.registerTool("attest_memory_context", {
606
+ description: "Attest persistent memory sources influencing decisions (AI-SKILL.2). " +
607
+ "Records which memory stores (vector DBs, conversation history, etc.) are active. " +
608
+ "Evidence only -- never blocks execution." +
609
+ (config.demo ? " Currently in DEMO mode -- anchors are minted locally." : ""),
610
+ inputSchema: {
611
+ memory_sources: z.array(z.object({
612
+ type: z.string().describe("Memory source type (vector_store, conversation, scratchpad, knowledge_base)"),
613
+ id: z.string().optional().describe("Source identifier"),
614
+ hash: z.string().optional().describe("SHA-256 hash of memory contents"),
615
+ })).describe("List of active memory sources"),
616
+ agent_id: z.string().optional().describe("Agent identity"),
617
+ cycle_id: z.string().optional().describe("Multi-agent chain link identifier"),
618
+ clearing_level: z.union([z.literal(0), z.literal(1), z.literal(2), z.literal(3)]).optional()
619
+ .describe("Data clearing level"),
620
+ },
621
+ annotations: { readOnlyHint: false },
622
+ }, async (args) => {
623
+ try {
624
+ const denial = await chainGate(args);
625
+ if (denial)
626
+ return { content: [{ type: "text", text: denial }], isError: true };
627
+ const text = await handleAttestMemoryContext(args, config, client);
628
+ trackProcedure(sessionState, "AI-SKILL.2");
629
+ return { content: [{ type: "text", text }] };
630
+ }
631
+ catch (err) {
632
+ return {
633
+ content: [{ type: "text", text: `Error: ${err.message}` }],
634
+ isError: true,
635
+ };
636
+ }
637
+ });
638
+ server.registerTool("verify_agent_trust", {
639
+ description: "Verify a counterpart agent's compliance posture before exchanging data or calling their tools (AI-TRUST.1). " +
640
+ "Checks: deny list, tenant trust, anchor freshness, signing status. " +
641
+ "Returns trust level (denied/basic/verified/attested/sovereign). " +
642
+ "Both PASS and FAIL produce cryptographic evidence anchors." +
643
+ (config.demo ? " Currently in DEMO mode -- anchors are minted locally." : ""),
644
+ inputSchema: {
645
+ counterpart_agent_id: z.string().describe("Agent ID of the counterpart to verify"),
646
+ counterpart_tenant_id: z.string().describe("Tenant ID of the counterpart agent"),
647
+ anchor_fingerprint: z.string().describe("Counterpart's latest SWT3 anchor fingerprint (12 hex chars)"),
648
+ anchor_timestamp_ms: z.number().optional().describe("When the counterpart's anchor was minted (ms since epoch)"),
649
+ is_signed: z.boolean().optional().describe("Whether the counterpart's anchor carries a payload signature"),
650
+ procedures: z.array(z.string()).optional().describe("UCT procedures the counterpart has witnessed"),
651
+ clearing_level: z.union([z.literal(0), z.literal(1), z.literal(2), z.literal(3)]).optional()
652
+ .describe("Counterpart's clearing level"),
653
+ has_hardware_attestation: z.boolean().optional().describe("Counterpart has AI-HW.1 hardware attestation"),
654
+ has_guardrails: z.boolean().optional().describe("Counterpart has active guardrails"),
655
+ agent_id: z.string().optional().describe("This agent's identity"),
656
+ cycle_id: z.string().optional().describe("Multi-agent chain link identifier"),
657
+ },
658
+ annotations: { readOnlyHint: false },
659
+ }, async (args) => {
660
+ try {
661
+ const text = await handleVerifyAgentTrust(args, config, client, sessionState);
662
+ trackProcedure(sessionState, "AI-TRUST.1");
663
+ return { content: [{ type: "text", text }] };
664
+ }
665
+ catch (err) {
666
+ return {
667
+ content: [{ type: "text", text: `Error: ${err.message}` }],
668
+ isError: true,
669
+ };
670
+ }
671
+ });
672
+ server.registerTool("present_trust_credential", {
673
+ description: "Get this agent's trust credential for presentation to another agent. " +
674
+ "Returns agent_id, tenant_id, anchor fingerprint, and trust metadata. " +
675
+ "Pass these fields to another agent's verify_agent_trust tool " +
676
+ "to establish mutual compliance trust before exchanging data.",
677
+ inputSchema: {
678
+ agent_id: z.string().optional().describe("Override agent identity for this credential"),
679
+ },
680
+ annotations: { readOnlyHint: true },
681
+ }, async (args) => {
682
+ try {
683
+ const text = handlePresentCredential(args, config);
684
+ return { content: [{ type: "text", text }] };
685
+ }
686
+ catch (err) {
687
+ return {
688
+ content: [{ type: "text", text: `Error: ${err.message}` }],
689
+ isError: true,
690
+ };
691
+ }
692
+ });
142
693
  // --- Resources ---
143
694
  server.registerResource(REGISTRY_RESOURCE.name, REGISTRY_RESOURCE.uri, { mimeType: REGISTRY_RESOURCE.mimeType, description: REGISTRY_RESOURCE.description }, async () => ({
144
695
  contents: [
@@ -158,6 +709,9 @@ export function createServer(config) {
158
709
  },
159
710
  ],
160
711
  }));
712
+ // Graceful shutdown hook for Redis reader
713
+ process.on("SIGTERM", () => { stopRedisReader(); });
714
+ process.on("SIGINT", () => { stopRedisReader(); });
161
715
  return server;
162
716
  }
163
717
  //# sourceMappingURL=server.js.map