@agentauditai/mcp-server 2.0.0 → 3.0.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/CHANGELOG.md ADDED
@@ -0,0 +1,37 @@
1
+ # Changelog — @agentauditai/mcp-server
2
+
3
+ All notable changes to this package. Versions follow [SemVer](https://semver.org/).
4
+
5
+ ## 3.0.0 — 2026-11-08
6
+
7
+ AgentAudit provides evidence, not certification. This release removes every tool
8
+ output that read as a compliance verdict.
9
+
10
+ ### Breaking
11
+ - **`check_conformity` removed.** The server no longer reads ConformityAssessment.
12
+ The deployed contract is unchanged; read it directly if you need the record.
13
+ - **Evidence report output changed.** The ConformityAssessment section is gone and
14
+ the report lists on-chain records with a disclaimer instead of a verdict.
15
+ - **`report_incident` no longer prints a reporting deadline.** Deadlines depend on
16
+ the type of incident; confirm them with your legal team. The tool states that it
17
+ creates an on-chain record, not a report to an authority.
18
+
19
+ ### Added
20
+ - `get_evidence_report`. `get_compliance_report` stays as a deprecated alias and
21
+ will be removed in a future major version.
22
+ - **Base Builder Code attribution (ERC-8021), on by default** for `audit_action`
23
+ and `report_incident`, on Base (8453) and Base Sepolia (84532) only. Appends a
24
+ 29-byte suffix for `bc_pd681w25`; evidence bytes are unchanged, only the tx hash
25
+ differs. Turn it off with `AGENTAUDIT_BUILDER_CODE=false` (also `off`, `0`), or
26
+ set your own code. An invalid value stops the server at startup.
27
+ - "Not legal advice." in evidence and incident tool descriptions and output.
28
+
29
+ ### Changed
30
+ - MCP handshake version `3.0.0`.
31
+ - `CHANGELOG.md` now ships in the package.
32
+
33
+ ## 2.0.0 — 2026-10-30
34
+
35
+ First npm release: `audit_action` (AuditVault v2 `commitBatch` on Base),
36
+ `get_audit_log`, `verify_proof`, `report_incident` (IncidentRegistry v2, 5
37
+ networks), `get_compliance_report`, `get_risk_score`, `check_conformity`.
package/README.md CHANGED
@@ -1,12 +1,12 @@
1
1
  # @agentauditai/mcp-server
2
2
 
3
- MCP (Model Context Protocol) server for [AgentAudit](https://github.com/agentauditAI/AgentAudit): on-chain audit logging and incident reporting for AI agents under the EU AI Act. Works with Claude, Cursor and any MCP client over stdio.
3
+ MCP (Model Context Protocol) server for [AgentAudit](https://github.com/agentauditAI/AgentAudit): on-chain audit logging and incident records for AI agents, providing evidence that supports EU AI Act record-keeping. It does not determine whether any legal obligation is satisfied. Works with Claude, Cursor and any MCP client over stdio.
4
4
 
5
5
  | What | Where it is live |
6
6
  |---|---|
7
7
  | Audit logging: `audit_action`, `get_audit_log`, `verify_proof` (AuditVault v2, Art. 12, 19) | **Base only**: AuditVault v2 `0xCeE831070aa9081422f30df5559c125aa47A75DB` |
8
- | Incident reporting: `report_incident` (IncidentRegistry v2, Art. 73) | **5 networks**: mantle, base, arbitrum, optimism, polygon |
9
- | Compliance reads: `get_compliance_report`, `get_risk_score`, `check_conformity` (Art. 9, 43, 44, 73) | mantle, base, arbitrum, optimism, polygon |
8
+ | Incident records: `report_incident` (IncidentRegistry v2, related to Art. 73) | **5 networks**: mantle, base, arbitrum, optimism, polygon |
9
+ | Evidence reads: `get_evidence_report`, `get_risk_score` (related to Art. 9, 73) | mantle, base, arbitrum, optimism, polygon |
10
10
 
11
11
  ## Install and run
12
12
 
@@ -14,8 +14,6 @@ MCP (Model Context Protocol) server for [AgentAudit](https://github.com/agentaud
14
14
  npx -y @agentauditai/mcp-server # starts the stdio server (bin: agentaudit-mcp)
15
15
  ```
16
16
 
17
- Until the package is on npm, run it from the repo: `git clone https://github.com/agentauditAI/AgentAudit && cd AgentAudit && npm install && npm run mcp`.
18
-
19
17
  The package is compiled JavaScript and needs Node.js 18 or newer; it does not use ts-node at runtime.
20
18
 
21
19
  Example MCP client config:
@@ -39,6 +37,7 @@ Example MCP client config:
39
37
  | `PRIVATE_KEY` | `audit_action`, `report_incident` | Signer for write tools. Not needed for read tools. Never commit it. |
40
38
  | `RPC_MANTLE`, `RPC_BASE`, `RPC_ARBITRUM`, `RPC_OPTIMISM`, `RPC_POLYGON` | all tools | Optional RPC overrides; public RPCs are used by default |
41
39
  | `AUDIT_VAULT_<NETWORK>` | `audit_action`, `get_audit_log`, `verify_proof` | Points a network at an AuditVault v2 deployment. Without it, every network except Base returns an error. |
40
+ | `AGENTAUDIT_BUILDER_CODE` | `audit_action`, `report_incident` | Base Builder Code attribution, on Base / Base Sepolia only. Unset or empty: AgentAudit's code `bc_pd681w25`. `false`, `off` or `0` (any case): no suffix. Any other value: your own code. See below. |
42
41
 
43
42
  The server also loads a `.env` file from the working directory (dotenv). Variables already set in the environment take precedence.
44
43
 
@@ -63,11 +62,36 @@ Checks one entry's inclusion proof with AuditVault v2 `verifyLog`. The leaf is a
63
62
 
64
63
  ### `report_incident` (write, 5 networks)
65
64
 
66
- Registers a serious incident on IncidentRegistry v2 (Art. 73). Before sending, it checks the registry has code. After mining, it requires an `IncidentRegistered` event from the registry address; otherwise it returns an error and never reports success. The result includes the Art. 73§2 reporting deadline (2 / 10 / 15 days by severity).
65
+ Records a serious incident on IncidentRegistry v2 (related to Art. 73). This is an on-chain record, not a report to any authority. Before sending, it checks the registry has code. After mining, it requires an `IncidentRegistered` event from the registry address; otherwise it returns an error and never reports success. It does not calculate reporting deadlines: they depend on the type of incident, so confirm them with your legal team.
66
+
67
+ ### `get_evidence_report`, `get_risk_score` (read)
68
+
69
+ Read the Phase 7 contracts: RiskManagementSystem (related to Art. 9) and IncidentRegistry (related to Art. 73). If a contract has no code on the selected network, the tool returns an error naming the network and address.
70
+
71
+ These tools list on-chain records. They do not determine whether any obligation under the EU AI Act or any other law is satisfied.
72
+
73
+ `get_compliance_report` is a deprecated alias of `get_evidence_report` and will be removed in a future major version.
74
+
75
+ ## Evidence, not certification
76
+
77
+ AgentAudit provides on-chain evidence. It does not certify AI systems, issue conformity assessments or replace an assessment under the EU AI Act. `check_conformity` was removed for this reason.
78
+
79
+ ## Base Builder Code attribution
67
80
 
68
- ### `get_compliance_report`, `get_risk_score`, `check_conformity` (read)
81
+ On **Base mainnet (8453) and Base Sepolia (84532) only**, the write tools (`audit_action`,
82
+ `report_incident`) append a [Base Builder Code](https://docs.base.org/base-chain/builder-codes/builder-codes)
83
+ suffix (ERC-8021, schema 0) to their transaction calldata. It is on by default with
84
+ AgentAudit's code `bc_pd681w25`, so Base can attribute these transactions to AgentAudit.
69
85
 
70
- Read the Phase 7 contracts: ConformityAssessment (Art. 43, 44), RiskManagementSystem (Art. 9) and IncidentRegistry (Art. 73). If a contract has no code on the selected network, the tool returns an error naming the network and address.
86
+ - **What is appended:** 29 bytes, `0x62635f70643638317732350b0080218021802180218021802180218021`
87
+ (`"bc_pd681w25"`, length `0x0b`, schema `0x00`, then the marker `0x8021` × 8).
88
+ - **What stays the same:** the contracts ignore the suffix, so events and state are
89
+ identical, and no evidence changes (`action_ref`, Merkle root, bundle). Only the tx hash differs.
90
+ - **The code is public** on-chain. The chain is read from the RPC (`eth_chainId`);
91
+ other chains never get a suffix.
92
+ - **Turn it off:** `AGENTAUDIT_BUILDER_CODE=false`. **Use your own code:**
93
+ `AGENTAUDIT_BUILDER_CODE=bc_yourcode` (ERC-8021 rules: 7-bit ASCII, no comma, ≤ 255 bytes;
94
+ an invalid value stops the server at startup).
71
95
 
72
96
  ## Caveat: AuditVault v2 does not authenticate submitters
73
97
 
@@ -0,0 +1,42 @@
1
+ import { ethers } from "ethers";
2
+ /** AgentAudit's Base Builder Code, used unless the integrator passes `builderCode`. */
3
+ export declare const AGENTAUDIT_BUILDER_CODE = "bc_pd681w25";
4
+ /** Chains on which the suffix is appended: Base mainnet and Base Sepolia. */
5
+ export declare const BUILDER_CODE_CHAIN_IDS: readonly bigint[];
6
+ /** ERC-8021 marker: 0x8021 repeated 8 times (16 bytes), at the very end of the calldata. */
7
+ export declare const ERC8021_MARKER = "0x80218021802180218021802180218021";
8
+ /**
9
+ * Checks a code against the ERC-8021 rules: non-empty, 7-bit ASCII, no comma
10
+ * (0x2C, the delimiter), at most 255 bytes. Returns the code unchanged.
11
+ */
12
+ export declare function validateBuilderCode(code: string): string;
13
+ /**
14
+ * ERC-8021 schema-0 data suffix: `codes ‖ codesLength (1 B) ‖ schemaId 0x00 ‖ 0x8021×8`,
15
+ * where `codes` is the ASCII codes joined by commas.
16
+ */
17
+ export declare function erc8021DataSuffix(codes: readonly string[]): string;
18
+ /**
19
+ * Resolves the `builderCode` option: `undefined` → AgentAudit's code,
20
+ * `false` → no attribution (`null`), a string → that code (validated).
21
+ */
22
+ export declare function resolveBuilderCode(option: string | false | undefined): string | null;
23
+ /**
24
+ * Reads AGENTAUDIT_BUILDER_CODE semantics: unset or "" → `undefined` (default, on);
25
+ * "false" / "off" / "0" (any case) → `false`; anything else → that string, to be
26
+ * validated by `resolveBuilderCode`.
27
+ */
28
+ export declare function builderCodeFromEnv(value: string | undefined): string | false | undefined;
29
+ /** The suffix for `chainId`: the code's ERC-8021 suffix on Base / Base Sepolia, else "0x". */
30
+ export declare function attributionSuffix(code: string | null, chainId: bigint): string;
31
+ /**
32
+ * The chain id the RPC actually reports (`provider.getNetwork()`), cached per
33
+ * provider. A failed lookup is not cached. The configured network name is never used.
34
+ */
35
+ export declare function providerChainId(provider: ethers.Provider): Promise<bigint>;
36
+ /**
37
+ * Sends `contract.method(...args)` like ethers' own contract call, with the
38
+ * attribution suffix appended to the calldata when it applies. The signer
39
+ * populates the final transaction, so gas is estimated on the calldata incl.
40
+ * the suffix. Returns the same `ContractTransactionResponse` ethers would.
41
+ */
42
+ export declare function sendAttributed(contract: ethers.Contract, method: string, args: readonly unknown[], builderCode: string | null): Promise<ethers.ContractTransactionResponse>;
@@ -0,0 +1,133 @@
1
+ "use strict";
2
+ // Base Builder Code attribution (ERC-8021 schema 0) for AgentAudit contract calls.
3
+ //
4
+ // What: a 29-byte suffix appended to the calldata of the SDK's own contract
5
+ // calls, on Base mainnet (8453) and Base Sepolia (84532) only. Contracts ignore
6
+ // trailing calldata; Base indexers read the code from the end of the calldata.
7
+ // The code is public on-chain. No audit evidence changes: action_ref, Merkle
8
+ // root, scope, contentURI and payload hashes are computed before the call is
9
+ // ABI-encoded, and the suffix goes after the encoding (only the tx hash differs).
10
+ //
11
+ // Opt out: `builderCode: false` (SDK) / AGENTAUDIT_BUILDER_CODE=false (MCP).
12
+ // Own code: `builderCode: "<your code>"`. See README "Base Builder Code attribution".
13
+ //
14
+ // The encoding is implemented here (no runtime dependency); test/sdk/attribution.test.ts
15
+ // checks it byte for byte against the ERC-8021 test vector and against `ox/erc8021`.
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.ERC8021_MARKER = exports.BUILDER_CODE_CHAIN_IDS = exports.AGENTAUDIT_BUILDER_CODE = void 0;
18
+ exports.validateBuilderCode = validateBuilderCode;
19
+ exports.erc8021DataSuffix = erc8021DataSuffix;
20
+ exports.resolveBuilderCode = resolveBuilderCode;
21
+ exports.builderCodeFromEnv = builderCodeFromEnv;
22
+ exports.attributionSuffix = attributionSuffix;
23
+ exports.providerChainId = providerChainId;
24
+ exports.sendAttributed = sendAttributed;
25
+ const ethers_1 = require("ethers");
26
+ /** AgentAudit's Base Builder Code, used unless the integrator passes `builderCode`. */
27
+ exports.AGENTAUDIT_BUILDER_CODE = "bc_pd681w25";
28
+ /** Chains on which the suffix is appended: Base mainnet and Base Sepolia. */
29
+ exports.BUILDER_CODE_CHAIN_IDS = [8453n, 84532n];
30
+ /** ERC-8021 marker: 0x8021 repeated 8 times (16 bytes), at the very end of the calldata. */
31
+ exports.ERC8021_MARKER = "0x80218021802180218021802180218021";
32
+ const SCHEMA_ID_0 = "00";
33
+ const MAX_CODES_LENGTH = 255; // codesLength is a single byte
34
+ /**
35
+ * Checks a code against the ERC-8021 rules: non-empty, 7-bit ASCII, no comma
36
+ * (0x2C, the delimiter), at most 255 bytes. Returns the code unchanged.
37
+ */
38
+ function validateBuilderCode(code) {
39
+ if (typeof code !== "string" || code.length === 0) {
40
+ throw new Error("Invalid builder code: must be a non-empty string");
41
+ }
42
+ if (!/^[\x00-\x7f]*$/.test(code)) {
43
+ throw new Error(`Invalid builder code ${JSON.stringify(code)}: ERC-8021 codes are 7-bit ASCII`);
44
+ }
45
+ if (code.includes(",")) {
46
+ throw new Error(`Invalid builder code ${JSON.stringify(code)}: ERC-8021 codes must not contain a comma`);
47
+ }
48
+ if (code.length > MAX_CODES_LENGTH) {
49
+ throw new Error(`Invalid builder code: ${code.length} bytes, ERC-8021 allows at most ${MAX_CODES_LENGTH}`);
50
+ }
51
+ return code;
52
+ }
53
+ /**
54
+ * ERC-8021 schema-0 data suffix: `codes ‖ codesLength (1 B) ‖ schemaId 0x00 ‖ 0x8021×8`,
55
+ * where `codes` is the ASCII codes joined by commas.
56
+ */
57
+ function erc8021DataSuffix(codes) {
58
+ if (codes.length === 0)
59
+ throw new Error("ERC-8021 suffix needs at least one code");
60
+ const joined = codes.map(validateBuilderCode).join(",");
61
+ if (joined.length > MAX_CODES_LENGTH) {
62
+ throw new Error(`ERC-8021 codes are ${joined.length} bytes with delimiters; at most ${MAX_CODES_LENGTH} allowed`);
63
+ }
64
+ const lengthByte = joined.length.toString(16).padStart(2, "0");
65
+ return ethers_1.ethers.hexlify(ethers_1.ethers.toUtf8Bytes(joined)) + lengthByte + SCHEMA_ID_0 + exports.ERC8021_MARKER.slice(2);
66
+ }
67
+ /**
68
+ * Resolves the `builderCode` option: `undefined` → AgentAudit's code,
69
+ * `false` → no attribution (`null`), a string → that code (validated).
70
+ */
71
+ function resolveBuilderCode(option) {
72
+ if (option === undefined)
73
+ return exports.AGENTAUDIT_BUILDER_CODE;
74
+ if (option === false)
75
+ return null;
76
+ if (typeof option !== "string") {
77
+ throw new Error("builderCode must be a string, false, or omitted");
78
+ }
79
+ return validateBuilderCode(option);
80
+ }
81
+ /**
82
+ * Reads AGENTAUDIT_BUILDER_CODE semantics: unset or "" → `undefined` (default, on);
83
+ * "false" / "off" / "0" (any case) → `false`; anything else → that string, to be
84
+ * validated by `resolveBuilderCode`.
85
+ */
86
+ function builderCodeFromEnv(value) {
87
+ const v = value?.trim();
88
+ if (!v)
89
+ return undefined;
90
+ if (/^(false|off|0)$/i.test(v))
91
+ return false;
92
+ return v;
93
+ }
94
+ /** The suffix for `chainId`: the code's ERC-8021 suffix on Base / Base Sepolia, else "0x". */
95
+ function attributionSuffix(code, chainId) {
96
+ if (code === null || !exports.BUILDER_CODE_CHAIN_IDS.includes(chainId))
97
+ return "0x";
98
+ return erc8021DataSuffix([code]);
99
+ }
100
+ const chainIds = new WeakMap();
101
+ /**
102
+ * The chain id the RPC actually reports (`provider.getNetwork()`), cached per
103
+ * provider. A failed lookup is not cached. The configured network name is never used.
104
+ */
105
+ function providerChainId(provider) {
106
+ let p = chainIds.get(provider);
107
+ if (!p) {
108
+ p = provider.getNetwork().then((n) => n.chainId);
109
+ p.catch(() => chainIds.delete(provider));
110
+ chainIds.set(provider, p);
111
+ }
112
+ return p;
113
+ }
114
+ /**
115
+ * Sends `contract.method(...args)` like ethers' own contract call, with the
116
+ * attribution suffix appended to the calldata when it applies. The signer
117
+ * populates the final transaction, so gas is estimated on the calldata incl.
118
+ * the suffix. Returns the same `ContractTransactionResponse` ethers would.
119
+ */
120
+ async function sendAttributed(contract, method, args, builderCode) {
121
+ const signer = contract.runner;
122
+ if (!signer || typeof signer.sendTransaction !== "function" || !signer.provider) {
123
+ throw new Error("sendAttributed needs a contract connected to a signer with a provider");
124
+ }
125
+ const tx = await contract.getFunction(method).populateTransaction(...args);
126
+ if (builderCode !== null) {
127
+ const suffix = attributionSuffix(builderCode, await providerChainId(signer.provider));
128
+ if (suffix !== "0x")
129
+ tx.data = ethers_1.ethers.concat([tx.data, suffix]);
130
+ }
131
+ const sent = await signer.sendTransaction(tx);
132
+ return new ethers_1.ethers.ContractTransactionResponse(contract.interface, signer.provider, sent);
133
+ }
@@ -1,5 +1,6 @@
1
1
  import type { BatchCommitter, CommitBatchParams, CommitBatchResult } from "./batch";
2
2
  export * from "./batch";
3
+ export * from "./attribution";
3
4
  export declare const NETWORKS: {
4
5
  readonly mantle: {
5
6
  readonly rpc: "https://rpc.mantle.xyz";
@@ -70,6 +71,12 @@ export interface AgentAuditConfig {
70
71
  incidentRegistryAddress?: string;
71
72
  /** PostMarketMonitor contract address (Art. 72) */
72
73
  postMarketMonitorAddress?: string;
74
+ /**
75
+ * Base Builder Code (ERC-8021) appended to the calldata of this SDK's contract
76
+ * calls on Base / Base Sepolia only. Omitted: AgentAudit's code (`bc_pd681w25`).
77
+ * `false`: no suffix. A string: your own code. See README "Base Builder Code attribution".
78
+ */
79
+ builderCode?: string | false;
73
80
  }
74
81
  export interface LogActionParams {
75
82
  agentId: number | string;
@@ -138,6 +145,7 @@ export declare class AgentAudit implements BatchCommitter {
138
145
  private _incidentRegistry?;
139
146
  private _postMarketMonitor?;
140
147
  private config;
148
+ private builderCode;
141
149
  constructor(config: AgentAuditConfig);
142
150
  private get registration();
143
151
  private get auditVaultV2();
@@ -149,6 +157,8 @@ export declare class AgentAudit implements BatchCommitter {
149
157
  private incidentRegistry;
150
158
  private verifyIncidentRegistry;
151
159
  private get postMarketMonitor();
160
+ /** Every state-changing call goes through here (Builder Code suffix on Base only). */
161
+ private send;
152
162
  private hashPayload;
153
163
  private riskToUint;
154
164
  private enumIndex;
@@ -331,6 +341,14 @@ export declare class AgentAudit implements BatchCommitter {
331
341
  explorer: "https://polygonscan.com";
332
342
  name: "Polygon Mainnet";
333
343
  };
344
+ /**
345
+ * Base Builder Code this instance appends on Base / Base Sepolia, or null when disabled
346
+ */
347
+ getBuilderCode(): string | null;
348
+ /**
349
+ * The exact calldata suffix this instance appends on the RPC's actual chain ("0x" = none)
350
+ */
351
+ getAttributionSuffix(): Promise<string>;
334
352
  /**
335
353
  * Get connected wallet address
336
354
  */
@@ -21,7 +21,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
21
21
  exports.AgentAudit = exports.NETWORKS = void 0;
22
22
  exports.createAgentAudit = createAgentAudit;
23
23
  const ethers_1 = require("ethers");
24
+ const attribution_1 = require("./attribution");
24
25
  __exportStar(require("./batch"), exports);
26
+ __exportStar(require("./attribution"), exports);
25
27
  // ─── Network Config ───────────────────────────────────────────────────────────
26
28
  //
27
29
  // vaultVersion: 1 = AuditVault v1, 2 = AuditVault v2 (verified bytecode),
@@ -127,6 +129,7 @@ const INCIDENT_STATUSES = ["OPEN", "REPORTED", "UNDER_INVESTIGATION", "RESOLVED"
127
129
  class AgentAudit {
128
130
  constructor(config) {
129
131
  this.config = config;
132
+ this.builderCode = (0, attribution_1.resolveBuilderCode)(config.builderCode);
130
133
  this.networkName = config.network ?? "base";
131
134
  this.network = exports.NETWORKS[this.networkName];
132
135
  const rpc = config.rpcUrl ?? this.network.rpc;
@@ -202,6 +205,10 @@ class AgentAudit {
202
205
  return this._postMarketMonitor;
203
206
  }
204
207
  // ─── Helpers ────────────────────────────────────────────────────────────────
208
+ /** Every state-changing call goes through here (Builder Code suffix on Base only). */
209
+ send(contract, method, ...args) {
210
+ return (0, attribution_1.sendAttributed)(contract, method, args, this.builderCode);
211
+ }
205
212
  hashPayload(payload) {
206
213
  const str = typeof payload === "string" ? payload : JSON.stringify(payload);
207
214
  return ethers_1.ethers.keccak256(ethers_1.ethers.toUtf8Bytes(str));
@@ -234,7 +241,7 @@ class AgentAudit {
234
241
  const payloadHash = this.hashPayload(params.payload);
235
242
  const risk = this.riskToUint(params.riskLevel);
236
243
  const agentId = String(params.agentId);
237
- const tx = await this.auditVault.logAction(agentId, params.actionType, payloadHash, risk);
244
+ const tx = await this.send(this.auditVault, "logAction", agentId, params.actionType, payloadHash, risk);
238
245
  await tx.wait();
239
246
  return {
240
247
  txHash: tx.hash,
@@ -255,7 +262,7 @@ class AgentAudit {
255
262
  const actionTypes = actions.map(a => a.actionType);
256
263
  const payloadHashes = actions.map(a => this.hashPayload(a.payload));
257
264
  const riskLevels = actions.map(a => this.riskToUint(a.riskLevel));
258
- const tx = await this.auditVault.logActionBatch(String(agentId), actionTypes, payloadHashes, riskLevels);
265
+ const tx = await this.send(this.auditVault, "logActionBatch", String(agentId), actionTypes, payloadHashes, riskLevels);
259
266
  await tx.wait();
260
267
  return {
261
268
  txHash: tx.hash,
@@ -276,7 +283,7 @@ class AgentAudit {
276
283
  */
277
284
  async commitBatch(params) {
278
285
  const vault = this.auditVaultV2;
279
- const tx = await vault.commitBatch(params.agentAddress, params.merkleRoot, params.contentURI, params.eventCount, params.complianceScore);
286
+ const tx = await this.send(vault, "commitBatch", params.agentAddress, params.merkleRoot, params.contentURI, params.eventCount, params.complianceScore);
280
287
  const receipt = await tx.wait();
281
288
  let batchIndex = null;
282
289
  for (const log of receipt?.logs ?? []) {
@@ -312,7 +319,7 @@ class AgentAudit {
312
319
  * Register a new AI agent on-chain (KYA standard, Art. 13, 26)
313
320
  */
314
321
  async registerAgent(params) {
315
- const tx = await this.registration.registerAgent(params.name, params.complianceLevel ?? "limited", params.spendLimit ?? ethers_1.ethers.parseEther("100"), params.auditVault ?? this.network.auditVault);
322
+ const tx = await this.send(this.registration, "registerAgent", params.name, params.complianceLevel ?? "limited", params.spendLimit ?? ethers_1.ethers.parseEther("100"), params.auditVault ?? this.network.auditVault);
316
323
  await tx.wait();
317
324
  // Get agent ID from event or return 0 (caller can query separately)
318
325
  return { agentId: 0, txHash: tx.hash, network: this.network.name };
@@ -321,7 +328,7 @@ class AgentAudit {
321
328
  * Revoke an agent
322
329
  */
323
330
  async revokeAgent(agentId) {
324
- const tx = await this.registration.revokeAgent(agentId);
331
+ const tx = await this.send(this.registration, "revokeAgent", agentId);
325
332
  await tx.wait();
326
333
  return { txHash: tx.hash };
327
334
  }
@@ -353,7 +360,7 @@ class AgentAudit {
353
360
  const evidenceHash = params.evidenceHash ?? this.hashPayload(params.description);
354
361
  // Block time, not Date.now(): the registry rejects occurredAt > block.timestamp
355
362
  const occurredAt = params.occurredAt ?? (await this.provider.getBlock("latest")).timestamp;
356
- const tx = await registry.registerIncident(this.incidentAgentId(params.agentId), severity, harmType, params.description, evidenceHash, affectedPersons, occurredAt);
363
+ const tx = await this.send(registry, "registerIncident", this.incidentAgentId(params.agentId), severity, harmType, params.description, evidenceHash, affectedPersons, occurredAt);
357
364
  const receipt = await tx.wait();
358
365
  let incidentId = 0;
359
366
  for (const log of receipt?.logs ?? []) {
@@ -378,7 +385,7 @@ class AgentAudit {
378
385
  */
379
386
  async markReportedToAuthority(incidentId, params) {
380
387
  const registry = await this.incidentRegistry();
381
- const tx = await registry.markReportedToAuthority(incidentId, ethers_1.ethers.getAddress(params.authority), params.authorityRef);
388
+ const tx = await this.send(registry, "markReportedToAuthority", incidentId, ethers_1.ethers.getAddress(params.authority), params.authorityRef);
382
389
  const receipt = await tx.wait();
383
390
  let withinDeadline = false;
384
391
  for (const log of receipt?.logs ?? []) {
@@ -440,7 +447,7 @@ class AgentAudit {
440
447
  * Enroll an agent in post-market monitoring (Art. 72)
441
448
  */
442
449
  async enrollMonitoring(params) {
443
- const tx = await this.postMarketMonitor.enroll(params.agentAddress, params.systemName, params.riskCategory, params.reviewIntervalDays);
450
+ const tx = await this.send(this.postMarketMonitor, "enroll", params.agentAddress, params.systemName, params.riskCategory, params.reviewIntervalDays);
444
451
  await tx.wait();
445
452
  return { txHash: tx.hash };
446
453
  }
@@ -448,7 +455,7 @@ class AgentAudit {
448
455
  * Record a performance metric (Art. 72)
449
456
  */
450
457
  async recordMetric(params) {
451
- const tx = await this.postMarketMonitor.recordMetric(params.agentAddress, this.metricTypeToUint(params.metricType), params.metricName, params.value, params.threshold, params.context ?? "sdk", params.txRef ? ethers_1.ethers.encodeBytes32String(params.txRef.slice(0, 31)) : ethers_1.ethers.ZeroHash);
458
+ const tx = await this.send(this.postMarketMonitor, "recordMetric", params.agentAddress, this.metricTypeToUint(params.metricType), params.metricName, params.value, params.threshold, params.context ?? "sdk", params.txRef ? ethers_1.ethers.encodeBytes32String(params.txRef.slice(0, 31)) : ethers_1.ethers.ZeroHash);
452
459
  await tx.wait();
453
460
  return { txHash: tx.hash };
454
461
  }
@@ -457,7 +464,7 @@ class AgentAudit {
457
464
  * @param complianceScore 0-10000 (e.g. 9500 = 95.00%)
458
465
  */
459
466
  async recordReview(params) {
460
- const tx = await this.postMarketMonitor.recordReview(params.agentAddress, params.complianceScore, params.notes);
467
+ const tx = await this.send(this.postMarketMonitor, "recordReview", params.agentAddress, params.complianceScore, params.notes);
461
468
  await tx.wait();
462
469
  return { txHash: tx.hash };
463
470
  }
@@ -475,6 +482,18 @@ class AgentAudit {
475
482
  getNetworkInfo() {
476
483
  return { name: this.networkName, ...this.network };
477
484
  }
485
+ /**
486
+ * Base Builder Code this instance appends on Base / Base Sepolia, or null when disabled
487
+ */
488
+ getBuilderCode() {
489
+ return this.builderCode;
490
+ }
491
+ /**
492
+ * The exact calldata suffix this instance appends on the RPC's actual chain ("0x" = none)
493
+ */
494
+ async getAttributionSuffix() {
495
+ return (0, attribution_1.attributionSuffix)(this.builderCode, await (0, attribution_1.providerChainId)(this.provider));
496
+ }
478
497
  /**
479
498
  * Get connected wallet address
480
499
  */
@@ -2,7 +2,7 @@
2
2
  import "dotenv/config";
3
3
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
4
  type NetworkKey = "mantle" | "base" | "arbitrum" | "optimism" | "polygon";
5
- type Phase7Contract = "RiskManagementSystem" | "ConformityAssessment" | "IncidentRegistry";
5
+ type Phase7Contract = "RiskManagementSystem" | "IncidentRegistry";
6
6
  export interface McpServerOptions {
7
7
  /** Signer key for audit_action and report_incident (env PRIVATE_KEY) */
8
8
  privateKey?: string;
@@ -12,6 +12,12 @@ export interface McpServerOptions {
12
12
  auditVaults?: Partial<Record<NetworkKey, string>>;
13
13
  /** Phase 7 address overrides; defaults come from deployments/phase7-<network>.json */
14
14
  phase7?: Partial<Record<NetworkKey, Partial<Record<Phase7Contract, string>>>>;
15
+ /**
16
+ * Base Builder Code (ERC-8021) appended to audit_action / report_incident calldata on
17
+ * Base and Base Sepolia only (env AGENTAUDIT_BUILDER_CODE). Omitted: AgentAudit's code
18
+ * `bc_pd681w25`. `false`: no suffix. A string: your own code.
19
+ */
20
+ builderCode?: string | false;
15
21
  }
16
22
  /** Options read from the environment — what the stdio entry point uses */
17
23
  export declare function optionsFromEnv(env?: NodeJS.ProcessEnv): McpServerOptions;
@@ -80,7 +80,8 @@ function optionsFromEnv(env = process.env) {
80
80
  if (vault)
81
81
  auditVaults[net] = vault;
82
82
  }
83
- return { privateKey: env.PRIVATE_KEY, rpcUrls, auditVaults };
83
+ // unset or "" → default code; "false" / "off" / "0" → off; anything else → custom code
84
+ return { privateKey: env.PRIVATE_KEY, rpcUrls, auditVaults, builderCode: (0, index_1.builderCodeFromEnv)(env.AGENTAUDIT_BUILDER_CODE) };
84
85
  }
85
86
  // ─── ABIs ────────────────────────────────────────────────────────────────────
86
87
  const VAULT_ABI = [...verify_1.AUDIT_VAULT_V2_ABI, "function getBatchCount(address agentId) view returns (uint256)"];
@@ -90,11 +91,6 @@ const RISK_ABI = [
90
91
  "function getAgentRisks(bytes32 agentId) external view returns (uint256[])",
91
92
  "function risks(uint256 id) external view returns (uint256 id, bytes32 agentId, uint8 category, uint8 severity, uint8 status, string description, string evidenceUri, uint16 likelihood, uint16 impact, address identifiedBy, uint256 identifiedAt, uint256 updatedAt)",
92
93
  ];
93
- const CONFORMITY_ABI = [
94
- "function isValid(bytes32 agentId) external view returns (bool)",
95
- "function getRecord(bytes32 agentId) external view returns (tuple(bytes32 agentId, uint8 assessmentType, uint8 status, string providerName, string providerAddress, string systemDescription, string notifiedBodyName, string notifiedBodyRef, string certificateRef, string standardsApplied, string declarationUri, uint256 validFrom, uint256 validUntil, address registeredBy, uint256 registeredAt, uint256 updatedAt, string withdrawalReason))",
96
- "function getRegisteredCount() external view returns (uint256)",
97
- ];
98
94
  const INCIDENT_ABI = [
99
95
  "function registerIncident(bytes32 agentId, uint8 severity, uint8 harmType, string description, string evidenceHash, uint256 affectedPersons, uint256 occurredAt) external returns (uint256 id)",
100
96
  "function getAgentIncidents(bytes32 agentId) external view returns (uint256[])",
@@ -104,7 +100,12 @@ const INCIDENT_ABI = [
104
100
  // ─── Helpers ─────────────────────────────────────────────────────────────────
105
101
  const SEVERITY_LABELS = ["NEGLIGIBLE", "LOW", "MEDIUM", "HIGH", "CRITICAL"];
106
102
  const RISK_STATUS_LABELS = ["IDENTIFIED", "ASSESSED", "MITIGATED", "TESTED", "RESIDUAL", "CLOSED"];
107
- const CONFORMITY_STATUS_LABELS = ["PENDING", "CERTIFIED", "WITHDRAWN", "EXPIRED"];
103
+ // ─── Output Notices ──────────────────────────────────────────────────────────
104
+ const EVIDENCE_DISCLAIMER = "This output lists on-chain records found by the checks above. " +
105
+ "It does not determine whether any obligation under the EU AI Act or any other law is satisfied. " +
106
+ "Not legal advice.";
107
+ const INCIDENT_RECORD_NOTE = "This is an on-chain record, not a report to any authority. " +
108
+ "This tool does not calculate reporting deadlines: they depend on the type of incident, so confirm them with your legal team. Not legal advice.";
108
109
  const INCIDENT_SEVERITY_LABELS = ["LOW", "MEDIUM", "HIGH", "CRITICAL"];
109
110
  const HARM_TYPE_LABELS = ["DEATH", "SERIOUS_HEALTH_HARM", "SIGNIFICANT_PROPERTY_DAMAGE", "FUNDAMENTAL_RIGHTS_VIOLATION", "OTHER"];
110
111
  /** Integration tag stored in each audit_action record (EVIDENCE_SCHEMA §2.5) */
@@ -124,6 +125,8 @@ function errorMessage(err) {
124
125
  // ─── MCP Server ──────────────────────────────────────────────────────────────
125
126
  function createServer(options = optionsFromEnv()) {
126
127
  const rpcUrl = (net) => options.rpcUrls?.[net] ?? DEFAULT_RPC_URLS[net];
128
+ // Validated at startup so a bad AGENTAUDIT_BUILDER_CODE fails fast
129
+ const builderCode = (0, index_1.resolveBuilderCode)(options.builderCode);
127
130
  const providers = new Map();
128
131
  // cacheTimeout -1: back-to-back writes must not reuse a cached nonce
129
132
  const provider = (net) => {
@@ -164,7 +167,7 @@ function createServer(options = optionsFromEnv()) {
164
167
  throw new Error(`Audit logging is live on Base only: ${net} has no AuditVault v2 (default ${sdkNet.auditVault} ${what}, no commitBatch). ` +
165
168
  `Use network "base" or set AUDIT_VAULT_${net.toUpperCase()} to an AuditVault v2 deployment.`);
166
169
  }
167
- const server = new mcp_js_1.McpServer({ name: "agentaudit", version: "2.0.0" }, { capabilities: { tools: {} } });
170
+ const server = new mcp_js_1.McpServer({ name: "agentaudit", version: "3.0.0" }, { capabilities: { tools: {} } });
168
171
  // ─── Tool 1: audit_action ────────────────────────────────────────────────────
169
172
  server.tool("audit_action", `Anchor one or more agent actions on AuditVault v2 in one commitBatch transaction (EU AI Act Art. 12, 19). ` +
170
173
  `Live on Base only (AuditVault v2 ${BASE_VAULT}); any other network returns an error unless AUDIT_VAULT_<NETWORK> points at an AuditVault v2. ` +
@@ -183,7 +186,13 @@ function createServer(options = optionsFromEnv()) {
183
186
  }, async ({ agent_id, entries, agent_address, compliance_score, network }) => {
184
187
  const vault = auditVault(network);
185
188
  await requireCode(network, vault, "AuditVault v2");
186
- const sdk = new index_1.AgentAudit({ privateKey: privateKey(), network, rpcUrl: rpcUrl(network), auditVaultAddress: vault });
189
+ const sdk = new index_1.AgentAudit({
190
+ privateKey: privateKey(),
191
+ network,
192
+ rpcUrl: rpcUrl(network),
193
+ auditVaultAddress: vault,
194
+ builderCode: builderCode ?? false,
195
+ });
187
196
  const agentAddress = agent_address ? ethers_1.ethers.getAddress(agent_address) : sdk.getAddress();
188
197
  // One buffer per call, flushed once. The committer is one-shot so the
189
198
  // buffer's retry timer can never resend a batch after this call returned.
@@ -394,46 +403,9 @@ function createServer(options = optionsFromEnv()) {
394
403
  }
395
404
  return { content: [{ type: "text", text: lines.join("\n") }] };
396
405
  });
397
- // ─── Tool 5: check_conformity ────────────────────────────────────────────────
398
- server.tool("check_conformity", "Query the ConformityAssessment contract for an agent's EU Declaration of Conformity status (Art. 43, 44). Deployed on mantle, base, arbitrum, optimism, polygon.", {
399
- agent_id: zod_1.z.string().regex(/^0x[0-9a-fA-F]{1,64}$/).describe("Agent ID as a hex bytes32 string"),
400
- network: networkEnum.default("mantle"),
401
- }, async ({ agent_id, network }) => {
402
- const agentId = ethers_1.ethers.zeroPadValue(agent_id, 32);
403
- const addr = phase7Address(network, "ConformityAssessment");
404
- await requireCode(network, addr, "ConformityAssessment");
405
- const contract = new ethers_1.ethers.Contract(addr, CONFORMITY_ABI, provider(network));
406
- const isValid = await contract.isValid(agentId);
407
- let record = null;
408
- try {
409
- record = await contract.getRecord(agentId);
410
- }
411
- catch {
412
- // not registered
413
- }
414
- const lines = [
415
- `check_conformity — network=${network} agentId=${agent_id}`,
416
- `contract=${addr}`,
417
- "",
418
- `Valid (certified + not expired): ${isValid ? "YES ✓" : "NO ✗"}`,
419
- ];
420
- if (record) {
421
- const statusLabel = CONFORMITY_STATUS_LABELS[Number(record.status)] ?? "UNKNOWN";
422
- const assessmentLabel = Number(record.assessmentType) === 0 ? "SELF_ASSESSMENT" : "NOTIFIED_BODY";
423
- const validFrom = new Date(Number(record.validFrom) * 1000).toISOString().slice(0, 10);
424
- const validUntil = new Date(Number(record.validUntil) * 1000).toISOString().slice(0, 10);
425
- lines.push(`Status : ${statusLabel}`, `Assessment : ${assessmentLabel}`, `Provider : ${record.providerName}`, `Valid from : ${validFrom}`, `Valid until : ${validUntil}`, `Declaration : ${record.declarationUri || "(none)"}`, `Certificate : ${record.certificateRef || "(none)"}`, `Notified body: ${record.notifiedBodyName || "(none)"}`);
426
- if (record.withdrawalReason) {
427
- lines.push(`Withdrawal : ${record.withdrawalReason}`);
428
- }
429
- }
430
- else {
431
- lines.push("No conformity record found for this agent.");
432
- }
433
- return { content: [{ type: "text", text: lines.join("\n") }] };
434
- });
435
- // ─── Tool 6: report_incident ─────────────────────────────────────────────────
436
- server.tool("report_incident", "Register a serious AI incident with IncidentRegistry v2 (EU AI Act Art. 73). Live on mantle, base, arbitrum, optimism, polygon. " +
406
+ // ─── Tool 5: report_incident ─────────────────────────────────────────────────
407
+ server.tool("report_incident", "Record a serious AI incident on IncidentRegistry v2 (related to EU AI Act Art. 73). Live on mantle, base, arbitrum, optimism, polygon. " +
408
+ "This is an on-chain record only, not a report to any authority. " +
437
409
  "Fails unless the registry has code and the transaction emits IncidentRegistered.", {
438
410
  agent_id: zod_1.z.string().regex(/^0x[0-9a-fA-F]{1,64}$/).describe("Agent ID as a hex bytes32 string"),
439
411
  severity: zod_1.z.enum(["LOW", "MEDIUM", "HIGH", "CRITICAL"]).describe("Incident severity"),
@@ -451,7 +423,15 @@ function createServer(options = optionsFromEnv()) {
451
423
  await requireCode(network, addr, "IncidentRegistry");
452
424
  const signer = new ethers_1.ethers.Wallet(privateKey(), provider(network));
453
425
  const contract = new ethers_1.ethers.Contract(addr, INCIDENT_ABI, signer);
454
- const tx = await contract.registerIncident(agentId, severityIdx, harmTypeIdx, description, evidence_hash, affected_persons, occurred_at);
426
+ const tx = await (0, index_1.sendAttributed)(contract, "registerIncident", [
427
+ agentId,
428
+ severityIdx,
429
+ harmTypeIdx,
430
+ description,
431
+ evidence_hash,
432
+ affected_persons,
433
+ occurred_at,
434
+ ], builderCode);
455
435
  const receipt = await tx.wait();
456
436
  // Only an IncidentRegistered from the registry itself counts as registration
457
437
  let incidentId = null;
@@ -472,12 +452,11 @@ function createServer(options = optionsFromEnv()) {
472
452
  if (incidentId === null) {
473
453
  throw new Error(`Incident NOT registered: tx ${tx.hash} on ${network} emitted no IncidentRegistered from ${addr}`);
474
454
  }
475
- const deadline = severity === "CRITICAL" ? "2 days" : severity === "HIGH" ? "10 days" : "15 days";
476
455
  return {
477
456
  content: [{
478
457
  type: "text",
479
458
  text: [
480
- `report_incident — Art. 73`,
459
+ `report_incident — IncidentRegistry v2 (related: Art. 73)`,
481
460
  `network=${network} contract=${addr}`,
482
461
  "",
483
462
  `Incident ID : ${incidentId}`,
@@ -485,22 +464,26 @@ function createServer(options = optionsFromEnv()) {
485
464
  `Severity : ${severity}`,
486
465
  `Harm type : ${harm_type}`,
487
466
  `Affected persons : ${affected_persons}`,
488
- `Art. 73§2 deadline: report to authority within ${deadline} of occurrence`,
467
+ "",
468
+ INCIDENT_RECORD_NOTE,
489
469
  ].join("\n"),
490
470
  }],
491
471
  };
492
472
  });
493
- // ─── Tool 7: get_compliance_report ───────────────────────────────────────────
494
- server.tool("get_compliance_report", "Generate an EU AI Act compliance report for an agent from the Phase 7 contracts (ConformityAssessment, RiskManagementSystem, IncidentRegistry) " +
495
- "on one or all of mantle, base, arbitrum, optimism, polygon. Read-only.", {
473
+ // ─── Tool 6: get_evidence_report (+ deprecated get_compliance_report alias) ──
474
+ const evidenceReportSchema = {
496
475
  agent_id: zod_1.z.string().regex(/^0x[0-9a-fA-F]{1,64}$/).describe("Agent ID as a hex bytes32 string"),
497
476
  network: zod_1.z.enum([...NETWORKS, "all"]).default("all"),
498
- }, async ({ agent_id, network }) => {
477
+ };
478
+ const evidenceReportDescription = "List the on-chain records found for an agent in the Phase 7 contracts (RiskManagementSystem, IncidentRegistry) " +
479
+ "on one or all of mantle, base, arbitrum, optimism, polygon. Read-only. " +
480
+ "It does not determine whether any legal obligation is satisfied. Not legal advice.";
481
+ const evidenceReport = async ({ agent_id, network }) => {
499
482
  const agentId = ethers_1.ethers.zeroPadValue(agent_id, 32);
500
483
  const targets = network === "all" ? [...NETWORKS] : [network];
501
484
  const lines = [
502
485
  "═══════════════════════════════════════════════════════════",
503
- " AgentAudit — EU AI Act Compliance Report",
486
+ " AgentAudit — On-chain Evidence Report",
504
487
  ` Agent : ${agent_id}`,
505
488
  ` Date : ${new Date().toISOString()}`,
506
489
  "═══════════════════════════════════════════════════════════",
@@ -508,26 +491,7 @@ function createServer(options = optionsFromEnv()) {
508
491
  for (const net of targets) {
509
492
  const p = provider(net);
510
493
  lines.push("", `── Network: ${net.toUpperCase()} ──────────────────────────────────`);
511
- // Art. 43/44 — Conformity Assessment
512
- try {
513
- const addr = phase7Address(net, "ConformityAssessment");
514
- await requireCode(net, addr, "ConformityAssessment");
515
- const conformity = new ethers_1.ethers.Contract(addr, CONFORMITY_ABI, p);
516
- const isValid = await conformity.isValid(agentId);
517
- let statusLabel = "NOT REGISTERED";
518
- let validUntil = "";
519
- try {
520
- const rec = await conformity.getRecord(agentId);
521
- statusLabel = CONFORMITY_STATUS_LABELS[Number(rec.status)];
522
- validUntil = new Date(Number(rec.validUntil) * 1000).toISOString().slice(0, 10);
523
- }
524
- catch { /* not registered */ }
525
- lines.push(` Art. 43/44 Conformity : ${statusLabel}${validUntil ? ` (expires ${validUntil})` : ""}`, ` → Currently valid : ${isValid ? "YES ✓" : "NO ✗"}`);
526
- }
527
- catch (e) {
528
- lines.push(` Art. 43/44 Conformity : error — ${errorMessage(e)}`);
529
- }
530
- // Art. 9 — Risk Management
494
+ // RiskManagementSystem
531
495
  try {
532
496
  const addr = phase7Address(net, "RiskManagementSystem");
533
497
  await requireCode(net, addr, "RiskManagementSystem");
@@ -537,12 +501,12 @@ function createServer(options = optionsFromEnv()) {
537
501
  risk.risksAboveSeverity(agentId, 3),
538
502
  risk.risksAboveSeverity(agentId, 4),
539
503
  ]);
540
- lines.push(` Art. 9 Risk Mgmt : open=${openCount} HIGH+=${highCount} CRITICAL=${criticalCount}`);
504
+ lines.push(` RiskManagementSystem (related: Art. 9): open=${openCount} HIGH+=${highCount} CRITICAL=${criticalCount}`);
541
505
  }
542
506
  catch (e) {
543
- lines.push(` Art. 9 Risk Mgmt : error — ${errorMessage(e)}`);
507
+ lines.push(` RiskManagementSystem (related: Art. 9): error — ${errorMessage(e)}`);
544
508
  }
545
- // Art. 73 — Incident Registry
509
+ // IncidentRegistry
546
510
  try {
547
511
  const addr = phase7Address(net, "IncidentRegistry");
548
512
  await requireCode(net, addr, "IncidentRegistry");
@@ -554,15 +518,17 @@ function createServer(options = optionsFromEnv()) {
554
518
  if (Number(inc.status) < 3)
555
519
  openIncidents++; // < RESOLVED
556
520
  }
557
- lines.push(` Art. 73 Incidents : total=${ids.length} open=${openIncidents}`);
521
+ lines.push(` IncidentRegistry (related: Art. 73): total=${ids.length} open=${openIncidents}`);
558
522
  }
559
523
  catch (e) {
560
- lines.push(` Art. 73 Incidents : error — ${errorMessage(e)}`);
524
+ lines.push(` IncidentRegistry (related: Art. 73): error — ${errorMessage(e)}`);
561
525
  }
562
526
  }
563
- lines.push("", "═══════════════════════════════════════════════════════════", " Contracts queried: ConformityAssessment, RiskManagementSystem,", " IncidentRegistry (Phase 7 deployment)", "═══════════════════════════════════════════════════════════");
527
+ lines.push("", "═══════════════════════════════════════════════════════════", " Contracts queried: RiskManagementSystem, IncidentRegistry", " (Phase 7 deployment)", "", ` ${EVIDENCE_DISCLAIMER}`, "═══════════════════════════════════════════════════════════");
564
528
  return { content: [{ type: "text", text: lines.join("\n") }] };
565
- });
529
+ };
530
+ server.tool("get_evidence_report", evidenceReportDescription, evidenceReportSchema, evidenceReport);
531
+ server.tool("get_compliance_report", `Deprecated alias of get_evidence_report; it will be removed in a future major version. ${evidenceReportDescription}`, evidenceReportSchema, evidenceReport);
566
532
  return server;
567
533
  }
568
534
  // ─── Start ───────────────────────────────────────────────────────────────────
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@agentauditai/mcp-server",
3
- "version": "2.0.0",
4
- "description": "MCP server for AgentAudit: EU AI Act audit logging on AuditVault v2 (Base), incident reporting (Art. 73) and compliance reads.",
3
+ "version": "3.0.0",
4
+ "description": "MCP server for AgentAudit: audit logging on AuditVault v2 (Base), on-chain incident records and evidence reads for EU AI Act record-keeping.",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
@@ -20,7 +20,8 @@
20
20
  "dist/sdk/src",
21
21
  "dist/tools/verify/src",
22
22
  "dist/deployments",
23
- "README.md"
23
+ "README.md",
24
+ "CHANGELOG.md"
24
25
  ],
25
26
  "scripts": {
26
27
  "build": "node scripts/clean.js && tsc -p tsconfig.build.json && node scripts/copy-deployments.js",