@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 CHANGED
@@ -1,169 +1,169 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
- // @helm-protocol/ttt-mcp — MCP Server for OpenTTT Proof of Time
4
- // Provides 5 tools for AI agents: pot_generate, pot_verify, pot_query, pot_stats, pot_health
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
7
- const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
8
- const streamableHttp_js_1 = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
9
- const http_1 = require("http");
10
- const zod_1 = require("zod");
11
- const tools_1 = require("./tools");
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
- // ---------- Tool 1: pot_generate ----------
17
- server.tool("pot_generate", "Generate a Proof of Time for a transaction. Returns potHash, timestamp, stratum, and GRG integrity shards.", {
18
- txHash: zod_1.z.string().describe("Transaction hash (hex with 0x prefix)"),
19
- chainId: zod_1.z.number().describe("Chain ID (e.g. 8453 for Base, 84532 for Base Sepolia)"),
20
- poolAddress: zod_1.z.string().describe("DEX pool contract address"),
21
- }, async (args) => {
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
- const result = await (0, tools_1.potGenerate)(args);
24
- return {
25
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
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
- catch (err) {
29
- const message = err instanceof Error ? err.message : String(err);
30
- return {
31
- content: [{ type: "text", text: `Error: ${message}` }],
32
- isError: true,
33
- };
34
- }
35
- });
36
- // ---------- Tool 2: pot_verify ----------
37
- server.tool("pot_verify", "Verify a Proof of Time using its hash and GRG shards. Returns validity, mode (turbo/full), and timestamp.", {
38
- potHash: zod_1.z.string().describe("PoT hash to verify (hex with 0x prefix)"),
39
- grgShards: zod_1.z.array(zod_1.z.string()).describe("Array of hex-encoded GRG integrity shards"),
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
- const result = await (0, tools_1.potVerify)(args);
45
- return {
46
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
47
- };
48
- }
49
- catch (err) {
50
- const message = err instanceof Error ? err.message : String(err);
51
- return {
52
- content: [{ type: "text", text: `Error: ${message}` }],
53
- isError: true,
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
- // ---------- Tool 3: pot_query ----------
58
- server.tool("pot_query", "Query Proof of Time history from local log and on-chain subgraph.", {
59
- startTime: zod_1.z.number().optional().describe("Start time (unix ms). Default: 24h ago"),
60
- endTime: zod_1.z.number().optional().describe("End time (unix ms). Default: now"),
61
- limit: zod_1.z.number().optional().describe("Max entries to return. Default: 100, max: 1000"),
62
- }, async (args) => {
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
- const result = await (0, tools_1.potQuery)(args);
65
- return {
66
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
67
- };
68
- }
69
- catch (err) {
70
- const message = err instanceof Error ? err.message : String(err);
71
- return {
72
- content: [{ type: "text", text: `Error: ${message}` }],
73
- isError: true,
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
- // ---------- Tool 4: pot_stats ----------
78
- server.tool("pot_stats", "Get PoT statistics: total swaps, turbo/full counts, and turbo ratio for a given period.", {
79
- period: zod_1.z.enum(["day", "week", "month"]).describe("Time period for statistics"),
80
- }, async (args) => {
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
- const result = await (0, tools_1.potStats)(args);
83
- return {
84
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
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
- catch (err) {
88
- const message = err instanceof Error ? err.message : String(err);
89
- return {
90
- content: [{ type: "text", text: `Error: ${message}` }],
91
- isError: true,
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
- const result = await (0, tools_1.potHealth)();
99
- return {
100
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
101
- };
102
- }
103
- catch (err) {
104
- const message = err instanceof Error ? err.message : String(err);
105
- return {
106
- content: [{ type: "text", text: `Error: ${message}` }],
107
- isError: true,
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
- // ---------- Start Server ----------
121
+ }
122
+ );
112
123
  async function main() {
113
- const port = process.env.PORT ? parseInt(process.env.PORT, 10) : null;
114
- if (port) {
115
- // HTTP mode for Docker/Glama — StreamableHTTP transport
116
- const transport = new streamableHttp_js_1.StreamableHTTPServerTransport({
117
- sessionIdGenerator: undefined,
118
- });
119
- await server.connect(transport);
120
- const httpServer = (0, http_1.createServer)(async (req, res) => {
121
- // Health check for Docker/Glama container probes
122
- if (req.method === "GET" && (req.url === "/health" || req.url === "/ping")) {
123
- res.writeHead(200, { "Content-Type": "application/json" });
124
- res.end(JSON.stringify({ status: "ok", server: "ttt-mcp", version: "0.1.5" }));
125
- return;
126
- }
127
- // SDK requires both application/json and text/event-stream in Accept.
128
- // Glama sends only application/json — Hono reads rawHeaders (not headers),
129
- // so we must patch rawHeaders directly.
130
- const accept = req.headers["accept"] ?? "";
131
- if (!accept.includes("text/event-stream")) {
132
- const newAccept = accept
133
- ? `${accept}, text/event-stream`
134
- : "application/json, text/event-stream";
135
- req.headers["accept"] = newAccept;
136
- const raw = req.rawHeaders;
137
- const idx = raw.findIndex((h, i) => i % 2 === 0 && h.toLowerCase() === "accept");
138
- if (idx >= 0) {
139
- raw[idx + 1] = newAccept;
140
- }
141
- else {
142
- raw.push("Accept", newAccept);
143
- }
144
- }
145
- try {
146
- await transport.handleRequest(req, res);
147
- }
148
- catch (err) {
149
- if (!res.headersSent) {
150
- res.writeHead(500, { "Content-Type": "application/json" });
151
- res.end(JSON.stringify({ error: "Internal server error" }));
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
- console.error("[ttt-mcp] Fatal:", err);
168
- process.exit(1);
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
- // @helm-protocol/ttt-mcp Anonymous telemetry counter
3
- // Increments a simple counter on each tool call. Fire-and-forget, never blocks.
4
- Object.defineProperty(exports, "__esModule", { value: true });
5
- exports.telemetryIncrement = telemetryIncrement;
6
- exports.getTelemetryCounts = getTelemetryCounts;
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
- counters[toolName] = (counters[toolName] ?? 0) + 1;
15
- // Fire-and-forget POST — no await, no error propagation
16
- try {
17
- const body = JSON.stringify({
18
- query: `{ _meta { block { number } } }`,
19
- extensions: {
20
- telemetry: {
21
- tool: toolName,
22
- count: counters[toolName],
23
- ts: Date.now(),
24
- pkg: "@helm-protocol/ttt-mcp",
25
- v: "0.1.0",
26
- },
27
- },
28
- });
29
- fetch(TELEMETRY_ENDPOINT, {
30
- method: "POST",
31
- headers: { "Content-Type": "application/json" },
32
- body,
33
- signal: AbortSignal.timeout(3000),
34
- }).catch(() => {
35
- // silently ignore — telemetry is best-effort
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
- return { ...counters };
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
- // @helm-protocol/ttt-mcp Tool implementations for Proof of Time MCP Server
3
- // Uses OpenTTT SDK: TimeSynthesis, IntegrityPipeline, PotSigner, AdaptiveSwitch
4
- Object.defineProperty(exports, "__esModule", { value: true });
5
- exports.potGenerate = potGenerate;
6
- exports.potVerify = potVerify;
7
- exports.potQuery = potQuery;
8
- exports.potStats = potStats;
9
- exports.potHealth = potHealth;
10
- const openttt_1 = require("openttt");
11
- const telemetry_1 = require("./telemetry");
12
- // ---------- Shared Instances ----------
13
- const timeSynth = new openttt_1.TimeSynthesis();
14
- const adaptiveSwitch = new openttt_1.AdaptiveSwitch();
15
- const potSigner = new openttt_1.PotSigner(); // ephemeral Ed25519 keypair per server session
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
- // In-memory PoT anchor log (bounded ring buffer)
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
- return typeof value === "bigint" ? value.toString() : value;
37
+ return typeof value === "bigint" ? value.toString() : value;
23
38
  }
24
39
  function serialize(obj) {
25
- return JSON.parse(JSON.stringify(obj, bigintReplacer));
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
- (0, telemetry_1.telemetryIncrement)("pot_generate");
31
- // 1. Synthesize time from multiple NTP/HTTPS sources
32
- const pot = await timeSynth.generateProofOfTime();
33
- const potHash = openttt_1.TimeSynthesis.getOnChainHash(pot);
34
- // 2. GRG pipeline encode tx data into integrity shards (black box)
35
- const txData = new TextEncoder().encode(args.txHash);
36
- const grgShards = openttt_1.GrgPipeline.processForward(txData, args.chainId, args.poolAddress);
37
- // 3. Ed25519 sign the PoT hash for non-repudiation
38
- const signature = potSigner.signPot(potHash);
39
- // 4. Log the anchor
40
- const entry = {
41
- potHash,
42
- timestamp: pot.timestamp.toString(),
43
- stratum: pot.stratum,
44
- mode: adaptiveSwitch.getCurrentMode(),
45
- chainId: args.chainId,
46
- poolAddress: args.poolAddress,
47
- createdAt: Date.now(),
48
- };
49
- potLog.push(entry);
50
- if (potLog.length > POT_LOG_MAX)
51
- potLog.shift();
52
- return serialize({
53
- potHash,
54
- timestamp: pot.timestamp.toString(),
55
- stratum: pot.stratum,
56
- uncertainty: pot.uncertainty,
57
- confidence: pot.confidence,
58
- sources: pot.sources,
59
- nonce: pot.nonce,
60
- expiresAt: pot.expiresAt.toString(),
61
- grgShards: grgShards.map((s) => Buffer.from(s).toString("hex")),
62
- signature: {
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
- (0, telemetry_1.telemetryIncrement)("pot_verify");
72
- const shards = args.grgShards.map((hex) => new Uint8Array(Buffer.from(hex, "hex")));
73
- let valid = false;
74
- let reconstructedSize = 0;
75
- try {
76
- const recovered = openttt_1.GrgPipeline.processInverse(shards, 0, args.chainId, args.poolAddress);
77
- valid = recovered.length > 0;
78
- reconstructedSize = recovered.length;
79
- }
80
- catch {
81
- valid = false;
82
- }
83
- const mode = adaptiveSwitch.getCurrentMode() === openttt_1.AdaptiveMode.TURBO ? "turbo" : "full";
84
- return serialize({
85
- valid,
86
- mode,
87
- potHash: args.potHash,
88
- reconstructedBytes: reconstructedSize,
89
- verifiedAt: Date.now(),
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
- (0, telemetry_1.telemetryIncrement)("pot_query");
95
- const limit = Math.min(args.limit ?? 100, 1000);
96
- const now = Date.now();
97
- const startTime = args.startTime ?? now - 86400_000;
98
- const endTime = args.endTime ?? now;
99
- // Filter from in-memory log
100
- const filtered = potLog
101
- .filter((e) => e.createdAt >= startTime && e.createdAt <= endTime)
102
- .slice(-limit);
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 / 1000)}", blockTimestamp_lte: "${Math.floor(endTime / 1000)}" }
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
- const controller = new AbortController();
120
- const timeout = setTimeout(() => controller.abort(), 5000);
121
- const resp = await fetch(SUBGRAPH_URL, {
122
- method: "POST",
123
- headers: { "Content-Type": "application/json" },
124
- body: JSON.stringify({ query }),
125
- signal: controller.signal,
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
- (0, telemetry_1.telemetryIncrement)("pot_stats");
146
- const now = Date.now();
147
- const periodMs = {
148
- day: 86400_000,
149
- week: 604800_000,
150
- month: 2592000_000,
151
- };
152
- const cutoff = now - (periodMs[args.period] ?? periodMs.day);
153
- const entries = potLog.filter((e) => e.createdAt >= cutoff);
154
- const turboCount = entries.filter((e) => e.mode === openttt_1.AdaptiveMode.TURBO).length;
155
- const fullCount = entries.filter((e) => e.mode === openttt_1.AdaptiveMode.FULL).length;
156
- const totalSwaps = entries.length;
157
- return serialize({
158
- period: args.period,
159
- totalSwaps,
160
- turboCount,
161
- fullCount,
162
- turboRatio: totalSwaps > 0 ? +(turboCount / totalSwaps).toFixed(4) : 0,
163
- currentMode: adaptiveSwitch.getCurrentMode(),
164
- windowStart: new Date(cutoff).toISOString(),
165
- windowEnd: new Date(now).toISOString(),
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
- (0, telemetry_1.telemetryIncrement)("pot_health");
171
- let timeStatus = "unknown";
172
- let synthSources = 0;
173
- try {
174
- const synth = await Promise.race([
175
- timeSynth.synthesize(),
176
- new Promise((_, reject) => setTimeout(() => reject(new Error("timeout")), 5000)),
177
- ]);
178
- if (synth && synth.sources >= 2) {
179
- timeStatus = "healthy";
180
- synthSources = synth.sources;
181
- }
182
- else {
183
- timeStatus = "degraded";
184
- synthSources = synth?.sources ?? 0;
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
- catch {
188
- timeStatus = "unhealthy";
189
- }
190
- let latestBlock = 0;
191
- let syncStatus = "unknown";
192
- try {
193
- const query = `{ _meta { block { number } hasIndexingErrors } }`;
194
- const controller = new AbortController();
195
- const timeout = setTimeout(() => controller.abort(), 5000);
196
- const resp = await fetch(SUBGRAPH_URL, {
197
- method: "POST",
198
- headers: { "Content-Type": "application/json" },
199
- body: JSON.stringify({ query }),
200
- signal: controller.signal,
201
- });
202
- clearTimeout(timeout);
203
- if (resp.ok) {
204
- const json = (await resp.json());
205
- latestBlock = json.data?._meta?.block?.number ?? 0;
206
- syncStatus = json.data?._meta?.hasIndexingErrors ? "indexing_errors" : "synced";
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
- catch {
210
- syncStatus = "unreachable";
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
- const uptimeMs = Date.now() - startedAt;
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.6",
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": "tsc",
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
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env node
2
- export {};
@@ -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>;