audit-ledger-mcp 0.1.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/LICENSE +192 -0
- package/README.md +265 -0
- package/dist/client.d.ts +58 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +141 -0
- package/dist/client.js.map +1 -0
- package/dist/hashing.d.ts +23 -0
- package/dist/hashing.d.ts.map +1 -0
- package/dist/hashing.js +31 -0
- package/dist/hashing.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +98 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/list_decisions.d.ts +57 -0
- package/dist/tools/list_decisions.d.ts.map +1 -0
- package/dist/tools/list_decisions.js +59 -0
- package/dist/tools/list_decisions.js.map +1 -0
- package/dist/tools/record_decision.d.ts +83 -0
- package/dist/tools/record_decision.d.ts.map +1 -0
- package/dist/tools/record_decision.js +76 -0
- package/dist/tools/record_decision.js.map +1 -0
- package/dist/tools/verify_decision.d.ts +39 -0
- package/dist/tools/verify_decision.d.ts.map +1 -0
- package/dist/tools/verify_decision.js +36 -0
- package/dist/tools/verify_decision.js.map +1 -0
- package/package.json +59 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SHA-256 helpers — mirror the hashing behaviour of the Python and Node SDKs
|
|
3
|
+
* shipped in the main audit-ledger repo. PII is hashed locally before any
|
|
4
|
+
* payload leaves the MCP server, so raw personal data never reaches the
|
|
5
|
+
* audit ledger API.
|
|
6
|
+
*/
|
|
7
|
+
/** SHA-256 hex digest of a UTF-8 string. Lowercased, 64 chars. */
|
|
8
|
+
export declare function sha256(input: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* Hash a system prompt before sending. Whitespace is normalised so that
|
|
11
|
+
* formatting-only changes do not produce a different hash — this matches
|
|
12
|
+
* the behaviour of the SDKs in the main repo and keeps prompt-version
|
|
13
|
+
* tracking stable across minor edits.
|
|
14
|
+
*/
|
|
15
|
+
export declare function hashPrompt(rawPrompt: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* Hash raw user input (e.g. a CV, a transaction payload, a customer record)
|
|
18
|
+
* before sending. No normalisation — the input is hashed verbatim so any
|
|
19
|
+
* change in the input produces a different hash, which is the right
|
|
20
|
+
* behaviour for an audit trail of model inputs.
|
|
21
|
+
*/
|
|
22
|
+
export declare function hashPii(rawInput: string): string;
|
|
23
|
+
//# sourceMappingURL=hashing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hashing.d.ts","sourceRoot":"","sources":["../src/hashing.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,kEAAkE;AAClE,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE5C;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAGpD;AAED;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEhD"}
|
package/dist/hashing.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SHA-256 helpers — mirror the hashing behaviour of the Python and Node SDKs
|
|
3
|
+
* shipped in the main audit-ledger repo. PII is hashed locally before any
|
|
4
|
+
* payload leaves the MCP server, so raw personal data never reaches the
|
|
5
|
+
* audit ledger API.
|
|
6
|
+
*/
|
|
7
|
+
import { createHash } from "node:crypto";
|
|
8
|
+
/** SHA-256 hex digest of a UTF-8 string. Lowercased, 64 chars. */
|
|
9
|
+
export function sha256(input) {
|
|
10
|
+
return createHash("sha256").update(input, "utf8").digest("hex");
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Hash a system prompt before sending. Whitespace is normalised so that
|
|
14
|
+
* formatting-only changes do not produce a different hash — this matches
|
|
15
|
+
* the behaviour of the SDKs in the main repo and keeps prompt-version
|
|
16
|
+
* tracking stable across minor edits.
|
|
17
|
+
*/
|
|
18
|
+
export function hashPrompt(rawPrompt) {
|
|
19
|
+
const normalised = rawPrompt.replace(/\s+/g, " ").trim();
|
|
20
|
+
return sha256(normalised);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Hash raw user input (e.g. a CV, a transaction payload, a customer record)
|
|
24
|
+
* before sending. No normalisation — the input is hashed verbatim so any
|
|
25
|
+
* change in the input produces a different hash, which is the right
|
|
26
|
+
* behaviour for an audit trail of model inputs.
|
|
27
|
+
*/
|
|
28
|
+
export function hashPii(rawInput) {
|
|
29
|
+
return sha256(rawInput);
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=hashing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hashing.js","sourceRoot":"","sources":["../src/hashing.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,kEAAkE;AAClE,MAAM,UAAU,MAAM,CAAC,KAAa;IAClC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAClE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,SAAiB;IAC1C,MAAM,UAAU,GAAG,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IACzD,OAAO,MAAM,CAAC,UAAU,CAAC,CAAC;AAC5B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,OAAO,CAAC,QAAgB;IACtC,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC;AAC1B,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* audit-ledger-mcp — Model Context Protocol server for AI Audit Ledger.
|
|
4
|
+
*
|
|
5
|
+
* Stdio transport — designed to be spawned by an MCP client (Claude Desktop,
|
|
6
|
+
* Cursor, LangGraph adapter, custom). Three tools exposed: record_decision,
|
|
7
|
+
* verify_decision, list_decisions.
|
|
8
|
+
*
|
|
9
|
+
* Configuration via environment variables — see .env.example. The server
|
|
10
|
+
* reads them at startup and refuses to start if AUDIT_API_URL is missing,
|
|
11
|
+
* since no tool can do anything useful without it.
|
|
12
|
+
*/
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;GAUG"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* audit-ledger-mcp — Model Context Protocol server for AI Audit Ledger.
|
|
4
|
+
*
|
|
5
|
+
* Stdio transport — designed to be spawned by an MCP client (Claude Desktop,
|
|
6
|
+
* Cursor, LangGraph adapter, custom). Three tools exposed: record_decision,
|
|
7
|
+
* verify_decision, list_decisions.
|
|
8
|
+
*
|
|
9
|
+
* Configuration via environment variables — see .env.example. The server
|
|
10
|
+
* reads them at startup and refuses to start if AUDIT_API_URL is missing,
|
|
11
|
+
* since no tool can do anything useful without it.
|
|
12
|
+
*/
|
|
13
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
14
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
15
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
16
|
+
import { AuditLedgerClient, AuditLedgerError } from "./client.js";
|
|
17
|
+
import { executeRecordDecision, recordDecisionToolDefinition, } from "./tools/record_decision.js";
|
|
18
|
+
import { executeVerifyDecision, verifyDecisionToolDefinition, } from "./tools/verify_decision.js";
|
|
19
|
+
import { executeListDecisions, listDecisionsToolDefinition, } from "./tools/list_decisions.js";
|
|
20
|
+
const PKG_NAME = "audit-ledger-mcp";
|
|
21
|
+
const PKG_VERSION = "0.1.0";
|
|
22
|
+
function requireEnv(name) {
|
|
23
|
+
const value = process.env[name];
|
|
24
|
+
if (!value) {
|
|
25
|
+
process.stderr.write(`[${PKG_NAME}] missing required env var: ${name}\n` +
|
|
26
|
+
`See .env.example or the README for required configuration.\n`);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
function buildClient() {
|
|
32
|
+
const apiUrl = requireEnv("AUDIT_API_URL");
|
|
33
|
+
const writeKey = process.env.AUDIT_WRITE_KEY;
|
|
34
|
+
const readKey = process.env.AUDIT_READ_KEY;
|
|
35
|
+
const timeoutMs = process.env.AUDIT_TIMEOUT_MS
|
|
36
|
+
? Number(process.env.AUDIT_TIMEOUT_MS)
|
|
37
|
+
: undefined;
|
|
38
|
+
const retryAttempts = process.env.AUDIT_RETRY_ATTEMPTS
|
|
39
|
+
? Number(process.env.AUDIT_RETRY_ATTEMPTS)
|
|
40
|
+
: undefined;
|
|
41
|
+
if (!writeKey && !readKey) {
|
|
42
|
+
process.stderr.write(`[${PKG_NAME}] neither AUDIT_WRITE_KEY nor AUDIT_READ_KEY is set — ` +
|
|
43
|
+
`all tools will fail. Set at least one. See .env.example.\n`);
|
|
44
|
+
}
|
|
45
|
+
return new AuditLedgerClient({ apiUrl, writeKey, readKey, timeoutMs, retryAttempts });
|
|
46
|
+
}
|
|
47
|
+
async function main() {
|
|
48
|
+
const client = buildClient();
|
|
49
|
+
const server = new Server({ name: PKG_NAME, version: PKG_VERSION }, { capabilities: { tools: {} } });
|
|
50
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
51
|
+
tools: [
|
|
52
|
+
recordDecisionToolDefinition,
|
|
53
|
+
verifyDecisionToolDefinition,
|
|
54
|
+
listDecisionsToolDefinition,
|
|
55
|
+
],
|
|
56
|
+
}));
|
|
57
|
+
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
58
|
+
const { name, arguments: args } = req.params;
|
|
59
|
+
try {
|
|
60
|
+
let result;
|
|
61
|
+
switch (name) {
|
|
62
|
+
case "record_decision":
|
|
63
|
+
result = await executeRecordDecision(client, args);
|
|
64
|
+
break;
|
|
65
|
+
case "verify_decision":
|
|
66
|
+
result = await executeVerifyDecision(client, args);
|
|
67
|
+
break;
|
|
68
|
+
case "list_decisions":
|
|
69
|
+
result = await executeListDecisions(client, args);
|
|
70
|
+
break;
|
|
71
|
+
default:
|
|
72
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
const message = err instanceof AuditLedgerError
|
|
80
|
+
? `${err.message}${err.status ? ` (HTTP ${err.status})` : ""}${err.body ? `\n${err.body}` : ""}`
|
|
81
|
+
: err instanceof Error
|
|
82
|
+
? err.message
|
|
83
|
+
: String(err);
|
|
84
|
+
return {
|
|
85
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
86
|
+
isError: true,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
const transport = new StdioServerTransport();
|
|
91
|
+
await server.connect(transport);
|
|
92
|
+
process.stderr.write(`[${PKG_NAME}] ${PKG_VERSION} listening on stdio\n`);
|
|
93
|
+
}
|
|
94
|
+
main().catch((err) => {
|
|
95
|
+
process.stderr.write(`[${PKG_NAME}] fatal: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
96
|
+
process.exit(1);
|
|
97
|
+
});
|
|
98
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* list_decisions tool — query recent decisions for the calling tenant. Useful
|
|
3
|
+
* for an agent to review its own recent activity, for compliance dashboards,
|
|
4
|
+
* and for spot-checks during an audit.
|
|
5
|
+
*
|
|
6
|
+
* Defaults: last 7 days, sorted newest first. Tenant scope is enforced by the
|
|
7
|
+
* read key — a tenant key only returns that tenant's records.
|
|
8
|
+
*/
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
import type { AuditLedgerClient } from "../client.js";
|
|
11
|
+
export declare const listDecisionsInputSchema: z.ZodObject<{
|
|
12
|
+
from: z.ZodOptional<z.ZodString>;
|
|
13
|
+
to: z.ZodOptional<z.ZodString>;
|
|
14
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
15
|
+
}, "strip", z.ZodTypeAny, {
|
|
16
|
+
from?: string | undefined;
|
|
17
|
+
to?: string | undefined;
|
|
18
|
+
limit?: number | undefined;
|
|
19
|
+
}, {
|
|
20
|
+
from?: string | undefined;
|
|
21
|
+
to?: string | undefined;
|
|
22
|
+
limit?: number | undefined;
|
|
23
|
+
}>;
|
|
24
|
+
export type ListDecisionsInput = z.infer<typeof listDecisionsInputSchema>;
|
|
25
|
+
export declare const listDecisionsToolDefinition: {
|
|
26
|
+
readonly name: "list_decisions";
|
|
27
|
+
readonly description: "List recorded AI decisions for the calling tenant within a time window. Returns newest first. The read key scopes the result to the caller's tenant — cross-tenant reads require an admin read key. Use this for compliance review, audit prep, or agent self-inspection of recent activity.";
|
|
28
|
+
readonly inputSchema: {
|
|
29
|
+
readonly type: "object";
|
|
30
|
+
readonly properties: {
|
|
31
|
+
readonly from: {
|
|
32
|
+
readonly type: "string";
|
|
33
|
+
readonly format: "date-time";
|
|
34
|
+
readonly description: string | undefined;
|
|
35
|
+
};
|
|
36
|
+
readonly to: {
|
|
37
|
+
readonly type: "string";
|
|
38
|
+
readonly format: "date-time";
|
|
39
|
+
readonly description: string | undefined;
|
|
40
|
+
};
|
|
41
|
+
readonly limit: {
|
|
42
|
+
readonly type: "number";
|
|
43
|
+
readonly minimum: 1;
|
|
44
|
+
readonly maximum: 500;
|
|
45
|
+
readonly description: string | undefined;
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
readonly required: readonly [];
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
export declare function executeListDecisions(client: AuditLedgerClient, rawInput: unknown): Promise<{
|
|
52
|
+
count: number;
|
|
53
|
+
from: string;
|
|
54
|
+
to: string;
|
|
55
|
+
decisions: unknown[];
|
|
56
|
+
}>;
|
|
57
|
+
//# sourceMappingURL=list_decisions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list_decisions.d.ts","sourceRoot":"","sources":["../../src/tools/list_decisions.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEtD,eAAO,MAAM,wBAAwB;;;;;;;;;;;;EAwBnC,CAAC;AAEH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;CAa9B,CAAC;AAEX,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,iBAAiB,EACzB,QAAQ,EAAE,OAAO,GAChB,OAAO,CAAC;IACT,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,OAAO,EAAE,CAAC;CACtB,CAAC,CAkBD"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* list_decisions tool — query recent decisions for the calling tenant. Useful
|
|
3
|
+
* for an agent to review its own recent activity, for compliance dashboards,
|
|
4
|
+
* and for spot-checks during an audit.
|
|
5
|
+
*
|
|
6
|
+
* Defaults: last 7 days, sorted newest first. Tenant scope is enforced by the
|
|
7
|
+
* read key — a tenant key only returns that tenant's records.
|
|
8
|
+
*/
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
export const listDecisionsInputSchema = z.object({
|
|
11
|
+
from: z
|
|
12
|
+
.string()
|
|
13
|
+
.datetime()
|
|
14
|
+
.optional()
|
|
15
|
+
.describe("ISO 8601 start of the time window (inclusive). Defaults to 7 days before now."),
|
|
16
|
+
to: z
|
|
17
|
+
.string()
|
|
18
|
+
.datetime()
|
|
19
|
+
.optional()
|
|
20
|
+
.describe("ISO 8601 end of the time window (inclusive). Defaults to now."),
|
|
21
|
+
limit: z
|
|
22
|
+
.number()
|
|
23
|
+
.int()
|
|
24
|
+
.positive()
|
|
25
|
+
.max(500)
|
|
26
|
+
.optional()
|
|
27
|
+
.describe("Maximum number of records to return (default 100, max 500). Returned newest-first."),
|
|
28
|
+
});
|
|
29
|
+
export const listDecisionsToolDefinition = {
|
|
30
|
+
name: "list_decisions",
|
|
31
|
+
description: "List recorded AI decisions for the calling tenant within a time window. Returns newest first. The read key scopes the result to the caller's tenant — cross-tenant reads require an admin read key. Use this for compliance review, audit prep, or agent self-inspection of recent activity.",
|
|
32
|
+
inputSchema: {
|
|
33
|
+
type: "object",
|
|
34
|
+
properties: {
|
|
35
|
+
from: { type: "string", format: "date-time", description: listDecisionsInputSchema.shape.from.description },
|
|
36
|
+
to: { type: "string", format: "date-time", description: listDecisionsInputSchema.shape.to.description },
|
|
37
|
+
limit: { type: "number", minimum: 1, maximum: 500, description: listDecisionsInputSchema.shape.limit.description },
|
|
38
|
+
},
|
|
39
|
+
required: [],
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
export async function executeListDecisions(client, rawInput) {
|
|
43
|
+
const input = listDecisionsInputSchema.parse(rawInput ?? {});
|
|
44
|
+
const to = input.to ?? new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
45
|
+
const from = input.from ??
|
|
46
|
+
new Date(Date.now() - 7 * 24 * 60 * 60 * 1000)
|
|
47
|
+
.toISOString()
|
|
48
|
+
.replace(/\.\d{3}Z$/, "Z");
|
|
49
|
+
const limit = input.limit ?? 100;
|
|
50
|
+
const all = await client.listDecisions({ from, to });
|
|
51
|
+
const decisions = all.slice(0, limit);
|
|
52
|
+
return {
|
|
53
|
+
count: decisions.length,
|
|
54
|
+
from,
|
|
55
|
+
to,
|
|
56
|
+
decisions,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=list_decisions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list_decisions.js","sourceRoot":"","sources":["../../src/tools/list_decisions.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/C,IAAI,EAAE,CAAC;SACJ,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,EAAE;SACV,QAAQ,CACP,+EAA+E,CAChF;IACH,EAAE,EAAE,CAAC;SACF,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,EAAE;SACV,QAAQ,CACP,+DAA+D,CAChE;IACH,KAAK,EAAE,CAAC;SACL,MAAM,EAAE;SACR,GAAG,EAAE;SACL,QAAQ,EAAE;SACV,GAAG,CAAC,GAAG,CAAC;SACR,QAAQ,EAAE;SACV,QAAQ,CACP,oFAAoF,CACrF;CACJ,CAAC,CAAC;AAIH,MAAM,CAAC,MAAM,2BAA2B,GAAG;IACzC,IAAI,EAAE,gBAAgB;IACtB,WAAW,EACT,8RAA8R;IAChS,WAAW,EAAE;QACX,IAAI,EAAE,QAAiB;QACvB,UAAU,EAAE;YACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,wBAAwB,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE;YAC3G,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,wBAAwB,CAAC,KAAK,CAAC,EAAE,CAAC,WAAW,EAAE;YACvG,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE,wBAAwB,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,EAAE;SACnH;QACD,QAAQ,EAAE,EAAE;KACb;CACO,CAAC;AAEX,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,MAAyB,EACzB,QAAiB;IAOjB,MAAM,KAAK,GAAG,wBAAwB,CAAC,KAAK,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;IAC7D,MAAM,EAAE,GAAG,KAAK,CAAC,EAAE,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;IAC1E,MAAM,IAAI,GACR,KAAK,CAAC,IAAI;QACV,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;aAC3C,WAAW,EAAE;aACb,OAAO,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;IAC/B,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,GAAG,CAAC;IAEjC,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;IACrD,MAAM,SAAS,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IACtC,OAAO;QACL,KAAK,EAAE,SAAS,CAAC,MAAM;QACvB,IAAI;QACJ,EAAE;QACF,SAAS;KACV,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* record_decision tool — agent logs an AI decision to the audit ledger.
|
|
3
|
+
*
|
|
4
|
+
* PII (raw_system_prompt and raw_user_input) is hashed locally inside this
|
|
5
|
+
* tool before any payload leaves the MCP server. The ledger never sees the
|
|
6
|
+
* raw values — only SHA-256 hashes, the structured decision output, and
|
|
7
|
+
* metadata.
|
|
8
|
+
*/
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
import type { AuditLedgerClient } from "../client.js";
|
|
11
|
+
export declare const recordDecisionInputSchema: z.ZodObject<{
|
|
12
|
+
model_version: z.ZodString;
|
|
13
|
+
raw_system_prompt: z.ZodString;
|
|
14
|
+
raw_user_input: z.ZodString;
|
|
15
|
+
ai_decision_output: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
16
|
+
human_in_loop: z.ZodBoolean;
|
|
17
|
+
event_id: z.ZodOptional<z.ZodString>;
|
|
18
|
+
timestamp: z.ZodOptional<z.ZodString>;
|
|
19
|
+
}, "strip", z.ZodTypeAny, {
|
|
20
|
+
model_version: string;
|
|
21
|
+
ai_decision_output: Record<string, unknown>;
|
|
22
|
+
human_in_loop: boolean;
|
|
23
|
+
raw_system_prompt: string;
|
|
24
|
+
raw_user_input: string;
|
|
25
|
+
event_id?: string | undefined;
|
|
26
|
+
timestamp?: string | undefined;
|
|
27
|
+
}, {
|
|
28
|
+
model_version: string;
|
|
29
|
+
ai_decision_output: Record<string, unknown>;
|
|
30
|
+
human_in_loop: boolean;
|
|
31
|
+
raw_system_prompt: string;
|
|
32
|
+
raw_user_input: string;
|
|
33
|
+
event_id?: string | undefined;
|
|
34
|
+
timestamp?: string | undefined;
|
|
35
|
+
}>;
|
|
36
|
+
export type RecordDecisionInput = z.infer<typeof recordDecisionInputSchema>;
|
|
37
|
+
export declare const recordDecisionToolDefinition: {
|
|
38
|
+
readonly name: "record_decision";
|
|
39
|
+
readonly description: "Record an AI decision to the audit ledger. Stores model version, hashed inputs, structured output, and human-review flag. The record is immutably sealed in S3 Object Lock for 7 years and queryable for the lifetime of the decision. Use this immediately after any AI decision that may need to be audited later — credit, hiring, fraud, customer routing, content moderation.";
|
|
40
|
+
readonly inputSchema: {
|
|
41
|
+
readonly type: "object";
|
|
42
|
+
readonly properties: {
|
|
43
|
+
readonly model_version: {
|
|
44
|
+
readonly type: "string";
|
|
45
|
+
readonly description: string | undefined;
|
|
46
|
+
};
|
|
47
|
+
readonly raw_system_prompt: {
|
|
48
|
+
readonly type: "string";
|
|
49
|
+
readonly description: string | undefined;
|
|
50
|
+
};
|
|
51
|
+
readonly raw_user_input: {
|
|
52
|
+
readonly type: "string";
|
|
53
|
+
readonly description: string | undefined;
|
|
54
|
+
};
|
|
55
|
+
readonly ai_decision_output: {
|
|
56
|
+
readonly type: "object";
|
|
57
|
+
readonly description: string | undefined;
|
|
58
|
+
readonly additionalProperties: true;
|
|
59
|
+
};
|
|
60
|
+
readonly human_in_loop: {
|
|
61
|
+
readonly type: "boolean";
|
|
62
|
+
readonly description: string | undefined;
|
|
63
|
+
};
|
|
64
|
+
readonly event_id: {
|
|
65
|
+
readonly type: "string";
|
|
66
|
+
readonly format: "uuid";
|
|
67
|
+
readonly description: string | undefined;
|
|
68
|
+
};
|
|
69
|
+
readonly timestamp: {
|
|
70
|
+
readonly type: "string";
|
|
71
|
+
readonly format: "date-time";
|
|
72
|
+
readonly description: string | undefined;
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
readonly required: readonly ["model_version", "raw_system_prompt", "raw_user_input", "ai_decision_output", "human_in_loop"];
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
export declare function executeRecordDecision(client: AuditLedgerClient, rawInput: unknown): Promise<{
|
|
79
|
+
event_id: string;
|
|
80
|
+
recorded_at: string;
|
|
81
|
+
note: string;
|
|
82
|
+
}>;
|
|
83
|
+
//# sourceMappingURL=record_decision.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"record_decision.d.ts","sourceRoot":"","sources":["../../src/tools/record_decision.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAGtD,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;EAyCpC,CAAC;AAEH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E,eAAO,MAAM,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiB/B,CAAC;AAEX,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,iBAAiB,EACzB,QAAQ,EAAE,OAAO,GAChB,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAqBlE"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* record_decision tool — agent logs an AI decision to the audit ledger.
|
|
3
|
+
*
|
|
4
|
+
* PII (raw_system_prompt and raw_user_input) is hashed locally inside this
|
|
5
|
+
* tool before any payload leaves the MCP server. The ledger never sees the
|
|
6
|
+
* raw values — only SHA-256 hashes, the structured decision output, and
|
|
7
|
+
* metadata.
|
|
8
|
+
*/
|
|
9
|
+
import { randomUUID } from "node:crypto";
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
import { hashPii, hashPrompt } from "../hashing.js";
|
|
12
|
+
export const recordDecisionInputSchema = z.object({
|
|
13
|
+
model_version: z
|
|
14
|
+
.string()
|
|
15
|
+
.min(1)
|
|
16
|
+
.describe("The model and version that produced the decision (e.g. 'claude-sonnet-4.7', 'gpt-4o-2024-08-06'). Required for traceability and model risk audits."),
|
|
17
|
+
raw_system_prompt: z
|
|
18
|
+
.string()
|
|
19
|
+
.describe("The system prompt used. Hashed locally before transit — the raw text never leaves this MCP server."),
|
|
20
|
+
raw_user_input: z
|
|
21
|
+
.string()
|
|
22
|
+
.describe("The user input the model decided on (CV text, transaction, customer message, etc.). Hashed locally before transit — raw PII never leaves this MCP server."),
|
|
23
|
+
ai_decision_output: z
|
|
24
|
+
.record(z.unknown())
|
|
25
|
+
.describe("The structured decision the model produced. Stored verbatim. Should NOT contain raw PII — only the decision itself (score, classification, recommendation, reasoning summary)."),
|
|
26
|
+
human_in_loop: z
|
|
27
|
+
.boolean()
|
|
28
|
+
.describe("Whether a human reviewed or approved this decision before it took effect. Critical for EU AI Act Article 14 (human oversight) compliance."),
|
|
29
|
+
event_id: z
|
|
30
|
+
.string()
|
|
31
|
+
.uuid()
|
|
32
|
+
.optional()
|
|
33
|
+
.describe("Optional UUID v4 to identify this decision. Auto-generated if omitted. Useful when the calling system already has its own decision ID."),
|
|
34
|
+
timestamp: z
|
|
35
|
+
.string()
|
|
36
|
+
.datetime()
|
|
37
|
+
.optional()
|
|
38
|
+
.describe("Optional ISO 8601 timestamp of when the decision was made. Defaults to the current time. Use this only if recording a backfilled decision."),
|
|
39
|
+
});
|
|
40
|
+
export const recordDecisionToolDefinition = {
|
|
41
|
+
name: "record_decision",
|
|
42
|
+
description: "Record an AI decision to the audit ledger. Stores model version, hashed inputs, structured output, and human-review flag. The record is immutably sealed in S3 Object Lock for 7 years and queryable for the lifetime of the decision. Use this immediately after any AI decision that may need to be audited later — credit, hiring, fraud, customer routing, content moderation.",
|
|
43
|
+
inputSchema: {
|
|
44
|
+
type: "object",
|
|
45
|
+
properties: {
|
|
46
|
+
model_version: { type: "string", description: recordDecisionInputSchema.shape.model_version.description },
|
|
47
|
+
raw_system_prompt: { type: "string", description: recordDecisionInputSchema.shape.raw_system_prompt.description },
|
|
48
|
+
raw_user_input: { type: "string", description: recordDecisionInputSchema.shape.raw_user_input.description },
|
|
49
|
+
ai_decision_output: { type: "object", description: recordDecisionInputSchema.shape.ai_decision_output.description, additionalProperties: true },
|
|
50
|
+
human_in_loop: { type: "boolean", description: recordDecisionInputSchema.shape.human_in_loop.description },
|
|
51
|
+
event_id: { type: "string", format: "uuid", description: recordDecisionInputSchema.shape.event_id.description },
|
|
52
|
+
timestamp: { type: "string", format: "date-time", description: recordDecisionInputSchema.shape.timestamp.description },
|
|
53
|
+
},
|
|
54
|
+
required: ["model_version", "raw_system_prompt", "raw_user_input", "ai_decision_output", "human_in_loop"],
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
export async function executeRecordDecision(client, rawInput) {
|
|
58
|
+
const input = recordDecisionInputSchema.parse(rawInput);
|
|
59
|
+
const eventId = input.event_id ?? randomUUID();
|
|
60
|
+
const timestamp = input.timestamp ?? new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
61
|
+
const result = await client.recordDecision({
|
|
62
|
+
event_id: eventId,
|
|
63
|
+
timestamp,
|
|
64
|
+
model_version: input.model_version,
|
|
65
|
+
system_prompt_hash: hashPrompt(input.raw_system_prompt),
|
|
66
|
+
input_data_hash: hashPii(input.raw_user_input),
|
|
67
|
+
ai_decision_output: input.ai_decision_output,
|
|
68
|
+
human_in_loop: input.human_in_loop,
|
|
69
|
+
});
|
|
70
|
+
return {
|
|
71
|
+
event_id: result.event_id,
|
|
72
|
+
recorded_at: timestamp,
|
|
73
|
+
note: "Decision recorded. PII was hashed locally — only the SHA-256 hashes were sent to the ledger. The record is now immutably stored in S3 Object Lock and queryable via verify_decision and list_decisions.",
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=record_decision.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"record_decision.js","sourceRoot":"","sources":["../../src/tools/record_decision.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* verify_decision tool — check that a recorded decision has not been tampered
|
|
3
|
+
* with. Hits the read API's tamper-evidence endpoint, which independently
|
|
4
|
+
* fetches the DynamoDB record AND the S3 Object Lock record and compares
|
|
5
|
+
* them. A mismatch is what an integrity failure looks like.
|
|
6
|
+
*/
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import type { AuditLedgerClient } from "../client.js";
|
|
9
|
+
export declare const verifyDecisionInputSchema: z.ZodObject<{
|
|
10
|
+
event_id: z.ZodString;
|
|
11
|
+
}, "strip", z.ZodTypeAny, {
|
|
12
|
+
event_id: string;
|
|
13
|
+
}, {
|
|
14
|
+
event_id: string;
|
|
15
|
+
}>;
|
|
16
|
+
export type VerifyDecisionInput = z.infer<typeof verifyDecisionInputSchema>;
|
|
17
|
+
export declare const verifyDecisionToolDefinition: {
|
|
18
|
+
readonly name: "verify_decision";
|
|
19
|
+
readonly description: "Verify a recorded AI decision has not been altered since it was written. The ledger fetches the queryable copy (DynamoDB) and the immutable copy (S3 Object Lock COMPLIANCE mode) independently and compares them. Returns integrity_verified=true if they match. Use this to satisfy a regulator request or to prove an audit trail to a compliance team.";
|
|
20
|
+
readonly inputSchema: {
|
|
21
|
+
readonly type: "object";
|
|
22
|
+
readonly properties: {
|
|
23
|
+
readonly event_id: {
|
|
24
|
+
readonly type: "string";
|
|
25
|
+
readonly format: "uuid";
|
|
26
|
+
readonly description: string | undefined;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
readonly required: readonly ["event_id"];
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
export declare function executeVerifyDecision(client: AuditLedgerClient, rawInput: unknown): Promise<{
|
|
33
|
+
event_id: string;
|
|
34
|
+
integrity_verified: boolean;
|
|
35
|
+
note: string;
|
|
36
|
+
dynamodb_record: unknown;
|
|
37
|
+
s3_record: unknown;
|
|
38
|
+
}>;
|
|
39
|
+
//# sourceMappingURL=verify_decision.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify_decision.d.ts","sourceRoot":"","sources":["../../src/tools/verify_decision.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEtD,eAAO,MAAM,yBAAyB;;;;;;EAKpC,CAAC;AAEH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E,eAAO,MAAM,4BAA4B;;;;;;;;;;;;;;CAW/B,CAAC;AAEX,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,iBAAiB,EACzB,QAAQ,EAAE,OAAO,GAChB,OAAO,CAAC;IACT,QAAQ,EAAE,MAAM,CAAC;IACjB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,OAAO,CAAC;IACzB,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC,CAUD"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* verify_decision tool — check that a recorded decision has not been tampered
|
|
3
|
+
* with. Hits the read API's tamper-evidence endpoint, which independently
|
|
4
|
+
* fetches the DynamoDB record AND the S3 Object Lock record and compares
|
|
5
|
+
* them. A mismatch is what an integrity failure looks like.
|
|
6
|
+
*/
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
export const verifyDecisionInputSchema = z.object({
|
|
9
|
+
event_id: z
|
|
10
|
+
.string()
|
|
11
|
+
.uuid()
|
|
12
|
+
.describe("The UUID v4 event ID of the decision to verify."),
|
|
13
|
+
});
|
|
14
|
+
export const verifyDecisionToolDefinition = {
|
|
15
|
+
name: "verify_decision",
|
|
16
|
+
description: "Verify a recorded AI decision has not been altered since it was written. The ledger fetches the queryable copy (DynamoDB) and the immutable copy (S3 Object Lock COMPLIANCE mode) independently and compares them. Returns integrity_verified=true if they match. Use this to satisfy a regulator request or to prove an audit trail to a compliance team.",
|
|
17
|
+
inputSchema: {
|
|
18
|
+
type: "object",
|
|
19
|
+
properties: {
|
|
20
|
+
event_id: { type: "string", format: "uuid", description: verifyDecisionInputSchema.shape.event_id.description },
|
|
21
|
+
},
|
|
22
|
+
required: ["event_id"],
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
export async function executeVerifyDecision(client, rawInput) {
|
|
26
|
+
const input = verifyDecisionInputSchema.parse(rawInput);
|
|
27
|
+
const result = await client.verifyDecision(input.event_id);
|
|
28
|
+
return {
|
|
29
|
+
event_id: input.event_id,
|
|
30
|
+
integrity_verified: result.integrity_verified,
|
|
31
|
+
note: result.note,
|
|
32
|
+
dynamodb_record: result.dynamodb_record,
|
|
33
|
+
s3_record: result.s3_record,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=verify_decision.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify_decision.js","sourceRoot":"","sources":["../../src/tools/verify_decision.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC,MAAM,CAAC;IAChD,QAAQ,EAAE,CAAC;SACR,MAAM,EAAE;SACR,IAAI,EAAE;SACN,QAAQ,CAAC,iDAAiD,CAAC;CAC/D,CAAC,CAAC;AAIH,MAAM,CAAC,MAAM,4BAA4B,GAAG;IAC1C,IAAI,EAAE,iBAAiB;IACvB,WAAW,EACT,4VAA4V;IAC9V,WAAW,EAAE;QACX,IAAI,EAAE,QAAiB;QACvB,UAAU,EAAE;YACV,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,yBAAyB,CAAC,KAAK,CAAC,QAAQ,CAAC,WAAW,EAAE;SAChH;QACD,QAAQ,EAAE,CAAC,UAAU,CAAC;KACvB;CACO,CAAC;AAEX,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,MAAyB,EACzB,QAAiB;IAQjB,MAAM,KAAK,GAAG,yBAAyB,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IACxD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC3D,OAAO;QACL,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,kBAAkB,EAAE,MAAM,CAAC,kBAAkB;QAC7C,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,eAAe,EAAE,MAAM,CAAC,eAAe;QACvC,SAAS,EAAE,MAAM,CAAC,SAAS;KAC5B,CAAC;AACJ,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "audit-ledger-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server for AI Audit Ledger — record AI decisions to a tamper-evident ledger from any agent (Claude, Cursor, LangGraph, custom).",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"audit-ledger-mcp": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"README.md",
|
|
14
|
+
"LICENSE"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc",
|
|
18
|
+
"start": "node dist/index.js",
|
|
19
|
+
"dev": "tsc --watch",
|
|
20
|
+
"test": "node --test --import tsx tests/*.test.ts",
|
|
21
|
+
"integration-test": "tsx tests/integration.test.ts",
|
|
22
|
+
"prepublishOnly": "npm run build"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"mcp",
|
|
26
|
+
"model-context-protocol",
|
|
27
|
+
"ai-audit",
|
|
28
|
+
"audit-trail",
|
|
29
|
+
"eu-ai-act",
|
|
30
|
+
"fca",
|
|
31
|
+
"compliance",
|
|
32
|
+
"fintech",
|
|
33
|
+
"hitl",
|
|
34
|
+
"langgraph",
|
|
35
|
+
"claude"
|
|
36
|
+
],
|
|
37
|
+
"author": "Shahid (https://github.com/shahidh68)",
|
|
38
|
+
"license": "Apache-2.0",
|
|
39
|
+
"homepage": "https://github.com/shahidh68/audit-ledger-mcp",
|
|
40
|
+
"repository": {
|
|
41
|
+
"type": "git",
|
|
42
|
+
"url": "git+https://github.com/shahidh68/audit-ledger-mcp.git"
|
|
43
|
+
},
|
|
44
|
+
"bugs": {
|
|
45
|
+
"url": "https://github.com/shahidh68/audit-ledger-mcp/issues"
|
|
46
|
+
},
|
|
47
|
+
"engines": {
|
|
48
|
+
"node": ">=20"
|
|
49
|
+
},
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
52
|
+
"zod": "^3.23.0"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@types/node": "^22.0.0",
|
|
56
|
+
"tsx": "^4.19.0",
|
|
57
|
+
"typescript": "^5.6.0"
|
|
58
|
+
}
|
|
59
|
+
}
|