athena-mcp 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.
Files changed (37) hide show
  1. package/README.md +477 -0
  2. package/install.js +327 -0
  3. package/mcp/servers.json +100 -0
  4. package/mcp/tools/README.md +64 -0
  5. package/mcp/tools/__init__.py +1 -0
  6. package/mcp/tools/aderyn_runner.py +226 -0
  7. package/mcp/tools/eas_attest.py +404 -0
  8. package/mcp/tools/evidence_chain.py +363 -0
  9. package/mcp/tools/exploit_simulator.py +545 -0
  10. package/mcp/tools/fuzz_runner.py +440 -0
  11. package/mcp/tools/gev_analyzer.py +362 -0
  12. package/mcp/tools/halmos_runner.py +408 -0
  13. package/mcp/tools/incremental_auditor.py +441 -0
  14. package/mcp/tools/knowledge_base.py +378 -0
  15. package/mcp/tools/poc_generator.py +479 -0
  16. package/mcp/tools/protocol_scanner.py +456 -0
  17. package/mcp/tools/repair_validator.py +421 -0
  18. package/mcp/tools/slither_runner.py +221 -0
  19. package/package.json +52 -0
  20. package/requirements.txt +20 -0
  21. package/skills/glm-audit-skill/SKILL.md +73 -0
  22. package/skills/glm-audit-skill/references/audit-agents/access-control-agent.md +42 -0
  23. package/skills/glm-audit-skill/references/audit-agents/asymmetry-agent.md +42 -0
  24. package/skills/glm-audit-skill/references/audit-agents/boundary-agent.md +42 -0
  25. package/skills/glm-audit-skill/references/audit-agents/economic-security-agent.md +42 -0
  26. package/skills/glm-audit-skill/references/audit-agents/execution-trace-agent.md +42 -0
  27. package/skills/glm-audit-skill/references/audit-agents/first-principles-agent.md +42 -0
  28. package/skills/glm-audit-skill/references/audit-agents/flow-gap-agent.md +38 -0
  29. package/skills/glm-audit-skill/references/audit-agents/invariant-agent.md +37 -0
  30. package/skills/glm-audit-skill/references/audit-agents/math-precision-agent.md +37 -0
  31. package/skills/glm-audit-skill/references/audit-agents/numerical-gap-agent.md +37 -0
  32. package/skills/glm-audit-skill/references/audit-agents/periphery-agent.md +37 -0
  33. package/skills/glm-audit-skill/references/audit-agents/shared-rules.md +37 -0
  34. package/skills/glm-audit-skill/references/audit-agents/trust-gap-agent.md +39 -0
  35. package/skills/glm-audit-skill/references/judging.md +45 -0
  36. package/skills/glm-audit-skill/references/report-formatting.md +22 -0
  37. package/skills/glm-audit-skill/references/senior-auditor-sop.md +34 -0
@@ -0,0 +1,363 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ MCP Server: Audit Evidence Chain
4
+ Manages Merkle-based audit evidence chain for verifiable audit steps.
5
+ """
6
+ import sys
7
+ import json
8
+ import asyncio
9
+ import hashlib
10
+ import logging
11
+
12
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
13
+ logger = logging.getLogger("evidence_chain")
14
+
15
+ TOOL_NAME = "evidence_chain"
16
+ TOOL_VERSION = "1.0.0"
17
+
18
+
19
+ def build_tool_definitions() -> list:
20
+ return [
21
+ {
22
+ "name": "create_audit_chain",
23
+ "description": "Create a new audit evidence chain for tracking audit steps",
24
+ "inputSchema": {
25
+ "type": "object",
26
+ "properties": {
27
+ "audit_id": {"type": "string", "description": "Unique audit identifier"},
28
+ "contract_name": {"type": "string", "description": "Name of contract being audited"},
29
+ "auditor": {"type": "string", "description": "Auditor address or identifier"}
30
+ },
31
+ "required": ["audit_id", "contract_name"]
32
+ }
33
+ },
34
+ {
35
+ "name": "record_audit_step",
36
+ "description": "Record a step in the audit evidence chain",
37
+ "inputSchema": {
38
+ "type": "object",
39
+ "properties": {
40
+ "audit_id": {"type": "string", "description": "Audit identifier"},
41
+ "step_number": {"type": "integer", "enum": [1,2,3,4,5,6,7,8], "description": "Step number (1-8)"},
42
+ "step_name": {"type": "string", "description": "Step name (scope, slither, aderyn, poc, fuzz, fix, report, certificate)"},
43
+ "input_data": {"type": "string", "description": "Input data (will be hashed)"},
44
+ "output_data": {"type": "string", "description": "Output data (will be hashed)"}
45
+ },
46
+ "required": ["audit_id", "step_number", "step_name", "input_data", "output_data"]
47
+ }
48
+ },
49
+ {
50
+ "name": "calculate_merkle_root",
51
+ "description": "Calculate Merkle root from all recorded steps",
52
+ "inputSchema": {
53
+ "type": "object",
54
+ "properties": {
55
+ "audit_id": {"type": "string", "description": "Audit identifier"}
56
+ },
57
+ "required": ["audit_id"]
58
+ }
59
+ },
60
+ {
61
+ "name": "verify_step",
62
+ "description": "Verify a step is in the audit evidence chain",
63
+ "inputSchema": {
64
+ "type": "object",
65
+ "properties": {
66
+ "audit_id": {"type": "string", "description": "Audit identifier"},
67
+ "step_number": {"type": "integer", "description": "Step number to verify"},
68
+ "proof": {"type": "array", "items": {"type": "string"}, "description": "Merkle proof (hex strings)"}
69
+ },
70
+ "required": ["audit_id", "step_number", "proof"]
71
+ }
72
+ },
73
+ {
74
+ "name": "get_audit_summary",
75
+ "description": "Get summary of all steps in an audit chain",
76
+ "inputSchema": {
77
+ "type": "object",
78
+ "properties": {
79
+ "audit_id": {"type": "string", "description": "Audit identifier"}
80
+ },
81
+ "required": ["audit_id"]
82
+ }
83
+ }
84
+ ]
85
+
86
+
87
+ # In-memory storage (in production, use database or on-chain)
88
+ audit_chains = {}
89
+
90
+
91
+ async def create_audit_chain(audit_id: str, contract_name: str, auditor: str = None) -> dict:
92
+ """Create a new audit evidence chain."""
93
+ if audit_id in audit_chains:
94
+ return {"success": False, "error": "Audit ID already exists"}
95
+
96
+ audit_chains[audit_id] = {
97
+ "audit_id": audit_id,
98
+ "contract_name": contract_name,
99
+ "auditor": auditor or "unknown",
100
+ "steps": {},
101
+ "merkle_root": None,
102
+ "created_at": asyncio.get_event_loop().time()
103
+ }
104
+
105
+ return {
106
+ "success": True,
107
+ "audit_id": audit_id,
108
+ "contract_name": contract_name,
109
+ "message": "Audit chain created"
110
+ }
111
+
112
+
113
+ async def record_audit_step(audit_id: str, step_number: int, step_name: str, input_data: str, output_data: str) -> dict:
114
+ """Record a step in the audit evidence chain."""
115
+ if audit_id not in audit_chains:
116
+ return {"success": False, "error": "Audit ID not found"}
117
+
118
+ if step_number < 1 or step_number > 8:
119
+ return {"success": False, "error": "Step number must be 1-8"}
120
+
121
+ # Calculate hashes
122
+ input_hash = hashlib.sha256(input_data.encode()).hexdigest()
123
+ output_hash = hashlib.sha256(output_data.encode()).hexdigest()
124
+
125
+ # Store step
126
+ audit_chains[audit_id]["steps"][step_number] = {
127
+ "step_number": step_number,
128
+ "step_name": step_name,
129
+ "input_hash": input_hash,
130
+ "output_hash": output_hash,
131
+ "timestamp": asyncio.get_event_loop().time()
132
+ }
133
+
134
+ return {
135
+ "success": True,
136
+ "audit_id": audit_id,
137
+ "step_number": step_number,
138
+ "step_name": step_name,
139
+ "input_hash": input_hash,
140
+ "output_hash": output_hash,
141
+ "total_steps": len(audit_chains[audit_id]["steps"])
142
+ }
143
+
144
+
145
+ async def calculate_merkle_root(audit_id: str) -> dict:
146
+ """Calculate Merkle root from all recorded steps."""
147
+ if audit_id not in audit_chains:
148
+ return {"success": False, "error": "Audit ID not found"}
149
+
150
+ chain = audit_chains[audit_id]
151
+ steps = chain["steps"]
152
+
153
+ if not steps:
154
+ return {"success": False, "error": "No steps recorded"}
155
+
156
+ # Create leaf hashes
157
+ leaves = []
158
+ for step_num in sorted(steps.keys()):
159
+ step = steps[step_num]
160
+ leaf_data = f"{audit_id}:{step['step_number']}:{step['step_name']}:{step['input_hash']}:{step['output_hash']}:{step['timestamp']}"
161
+ leaf_hash = hashlib.sha256(leaf_data.encode()).hexdigest()
162
+ leaves.append(leaf_hash)
163
+
164
+ # Calculate Merkle root
165
+ merkle_root = _calculate_merkle_root(leaves)
166
+ chain["merkle_root"] = merkle_root
167
+
168
+ return {
169
+ "success": True,
170
+ "audit_id": audit_id,
171
+ "merkle_root": merkle_root,
172
+ "step_count": len(leaves),
173
+ "leaves": leaves
174
+ }
175
+
176
+
177
+ def _calculate_merkle_root(leaves: list) -> str:
178
+ """Calculate Merkle root from list of leaf hashes."""
179
+ if not leaves:
180
+ return None
181
+ if len(leaves) == 1:
182
+ return leaves[0]
183
+
184
+ # Build tree
185
+ current_level = leaves
186
+ while len(current_level) > 1:
187
+ next_level = []
188
+ for i in range(0, len(current_level), 2):
189
+ if i + 1 < len(current_level):
190
+ # Hash pair
191
+ pair = current_level[i] + current_level[i + 1]
192
+ next_level.append(hashlib.sha256(pair.encode()).hexdigest())
193
+ else:
194
+ # Odd leaf, promote
195
+ next_level.append(current_level[i])
196
+ current_level = next_level
197
+
198
+ return current_level[0]
199
+
200
+
201
+ async def verify_step(audit_id: str, step_number: int, proof: list) -> dict:
202
+ """Verify a step is in the audit evidence chain."""
203
+ if audit_id not in audit_chains:
204
+ return {"success": False, "error": "Audit ID not found"}
205
+
206
+ chain = audit_chains[audit_id]
207
+
208
+ if step_number not in chain["steps"]:
209
+ return {"success": False, "error": "Step not found"}
210
+
211
+ if not chain["merkle_root"]:
212
+ return {"success": False, "error": "Merkle root not calculated"}
213
+
214
+ # Calculate leaf hash
215
+ step = chain["steps"][step_number]
216
+ leaf_data = f"{audit_id}:{step['step_number']}:{step['step_name']}:{step['input_hash']}:{step['output_hash']}:{step['timestamp']}"
217
+ leaf_hash = hashlib.sha256(leaf_data.encode()).hexdigest()
218
+
219
+ # Verify proof
220
+ computed_hash = leaf_hash
221
+ for proof_element in proof:
222
+ # Sort hashes for consistent ordering
223
+ if computed_hash < proof_element:
224
+ combined = computed_hash + proof_element
225
+ else:
226
+ combined = proof_element + computed_hash
227
+ computed_hash = hashlib.sha256(combined.encode()).hexdigest()
228
+
229
+ is_valid = computed_hash == chain["merkle_root"]
230
+
231
+ return {
232
+ "success": True,
233
+ "audit_id": audit_id,
234
+ "step_number": step_number,
235
+ "is_valid": is_valid,
236
+ "computed_root": computed_hash,
237
+ "expected_root": chain["merkle_root"]
238
+ }
239
+
240
+
241
+ async def get_audit_summary(audit_id: str) -> dict:
242
+ """Get summary of all steps in an audit chain."""
243
+ if audit_id not in audit_chains:
244
+ return {"success": False, "error": "Audit ID not found"}
245
+
246
+ chain = audit_chains[audit_id]
247
+
248
+ steps_summary = []
249
+ for step_num in sorted(chain["steps"].keys()):
250
+ step = chain["steps"][step_num]
251
+ steps_summary.append({
252
+ "step_number": step["step_number"],
253
+ "step_name": step["step_name"],
254
+ "input_hash": step["input_hash"][:16] + "...",
255
+ "output_hash": step["output_hash"][:16] + "..."
256
+ })
257
+
258
+ return {
259
+ "success": True,
260
+ "audit_id": audit_id,
261
+ "contract_name": chain["contract_name"],
262
+ "auditor": chain["auditor"],
263
+ "total_steps": len(steps_summary),
264
+ "merkle_root": chain["merkle_root"],
265
+ "steps": steps_summary
266
+ }
267
+
268
+
269
+ async def execute_tool(tool_name: str, arguments: dict) -> dict:
270
+ if tool_name == "create_audit_chain":
271
+ return await create_audit_chain(
272
+ arguments.get("audit_id", ""),
273
+ arguments.get("contract_name", ""),
274
+ arguments.get("auditor")
275
+ )
276
+ elif tool_name == "record_audit_step":
277
+ return await record_audit_step(
278
+ arguments.get("audit_id", ""),
279
+ arguments.get("step_number", 0),
280
+ arguments.get("step_name", ""),
281
+ arguments.get("input_data", ""),
282
+ arguments.get("output_data", "")
283
+ )
284
+ elif tool_name == "calculate_merkle_root":
285
+ return await calculate_merkle_root(arguments.get("audit_id", ""))
286
+ elif tool_name == "verify_step":
287
+ return await verify_step(
288
+ arguments.get("audit_id", ""),
289
+ arguments.get("step_number", 0),
290
+ arguments.get("proof", [])
291
+ )
292
+ elif tool_name == "get_audit_summary":
293
+ return await get_audit_summary(arguments.get("audit_id", ""))
294
+ return {"success": False, "error": f"Unknown tool: {tool_name}"}
295
+
296
+
297
+ async def handle_request(request: dict) -> dict:
298
+ method = request.get("method")
299
+ params = request.get("params", {})
300
+
301
+ if method == "initialize":
302
+ return {
303
+ "protocolVersion": "2024-11-05",
304
+ "capabilities": {"tools": {}},
305
+ "serverInfo": {"name": TOOL_NAME, "version": TOOL_VERSION}
306
+ }
307
+ elif method == "tools/list":
308
+ return {"tools": build_tool_definitions()}
309
+ elif method == "tools/call":
310
+ tool_name = params.get("name")
311
+ arguments = params.get("arguments", {})
312
+ result = await execute_tool(tool_name, arguments)
313
+ return {"content": [{"type": "text", "text": json.dumps(result, indent=2)}]}
314
+ elif method == "ping":
315
+ return {}
316
+ return {"error": {"code": -32601, "message": f"Method not found: {method}"}}
317
+
318
+
319
+ async def main():
320
+ reader = asyncio.StreamReader()
321
+ protocol = asyncio.StreamReaderProtocol(reader)
322
+ loop = asyncio.get_event_loop()
323
+ await loop.connect_read_pipe(lambda: protocol, sys.stdin)
324
+
325
+ logger.info(f"{TOOL_NAME} MCP server started")
326
+
327
+ while True:
328
+ line = await reader.readline()
329
+ if not line:
330
+ break
331
+
332
+ line_str = line.decode("utf-8").strip()
333
+ if not line_str:
334
+ continue
335
+
336
+ try:
337
+ request = json.loads(line_str)
338
+ response = await handle_request(request)
339
+ response["jsonrpc"] = "2.0"
340
+ response["id"] = request.get("id")
341
+ sys.stdout.write(json.dumps(response) + "\n")
342
+ sys.stdout.flush()
343
+ except json.JSONDecodeError:
344
+ error_resp = {
345
+ "jsonrpc": "2.0",
346
+ "id": None,
347
+ "error": {"code": -32700, "message": "Parse error"}
348
+ }
349
+ sys.stdout.write(json.dumps(error_resp) + "\n")
350
+ sys.stdout.flush()
351
+ except Exception as e:
352
+ logger.exception("Error handling request")
353
+ error_resp = {
354
+ "jsonrpc": "2.0",
355
+ "id": request.get("id") if "request" in dir() else None,
356
+ "error": {"code": -32603, "message": f"Internal error: {str(e)}"}
357
+ }
358
+ sys.stdout.write(json.dumps(error_resp) + "\n")
359
+ sys.stdout.flush()
360
+
361
+
362
+ if __name__ == "__main__":
363
+ asyncio.run(main())