hedera-mcp-platform 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 +45 -0
- package/package.json +26 -0
- package/server.json +47 -0
- package/src/index.js +87 -0
- package/src/modules/compliance/tools.js +220 -0
- package/src/modules/hcs/hedera.js +29 -0
- package/src/modules/hcs/intelligence.js +71 -0
- package/src/modules/hcs/tools.js +102 -0
- package/src/payments.js +49 -0
- package/src/server.js +65 -0
package/README.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Hedera MCP Platform
|
|
2
|
+
|
|
3
|
+
Complete Hedera ecosystem intelligence platform for AI agents. 8 MCP servers, 24 tools covering HCS, compliance, governance, tokens, identity, contracts, NFTs and cross-chain bridges. Pay per call in HBAR.
|
|
4
|
+
|
|
5
|
+
## Live Endpoint
|
|
6
|
+
|
|
7
|
+
https://hedera-mcp-platform-production.up.railway.app/mcp
|
|
8
|
+
|
|
9
|
+
## Available Tools
|
|
10
|
+
|
|
11
|
+
### Module 1 � HCS Topic Intelligence
|
|
12
|
+
|
|
13
|
+
| Tool | Description | Cost |
|
|
14
|
+
|------|-------------|------|
|
|
15
|
+
| hcs_monitor | Topic metadata and recent activity | Free |
|
|
16
|
+
| hcs_query | Natural language query with AI analysis | 0.05 HBAR |
|
|
17
|
+
| hcs_understand | Deep pattern analysis and anomaly detection | 0.50 HBAR |
|
|
18
|
+
|
|
19
|
+
### Module 2 � Compliance & Audit Trail
|
|
20
|
+
|
|
21
|
+
| Tool | Description | Cost |
|
|
22
|
+
|------|-------------|------|
|
|
23
|
+
| hcs_write_record | Write tamper-evident compliance record | 2.00 HBAR |
|
|
24
|
+
| hcs_verify_record | Verify record integrity on-chain | 0.50 HBAR |
|
|
25
|
+
| hcs_audit_trail | Full chronological audit history | 1.00 HBAR |
|
|
26
|
+
|
|
27
|
+
## Setup
|
|
28
|
+
|
|
29
|
+
npm install
|
|
30
|
+
cp .env.example .env
|
|
31
|
+
npm start
|
|
32
|
+
|
|
33
|
+
## Environment Variables
|
|
34
|
+
|
|
35
|
+
| Variable | Description |
|
|
36
|
+
|----------|-------------|
|
|
37
|
+
| HEDERA_ACCOUNT_ID | Your Hedera account (0.0.XXXXXXX) |
|
|
38
|
+
| HEDERA_PRIVATE_KEY | ECDSA private key |
|
|
39
|
+
| HEDERA_NETWORK | testnet or mainnet |
|
|
40
|
+
| OPENAI_API_KEY | For GPT-4o Mini analysis |
|
|
41
|
+
|
|
42
|
+
## MCP Registry
|
|
43
|
+
|
|
44
|
+
Listed at: https://registry.modelcontextprotocol.io
|
|
45
|
+
npm: https://www.npmjs.com/package/hedera-mcp-platform
|
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "hedera-mcp-platform",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"mcpName": "io.github.mountainmystic/hedera-mcp-platform",
|
|
5
|
+
"description": "Complete Hedera ecosystem intelligence platform for AI agents. 8 MCP servers, 24 tools. Pay per call in HBAR.",
|
|
6
|
+
"main": "src/index.js",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": ">=20.0.0"
|
|
10
|
+
},
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "https://github.com/mountainmystic/hedera-mcp-platform.git"
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"start": "node src/index.js",
|
|
17
|
+
"dev": "node --watch src/index.js"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"@hashgraph/sdk": "^2.46.0",
|
|
21
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
22
|
+
"openai": "^4.56.0",
|
|
23
|
+
"dotenv": "^16.4.5",
|
|
24
|
+
"axios": "^1.7.7"
|
|
25
|
+
}
|
|
26
|
+
}
|
package/server.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
|
+
"name": "io.github.mountainmystic/hedera-mcp-platform",
|
|
4
|
+
"description": "Hedera ecosystem intelligence for AI agents. 6 tools, 2 modules. Pay per call in HBAR.",
|
|
5
|
+
"repository": {
|
|
6
|
+
"url": "https://github.com/mountainmystic/hedera-mcp-platform",
|
|
7
|
+
"source": "github"
|
|
8
|
+
},
|
|
9
|
+
"version": "1.0.0",
|
|
10
|
+
"packages": [
|
|
11
|
+
{
|
|
12
|
+
"registryType": "github",
|
|
13
|
+
"identifier": "mountainmystic/hedera-mcp-platform",
|
|
14
|
+
"version": "1.0.0",
|
|
15
|
+
"transport": {
|
|
16
|
+
"type": "streamable-http",
|
|
17
|
+
"url": "https://hedera-mcp-platform-production.up.railway.app/mcp"
|
|
18
|
+
},
|
|
19
|
+
"environmentVariables": [
|
|
20
|
+
{
|
|
21
|
+
"description": "Hedera account ID in format 0.0.XXXXXXX",
|
|
22
|
+
"isRequired": true,
|
|
23
|
+
"isSecret": false,
|
|
24
|
+
"name": "HEDERA_ACCOUNT_ID"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"description": "Hedera account private key (ECDSA hex format)",
|
|
28
|
+
"isRequired": true,
|
|
29
|
+
"isSecret": true,
|
|
30
|
+
"name": "HEDERA_PRIVATE_KEY"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"description": "Hedera network: testnet or mainnet",
|
|
34
|
+
"isRequired": true,
|
|
35
|
+
"isSecret": false,
|
|
36
|
+
"name": "HEDERA_NETWORK"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"description": "OpenAI API key for GPT-4o Mini powered analysis",
|
|
40
|
+
"isRequired": true,
|
|
41
|
+
"isSecret": true,
|
|
42
|
+
"name": "OPENAI_API_KEY"
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// index.js - Main entry point with stdio and Streamable HTTP transport
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
4
|
+
import http from "http";
|
|
5
|
+
import "dotenv/config";
|
|
6
|
+
import { createServer, ALL_TOOLS } from "./server.js";
|
|
7
|
+
import { getCosts } from "./payments.js";
|
|
8
|
+
|
|
9
|
+
function validateEnv() {
|
|
10
|
+
const required = ["HEDERA_ACCOUNT_ID", "HEDERA_PRIVATE_KEY", "OPENAI_API_KEY"];
|
|
11
|
+
const missing = required.filter((key) => !process.env[key]);
|
|
12
|
+
if (missing.length > 0) {
|
|
13
|
+
console.error("Missing env vars: " + missing.join(", "));
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function startHTTPServer() {
|
|
19
|
+
const port = process.env.PORT || 3000;
|
|
20
|
+
const httpServer = http.createServer(async (req, res) => {
|
|
21
|
+
const url = new URL(req.url, `http://localhost:${port}`);
|
|
22
|
+
|
|
23
|
+
if (req.method === "GET" && (url.pathname === "/" || url.pathname === "/health")) {
|
|
24
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
25
|
+
res.end(JSON.stringify({
|
|
26
|
+
status: "ok",
|
|
27
|
+
service: "Hedera MCP Platform",
|
|
28
|
+
version: "1.0.0",
|
|
29
|
+
network: process.env.HEDERA_NETWORK,
|
|
30
|
+
account: process.env.HEDERA_ACCOUNT_ID,
|
|
31
|
+
modules: ["hcs", "compliance"],
|
|
32
|
+
tools: ALL_TOOLS.map((t) => t.name),
|
|
33
|
+
costs: getCosts(),
|
|
34
|
+
mcp_endpoint: "/mcp",
|
|
35
|
+
timestamp: new Date().toISOString(),
|
|
36
|
+
}));
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (url.pathname === "/mcp") {
|
|
41
|
+
const server = createServer();
|
|
42
|
+
const transport = new StreamableHTTPServerTransport({
|
|
43
|
+
sessionIdGenerator: undefined,
|
|
44
|
+
});
|
|
45
|
+
res.on("close", () => {
|
|
46
|
+
transport.close();
|
|
47
|
+
server.close();
|
|
48
|
+
});
|
|
49
|
+
await server.connect(transport);
|
|
50
|
+
await transport.handleRequest(req, res);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
55
|
+
res.end(JSON.stringify({ error: "Not found", mcp_endpoint: "/mcp" }));
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
httpServer.listen(port, () => {
|
|
59
|
+
console.error("HTTP server on port " + port);
|
|
60
|
+
console.error("Health: http://localhost:" + port + "/");
|
|
61
|
+
console.error("MCP: http://localhost:" + port + "/mcp");
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async function main() {
|
|
66
|
+
validateEnv();
|
|
67
|
+
const isStdio = !process.env.PORT && process.stdin.isTTY === false;
|
|
68
|
+
|
|
69
|
+
if (isStdio) {
|
|
70
|
+
const server = createServer();
|
|
71
|
+
const transport = new StdioServerTransport();
|
|
72
|
+
await server.connect(transport);
|
|
73
|
+
console.error("Hedera MCP Platform running (stdio)");
|
|
74
|
+
console.error("Network: " + process.env.HEDERA_NETWORK);
|
|
75
|
+
console.error("Tools: " + ALL_TOOLS.map(t => t.name).join(", "));
|
|
76
|
+
} else {
|
|
77
|
+
startHTTPServer();
|
|
78
|
+
console.error("Hedera MCP Platform running (HTTP)");
|
|
79
|
+
console.error("Network: " + process.env.HEDERA_NETWORK);
|
|
80
|
+
console.error("Tools: " + ALL_TOOLS.map(t => t.name).join(", "));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
main().catch((err) => {
|
|
85
|
+
console.error("Fatal error:", err);
|
|
86
|
+
process.exit(1);
|
|
87
|
+
});
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
// compliance/tools.js - Compliance & Audit Trail tool definitions and handlers
|
|
2
|
+
import {
|
|
3
|
+
Client,
|
|
4
|
+
AccountId,
|
|
5
|
+
PrivateKey,
|
|
6
|
+
TopicMessageSubmitTransaction,
|
|
7
|
+
} from "@hashgraph/sdk";
|
|
8
|
+
import axios from "axios";
|
|
9
|
+
import crypto from "crypto";
|
|
10
|
+
import { chargeForTool } from "../../payments.js";
|
|
11
|
+
|
|
12
|
+
let hederaClient;
|
|
13
|
+
|
|
14
|
+
function getClient() {
|
|
15
|
+
if (!hederaClient) {
|
|
16
|
+
const network = process.env.HEDERA_NETWORK || "testnet";
|
|
17
|
+
hederaClient = network === "mainnet" ? Client.forMainnet() : Client.forTestnet();
|
|
18
|
+
hederaClient.setOperator(
|
|
19
|
+
AccountId.fromString(process.env.HEDERA_ACCOUNT_ID),
|
|
20
|
+
PrivateKey.fromStringECDSA(process.env.HEDERA_PRIVATE_KEY)
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
return hederaClient;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function getMirrorNodeBase() {
|
|
27
|
+
return process.env.HEDERA_NETWORK === "mainnet"
|
|
28
|
+
? "https://mainnet-public.mirrornode.hedera.com"
|
|
29
|
+
: "https://testnet.mirrornode.hedera.com";
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const COMPLIANCE_TOOL_DEFINITIONS = [
|
|
33
|
+
{
|
|
34
|
+
name: "hcs_write_record",
|
|
35
|
+
description: "Write a tamper-evident compliance record to the Hedera blockchain. Returns a record ID and transaction proof. Costs 2 HBAR.",
|
|
36
|
+
inputSchema: {
|
|
37
|
+
type: "object",
|
|
38
|
+
properties: {
|
|
39
|
+
topic_id: { type: "string", description: "HCS topic ID to write the record to" },
|
|
40
|
+
record_type: { type: "string", description: "Type of compliance record (e.g. transaction, approval, audit_event)" },
|
|
41
|
+
entity_id: { type: "string", description: "ID of the entity this record relates to" },
|
|
42
|
+
data: { type: "object", description: "The compliance data to record (any JSON object)" },
|
|
43
|
+
api_key: { type: "string", description: "Your AgentLens API key" },
|
|
44
|
+
},
|
|
45
|
+
required: ["topic_id", "record_type", "entity_id", "data", "api_key"],
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: "hcs_verify_record",
|
|
50
|
+
description: "Verify a compliance record exists on the Hedera blockchain and has not been tampered with. Costs 0.5 HBAR.",
|
|
51
|
+
inputSchema: {
|
|
52
|
+
type: "object",
|
|
53
|
+
properties: {
|
|
54
|
+
topic_id: { type: "string", description: "HCS topic ID where the record was written" },
|
|
55
|
+
record_id: { type: "string", description: "Record ID returned when the record was written" },
|
|
56
|
+
api_key: { type: "string", description: "Your AgentLens API key" },
|
|
57
|
+
},
|
|
58
|
+
required: ["topic_id", "record_id", "api_key"],
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: "hcs_audit_trail",
|
|
63
|
+
description: "Retrieve the full chronological audit trail for an entity from the Hedera blockchain. Costs 1 HBAR.",
|
|
64
|
+
inputSchema: {
|
|
65
|
+
type: "object",
|
|
66
|
+
properties: {
|
|
67
|
+
topic_id: { type: "string", description: "HCS topic ID to query" },
|
|
68
|
+
entity_id: { type: "string", description: "Entity ID to retrieve audit trail for" },
|
|
69
|
+
limit: { type: "number", description: "Max records to retrieve (default 50)" },
|
|
70
|
+
api_key: { type: "string", description: "Your AgentLens API key" },
|
|
71
|
+
},
|
|
72
|
+
required: ["topic_id", "entity_id", "api_key"],
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
export async function executeComplianceTool(name, args) {
|
|
78
|
+
if (name === "hcs_write_record") {
|
|
79
|
+
const payment = chargeForTool("hcs_write_record", args.api_key);
|
|
80
|
+
const client = getClient();
|
|
81
|
+
|
|
82
|
+
const record = {
|
|
83
|
+
record_id: crypto.randomUUID(),
|
|
84
|
+
record_type: args.record_type,
|
|
85
|
+
entity_id: args.entity_id,
|
|
86
|
+
data: args.data,
|
|
87
|
+
written_at: new Date().toISOString(),
|
|
88
|
+
written_by: process.env.HEDERA_ACCOUNT_ID,
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const hash = crypto
|
|
92
|
+
.createHash("sha256")
|
|
93
|
+
.update(JSON.stringify(record))
|
|
94
|
+
.digest("hex");
|
|
95
|
+
|
|
96
|
+
record.hash = hash;
|
|
97
|
+
|
|
98
|
+
const tx = await new TopicMessageSubmitTransaction()
|
|
99
|
+
.setTopicId(args.topic_id)
|
|
100
|
+
.setMessage(JSON.stringify(record))
|
|
101
|
+
.execute(client);
|
|
102
|
+
|
|
103
|
+
const receipt = await tx.getReceipt(client);
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
success: true,
|
|
107
|
+
record_id: record.record_id,
|
|
108
|
+
topic_id: args.topic_id,
|
|
109
|
+
entity_id: args.entity_id,
|
|
110
|
+
record_type: args.record_type,
|
|
111
|
+
hash,
|
|
112
|
+
transaction_id: tx.transactionId.toString(),
|
|
113
|
+
written_at: record.written_at,
|
|
114
|
+
verification_note: "This record is permanently stored on the Hedera blockchain and cannot be altered.",
|
|
115
|
+
payment,
|
|
116
|
+
timestamp: new Date().toISOString(),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (name === "hcs_verify_record") {
|
|
121
|
+
const payment = chargeForTool("hcs_verify_record", args.api_key);
|
|
122
|
+
const base = getMirrorNodeBase();
|
|
123
|
+
|
|
124
|
+
const response = await axios.get(
|
|
125
|
+
`${base}/api/v1/topics/${args.topic_id}/messages?limit=100&order=asc`
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
const messages = response.data.messages || [];
|
|
129
|
+
let foundRecord = null;
|
|
130
|
+
|
|
131
|
+
for (const msg of messages) {
|
|
132
|
+
try {
|
|
133
|
+
const content = Buffer.from(msg.message, "base64").toString("utf-8");
|
|
134
|
+
const record = JSON.parse(content);
|
|
135
|
+
if (record.record_id === args.record_id) {
|
|
136
|
+
const { hash, ...recordWithoutHash } = record;
|
|
137
|
+
const computedHash = crypto
|
|
138
|
+
.createHash("sha256")
|
|
139
|
+
.update(JSON.stringify(recordWithoutHash))
|
|
140
|
+
.digest("hex");
|
|
141
|
+
|
|
142
|
+
foundRecord = {
|
|
143
|
+
record_id: record.record_id,
|
|
144
|
+
record_type: record.record_type,
|
|
145
|
+
entity_id: record.entity_id,
|
|
146
|
+
written_at: record.written_at,
|
|
147
|
+
consensus_timestamp: msg.consensus_timestamp,
|
|
148
|
+
hash_valid: computedHash === hash,
|
|
149
|
+
tampered: computedHash !== hash,
|
|
150
|
+
sequence_number: msg.sequence_number,
|
|
151
|
+
};
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
} catch (e) {
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (!foundRecord) {
|
|
160
|
+
return {
|
|
161
|
+
verified: false,
|
|
162
|
+
record_id: args.record_id,
|
|
163
|
+
error: "Record not found on blockchain",
|
|
164
|
+
payment,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
verified: true,
|
|
170
|
+
tampered: foundRecord.tampered,
|
|
171
|
+
hash_valid: foundRecord.hash_valid,
|
|
172
|
+
...foundRecord,
|
|
173
|
+
payment,
|
|
174
|
+
timestamp: new Date().toISOString(),
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (name === "hcs_audit_trail") {
|
|
179
|
+
const payment = chargeForTool("hcs_audit_trail", args.api_key);
|
|
180
|
+
const base = getMirrorNodeBase();
|
|
181
|
+
const limit = args.limit || 50;
|
|
182
|
+
|
|
183
|
+
const response = await axios.get(
|
|
184
|
+
`${base}/api/v1/topics/${args.topic_id}/messages?limit=100&order=asc`
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
const messages = response.data.messages || [];
|
|
188
|
+
const trail = [];
|
|
189
|
+
|
|
190
|
+
for (const msg of messages) {
|
|
191
|
+
try {
|
|
192
|
+
const content = Buffer.from(msg.message, "base64").toString("utf-8");
|
|
193
|
+
const record = JSON.parse(content);
|
|
194
|
+
if (record.entity_id === args.entity_id) {
|
|
195
|
+
trail.push({
|
|
196
|
+
record_id: record.record_id,
|
|
197
|
+
record_type: record.record_type,
|
|
198
|
+
written_at: record.written_at,
|
|
199
|
+
consensus_timestamp: msg.consensus_timestamp,
|
|
200
|
+
sequence_number: msg.sequence_number,
|
|
201
|
+
data: record.data,
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
} catch (e) {
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return {
|
|
210
|
+
entity_id: args.entity_id,
|
|
211
|
+
topic_id: args.topic_id,
|
|
212
|
+
total_records: trail.length,
|
|
213
|
+
audit_trail: trail.slice(0, limit),
|
|
214
|
+
payment,
|
|
215
|
+
timestamp: new Date().toISOString(),
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
throw new Error(`Unknown compliance tool: ${name}`);
|
|
220
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// hcs/hedera.js - Hedera mirror node integration
|
|
2
|
+
import axios from "axios";
|
|
3
|
+
|
|
4
|
+
function getMirrorNodeBase() {
|
|
5
|
+
const network = process.env.HEDERA_NETWORK || "testnet";
|
|
6
|
+
return network === "mainnet"
|
|
7
|
+
? "https://mainnet-public.mirrornode.hedera.com"
|
|
8
|
+
: "https://testnet.mirrornode.hedera.com";
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export async function getTopicInfo(topicId) {
|
|
12
|
+
const base = getMirrorNodeBase();
|
|
13
|
+
const response = await axios.get(`${base}/api/v1/topics/${topicId}`);
|
|
14
|
+
return response.data;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export async function getTopicMessages(topicId, limit = 50, since = null) {
|
|
18
|
+
const base = getMirrorNodeBase();
|
|
19
|
+
let url = `${base}/api/v1/topics/${topicId}/messages?limit=${limit}&order=desc`;
|
|
20
|
+
if (since) url += `×tamp=gte:${since}`;
|
|
21
|
+
const response = await axios.get(url);
|
|
22
|
+
const messages = response.data.messages || [];
|
|
23
|
+
return messages.map((msg) => ({
|
|
24
|
+
sequence_number: msg.sequence_number,
|
|
25
|
+
consensus_timestamp: msg.consensus_timestamp,
|
|
26
|
+
content: Buffer.from(msg.message, "base64").toString("utf-8"),
|
|
27
|
+
payer_account_id: msg.payer_account_id,
|
|
28
|
+
}));
|
|
29
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// hcs/intelligence.js - GPT-4o Mini analysis engine
|
|
2
|
+
import OpenAI from "openai";
|
|
3
|
+
|
|
4
|
+
let openai;
|
|
5
|
+
|
|
6
|
+
function getOpenAI() {
|
|
7
|
+
if (!openai) openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
|
|
8
|
+
return openai;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export async function analyzeMessages(messages, query) {
|
|
12
|
+
const client = getOpenAI();
|
|
13
|
+
const messagesText = messages
|
|
14
|
+
.map((m) => `[${m.sequence_number}] ${m.content}`)
|
|
15
|
+
.join("\n");
|
|
16
|
+
|
|
17
|
+
const response = await client.chat.completions.create({
|
|
18
|
+
model: "gpt-4o-mini",
|
|
19
|
+
response_format: { type: "json_object" },
|
|
20
|
+
messages: [
|
|
21
|
+
{
|
|
22
|
+
role: "system",
|
|
23
|
+
content: `You are an AI analyst for Hedera blockchain data.
|
|
24
|
+
Analyze HCS topic messages and answer the user query.
|
|
25
|
+
Respond with JSON: { summary, anomalies, recommended_action, relevant_messages: [{sequence_number, content, relevance_score}] }
|
|
26
|
+
relevance_score is 0.0-1.0. Only include messages with score > 0.5.`,
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
role: "user",
|
|
30
|
+
content: `Query: ${query}\n\nMessages:\n${messagesText}`,
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
max_tokens: 1000,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
return JSON.parse(response.choices[0].message.content);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function deepAnalyze(messages, analysisType) {
|
|
40
|
+
const client = getOpenAI();
|
|
41
|
+
const messagesText = messages
|
|
42
|
+
.map((m) => `[${m.sequence_number}] ${m.content}`)
|
|
43
|
+
.join("\n");
|
|
44
|
+
|
|
45
|
+
const prompts = {
|
|
46
|
+
anomaly_detection: "Detect unusual patterns, outliers, or suspicious activity.",
|
|
47
|
+
trend_analysis: "Identify trends, patterns, and changes over time.",
|
|
48
|
+
entity_extraction: "Extract key entities, actors, and relationships.",
|
|
49
|
+
risk_assessment: "Assess risks, vulnerabilities, and concerns.",
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const response = await client.chat.completions.create({
|
|
53
|
+
model: "gpt-4o-mini",
|
|
54
|
+
response_format: { type: "json_object" },
|
|
55
|
+
messages: [
|
|
56
|
+
{
|
|
57
|
+
role: "system",
|
|
58
|
+
content: `You are a deep analyst for Hedera blockchain data.
|
|
59
|
+
${prompts[analysisType]}
|
|
60
|
+
Respond with JSON: { executive_summary, findings: [], risk_level, recommendations: [] }`,
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
role: "user",
|
|
64
|
+
content: `Analyze these HCS messages:\n${messagesText}`,
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
max_tokens: 1500,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
return JSON.parse(response.choices[0].message.content);
|
|
71
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// hcs/tools.js - HCS Topic Intelligence tool definitions and handlers
|
|
2
|
+
import { getTopicInfo, getTopicMessages } from "./hedera.js";
|
|
3
|
+
import { analyzeMessages, deepAnalyze } from "./intelligence.js";
|
|
4
|
+
import { chargeForTool } from "../../payments.js";
|
|
5
|
+
|
|
6
|
+
export const HCS_TOOL_DEFINITIONS = [
|
|
7
|
+
{
|
|
8
|
+
name: "hcs_monitor",
|
|
9
|
+
description: "Get current status and metadata of any HCS topic - message count, creation time, memo, and recent activity. Free to call.",
|
|
10
|
+
inputSchema: {
|
|
11
|
+
type: "object",
|
|
12
|
+
properties: {
|
|
13
|
+
topic_id: { type: "string", description: "Hedera topic ID (e.g. 0.0.8026796)" },
|
|
14
|
+
api_key: { type: "string", description: "Your AgentLens API key" },
|
|
15
|
+
},
|
|
16
|
+
required: ["topic_id", "api_key"],
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
name: "hcs_query",
|
|
21
|
+
description: "Query an HCS topic with a natural language question. Returns AI-ranked relevant messages and a plain English summary. Costs 0.05 HBAR.",
|
|
22
|
+
inputSchema: {
|
|
23
|
+
type: "object",
|
|
24
|
+
properties: {
|
|
25
|
+
topic_id: { type: "string", description: "Hedera topic ID (e.g. 0.0.8026796)" },
|
|
26
|
+
query: { type: "string", description: "Natural language question about the topic" },
|
|
27
|
+
limit: { type: "number", description: "Max messages to retrieve (default 50)" },
|
|
28
|
+
api_key: { type: "string", description: "Your AgentLens API key" },
|
|
29
|
+
},
|
|
30
|
+
required: ["topic_id", "query", "api_key"],
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: "hcs_understand",
|
|
35
|
+
description: "Deep pattern analysis of an HCS topic - anomaly detection, trend analysis, entity extraction, or risk assessment. Costs 0.50 HBAR.",
|
|
36
|
+
inputSchema: {
|
|
37
|
+
type: "object",
|
|
38
|
+
properties: {
|
|
39
|
+
topic_id: { type: "string", description: "Hedera topic ID" },
|
|
40
|
+
analysis_type: {
|
|
41
|
+
type: "string",
|
|
42
|
+
enum: ["anomaly_detection", "trend_analysis", "entity_extraction", "risk_assessment"],
|
|
43
|
+
description: "Type of analysis to perform",
|
|
44
|
+
},
|
|
45
|
+
lookback_days: { type: "number", description: "Days of history to analyze (default 7, max 30)" },
|
|
46
|
+
api_key: { type: "string", description: "Your AgentLens API key" },
|
|
47
|
+
},
|
|
48
|
+
required: ["topic_id", "analysis_type", "api_key"],
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
export async function executeHCSTool(name, args) {
|
|
54
|
+
if (name === "hcs_monitor") {
|
|
55
|
+
const info = await getTopicInfo(args.topic_id);
|
|
56
|
+
const messages = await getTopicMessages(args.topic_id, 5);
|
|
57
|
+
return {
|
|
58
|
+
topic_id: args.topic_id,
|
|
59
|
+
memo: info.memo,
|
|
60
|
+
created_timestamp: info.created_timestamp,
|
|
61
|
+
deleted: info.deleted,
|
|
62
|
+
recent_message_count: messages.length,
|
|
63
|
+
latest_message: messages[0] || null,
|
|
64
|
+
network: process.env.HEDERA_NETWORK,
|
|
65
|
+
timestamp: new Date().toISOString(),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (name === "hcs_query") {
|
|
70
|
+
const payment = chargeForTool("hcs_query", args.api_key);
|
|
71
|
+
const messages = await getTopicMessages(args.topic_id, args.limit || 50);
|
|
72
|
+
const analysis = await analyzeMessages(messages, args.query);
|
|
73
|
+
return {
|
|
74
|
+
topic_id: args.topic_id,
|
|
75
|
+
query: args.query,
|
|
76
|
+
messages_retrieved: messages.length,
|
|
77
|
+
messages_relevant: analysis.relevant_messages?.length || 0,
|
|
78
|
+
summary: analysis.summary,
|
|
79
|
+
anomalies: analysis.anomalies,
|
|
80
|
+
recommended_action: analysis.recommended_action,
|
|
81
|
+
relevant_messages: analysis.relevant_messages || [],
|
|
82
|
+
payment,
|
|
83
|
+
timestamp: new Date().toISOString(),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (name === "hcs_understand") {
|
|
88
|
+
const payment = chargeForTool("hcs_understand", args.api_key);
|
|
89
|
+
const messages = await getTopicMessages(args.topic_id, 100);
|
|
90
|
+
const analysis = await deepAnalyze(messages, args.analysis_type);
|
|
91
|
+
return {
|
|
92
|
+
topic_id: args.topic_id,
|
|
93
|
+
analysis_type: args.analysis_type,
|
|
94
|
+
messages_analyzed: messages.length,
|
|
95
|
+
...analysis,
|
|
96
|
+
payment,
|
|
97
|
+
timestamp: new Date().toISOString(),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
throw new Error(`Unknown HCS tool: ${name}`);
|
|
102
|
+
}
|
package/src/payments.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// payments.js - Shared HBAR micropayment system for all modules
|
|
2
|
+
|
|
3
|
+
const COSTS = {
|
|
4
|
+
// Server 1 - HCS Topic Intelligence
|
|
5
|
+
hcs_monitor: { tinybars: 5000000, hbar: "0.0500" },
|
|
6
|
+
hcs_query: { tinybars: 5000000, hbar: "0.0500" },
|
|
7
|
+
hcs_understand: { tinybars: 50000000, hbar: "0.5000" },
|
|
8
|
+
// Server 2 - Compliance & Audit Trail
|
|
9
|
+
hcs_write_record: { tinybars: 200000000, hbar: "2.0000" },
|
|
10
|
+
hcs_verify_record: { tinybars: 50000000, hbar: "0.5000" },
|
|
11
|
+
hcs_audit_trail: { tinybars: 100000000, hbar: "1.0000" },
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const accounts = new Map();
|
|
15
|
+
|
|
16
|
+
function getAccount(apiKey) {
|
|
17
|
+
if (!accounts.has(apiKey)) {
|
|
18
|
+
accounts.set(apiKey, { balance: 100000000 });
|
|
19
|
+
}
|
|
20
|
+
return accounts.get(apiKey);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function chargeForTool(toolName, apiKey) {
|
|
24
|
+
const cost = COSTS[toolName];
|
|
25
|
+
if (!cost) return null;
|
|
26
|
+
|
|
27
|
+
const account = getAccount(apiKey);
|
|
28
|
+
if (account.balance < cost.tinybars) {
|
|
29
|
+
throw new Error(
|
|
30
|
+
"Insufficient HBAR balance. Required: " + cost.hbar + " HBAR. Please top up your AgentLens account."
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
account.balance -= cost.tinybars;
|
|
35
|
+
const remainingHbar = (account.balance / 100000000).toFixed(4);
|
|
36
|
+
return {
|
|
37
|
+
charged_hbar: cost.hbar,
|
|
38
|
+
remaining_hbar: remainingHbar,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function getCosts() {
|
|
43
|
+
return COSTS;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function getBalance(apiKey) {
|
|
47
|
+
const account = getAccount(apiKey);
|
|
48
|
+
return (account.balance / 100000000).toFixed(4);
|
|
49
|
+
}
|
package/src/server.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// server.js - MCP server factory, registers all module tools
|
|
2
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
|
+
import {
|
|
4
|
+
CallToolRequestSchema,
|
|
5
|
+
ListToolsRequestSchema,
|
|
6
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
7
|
+
import { HCS_TOOL_DEFINITIONS, executeHCSTool } from "./modules/hcs/tools.js";
|
|
8
|
+
import { COMPLIANCE_TOOL_DEFINITIONS, executeComplianceTool } from "./modules/compliance/tools.js";
|
|
9
|
+
|
|
10
|
+
const ALL_TOOLS = [
|
|
11
|
+
...HCS_TOOL_DEFINITIONS,
|
|
12
|
+
...COMPLIANCE_TOOL_DEFINITIONS,
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
async function routeTool(name, args) {
|
|
16
|
+
if (["hcs_monitor", "hcs_query", "hcs_understand"].includes(name)) {
|
|
17
|
+
return executeHCSTool(name, args);
|
|
18
|
+
}
|
|
19
|
+
if (["hcs_write_record", "hcs_verify_record", "hcs_audit_trail"].includes(name)) {
|
|
20
|
+
return executeComplianceTool(name, args);
|
|
21
|
+
}
|
|
22
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function createServer() {
|
|
26
|
+
const server = new Server(
|
|
27
|
+
{ name: "hedera-mcp-platform", version: "1.0.0" },
|
|
28
|
+
{ capabilities: { tools: {} } }
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
32
|
+
return { tools: ALL_TOOLS };
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
36
|
+
const { name, arguments: args } = request.params;
|
|
37
|
+
console.error("Tool: " + name);
|
|
38
|
+
try {
|
|
39
|
+
const result = await routeTool(name, args);
|
|
40
|
+
console.error("Done: " + name);
|
|
41
|
+
return {
|
|
42
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
43
|
+
};
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.error("Error: " + name + " - " + error.message);
|
|
46
|
+
return {
|
|
47
|
+
content: [
|
|
48
|
+
{
|
|
49
|
+
type: "text",
|
|
50
|
+
text: JSON.stringify({
|
|
51
|
+
error: error.message,
|
|
52
|
+
tool: name,
|
|
53
|
+
timestamp: new Date().toISOString(),
|
|
54
|
+
}),
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
isError: true,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
return server;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export { ALL_TOOLS };
|