@proofai/mcp-server 1.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/README.md +73 -0
- package/SKILL.md +53 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +232 -0
- package/package.json +23 -0
- package/src/index.ts +282 -0
- package/tsconfig.json +14 -0
package/README.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# @proofai/mcp-server
|
|
2
|
+
|
|
3
|
+
MCP Server for ProofAI — plug AI compliance into Claude Code.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @proofai/mcp-server
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Configure Claude Code
|
|
12
|
+
|
|
13
|
+
Add to `~/.claude/claude_desktop_config.json`:
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
{
|
|
17
|
+
"mcpServers": {
|
|
18
|
+
"proofai": {
|
|
19
|
+
"command": "proofai-mcp",
|
|
20
|
+
"env": {
|
|
21
|
+
"PROOFAI_API_KEY": "pk_live_xxx",
|
|
22
|
+
"PROOFAI_ANON_KEY": "your-supabase-anon-key"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Tools
|
|
30
|
+
|
|
31
|
+
### `proofai_certify`
|
|
32
|
+
Certify an AI decision with cryptographic proof. Full pipeline in one call.
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
> Use proofai_certify to certify my analysis of this contract
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### `proofai_log`
|
|
39
|
+
Log an AI decision that already happened (prompt + response you provide).
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
> Use proofai_log to record this decision with prompt "..." and response "..."
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### `proofai_verify`
|
|
46
|
+
Verify a bundle's integrity and blockchain anchoring.
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
> Use proofai_verify to check bundle bnd_8019b37a7f44_1774735436195
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### `proofai_polygonscan`
|
|
53
|
+
Get the Polygonscan URL for independent verification.
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
> Use proofai_polygonscan for tx 0xbbf92ceb6354a066...
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### `proofai_monitor`
|
|
60
|
+
Get AI compliance monitoring stats (EU AI Act Article 72).
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
> Use proofai_monitor to check compliance status
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Why MCP?
|
|
67
|
+
|
|
68
|
+
Developers use Claude Code, not web dashboards. This MCP server brings AI compliance directly into the coding workflow:
|
|
69
|
+
|
|
70
|
+
- Every AI decision can be certified without leaving the terminal
|
|
71
|
+
- Verification is one tool call away
|
|
72
|
+
- Monitoring happens alongside development
|
|
73
|
+
- No context switching between coding and compliance
|
package/SKILL.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# proofai
|
|
2
|
+
|
|
3
|
+
Cryptographic proof that AI thought before it answered. EU AI Act Article 12 compliant.
|
|
4
|
+
|
|
5
|
+
## Tools
|
|
6
|
+
|
|
7
|
+
### proofai_certify
|
|
8
|
+
Certify an AI decision with cryptographic proof. Runs the full ProofAI pipeline: compress, execute AI, analyze cognitive graph, sign with Ed25519, bundle evidence, anchor to Polygon blockchain, and verify. Returns a blockchain-verified evidence bundle with Polygonscan URL.
|
|
9
|
+
|
|
10
|
+
**When to use:** When you need tamper-evident proof of an AI decision for compliance, audit, or legal purposes.
|
|
11
|
+
|
|
12
|
+
### proofai_log
|
|
13
|
+
Log an AI decision that already happened. Provide the original prompt and AI response — ProofAI signs it with Ed25519, bundles the evidence, and anchors the hash to Polygon. No AI execution needed.
|
|
14
|
+
|
|
15
|
+
**When to use:** When you want to retroactively certify an AI interaction that already occurred.
|
|
16
|
+
|
|
17
|
+
### proofai_verify
|
|
18
|
+
Verify an evidence bundle's integrity and blockchain anchoring. Checks data integrity, timestamp validity, ledger anchoring, and hash matching against EU AI Act requirements.
|
|
19
|
+
|
|
20
|
+
**When to use:** When you need to confirm a bundle hasn't been tampered with.
|
|
21
|
+
|
|
22
|
+
### proofai_polygonscan
|
|
23
|
+
Get the Polygonscan verification URL for a Polygon transaction hash. Anyone can independently verify the proof without an account.
|
|
24
|
+
|
|
25
|
+
**When to use:** When you need to share a verification link with a regulator, client, or auditor.
|
|
26
|
+
|
|
27
|
+
### proofai_monitor
|
|
28
|
+
Get post-market monitoring statistics for AI compliance (EU AI Act Article 72). Returns anomaly counts, risk distribution, human review stats, and overall compliance status for the last 30 days.
|
|
29
|
+
|
|
30
|
+
**When to use:** When you need a compliance health check of your AI system.
|
|
31
|
+
|
|
32
|
+
## Setup
|
|
33
|
+
|
|
34
|
+
```json
|
|
35
|
+
{
|
|
36
|
+
"mcpServers": {
|
|
37
|
+
"proofai": {
|
|
38
|
+
"command": "npx",
|
|
39
|
+
"args": ["-y", "@proofai/mcp-server"],
|
|
40
|
+
"env": {
|
|
41
|
+
"PROOFAI_API_KEY": "pk_live_xxx",
|
|
42
|
+
"PROOFAI_ANON_KEY": "your-supabase-anon-key"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Links
|
|
50
|
+
|
|
51
|
+
- GitHub: https://github.com/proof-ai/proofai
|
|
52
|
+
- npm SDK: https://www.npmjs.com/package/@proofai/sdk
|
|
53
|
+
- Regulator Portal: https://proofai-ochre.vercel.app/regulator
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
5
|
+
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
6
|
+
const zod_1 = require("zod");
|
|
7
|
+
const API_BASE = process.env.PROOFAI_API_URL || "https://apzgbajvwzykygrxxrwm.supabase.co/functions/v1";
|
|
8
|
+
const API_KEY = process.env.PROOFAI_API_KEY || "";
|
|
9
|
+
const ANON_KEY = process.env.PROOFAI_ANON_KEY || "";
|
|
10
|
+
async function callAPI(path, body) {
|
|
11
|
+
const headers = { "Content-Type": "application/json" };
|
|
12
|
+
if (API_KEY.startsWith("pk_live_")) {
|
|
13
|
+
headers["x-api-key"] = API_KEY;
|
|
14
|
+
}
|
|
15
|
+
if (ANON_KEY) {
|
|
16
|
+
headers["Authorization"] = `Bearer ${ANON_KEY}`;
|
|
17
|
+
}
|
|
18
|
+
const res = await fetch(`${API_BASE}/${path}`, {
|
|
19
|
+
method: "POST",
|
|
20
|
+
headers,
|
|
21
|
+
body: JSON.stringify(body),
|
|
22
|
+
});
|
|
23
|
+
if (!res.ok) {
|
|
24
|
+
const text = await res.text();
|
|
25
|
+
throw new Error(`ProofAI API error ${res.status}: ${text}`);
|
|
26
|
+
}
|
|
27
|
+
return res.json();
|
|
28
|
+
}
|
|
29
|
+
const server = new mcp_js_1.McpServer({
|
|
30
|
+
name: "proofai",
|
|
31
|
+
version: "1.0.0",
|
|
32
|
+
});
|
|
33
|
+
// --- Tool: Log an AI decision (full pipeline) ---
|
|
34
|
+
server.tool("proofai_certify", "Certify an AI decision with cryptographic proof. Runs the full ProofAI pipeline: compress → execute → analyze → sign → bundle → anchor → verify. Returns a blockchain-verified evidence bundle.", {
|
|
35
|
+
prompt: zod_1.z.string().describe("The AI prompt to certify"),
|
|
36
|
+
provider: zod_1.z.enum(["anthropic", "openai", "gemini"]).default("anthropic").describe("AI provider"),
|
|
37
|
+
temperature: zod_1.z.number().default(0.7).describe("Generation temperature"),
|
|
38
|
+
maxTokens: zod_1.z.number().default(1024).describe("Max output tokens"),
|
|
39
|
+
}, async ({ prompt, provider, temperature, maxTokens }) => {
|
|
40
|
+
try {
|
|
41
|
+
// 1. Compress
|
|
42
|
+
const compressed = (await callAPI("compress", { prompt, options: { compressionLevel: "medium" } }));
|
|
43
|
+
// 2. Execute
|
|
44
|
+
const execution = (await callAPI("execute", {
|
|
45
|
+
promptRef: compressed.id,
|
|
46
|
+
options: { provider, modelId: "auto", temperature, maxTokens },
|
|
47
|
+
}));
|
|
48
|
+
// 3. Analyze
|
|
49
|
+
const analysis = (await callAPI("analyze", {
|
|
50
|
+
executionId: execution.id,
|
|
51
|
+
analysisText: execution.output,
|
|
52
|
+
}));
|
|
53
|
+
// 4. Sign
|
|
54
|
+
const signature = (await callAPI("sign", {
|
|
55
|
+
executionId: execution.id,
|
|
56
|
+
rawOutput: execution.output,
|
|
57
|
+
modelProvider: execution.metadata.provider,
|
|
58
|
+
modelId: execution.metadata.model,
|
|
59
|
+
modelVersion: "latest",
|
|
60
|
+
modelParameters: { temperature },
|
|
61
|
+
executionMetrics: { tokens: execution.metadata.tokens.total },
|
|
62
|
+
requesterInfo: { source: "mcp-server" },
|
|
63
|
+
timestamps: { request_received: new Date().toISOString() },
|
|
64
|
+
}));
|
|
65
|
+
// 5. Bundle
|
|
66
|
+
const bundle = (await callAPI("bundle", {
|
|
67
|
+
promptId: compressed.id,
|
|
68
|
+
executionId: execution.id,
|
|
69
|
+
analysisId: analysis.id,
|
|
70
|
+
signatureId: signature.signatureId,
|
|
71
|
+
cognitiveHash: analysis.cognitiveHash,
|
|
72
|
+
promptContent: prompt,
|
|
73
|
+
aiResponse: execution.output,
|
|
74
|
+
}));
|
|
75
|
+
// 6. Anchor
|
|
76
|
+
let anchor = {};
|
|
77
|
+
try {
|
|
78
|
+
anchor = (await callAPI("anchor", { bundleId: bundle.id, network: "polygon" }));
|
|
79
|
+
}
|
|
80
|
+
catch { /* anchor is best-effort */ }
|
|
81
|
+
// 7. Verify
|
|
82
|
+
const verification = (await callAPI("verify", { bundleId: bundle.id }));
|
|
83
|
+
return {
|
|
84
|
+
content: [
|
|
85
|
+
{
|
|
86
|
+
type: "text",
|
|
87
|
+
text: JSON.stringify({
|
|
88
|
+
status: "certified",
|
|
89
|
+
bundleId: bundle.id,
|
|
90
|
+
bundleHash: bundle.bundleHash,
|
|
91
|
+
verified: verification.verified,
|
|
92
|
+
explorerUrl: anchor.explorerUrl || null,
|
|
93
|
+
transactionHash: anchor.transactionHash || null,
|
|
94
|
+
provider: execution.metadata.provider,
|
|
95
|
+
model: execution.metadata.model,
|
|
96
|
+
tokens: execution.metadata.tokens.total,
|
|
97
|
+
cognitiveNodes: analysis.metrics.nodeCount,
|
|
98
|
+
consistencyScore: analysis.metrics.consistencyScore,
|
|
99
|
+
aiResponse: execution.output.substring(0, 500) + (execution.output.length > 500 ? "..." : ""),
|
|
100
|
+
}, null, 2),
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
return {
|
|
107
|
+
content: [{ type: "text", text: `Error: ${err.message}` }],
|
|
108
|
+
isError: true,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
// --- Tool: Log a decision (just record, don't execute AI) ---
|
|
113
|
+
server.tool("proofai_log", "Log an AI decision that already happened. Creates an evidence bundle with the prompt and response you provide, signs it with Ed25519, and anchors to Polygon.", {
|
|
114
|
+
prompt: zod_1.z.string().describe("The original prompt"),
|
|
115
|
+
response: zod_1.z.string().describe("The AI response to log"),
|
|
116
|
+
provider: zod_1.z.string().default("unknown").describe("Which AI provider generated the response"),
|
|
117
|
+
model: zod_1.z.string().default("unknown").describe("Which model was used"),
|
|
118
|
+
}, async ({ prompt, response, provider, model }) => {
|
|
119
|
+
try {
|
|
120
|
+
// Compress
|
|
121
|
+
const compressed = (await callAPI("compress", { prompt, options: {} }));
|
|
122
|
+
// Analyze
|
|
123
|
+
const analysis = (await callAPI("analyze", {
|
|
124
|
+
executionId: `ext_${Date.now()}`,
|
|
125
|
+
analysisText: response,
|
|
126
|
+
}));
|
|
127
|
+
// Sign
|
|
128
|
+
const signature = (await callAPI("sign", {
|
|
129
|
+
executionId: `ext_${Date.now()}`,
|
|
130
|
+
rawOutput: response,
|
|
131
|
+
modelProvider: provider,
|
|
132
|
+
modelId: model,
|
|
133
|
+
modelVersion: "external",
|
|
134
|
+
modelParameters: {},
|
|
135
|
+
executionMetrics: {},
|
|
136
|
+
requesterInfo: { source: "mcp-server-log" },
|
|
137
|
+
timestamps: { logged_at: new Date().toISOString() },
|
|
138
|
+
}));
|
|
139
|
+
// Bundle
|
|
140
|
+
const bundle = (await callAPI("bundle", {
|
|
141
|
+
promptId: compressed.id,
|
|
142
|
+
executionId: `ext_${Date.now()}`,
|
|
143
|
+
analysisId: analysis.id,
|
|
144
|
+
signatureId: signature.signatureId,
|
|
145
|
+
cognitiveHash: analysis.cognitiveHash,
|
|
146
|
+
promptContent: prompt,
|
|
147
|
+
aiResponse: response,
|
|
148
|
+
provider,
|
|
149
|
+
model,
|
|
150
|
+
}));
|
|
151
|
+
// Anchor
|
|
152
|
+
let explorerUrl = null;
|
|
153
|
+
try {
|
|
154
|
+
const anchor = (await callAPI("anchor", { bundleId: bundle.id, network: "polygon" }));
|
|
155
|
+
explorerUrl = anchor.explorerUrl;
|
|
156
|
+
}
|
|
157
|
+
catch { /* best-effort */ }
|
|
158
|
+
return {
|
|
159
|
+
content: [{
|
|
160
|
+
type: "text",
|
|
161
|
+
text: `Logged and certified.\n\nBundle ID: ${bundle.id}\nBundle Hash: ${bundle.bundleHash}\nPolygonscan: ${explorerUrl || "pending"}\n\nThis decision is now tamper-evident and blockchain-anchored.`,
|
|
162
|
+
}],
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
catch (err) {
|
|
166
|
+
return {
|
|
167
|
+
content: [{ type: "text", text: `Error: ${err.message}` }],
|
|
168
|
+
isError: true,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
// --- Tool: Verify a bundle ---
|
|
173
|
+
server.tool("proofai_verify", "Verify an evidence bundle's integrity and blockchain anchoring. Returns compliance checks against EU AI Act articles.", {
|
|
174
|
+
bundleId: zod_1.z.string().describe("The bundle ID to verify (e.g., bnd_xxx)"),
|
|
175
|
+
}, async ({ bundleId }) => {
|
|
176
|
+
try {
|
|
177
|
+
const result = (await callAPI("verify", { bundleId }));
|
|
178
|
+
const checksText = Object.entries(result.checks)
|
|
179
|
+
.map(([k, v]) => ` ${v ? "✅" : "❌"} ${k}`)
|
|
180
|
+
.join("\n");
|
|
181
|
+
return {
|
|
182
|
+
content: [{
|
|
183
|
+
type: "text",
|
|
184
|
+
text: `Bundle: ${result.bundleId}\nVerified: ${result.verified ? "✅ YES" : "❌ NO"}\n\nChecks:\n${checksText}${result.ledgerInfo
|
|
185
|
+
? `\n\nBlockchain:\n Network: ${result.ledgerInfo.network}\n Block: #${result.ledgerInfo.blockNumber}\n Tx: ${result.ledgerInfo.transactionHash}`
|
|
186
|
+
: ""}`,
|
|
187
|
+
}],
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
catch (err) {
|
|
191
|
+
return {
|
|
192
|
+
content: [{ type: "text", text: `Error: ${err.message}` }],
|
|
193
|
+
isError: true,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
// --- Tool: Get Polygonscan link ---
|
|
198
|
+
server.tool("proofai_polygonscan", "Get the Polygonscan verification URL for a transaction hash. Anyone can verify the proof independently.", {
|
|
199
|
+
txHash: zod_1.z.string().describe("The Polygon transaction hash (0x...)"),
|
|
200
|
+
}, async ({ txHash }) => {
|
|
201
|
+
const url = `https://amoy.polygonscan.com/tx/${txHash}`;
|
|
202
|
+
return {
|
|
203
|
+
content: [{
|
|
204
|
+
type: "text",
|
|
205
|
+
text: `Polygonscan verification URL:\n${url}\n\nAnyone can verify this proof — no account, no login, no middleman. Just math.`,
|
|
206
|
+
}],
|
|
207
|
+
};
|
|
208
|
+
});
|
|
209
|
+
// --- Tool: Get monitoring stats ---
|
|
210
|
+
server.tool("proofai_monitor", "Get post-market monitoring statistics for AI compliance (EU AI Act Article 72). Shows anomalies, risk distribution, and compliance status.", {}, async () => {
|
|
211
|
+
try {
|
|
212
|
+
const stats = await callAPI("monitor", {});
|
|
213
|
+
return {
|
|
214
|
+
content: [{
|
|
215
|
+
type: "text",
|
|
216
|
+
text: JSON.stringify(stats, null, 2),
|
|
217
|
+
}],
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
catch (err) {
|
|
221
|
+
return {
|
|
222
|
+
content: [{ type: "text", text: `Error: ${err.message}` }],
|
|
223
|
+
isError: true,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
// Start server
|
|
228
|
+
async function main() {
|
|
229
|
+
const transport = new stdio_js_1.StdioServerTransport();
|
|
230
|
+
await server.connect(transport);
|
|
231
|
+
}
|
|
232
|
+
main().catch(console.error);
|
package/package.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@proofai/mcp-server",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "ProofAI MCP Server — AI compliance tools for Claude Code",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"proofai-mcp": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"start": "node dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"author": "HIRAM",
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
17
|
+
"zod": "^4.3.6"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@types/node": "^22.0.0",
|
|
21
|
+
"typescript": "^5.8.0"
|
|
22
|
+
}
|
|
23
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
|
|
7
|
+
const API_BASE = process.env.PROOFAI_API_URL || "https://apzgbajvwzykygrxxrwm.supabase.co/functions/v1";
|
|
8
|
+
const API_KEY = process.env.PROOFAI_API_KEY || "";
|
|
9
|
+
const ANON_KEY = process.env.PROOFAI_ANON_KEY || "";
|
|
10
|
+
|
|
11
|
+
async function callAPI(path: string, body: Record<string, unknown>): Promise<unknown> {
|
|
12
|
+
const headers: Record<string, string> = { "Content-Type": "application/json" };
|
|
13
|
+
if (API_KEY.startsWith("pk_live_")) {
|
|
14
|
+
headers["x-api-key"] = API_KEY;
|
|
15
|
+
}
|
|
16
|
+
if (ANON_KEY) {
|
|
17
|
+
headers["Authorization"] = `Bearer ${ANON_KEY}`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const res = await fetch(`${API_BASE}/${path}`, {
|
|
21
|
+
method: "POST",
|
|
22
|
+
headers,
|
|
23
|
+
body: JSON.stringify(body),
|
|
24
|
+
});
|
|
25
|
+
if (!res.ok) {
|
|
26
|
+
const text = await res.text();
|
|
27
|
+
throw new Error(`ProofAI API error ${res.status}: ${text}`);
|
|
28
|
+
}
|
|
29
|
+
return res.json();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const server = new McpServer({
|
|
33
|
+
name: "proofai",
|
|
34
|
+
version: "1.0.0",
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// --- Tool: Log an AI decision (full pipeline) ---
|
|
38
|
+
server.tool(
|
|
39
|
+
"proofai_certify",
|
|
40
|
+
"Certify an AI decision with cryptographic proof. Runs the full ProofAI pipeline: compress → execute → analyze → sign → bundle → anchor → verify. Returns a blockchain-verified evidence bundle.",
|
|
41
|
+
{
|
|
42
|
+
prompt: z.string().describe("The AI prompt to certify"),
|
|
43
|
+
provider: z.enum(["anthropic", "openai", "gemini"]).default("anthropic").describe("AI provider"),
|
|
44
|
+
temperature: z.number().default(0.7).describe("Generation temperature"),
|
|
45
|
+
maxTokens: z.number().default(1024).describe("Max output tokens"),
|
|
46
|
+
},
|
|
47
|
+
async ({ prompt, provider, temperature, maxTokens }) => {
|
|
48
|
+
try {
|
|
49
|
+
// 1. Compress
|
|
50
|
+
const compressed = (await callAPI("compress", { prompt, options: { compressionLevel: "medium" } })) as { id: string; compressedDsl: string };
|
|
51
|
+
|
|
52
|
+
// 2. Execute
|
|
53
|
+
const execution = (await callAPI("execute", {
|
|
54
|
+
promptRef: compressed.id,
|
|
55
|
+
options: { provider, modelId: "auto", temperature, maxTokens },
|
|
56
|
+
})) as { id: string; output: string; metadata: { provider: string; model: string; tokens: { total: number } } };
|
|
57
|
+
|
|
58
|
+
// 3. Analyze
|
|
59
|
+
const analysis = (await callAPI("analyze", {
|
|
60
|
+
executionId: execution.id,
|
|
61
|
+
analysisText: execution.output,
|
|
62
|
+
})) as { id: string; cognitiveHash: string; metrics: { nodeCount: number; consistencyScore: number } };
|
|
63
|
+
|
|
64
|
+
// 4. Sign
|
|
65
|
+
const signature = (await callAPI("sign", {
|
|
66
|
+
executionId: execution.id,
|
|
67
|
+
rawOutput: execution.output,
|
|
68
|
+
modelProvider: execution.metadata.provider,
|
|
69
|
+
modelId: execution.metadata.model,
|
|
70
|
+
modelVersion: "latest",
|
|
71
|
+
modelParameters: { temperature },
|
|
72
|
+
executionMetrics: { tokens: execution.metadata.tokens.total },
|
|
73
|
+
requesterInfo: { source: "mcp-server" },
|
|
74
|
+
timestamps: { request_received: new Date().toISOString() },
|
|
75
|
+
})) as { signatureId: string };
|
|
76
|
+
|
|
77
|
+
// 5. Bundle
|
|
78
|
+
const bundle = (await callAPI("bundle", {
|
|
79
|
+
promptId: compressed.id,
|
|
80
|
+
executionId: execution.id,
|
|
81
|
+
analysisId: analysis.id,
|
|
82
|
+
signatureId: signature.signatureId,
|
|
83
|
+
cognitiveHash: analysis.cognitiveHash,
|
|
84
|
+
promptContent: prompt,
|
|
85
|
+
aiResponse: execution.output,
|
|
86
|
+
})) as { id: string; bundleHash: string };
|
|
87
|
+
|
|
88
|
+
// 6. Anchor
|
|
89
|
+
let anchor: { transactionHash?: string; explorerUrl?: string; status?: string } = {};
|
|
90
|
+
try {
|
|
91
|
+
anchor = (await callAPI("anchor", { bundleId: bundle.id, network: "polygon" })) as typeof anchor;
|
|
92
|
+
} catch { /* anchor is best-effort */ }
|
|
93
|
+
|
|
94
|
+
// 7. Verify
|
|
95
|
+
const verification = (await callAPI("verify", { bundleId: bundle.id })) as { verified: boolean };
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
content: [
|
|
99
|
+
{
|
|
100
|
+
type: "text" as const,
|
|
101
|
+
text: JSON.stringify({
|
|
102
|
+
status: "certified",
|
|
103
|
+
bundleId: bundle.id,
|
|
104
|
+
bundleHash: bundle.bundleHash,
|
|
105
|
+
verified: verification.verified,
|
|
106
|
+
explorerUrl: anchor.explorerUrl || null,
|
|
107
|
+
transactionHash: anchor.transactionHash || null,
|
|
108
|
+
provider: execution.metadata.provider,
|
|
109
|
+
model: execution.metadata.model,
|
|
110
|
+
tokens: execution.metadata.tokens.total,
|
|
111
|
+
cognitiveNodes: analysis.metrics.nodeCount,
|
|
112
|
+
consistencyScore: analysis.metrics.consistencyScore,
|
|
113
|
+
aiResponse: execution.output.substring(0, 500) + (execution.output.length > 500 ? "..." : ""),
|
|
114
|
+
}, null, 2),
|
|
115
|
+
},
|
|
116
|
+
],
|
|
117
|
+
};
|
|
118
|
+
} catch (err) {
|
|
119
|
+
return {
|
|
120
|
+
content: [{ type: "text" as const, text: `Error: ${(err as Error).message}` }],
|
|
121
|
+
isError: true,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
// --- Tool: Log a decision (just record, don't execute AI) ---
|
|
128
|
+
server.tool(
|
|
129
|
+
"proofai_log",
|
|
130
|
+
"Log an AI decision that already happened. Creates an evidence bundle with the prompt and response you provide, signs it with Ed25519, and anchors to Polygon.",
|
|
131
|
+
{
|
|
132
|
+
prompt: z.string().describe("The original prompt"),
|
|
133
|
+
response: z.string().describe("The AI response to log"),
|
|
134
|
+
provider: z.string().default("unknown").describe("Which AI provider generated the response"),
|
|
135
|
+
model: z.string().default("unknown").describe("Which model was used"),
|
|
136
|
+
},
|
|
137
|
+
async ({ prompt, response, provider, model }) => {
|
|
138
|
+
try {
|
|
139
|
+
// Compress
|
|
140
|
+
const compressed = (await callAPI("compress", { prompt, options: {} })) as { id: string };
|
|
141
|
+
|
|
142
|
+
// Analyze
|
|
143
|
+
const analysis = (await callAPI("analyze", {
|
|
144
|
+
executionId: `ext_${Date.now()}`,
|
|
145
|
+
analysisText: response,
|
|
146
|
+
})) as { id: string; cognitiveHash: string };
|
|
147
|
+
|
|
148
|
+
// Sign
|
|
149
|
+
const signature = (await callAPI("sign", {
|
|
150
|
+
executionId: `ext_${Date.now()}`,
|
|
151
|
+
rawOutput: response,
|
|
152
|
+
modelProvider: provider,
|
|
153
|
+
modelId: model,
|
|
154
|
+
modelVersion: "external",
|
|
155
|
+
modelParameters: {},
|
|
156
|
+
executionMetrics: {},
|
|
157
|
+
requesterInfo: { source: "mcp-server-log" },
|
|
158
|
+
timestamps: { logged_at: new Date().toISOString() },
|
|
159
|
+
})) as { signatureId: string };
|
|
160
|
+
|
|
161
|
+
// Bundle
|
|
162
|
+
const bundle = (await callAPI("bundle", {
|
|
163
|
+
promptId: compressed.id,
|
|
164
|
+
executionId: `ext_${Date.now()}`,
|
|
165
|
+
analysisId: analysis.id,
|
|
166
|
+
signatureId: signature.signatureId,
|
|
167
|
+
cognitiveHash: analysis.cognitiveHash,
|
|
168
|
+
promptContent: prompt,
|
|
169
|
+
aiResponse: response,
|
|
170
|
+
provider,
|
|
171
|
+
model,
|
|
172
|
+
})) as { id: string; bundleHash: string };
|
|
173
|
+
|
|
174
|
+
// Anchor
|
|
175
|
+
let explorerUrl: string | null = null;
|
|
176
|
+
try {
|
|
177
|
+
const anchor = (await callAPI("anchor", { bundleId: bundle.id, network: "polygon" })) as { explorerUrl: string };
|
|
178
|
+
explorerUrl = anchor.explorerUrl;
|
|
179
|
+
} catch { /* best-effort */ }
|
|
180
|
+
|
|
181
|
+
return {
|
|
182
|
+
content: [{
|
|
183
|
+
type: "text" as const,
|
|
184
|
+
text: `Logged and certified.\n\nBundle ID: ${bundle.id}\nBundle Hash: ${bundle.bundleHash}\nPolygonscan: ${explorerUrl || "pending"}\n\nThis decision is now tamper-evident and blockchain-anchored.`,
|
|
185
|
+
}],
|
|
186
|
+
};
|
|
187
|
+
} catch (err) {
|
|
188
|
+
return {
|
|
189
|
+
content: [{ type: "text" as const, text: `Error: ${(err as Error).message}` }],
|
|
190
|
+
isError: true,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
// --- Tool: Verify a bundle ---
|
|
197
|
+
server.tool(
|
|
198
|
+
"proofai_verify",
|
|
199
|
+
"Verify an evidence bundle's integrity and blockchain anchoring. Returns compliance checks against EU AI Act articles.",
|
|
200
|
+
{
|
|
201
|
+
bundleId: z.string().describe("The bundle ID to verify (e.g., bnd_xxx)"),
|
|
202
|
+
},
|
|
203
|
+
async ({ bundleId }) => {
|
|
204
|
+
try {
|
|
205
|
+
const result = (await callAPI("verify", { bundleId })) as {
|
|
206
|
+
bundleId: string;
|
|
207
|
+
verified: boolean;
|
|
208
|
+
checks: Record<string, boolean>;
|
|
209
|
+
ledgerInfo?: { transactionHash: string; blockNumber: number; network: string };
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
const checksText = Object.entries(result.checks)
|
|
213
|
+
.map(([k, v]) => ` ${v ? "✅" : "❌"} ${k}`)
|
|
214
|
+
.join("\n");
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
content: [{
|
|
218
|
+
type: "text" as const,
|
|
219
|
+
text: `Bundle: ${result.bundleId}\nVerified: ${result.verified ? "✅ YES" : "❌ NO"}\n\nChecks:\n${checksText}${
|
|
220
|
+
result.ledgerInfo
|
|
221
|
+
? `\n\nBlockchain:\n Network: ${result.ledgerInfo.network}\n Block: #${result.ledgerInfo.blockNumber}\n Tx: ${result.ledgerInfo.transactionHash}`
|
|
222
|
+
: ""
|
|
223
|
+
}`,
|
|
224
|
+
}],
|
|
225
|
+
};
|
|
226
|
+
} catch (err) {
|
|
227
|
+
return {
|
|
228
|
+
content: [{ type: "text" as const, text: `Error: ${(err as Error).message}` }],
|
|
229
|
+
isError: true,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
// --- Tool: Get Polygonscan link ---
|
|
236
|
+
server.tool(
|
|
237
|
+
"proofai_polygonscan",
|
|
238
|
+
"Get the Polygonscan verification URL for a transaction hash. Anyone can verify the proof independently.",
|
|
239
|
+
{
|
|
240
|
+
txHash: z.string().describe("The Polygon transaction hash (0x...)"),
|
|
241
|
+
},
|
|
242
|
+
async ({ txHash }) => {
|
|
243
|
+
const url = `https://amoy.polygonscan.com/tx/${txHash}`;
|
|
244
|
+
return {
|
|
245
|
+
content: [{
|
|
246
|
+
type: "text" as const,
|
|
247
|
+
text: `Polygonscan verification URL:\n${url}\n\nAnyone can verify this proof — no account, no login, no middleman. Just math.`,
|
|
248
|
+
}],
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
// --- Tool: Get monitoring stats ---
|
|
254
|
+
server.tool(
|
|
255
|
+
"proofai_monitor",
|
|
256
|
+
"Get post-market monitoring statistics for AI compliance (EU AI Act Article 72). Shows anomalies, risk distribution, and compliance status.",
|
|
257
|
+
{},
|
|
258
|
+
async () => {
|
|
259
|
+
try {
|
|
260
|
+
const stats = await callAPI("monitor", {});
|
|
261
|
+
return {
|
|
262
|
+
content: [{
|
|
263
|
+
type: "text" as const,
|
|
264
|
+
text: JSON.stringify(stats, null, 2),
|
|
265
|
+
}],
|
|
266
|
+
};
|
|
267
|
+
} catch (err) {
|
|
268
|
+
return {
|
|
269
|
+
content: [{ type: "text" as const, text: `Error: ${(err as Error).message}` }],
|
|
270
|
+
isError: true,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
// Start server
|
|
277
|
+
async function main() {
|
|
278
|
+
const transport = new StdioServerTransport();
|
|
279
|
+
await server.connect(transport);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
main().catch(console.error);
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"skipLibCheck": true,
|
|
7
|
+
"declaration": true,
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"outDir": "dist",
|
|
11
|
+
"rootDir": "src"
|
|
12
|
+
},
|
|
13
|
+
"include": ["src"]
|
|
14
|
+
}
|