@helm-protocol/ttt-mcp 0.1.6 → 0.1.7
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/dist/index.js +152 -152
- package/dist/telemetry.js +52 -38
- package/dist/tools.js +197 -192
- package/package.json +3 -3
- package/dist/index.d.ts +0 -2
- package/dist/telemetry.d.ts +0 -7
- package/dist/tools.d.ts +0 -20
package/dist/index.js
CHANGED
|
@@ -1,169 +1,169 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const server = new mcp_js_1.McpServer({
|
|
13
|
-
name: "ttt-mcp",
|
|
14
|
-
version: "0.1.0",
|
|
3
|
+
var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
4
|
+
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
5
|
+
var import_streamableHttp = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
|
|
6
|
+
var import_http = require("http");
|
|
7
|
+
var import_zod = require("zod");
|
|
8
|
+
var import_tools = require("./tools");
|
|
9
|
+
const server = new import_mcp.McpServer({
|
|
10
|
+
name: "ttt-mcp",
|
|
11
|
+
version: "0.1.0"
|
|
15
12
|
});
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
13
|
+
server.tool(
|
|
14
|
+
"pot_generate",
|
|
15
|
+
"Generate a Proof of Time for a transaction. Returns potHash, timestamp, stratum, and GRG integrity shards.",
|
|
16
|
+
{
|
|
17
|
+
txHash: import_zod.z.string().describe("Transaction hash (hex with 0x prefix)"),
|
|
18
|
+
chainId: import_zod.z.number().describe("Chain ID (e.g. 8453 for Base, 84532 for Base Sepolia)"),
|
|
19
|
+
poolAddress: import_zod.z.string().describe("DEX pool contract address")
|
|
20
|
+
},
|
|
21
|
+
async (args) => {
|
|
22
22
|
try {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
const result = await (0, import_tools.potGenerate)(args);
|
|
24
|
+
return {
|
|
25
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
26
|
+
};
|
|
27
|
+
} catch (err) {
|
|
28
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
29
|
+
return {
|
|
30
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
31
|
+
isError: true
|
|
32
|
+
};
|
|
27
33
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
chainId: zod_1.z.number().describe("EVM chain ID (e.g. 84532 for Base Sepolia)"),
|
|
41
|
-
poolAddress: zod_1.z.string().describe("Uniswap V4 pool address (0x-prefixed)"),
|
|
42
|
-
}, async (args) => {
|
|
34
|
+
}
|
|
35
|
+
);
|
|
36
|
+
server.tool(
|
|
37
|
+
"pot_verify",
|
|
38
|
+
"Verify a Proof of Time using its hash and GRG shards. Returns validity, mode (turbo/full), and timestamp.",
|
|
39
|
+
{
|
|
40
|
+
potHash: import_zod.z.string().describe("PoT hash to verify (hex with 0x prefix)"),
|
|
41
|
+
grgShards: import_zod.z.array(import_zod.z.string()).describe("Array of hex-encoded GRG integrity shards"),
|
|
42
|
+
chainId: import_zod.z.number().describe("EVM chain ID (e.g. 84532 for Base Sepolia)"),
|
|
43
|
+
poolAddress: import_zod.z.string().describe("Uniswap V4 pool address (0x-prefixed)")
|
|
44
|
+
},
|
|
45
|
+
async (args) => {
|
|
43
46
|
try {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
};
|
|
47
|
+
const result = await (0, import_tools.potVerify)(args);
|
|
48
|
+
return {
|
|
49
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
50
|
+
};
|
|
51
|
+
} catch (err) {
|
|
52
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
53
|
+
return {
|
|
54
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
55
|
+
isError: true
|
|
56
|
+
};
|
|
55
57
|
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
server.tool(
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
58
|
+
}
|
|
59
|
+
);
|
|
60
|
+
server.tool(
|
|
61
|
+
"pot_query",
|
|
62
|
+
"Query Proof of Time history from local log and on-chain subgraph.",
|
|
63
|
+
{
|
|
64
|
+
startTime: import_zod.z.number().optional().describe("Start time (unix ms). Default: 24h ago"),
|
|
65
|
+
endTime: import_zod.z.number().optional().describe("End time (unix ms). Default: now"),
|
|
66
|
+
limit: import_zod.z.number().optional().describe("Max entries to return. Default: 100, max: 1000")
|
|
67
|
+
},
|
|
68
|
+
async (args) => {
|
|
63
69
|
try {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
};
|
|
70
|
+
const result = await (0, import_tools.potQuery)(args);
|
|
71
|
+
return {
|
|
72
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
73
|
+
};
|
|
74
|
+
} catch (err) {
|
|
75
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
76
|
+
return {
|
|
77
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
78
|
+
isError: true
|
|
79
|
+
};
|
|
75
80
|
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
server.tool(
|
|
79
|
-
|
|
80
|
-
|
|
81
|
+
}
|
|
82
|
+
);
|
|
83
|
+
server.tool(
|
|
84
|
+
"pot_stats",
|
|
85
|
+
"Get PoT statistics: total swaps, turbo/full counts, and turbo ratio for a given period.",
|
|
86
|
+
{
|
|
87
|
+
period: import_zod.z.enum(["day", "week", "month"]).describe("Time period for statistics")
|
|
88
|
+
},
|
|
89
|
+
async (args) => {
|
|
81
90
|
try {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
91
|
+
const result = await (0, import_tools.potStats)(args);
|
|
92
|
+
return {
|
|
93
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
94
|
+
};
|
|
95
|
+
} catch (err) {
|
|
96
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
97
|
+
return {
|
|
98
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
99
|
+
isError: true
|
|
100
|
+
};
|
|
86
101
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
});
|
|
95
|
-
// ---------- Tool 5: pot_health ----------
|
|
96
|
-
server.tool("pot_health", "Check PoT system health: time source status, subgraph sync, server uptime, and current mode.", {}, async () => {
|
|
102
|
+
}
|
|
103
|
+
);
|
|
104
|
+
server.tool(
|
|
105
|
+
"pot_health",
|
|
106
|
+
"Check PoT system health: time source status, subgraph sync, server uptime, and current mode.",
|
|
107
|
+
{},
|
|
108
|
+
async () => {
|
|
97
109
|
try {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
};
|
|
110
|
+
const result = await (0, import_tools.potHealth)();
|
|
111
|
+
return {
|
|
112
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
113
|
+
};
|
|
114
|
+
} catch (err) {
|
|
115
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
116
|
+
return {
|
|
117
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
118
|
+
isError: true
|
|
119
|
+
};
|
|
109
120
|
}
|
|
110
|
-
}
|
|
111
|
-
|
|
121
|
+
}
|
|
122
|
+
);
|
|
112
123
|
async function main() {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
});
|
|
155
|
-
httpServer.listen(port, () => {
|
|
156
|
-
console.error(`[ttt-mcp] OpenTTT MCP Server (HTTP) on port ${port}`);
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
else {
|
|
160
|
-
// stdio mode for npx/Claude Desktop usage
|
|
161
|
-
const transport = new stdio_js_1.StdioServerTransport();
|
|
162
|
-
await server.connect(transport);
|
|
163
|
-
console.error("[ttt-mcp] OpenTTT MCP Server running on stdio");
|
|
164
|
-
}
|
|
124
|
+
const port = process.env.PORT ? parseInt(process.env.PORT, 10) : null;
|
|
125
|
+
if (port) {
|
|
126
|
+
const transport = new import_streamableHttp.StreamableHTTPServerTransport({
|
|
127
|
+
sessionIdGenerator: void 0
|
|
128
|
+
});
|
|
129
|
+
await server.connect(transport);
|
|
130
|
+
const httpServer = (0, import_http.createServer)(async (req, res) => {
|
|
131
|
+
if (req.method === "GET" && (req.url === "/health" || req.url === "/ping")) {
|
|
132
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
133
|
+
res.end(JSON.stringify({ status: "ok", server: "ttt-mcp", version: "0.1.5" }));
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const accept = req.headers["accept"] ?? "";
|
|
137
|
+
if (!accept.includes("text/event-stream")) {
|
|
138
|
+
const newAccept = accept ? `${accept}, text/event-stream` : "application/json, text/event-stream";
|
|
139
|
+
req.headers["accept"] = newAccept;
|
|
140
|
+
const raw = req.rawHeaders;
|
|
141
|
+
const idx = raw.findIndex((h, i) => i % 2 === 0 && h.toLowerCase() === "accept");
|
|
142
|
+
if (idx >= 0) {
|
|
143
|
+
raw[idx + 1] = newAccept;
|
|
144
|
+
} else {
|
|
145
|
+
raw.push("Accept", newAccept);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
try {
|
|
149
|
+
await transport.handleRequest(req, res);
|
|
150
|
+
} catch (err) {
|
|
151
|
+
if (!res.headersSent) {
|
|
152
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
153
|
+
res.end(JSON.stringify({ error: "Internal server error" }));
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
httpServer.listen(port, () => {
|
|
158
|
+
console.error(`[ttt-mcp] OpenTTT MCP Server (HTTP) on port ${port}`);
|
|
159
|
+
});
|
|
160
|
+
} else {
|
|
161
|
+
const transport = new import_stdio.StdioServerTransport();
|
|
162
|
+
await server.connect(transport);
|
|
163
|
+
console.error("[ttt-mcp] OpenTTT MCP Server running on stdio");
|
|
164
|
+
}
|
|
165
165
|
}
|
|
166
166
|
main().catch((err) => {
|
|
167
|
-
|
|
168
|
-
|
|
167
|
+
console.error("[ttt-mcp] Fatal:", err);
|
|
168
|
+
process.exit(1);
|
|
169
169
|
});
|
package/dist/telemetry.js
CHANGED
|
@@ -1,45 +1,59 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var telemetry_exports = {};
|
|
20
|
+
__export(telemetry_exports, {
|
|
21
|
+
getTelemetryCounts: () => getTelemetryCounts,
|
|
22
|
+
telemetryIncrement: () => telemetryIncrement
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(telemetry_exports);
|
|
7
25
|
const counters = {};
|
|
8
26
|
const TELEMETRY_ENDPOINT = "https://api.studio.thegraph.com/query/1744392/openttt-base-sepolia/v0.1.0";
|
|
9
|
-
/**
|
|
10
|
-
* Increment an anonymous tool-call counter.
|
|
11
|
-
* Posts to subgraph endpoint as a best-effort ping. Never throws.
|
|
12
|
-
*/
|
|
13
27
|
function telemetryIncrement(toolName) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
catch {
|
|
39
|
-
// never block the tool call
|
|
40
|
-
}
|
|
28
|
+
counters[toolName] = (counters[toolName] ?? 0) + 1;
|
|
29
|
+
try {
|
|
30
|
+
const body = JSON.stringify({
|
|
31
|
+
query: `{ _meta { block { number } } }`,
|
|
32
|
+
extensions: {
|
|
33
|
+
telemetry: {
|
|
34
|
+
tool: toolName,
|
|
35
|
+
count: counters[toolName],
|
|
36
|
+
ts: Date.now(),
|
|
37
|
+
pkg: "@helm-protocol/ttt-mcp",
|
|
38
|
+
v: "0.1.0"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
fetch(TELEMETRY_ENDPOINT, {
|
|
43
|
+
method: "POST",
|
|
44
|
+
headers: { "Content-Type": "application/json" },
|
|
45
|
+
body,
|
|
46
|
+
signal: AbortSignal.timeout(3e3)
|
|
47
|
+
}).catch(() => {
|
|
48
|
+
});
|
|
49
|
+
} catch {
|
|
50
|
+
}
|
|
41
51
|
}
|
|
42
|
-
/** Get current session counters (for debugging) */
|
|
43
52
|
function getTelemetryCounts() {
|
|
44
|
-
|
|
53
|
+
return { ...counters };
|
|
45
54
|
}
|
|
55
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
56
|
+
0 && (module.exports = {
|
|
57
|
+
getTelemetryCounts,
|
|
58
|
+
telemetryIncrement
|
|
59
|
+
});
|
package/dist/tools.js
CHANGED
|
@@ -1,114 +1,116 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var tools_exports = {};
|
|
20
|
+
__export(tools_exports, {
|
|
21
|
+
potGenerate: () => potGenerate,
|
|
22
|
+
potHealth: () => potHealth,
|
|
23
|
+
potQuery: () => potQuery,
|
|
24
|
+
potStats: () => potStats,
|
|
25
|
+
potVerify: () => potVerify
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(tools_exports);
|
|
28
|
+
var import_openttt = require("openttt");
|
|
29
|
+
var import_telemetry = require("./telemetry");
|
|
30
|
+
const timeSynth = new import_openttt.TimeSynthesis();
|
|
31
|
+
const adaptiveSwitch = new import_openttt.AdaptiveSwitch();
|
|
32
|
+
const potSigner = new import_openttt.PotSigner();
|
|
16
33
|
const startedAt = Date.now();
|
|
17
|
-
|
|
18
|
-
const POT_LOG_MAX = 10000;
|
|
34
|
+
const POT_LOG_MAX = 1e4;
|
|
19
35
|
const potLog = [];
|
|
20
|
-
// ---------- Helpers ----------
|
|
21
36
|
function bigintReplacer(_key, value) {
|
|
22
|
-
|
|
37
|
+
return typeof value === "bigint" ? value.toString() : value;
|
|
23
38
|
}
|
|
24
39
|
function serialize(obj) {
|
|
25
|
-
|
|
40
|
+
return JSON.parse(JSON.stringify(obj, bigintReplacer));
|
|
26
41
|
}
|
|
27
42
|
const SUBGRAPH_URL = "https://api.studio.thegraph.com/query/1744392/openttt-base-sepolia/v0.1.0";
|
|
28
|
-
// ---------- Tool: pot_generate ----------
|
|
29
43
|
async function potGenerate(args) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
issuerPubKey: signature.issuerPubKey,
|
|
64
|
-
signature: signature.signature,
|
|
65
|
-
issuedAt: signature.issuedAt.toString(),
|
|
66
|
-
},
|
|
67
|
-
});
|
|
44
|
+
(0, import_telemetry.telemetryIncrement)("pot_generate");
|
|
45
|
+
const pot = await timeSynth.generateProofOfTime();
|
|
46
|
+
const potHash = import_openttt.TimeSynthesis.getOnChainHash(pot);
|
|
47
|
+
const txData = new TextEncoder().encode(args.txHash);
|
|
48
|
+
const grgShards = import_openttt.GrgPipeline.processForward(txData, args.chainId, args.poolAddress);
|
|
49
|
+
const signature = potSigner.signPot(potHash);
|
|
50
|
+
const entry = {
|
|
51
|
+
potHash,
|
|
52
|
+
timestamp: pot.timestamp.toString(),
|
|
53
|
+
stratum: pot.stratum,
|
|
54
|
+
mode: adaptiveSwitch.getCurrentMode(),
|
|
55
|
+
chainId: args.chainId,
|
|
56
|
+
poolAddress: args.poolAddress,
|
|
57
|
+
createdAt: Date.now()
|
|
58
|
+
};
|
|
59
|
+
potLog.push(entry);
|
|
60
|
+
if (potLog.length > POT_LOG_MAX) potLog.shift();
|
|
61
|
+
return serialize({
|
|
62
|
+
potHash,
|
|
63
|
+
timestamp: pot.timestamp.toString(),
|
|
64
|
+
stratum: pot.stratum,
|
|
65
|
+
uncertainty: pot.uncertainty,
|
|
66
|
+
confidence: pot.confidence,
|
|
67
|
+
sources: pot.sources,
|
|
68
|
+
nonce: pot.nonce,
|
|
69
|
+
expiresAt: pot.expiresAt.toString(),
|
|
70
|
+
grgShards: grgShards.map((s) => Buffer.from(s).toString("hex")),
|
|
71
|
+
signature: {
|
|
72
|
+
issuerPubKey: signature.issuerPubKey,
|
|
73
|
+
signature: signature.signature,
|
|
74
|
+
issuedAt: signature.issuedAt.toString()
|
|
75
|
+
}
|
|
76
|
+
});
|
|
68
77
|
}
|
|
69
|
-
// ---------- Tool: pot_verify ----------
|
|
70
78
|
async function potVerify(args) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
});
|
|
79
|
+
(0, import_telemetry.telemetryIncrement)("pot_verify");
|
|
80
|
+
const shards = args.grgShards.map((hex) => new Uint8Array(Buffer.from(hex, "hex")));
|
|
81
|
+
let valid = false;
|
|
82
|
+
let reconstructedSize = 0;
|
|
83
|
+
try {
|
|
84
|
+
const recovered = import_openttt.GrgPipeline.processInverse(shards, 0, args.chainId, args.poolAddress);
|
|
85
|
+
valid = recovered.length > 0;
|
|
86
|
+
reconstructedSize = recovered.length;
|
|
87
|
+
} catch {
|
|
88
|
+
valid = false;
|
|
89
|
+
}
|
|
90
|
+
const mode = adaptiveSwitch.getCurrentMode() === import_openttt.AdaptiveMode.TURBO ? "turbo" : "full";
|
|
91
|
+
return serialize({
|
|
92
|
+
valid,
|
|
93
|
+
mode,
|
|
94
|
+
potHash: args.potHash,
|
|
95
|
+
reconstructedBytes: reconstructedSize,
|
|
96
|
+
verifiedAt: Date.now()
|
|
97
|
+
});
|
|
91
98
|
}
|
|
92
|
-
// ---------- Tool: pot_query ----------
|
|
93
99
|
async function potQuery(args) {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
// Best-effort subgraph query
|
|
104
|
-
let subgraphEntries = [];
|
|
105
|
-
try {
|
|
106
|
-
const query = `{
|
|
100
|
+
(0, import_telemetry.telemetryIncrement)("pot_query");
|
|
101
|
+
const limit = Math.min(args.limit ?? 100, 1e3);
|
|
102
|
+
const now = Date.now();
|
|
103
|
+
const startTime = args.startTime ?? now - 864e5;
|
|
104
|
+
const endTime = args.endTime ?? now;
|
|
105
|
+
const filtered = potLog.filter((e) => e.createdAt >= startTime && e.createdAt <= endTime).slice(-limit);
|
|
106
|
+
let subgraphEntries = [];
|
|
107
|
+
try {
|
|
108
|
+
const query = `{
|
|
107
109
|
potAnchors(
|
|
108
110
|
first: ${limit},
|
|
109
111
|
orderBy: blockTimestamp,
|
|
110
112
|
orderDirection: desc,
|
|
111
|
-
where: { blockTimestamp_gte: "${Math.floor(startTime /
|
|
113
|
+
where: { blockTimestamp_gte: "${Math.floor(startTime / 1e3)}", blockTimestamp_lte: "${Math.floor(endTime / 1e3)}" }
|
|
112
114
|
) {
|
|
113
115
|
id
|
|
114
116
|
potHash
|
|
@@ -116,110 +118,113 @@ async function potQuery(args) {
|
|
|
116
118
|
txHash
|
|
117
119
|
}
|
|
118
120
|
}`;
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
});
|
|
127
|
-
clearTimeout(timeout);
|
|
128
|
-
if (resp.ok) {
|
|
129
|
-
const json = (await resp.json());
|
|
130
|
-
subgraphEntries = json.data?.potAnchors ?? [];
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
catch {
|
|
134
|
-
// Subgraph unavailable — local log still returned
|
|
135
|
-
}
|
|
136
|
-
return serialize({
|
|
137
|
-
local: filtered,
|
|
138
|
-
subgraph: subgraphEntries,
|
|
139
|
-
totalLocal: potLog.length,
|
|
140
|
-
query: { startTime, endTime, limit },
|
|
121
|
+
const controller = new AbortController();
|
|
122
|
+
const timeout = setTimeout(() => controller.abort(), 5e3);
|
|
123
|
+
const resp = await fetch(SUBGRAPH_URL, {
|
|
124
|
+
method: "POST",
|
|
125
|
+
headers: { "Content-Type": "application/json" },
|
|
126
|
+
body: JSON.stringify({ query }),
|
|
127
|
+
signal: controller.signal
|
|
141
128
|
});
|
|
129
|
+
clearTimeout(timeout);
|
|
130
|
+
if (resp.ok) {
|
|
131
|
+
const json = await resp.json();
|
|
132
|
+
subgraphEntries = json.data?.potAnchors ?? [];
|
|
133
|
+
}
|
|
134
|
+
} catch {
|
|
135
|
+
}
|
|
136
|
+
return serialize({
|
|
137
|
+
local: filtered,
|
|
138
|
+
subgraph: subgraphEntries,
|
|
139
|
+
totalLocal: potLog.length,
|
|
140
|
+
query: { startTime, endTime, limit }
|
|
141
|
+
});
|
|
142
142
|
}
|
|
143
|
-
// ---------- Tool: pot_stats ----------
|
|
144
143
|
async function potStats(args) {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
144
|
+
(0, import_telemetry.telemetryIncrement)("pot_stats");
|
|
145
|
+
const now = Date.now();
|
|
146
|
+
const periodMs = {
|
|
147
|
+
day: 864e5,
|
|
148
|
+
week: 6048e5,
|
|
149
|
+
month: 2592e6
|
|
150
|
+
};
|
|
151
|
+
const cutoff = now - (periodMs[args.period] ?? periodMs.day);
|
|
152
|
+
const entries = potLog.filter((e) => e.createdAt >= cutoff);
|
|
153
|
+
const turboCount = entries.filter((e) => e.mode === import_openttt.AdaptiveMode.TURBO).length;
|
|
154
|
+
const fullCount = entries.filter((e) => e.mode === import_openttt.AdaptiveMode.FULL).length;
|
|
155
|
+
const totalSwaps = entries.length;
|
|
156
|
+
return serialize({
|
|
157
|
+
period: args.period,
|
|
158
|
+
totalSwaps,
|
|
159
|
+
turboCount,
|
|
160
|
+
fullCount,
|
|
161
|
+
turboRatio: totalSwaps > 0 ? +(turboCount / totalSwaps).toFixed(4) : 0,
|
|
162
|
+
currentMode: adaptiveSwitch.getCurrentMode(),
|
|
163
|
+
windowStart: new Date(cutoff).toISOString(),
|
|
164
|
+
windowEnd: new Date(now).toISOString()
|
|
165
|
+
});
|
|
167
166
|
}
|
|
168
|
-
// ---------- Tool: pot_health ----------
|
|
169
167
|
async function potHealth() {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
168
|
+
(0, import_telemetry.telemetryIncrement)("pot_health");
|
|
169
|
+
let timeStatus = "unknown";
|
|
170
|
+
let synthSources = 0;
|
|
171
|
+
try {
|
|
172
|
+
const synth = await Promise.race([
|
|
173
|
+
timeSynth.synthesize(),
|
|
174
|
+
new Promise(
|
|
175
|
+
(_, reject) => setTimeout(() => reject(new Error("timeout")), 5e3)
|
|
176
|
+
)
|
|
177
|
+
]);
|
|
178
|
+
if (synth && synth.sources >= 2) {
|
|
179
|
+
timeStatus = "healthy";
|
|
180
|
+
synthSources = synth.sources;
|
|
181
|
+
} else {
|
|
182
|
+
timeStatus = "degraded";
|
|
183
|
+
synthSources = synth?.sources ?? 0;
|
|
186
184
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
}
|
|
185
|
+
} catch {
|
|
186
|
+
timeStatus = "unhealthy";
|
|
187
|
+
}
|
|
188
|
+
let latestBlock = 0;
|
|
189
|
+
let syncStatus = "unknown";
|
|
190
|
+
try {
|
|
191
|
+
const query = `{ _meta { block { number } hasIndexingErrors } }`;
|
|
192
|
+
const controller = new AbortController();
|
|
193
|
+
const timeout = setTimeout(() => controller.abort(), 5e3);
|
|
194
|
+
const resp = await fetch(SUBGRAPH_URL, {
|
|
195
|
+
method: "POST",
|
|
196
|
+
headers: { "Content-Type": "application/json" },
|
|
197
|
+
body: JSON.stringify({ query }),
|
|
198
|
+
signal: controller.signal
|
|
199
|
+
});
|
|
200
|
+
clearTimeout(timeout);
|
|
201
|
+
if (resp.ok) {
|
|
202
|
+
const json = await resp.json();
|
|
203
|
+
latestBlock = json.data?._meta?.block?.number ?? 0;
|
|
204
|
+
syncStatus = json.data?._meta?.hasIndexingErrors ? "indexing_errors" : "synced";
|
|
208
205
|
}
|
|
209
|
-
|
|
210
|
-
|
|
206
|
+
} catch {
|
|
207
|
+
syncStatus = "unreachable";
|
|
208
|
+
}
|
|
209
|
+
const uptimeMs = Date.now() - startedAt;
|
|
210
|
+
return serialize({
|
|
211
|
+
status: timeStatus === "healthy" ? "ok" : timeStatus,
|
|
212
|
+
timeSources: { status: timeStatus, activeSources: synthSources },
|
|
213
|
+
subgraph: { latestBlock, syncStatus },
|
|
214
|
+
server: {
|
|
215
|
+
uptime: uptimeMs,
|
|
216
|
+
uptimeHuman: `${(uptimeMs / 36e5).toFixed(1)}h`,
|
|
217
|
+
potCount: potLog.length,
|
|
218
|
+
currentMode: adaptiveSwitch.getCurrentMode(),
|
|
219
|
+
signerPubKey: potSigner.getPubKeyHex()
|
|
211
220
|
}
|
|
212
|
-
|
|
213
|
-
return serialize({
|
|
214
|
-
status: timeStatus === "healthy" ? "ok" : timeStatus,
|
|
215
|
-
timeSources: { status: timeStatus, activeSources: synthSources },
|
|
216
|
-
subgraph: { latestBlock, syncStatus },
|
|
217
|
-
server: {
|
|
218
|
-
uptime: uptimeMs,
|
|
219
|
-
uptimeHuman: `${(uptimeMs / 3600000).toFixed(1)}h`,
|
|
220
|
-
potCount: potLog.length,
|
|
221
|
-
currentMode: adaptiveSwitch.getCurrentMode(),
|
|
222
|
-
signerPubKey: potSigner.getPubKeyHex(),
|
|
223
|
-
},
|
|
224
|
-
});
|
|
221
|
+
});
|
|
225
222
|
}
|
|
223
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
224
|
+
0 && (module.exports = {
|
|
225
|
+
potGenerate,
|
|
226
|
+
potHealth,
|
|
227
|
+
potQuery,
|
|
228
|
+
potStats,
|
|
229
|
+
potVerify
|
|
230
|
+
});
|
package/package.json
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@helm-protocol/ttt-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "MCP Server for OpenTTT — Proof of Time tools for AI agents",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
|
-
"types": "dist/index.d.ts",
|
|
7
6
|
"bin": {
|
|
8
7
|
"ttt-mcp": "./dist/index.js"
|
|
9
8
|
},
|
|
@@ -12,7 +11,7 @@
|
|
|
12
11
|
"README.md"
|
|
13
12
|
],
|
|
14
13
|
"scripts": {
|
|
15
|
-
"build": "
|
|
14
|
+
"build": "esbuild index.ts tools.ts telemetry.ts --platform=node --target=node18 --format=cjs --outdir=dist",
|
|
16
15
|
"start": "node dist/index.js",
|
|
17
16
|
"dev": "npx ts-node index.ts"
|
|
18
17
|
},
|
|
@@ -42,6 +41,7 @@
|
|
|
42
41
|
},
|
|
43
42
|
"devDependencies": {
|
|
44
43
|
"@types/node": "^20.11.19",
|
|
44
|
+
"esbuild": "^0.27.4",
|
|
45
45
|
"typescript": "^5.3.3"
|
|
46
46
|
},
|
|
47
47
|
"mcpName": "io.github.Helm-Protocol/openttt-pot"
|
package/dist/index.d.ts
DELETED
package/dist/telemetry.d.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Increment an anonymous tool-call counter.
|
|
3
|
-
* Posts to subgraph endpoint as a best-effort ping. Never throws.
|
|
4
|
-
*/
|
|
5
|
-
export declare function telemetryIncrement(toolName: string): void;
|
|
6
|
-
/** Get current session counters (for debugging) */
|
|
7
|
-
export declare function getTelemetryCounts(): Record<string, number>;
|
package/dist/tools.d.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
export declare function potGenerate(args: {
|
|
2
|
-
txHash: string;
|
|
3
|
-
chainId: number;
|
|
4
|
-
poolAddress: string;
|
|
5
|
-
}): Promise<unknown>;
|
|
6
|
-
export declare function potVerify(args: {
|
|
7
|
-
potHash: string;
|
|
8
|
-
grgShards: string[];
|
|
9
|
-
chainId: number;
|
|
10
|
-
poolAddress: string;
|
|
11
|
-
}): Promise<unknown>;
|
|
12
|
-
export declare function potQuery(args: {
|
|
13
|
-
startTime?: number;
|
|
14
|
-
endTime?: number;
|
|
15
|
-
limit?: number;
|
|
16
|
-
}): Promise<unknown>;
|
|
17
|
-
export declare function potStats(args: {
|
|
18
|
-
period: "day" | "week" | "month";
|
|
19
|
-
}): Promise<unknown>;
|
|
20
|
-
export declare function potHealth(): Promise<unknown>;
|