@helm-protocol/ttt-mcp 0.1.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 ADDED
@@ -0,0 +1,104 @@
1
+ # @helm-protocol/ttt-mcp
2
+
3
+ **MCP Server for OpenTTT — Proof of Time tools for AI agents**
4
+
5
+ > AI Agent A and Agent B both trigger a payment at the same time.
6
+ > Who was first?
7
+ >
8
+ > OpenTTT answers this with cryptographic Proof of Time — synthesized from
9
+ > multiple independent time sources, verified through GRG integrity shards,
10
+ > and signed with Ed25519 for non-repudiation.
11
+
12
+ ## Quick Start
13
+
14
+ ```bash
15
+ npm install @helm-protocol/ttt-mcp
16
+ ```
17
+
18
+ ```json
19
+ // claude_desktop_config.json
20
+ {
21
+ "mcpServers": {
22
+ "ttt": {
23
+ "command": "npx",
24
+ "args": ["@helm-protocol/ttt-mcp"]
25
+ }
26
+ }
27
+ }
28
+ ```
29
+
30
+ That's it. Your AI agent now has access to 5 Proof of Time tools.
31
+
32
+ ## Tools
33
+
34
+ | Tool | Description |
35
+ |------|-------------|
36
+ | `pot_generate` | Generate a Proof of Time for a transaction |
37
+ | `pot_verify` | Verify a Proof of Time using its hash and GRG shards |
38
+ | `pot_query` | Query PoT history from local log and on-chain subgraph |
39
+ | `pot_stats` | Get turbo/full mode statistics for a time period |
40
+ | `pot_health` | Check system health: time sources, subgraph sync, uptime |
41
+
42
+ ## Example: Generate and Verify a PoT
43
+
44
+ ```typescript
45
+ // In your AI agent's tool call:
46
+ const pot = await pot_generate({
47
+ txHash: "0xabc123...",
48
+ chainId: 84532,
49
+ poolAddress: "0xdef456..."
50
+ });
51
+
52
+ // pot.potHash — unique Proof of Time hash
53
+ // pot.grgShards — GRG integrity shards for verification
54
+ // pot.timestamp — synthesized nanosecond timestamp
55
+ // pot.mode — "turbo" (honest) or "full" (requires full verification)
56
+
57
+ const verification = await pot_verify({
58
+ potHash: pot.potHash,
59
+ grgShards: pot.grgShards
60
+ });
61
+ // verification.valid — true if integrity shards reconstruct correctly
62
+ ```
63
+
64
+ ## How It Works
65
+
66
+ 1. **Time Synthesis** — Queries multiple independent time sources (NIST, Google, Cloudflare) via HTTPS/NTP and synthesizes a median timestamp with uncertainty bounds
67
+ 2. **GRG Pipeline** — Encodes transaction data through a Golomb-Rice + Reed-Solomon + Golay(24,12) integrity pipeline, producing verifiable shards
68
+ 3. **Ed25519 Signing** — Signs the PoT hash for non-repudiation
69
+ 4. **Adaptive Mode** — Honest builders get `turbo` mode (fast, profitable); tampered sequences get `full` mode (slow, costly) — natural economic selection
70
+
71
+ ## Claude Desktop Configuration
72
+
73
+ Add to your `claude_desktop_config.json`:
74
+
75
+ ```json
76
+ {
77
+ "mcpServers": {
78
+ "ttt": {
79
+ "command": "npx",
80
+ "args": ["@helm-protocol/ttt-mcp"]
81
+ }
82
+ }
83
+ }
84
+ ```
85
+
86
+ Config file locations:
87
+ - **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
88
+ - **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
89
+ - **Linux**: `~/.config/Claude/claude_desktop_config.json`
90
+
91
+ ## Requirements
92
+
93
+ - Node.js >= 18
94
+ - Network access for time synthesis (HTTPS to time.nist.gov, time.google.com, time.cloudflare.com)
95
+
96
+ ## Learn More
97
+
98
+ - [OpenTTT SDK](https://www.npmjs.com/package/openttt) — The underlying SDK
99
+ - [IETF Draft: draft-helmprotocol-tttps-00](https://datatracker.ietf.org/doc/draft-helmprotocol-tttps/) — TTTPS Protocol Specification
100
+ - [Helm Protocol](https://github.com/Helm-Protocol) — GitHub
101
+
102
+ ## License
103
+
104
+ MIT
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,116 @@
1
+ #!/usr/bin/env node
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 zod_1 = require("zod");
9
+ const tools_1 = require("./tools");
10
+ const server = new mcp_js_1.McpServer({
11
+ name: "ttt-mcp",
12
+ version: "0.1.0",
13
+ });
14
+ // ---------- Tool 1: pot_generate ----------
15
+ server.tool("pot_generate", "Generate a Proof of Time for a transaction. Returns potHash, timestamp, stratum, and GRG integrity shards.", {
16
+ txHash: zod_1.z.string().describe("Transaction hash (hex with 0x prefix)"),
17
+ chainId: zod_1.z.number().describe("Chain ID (e.g. 8453 for Base, 84532 for Base Sepolia)"),
18
+ poolAddress: zod_1.z.string().describe("DEX pool contract address"),
19
+ }, async (args) => {
20
+ try {
21
+ const result = await (0, tools_1.potGenerate)(args);
22
+ return {
23
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
24
+ };
25
+ }
26
+ catch (err) {
27
+ const message = err instanceof Error ? err.message : String(err);
28
+ return {
29
+ content: [{ type: "text", text: `Error: ${message}` }],
30
+ isError: true,
31
+ };
32
+ }
33
+ });
34
+ // ---------- Tool 2: pot_verify ----------
35
+ server.tool("pot_verify", "Verify a Proof of Time using its hash and GRG shards. Returns validity, mode (turbo/full), and timestamp.", {
36
+ potHash: zod_1.z.string().describe("PoT hash to verify (hex with 0x prefix)"),
37
+ grgShards: zod_1.z.array(zod_1.z.string()).describe("Array of hex-encoded GRG integrity shards"),
38
+ }, async (args) => {
39
+ try {
40
+ const result = await (0, tools_1.potVerify)(args);
41
+ return {
42
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
43
+ };
44
+ }
45
+ catch (err) {
46
+ const message = err instanceof Error ? err.message : String(err);
47
+ return {
48
+ content: [{ type: "text", text: `Error: ${message}` }],
49
+ isError: true,
50
+ };
51
+ }
52
+ });
53
+ // ---------- Tool 3: pot_query ----------
54
+ server.tool("pot_query", "Query Proof of Time history from local log and on-chain subgraph.", {
55
+ startTime: zod_1.z.number().optional().describe("Start time (unix ms). Default: 24h ago"),
56
+ endTime: zod_1.z.number().optional().describe("End time (unix ms). Default: now"),
57
+ limit: zod_1.z.number().optional().describe("Max entries to return. Default: 100, max: 1000"),
58
+ }, async (args) => {
59
+ try {
60
+ const result = await (0, tools_1.potQuery)(args);
61
+ return {
62
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
63
+ };
64
+ }
65
+ catch (err) {
66
+ const message = err instanceof Error ? err.message : String(err);
67
+ return {
68
+ content: [{ type: "text", text: `Error: ${message}` }],
69
+ isError: true,
70
+ };
71
+ }
72
+ });
73
+ // ---------- Tool 4: pot_stats ----------
74
+ server.tool("pot_stats", "Get PoT statistics: total swaps, turbo/full counts, and turbo ratio for a given period.", {
75
+ period: zod_1.z.enum(["day", "week", "month"]).describe("Time period for statistics"),
76
+ }, async (args) => {
77
+ try {
78
+ const result = await (0, tools_1.potStats)(args);
79
+ return {
80
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
81
+ };
82
+ }
83
+ catch (err) {
84
+ const message = err instanceof Error ? err.message : String(err);
85
+ return {
86
+ content: [{ type: "text", text: `Error: ${message}` }],
87
+ isError: true,
88
+ };
89
+ }
90
+ });
91
+ // ---------- Tool 5: pot_health ----------
92
+ server.tool("pot_health", "Check PoT system health: time source status, subgraph sync, server uptime, and current mode.", {}, async () => {
93
+ try {
94
+ const result = await (0, tools_1.potHealth)();
95
+ return {
96
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
97
+ };
98
+ }
99
+ catch (err) {
100
+ const message = err instanceof Error ? err.message : String(err);
101
+ return {
102
+ content: [{ type: "text", text: `Error: ${message}` }],
103
+ isError: true,
104
+ };
105
+ }
106
+ });
107
+ // ---------- Start Server ----------
108
+ async function main() {
109
+ const transport = new stdio_js_1.StdioServerTransport();
110
+ await server.connect(transport);
111
+ console.error("[ttt-mcp] OpenTTT MCP Server running on stdio");
112
+ }
113
+ main().catch((err) => {
114
+ console.error("[ttt-mcp] Fatal:", err);
115
+ process.exit(1);
116
+ });
@@ -0,0 +1,7 @@
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>;
@@ -0,0 +1,45 @@
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;
7
+ const counters = {};
8
+ 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
+ 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
+ }
41
+ }
42
+ /** Get current session counters (for debugging) */
43
+ function getTelemetryCounts() {
44
+ return { ...counters };
45
+ }
@@ -0,0 +1,18 @@
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
+ }): Promise<unknown>;
10
+ export declare function potQuery(args: {
11
+ startTime?: number;
12
+ endTime?: number;
13
+ limit?: number;
14
+ }): Promise<unknown>;
15
+ export declare function potStats(args: {
16
+ period: "day" | "week" | "month";
17
+ }): Promise<unknown>;
18
+ export declare function potHealth(): Promise<unknown>;
package/dist/tools.js ADDED
@@ -0,0 +1,225 @@
1
+ "use strict";
2
+ // @helm-protocol/ttt-mcp — Tool implementations for Proof of Time MCP Server
3
+ // Uses OpenTTT SDK: TimeSynthesis, GrgPipeline, 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
16
+ const startedAt = Date.now();
17
+ // In-memory PoT anchor log (bounded ring buffer)
18
+ const POT_LOG_MAX = 10000;
19
+ const potLog = [];
20
+ // ---------- Helpers ----------
21
+ function bigintReplacer(_key, value) {
22
+ return typeof value === "bigint" ? value.toString() : value;
23
+ }
24
+ function serialize(obj) {
25
+ return JSON.parse(JSON.stringify(obj, bigintReplacer));
26
+ }
27
+ const SUBGRAPH_URL = "https://api.studio.thegraph.com/query/1744392/openttt-base-sepolia/v0.1.0";
28
+ // ---------- Tool: pot_generate ----------
29
+ 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);
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
+ });
68
+ }
69
+ // ---------- Tool: pot_verify ----------
70
+ 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);
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
+ });
91
+ }
92
+ // ---------- Tool: pot_query ----------
93
+ 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 = `{
107
+ potAnchors(
108
+ first: ${limit},
109
+ orderBy: blockTimestamp,
110
+ orderDirection: desc,
111
+ where: { blockTimestamp_gte: "${Math.floor(startTime / 1000)}", blockTimestamp_lte: "${Math.floor(endTime / 1000)}" }
112
+ ) {
113
+ id
114
+ potHash
115
+ blockTimestamp
116
+ txHash
117
+ }
118
+ }`;
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 },
141
+ });
142
+ }
143
+ // ---------- Tool: pot_stats ----------
144
+ 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
+ });
167
+ }
168
+ // ---------- Tool: pot_health ----------
169
+ 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
+ }
186
+ }
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
+ }
208
+ }
209
+ catch {
210
+ syncStatus = "unreachable";
211
+ }
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
+ });
225
+ }
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@helm-protocol/ttt-mcp",
3
+ "version": "0.1.0",
4
+ "description": "MCP Server for OpenTTT — Proof of Time tools for AI agents",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "bin": {
8
+ "ttt-mcp": "./dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "README.md"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsc",
16
+ "start": "node dist/index.js",
17
+ "dev": "npx ts-node index.ts"
18
+ },
19
+ "keywords": [
20
+ "mcp",
21
+ "model-context-protocol",
22
+ "openttt",
23
+ "proof-of-time",
24
+ "ai-agent",
25
+ "defi",
26
+ "mev",
27
+ "transaction-ordering"
28
+ ],
29
+ "license": "MIT",
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "https://github.com/Helm-Protocol/OpenTTT"
33
+ },
34
+ "engines": {
35
+ "node": ">=18"
36
+ },
37
+ "dependencies": {
38
+ "@modelcontextprotocol/sdk": "^1.12.1",
39
+ "openttt": "^0.1.2"
40
+ },
41
+ "devDependencies": {
42
+ "typescript": "^5.3.3",
43
+ "@types/node": "^20.11.19"
44
+ }
45
+ }