@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 +37 -0
- package/README.md +32 -8
- package/dist/sdk/src/attribution.d.ts +42 -0
- package/dist/sdk/src/attribution.js +133 -0
- package/dist/sdk/src/index.d.ts +18 -0
- package/dist/sdk/src/index.js +29 -10
- package/dist/src/mcp-server.d.ts +7 -1
- package/dist/src/mcp-server.js +51 -85
- package/package.json +4 -3
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
|
|
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
|
|
9
|
-
|
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
+
}
|
package/dist/sdk/src/index.d.ts
CHANGED
|
@@ -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
|
*/
|
package/dist/sdk/src/index.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
*/
|
package/dist/src/mcp-server.d.ts
CHANGED
|
@@ -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" | "
|
|
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;
|
package/dist/src/mcp-server.js
CHANGED
|
@@ -80,7 +80,8 @@ function optionsFromEnv(env = process.env) {
|
|
|
80
80
|
if (vault)
|
|
81
81
|
auditVaults[net] = vault;
|
|
82
82
|
}
|
|
83
|
-
|
|
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
|
-
|
|
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: "
|
|
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({
|
|
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:
|
|
398
|
-
server.tool("
|
|
399
|
-
|
|
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
|
|
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
|
-
|
|
467
|
+
"",
|
|
468
|
+
INCIDENT_RECORD_NOTE,
|
|
489
469
|
].join("\n"),
|
|
490
470
|
}],
|
|
491
471
|
};
|
|
492
472
|
});
|
|
493
|
-
// ─── Tool
|
|
494
|
-
|
|
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
|
-
}
|
|
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 —
|
|
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
|
-
//
|
|
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
|
|
504
|
+
lines.push(` RiskManagementSystem (related: Art. 9): open=${openCount} HIGH+=${highCount} CRITICAL=${criticalCount}`);
|
|
541
505
|
}
|
|
542
506
|
catch (e) {
|
|
543
|
-
lines.push(` Art. 9
|
|
507
|
+
lines.push(` RiskManagementSystem (related: Art. 9): error — ${errorMessage(e)}`);
|
|
544
508
|
}
|
|
545
|
-
//
|
|
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
|
|
521
|
+
lines.push(` IncidentRegistry (related: Art. 73): total=${ids.length} open=${openIncidents}`);
|
|
558
522
|
}
|
|
559
523
|
catch (e) {
|
|
560
|
-
lines.push(` Art. 73
|
|
524
|
+
lines.push(` IncidentRegistry (related: Art. 73): error — ${errorMessage(e)}`);
|
|
561
525
|
}
|
|
562
526
|
}
|
|
563
|
-
lines.push("", "═══════════════════════════════════════════════════════════", " Contracts queried:
|
|
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": "
|
|
4
|
-
"description": "MCP server for AgentAudit:
|
|
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",
|