@helm-protocol/ttt-mcp 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/README.md +185 -179
  2. package/dist/index.js +166 -167
  3. package/dist/tools.js +277 -9
  4. package/package.json +23 -3
package/README.md CHANGED
@@ -4,291 +4,298 @@
4
4
 
5
5
  **MCP Server for OpenTTT — Proof of Time tools for AI agents**
6
6
 
7
- > AI Agent A and Agent B both trigger a payment at the same time.
8
- > Who was first?
9
- >
10
- > OpenTTT answers this with cryptographic Proof of Time — synthesized from
11
- > multiple independent time sources, verified through GRG integrity shards,
12
- > and signed with Ed25519 for non-repudiation.
7
+ ---
13
8
 
14
- ## Quick Start
9
+ ## The Problem: Workflow Amnesia
15
10
 
16
- ```bash
17
- npm install @helm-protocol/ttt-mcp
11
+ Large Claude Code workflows — 20-agent Dynamic Workflows, multi-day multi-session projects, 100K+ token contexts — all face the same failure mode: **context compression erases action history.**
12
+
13
+ Agent B has no memory of what Agent A decided. Agent A resumes after compression with no record of its own prior steps. Duplicate work. Lost decisions. State corruption.
14
+
15
+ **ttt-mcp is the external nervous system that survives context compression.**
16
+
17
+ Every workflow step is anchored to a cryptographic timestamp on an **external server** — physically separate from Claude's context window. When compression happens, agents query their exact action history through the MCP tools and resume with full causal context.
18
+
19
+ ```
20
+ Claude workflow → [context compressed] → agents call pot_query(eventId)
21
+ → external server returns full timeline
22
+ → workflow resumes, zero lost state
18
23
  ```
19
24
 
25
+ ---
26
+
27
+ ## Mathematical Guarantee
28
+
29
+ | Layer | Mechanism | Guarantee |
30
+ |-------|-----------|-----------|
31
+ | **Identity** | SHA-3 eventId (256-bit) | Collision probability 2⁻²⁵⁶ ≈ 0 — practically 100% exact step recall |
32
+ | **Ordering** | TTTPS causal timestamps | Total order on events — tamper-proof sequence proof |
33
+ | **Causal chain** | prevEventId DAG | O(depth) traversal — depth ~100 for 1B-token workflows |
34
+ | **Fingerprint** | Multi-layer cryptographic pipeline | Formally bounded tamper-evident step identity |
35
+ | **Non-repudiation** | Ed25519 signature | Cryptographic proof of who acted when |
36
+
37
+ ---
38
+
39
+ ## Quick Start
40
+
41
+ ```bash
42
+ # Claude Desktop
20
43
  ```json
21
- // claude_desktop_config.json
22
44
  {
23
45
  "mcpServers": {
24
46
  "ttt": {
25
47
  "command": "npx",
26
- "args": ["@helm-protocol/ttt-mcp"]
48
+ "args": ["-y", "@helm-protocol/ttt-mcp"]
27
49
  }
28
50
  }
29
51
  }
30
52
  ```
31
53
 
32
- That's it. Your AI agent now has access to 5 Proof of Time tools.
54
+ Add `TTT_API_KEY` for unlimited calls (free tier: 100 calls/day per IP).
55
+
56
+ ---
33
57
 
34
58
  ## Tools
35
59
 
36
60
  | Tool | Description |
37
61
  |------|-------------|
38
- | `pot_generate` | Generate a Proof of Time for a transaction |
39
- | `pot_verify` | Verify a Proof of Time using its hash and GRG shards |
40
- | `pot_query` | Query PoT history from local log and on-chain subgraph |
62
+ | `pot_generate` | Stamp a workflow step with eventId + prevEventId (builds causal chain) |
63
+ | `pot_verify` | Verify a Proof of Time using its hash and integrity shards |
64
+ | `pot_query` | O(1) exact lookup by eventId call this after context compression |
65
+ | `pot_graph` | Traverse full causal DAG — backward + forward chain from any step |
41
66
  | `pot_stats` | Get turbo/full mode statistics for a time period |
42
- | `pot_health` | Check system health: time sources, subgraph sync, uptime |
67
+ | `pot_health` | Check system health: time sources, uptime, current mode |
68
+ | `pot_checkpoint` | Create a compressed rollup checkpoint of workflow history |
69
+
70
+ ---
43
71
 
44
72
  ## Tool Parameters
45
73
 
46
74
  ### pot_generate
47
75
 
48
- Generate a Proof of Time for a transaction. Returns potHash, timestamp, stratum, and GRG integrity shards.
49
-
50
- | Parameter | Type | Required | Description |
51
- |-----------|------|----------|-------------|
52
- | txHash | string | Yes | Transaction hash (hex with 0x prefix) |
53
- | chainId | number | Yes | Chain ID (e.g. 8453 for Base, 84532 for Base Sepolia) |
54
- | poolAddress | string | Yes | DEX pool contract address |
55
-
56
- ### pot_verify
57
-
58
- Verify a Proof of Time using its hash and GRG shards. Returns validity, mode (turbo/full), and timestamp.
76
+ Stamp a workflow step with a cryptographic timestamp. For Claude Code: use `eventId` + `prevEventId`. For DeFi: use `txHash` + `chainId` + `poolAddress`. One of `eventId` or `txHash` is required.
59
77
 
60
78
  | Parameter | Type | Required | Description |
61
79
  |-----------|------|----------|-------------|
62
- | potHash | string | Yes | PoT hash to verify (hex with 0x prefix) |
63
- | grgShards | string[] | Yes | Array of hex-encoded GRG integrity shards |
64
- | chainId | number | Yes | EVM chain ID (e.g. 84532 for Base Sepolia) |
65
- | poolAddress | string | Yes | Uniswap V4 pool address (0x-prefixed) |
80
+ | eventId | string | Either/or | Workflow step identifier. E.g. `"refactor_auth_step1"` |
81
+ | prevEventId | string | No | Previous step's eventId links steps into a causal chain |
82
+ | txHash | string | Either/or | Transaction hash (DeFi, hex with 0x prefix) |
83
+ | chainId | number | No | EVM chain ID (DeFi) |
84
+ | poolAddress | string | No | DEX pool contract address (DeFi) |
66
85
 
67
86
  ### pot_query
68
87
 
69
- Query Proof of Time history from local log and on-chain subgraph.
88
+ Query Proof of Time records. Use `eventId` for O(1) exact lookup after context compression.
70
89
 
71
90
  | Parameter | Type | Required | Description |
72
91
  |-----------|------|----------|-------------|
92
+ | eventId | string | No | Exact step lookup — collision probability 2⁻²⁵⁶ |
73
93
  | startTime | number | No | Start time (unix ms). Default: 24h ago |
74
94
  | endTime | number | No | End time (unix ms). Default: now |
75
95
  | limit | number | No | Max entries to return. Default: 100, max: 1000 |
76
96
 
77
- ### pot_stats
97
+ ### pot_graph
78
98
 
79
- Get PoT statistics: total swaps, turbo/full counts, and turbo ratio for a given period.
99
+ Traverse the causal chain from any step. Returns backward chain (ancestors) and forward chain (descendants).
80
100
 
81
101
  | Parameter | Type | Required | Description |
82
102
  |-----------|------|----------|-------------|
83
- | period | `"day"` \| `"week"` \| `"month"` | Yes | Time period for statistics |
84
-
85
- ### pot_health
103
+ | eventId | string | Yes | Step to traverse from |
104
+ | depth | number | No | Max backward depth. Default: 10, max: 100 |
86
105
 
87
- Check PoT system health: time source status, subgraph sync, server uptime, and current mode.
106
+ ### pot_verify
88
107
 
89
108
  | Parameter | Type | Required | Description |
90
109
  |-----------|------|----------|-------------|
91
- | *(none)* | | | This tool takes no parameters |
92
-
93
- ## Example: Generate and Verify a PoT
94
-
95
- ```typescript
96
- // In your AI agent's tool call:
97
- const pot = await pot_generate({
98
- txHash: "0xabc123...",
99
- chainId: 84532,
100
- poolAddress: "0xdef456..."
101
- });
102
-
103
- // pot.potHash — unique Proof of Time hash
104
- // pot.grgShards — GRG integrity shards for verification
105
- // pot.timestamp — synthesized nanosecond timestamp
106
- // pot.mode — "turbo" (honest) or "full" (requires full verification)
107
-
108
- const verification = await pot_verify({
109
- potHash: pot.potHash,
110
- grgShards: pot.grgShards
111
- });
112
- // verification.valid — true if integrity shards reconstruct correctly
113
- ```
110
+ | potHash | string | Yes | PoT hash to verify (hex with 0x prefix) |
111
+ | grgShards | string[] | Yes | Array of hex-encoded cryptographic integrity shards |
112
+ | chainId | number | Yes | EVM chain ID |
113
+ | poolAddress | string | Yes | Uniswap V4 pool address |
114
114
 
115
- ## How It Works
115
+ ### pot_stats
116
116
 
117
- 1. **Time Synthesis** Queries multiple independent time sources (NIST, Google, Cloudflare) via HTTPS/NTP and synthesizes a median timestamp with uncertainty bounds
118
- 2. **GRG Pipeline** — Encodes transaction data through a multi-layer integrity pipeline, producing verifiable shards
119
- 3. **Ed25519 Signing** Signs the PoT hash for non-repudiation
120
- 4. **Adaptive Mode** — Honest builders get `turbo` mode (fast, profitable); tampered sequences get `full` mode (slow, costly) — natural economic selection
117
+ | Parameter | Type | Required | Description |
118
+ |-----------|------|----------|-------------|
119
+ | period | `"day"` \| `"week"` \| `"month"` | Yes | Time period for statistics |
121
120
 
122
- ## Claude Desktop Configuration
121
+ ### pot_health
123
122
 
124
- Add to your `claude_desktop_config.json`:
123
+ No parameters.
125
124
 
126
- ```json
127
- {
128
- "mcpServers": {
129
- "ttt": {
130
- "command": "npx",
131
- "args": ["@helm-protocol/ttt-mcp"]
132
- }
133
- }
134
- }
135
- ```
125
+ ### pot_checkpoint
136
126
 
137
- Config file locations:
138
- - **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
139
- - **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
140
- - **Linux**: `~/.config/Claude/claude_desktop_config.json`
127
+ Creates a compressed rollup checkpoint of workflow history.
141
128
 
142
- ## Requirements
129
+ **Use when:** Approaching context limit, before long tasks, or every ~100 events.
143
130
 
144
- - Node.js >= 18
145
- - Network access for time synthesis (HTTPS to time.nist.gov, time.google.com, time.cloudflare.com)
131
+ | Parameter | Type | Required | Description |
132
+ |-----------|------|----------|-------------|
133
+ | fromEventId | string | No | Start of range — first eventId in the causal chain to include |
134
+ | toEventId | string | No | End of range — last eventId in the causal chain to include |
135
+ | startTime | number | No | Unix ms. Default: 1 hour ago |
136
+ | endTime | number | No | Unix ms. Default: now |
137
+ | maxTokens | number | No | Approximate max tokens for rollup output. Default: 2000 |
138
+
139
+ **Returns:**
140
+ - `checkpointId` — unique checkpoint identifier
141
+ - `rollupSummary` — compressed event history (depth-adaptive: full/compact/minimal/rollup)
142
+ - `chainIntact` — whether the causal chain is unbroken
143
+ - `nextCheckpointHint` — recommended events before next checkpoint
144
+
145
+ **Depth-adaptive compression:**
146
+
147
+ | Depth | Format | ~Tokens |
148
+ |-------|--------|---------|
149
+ | 1–5 | Full entry | ~200/event |
150
+ | 6–20 | Compact (id+hash+ts) | ~80/event |
151
+ | 21–50 | Minimal (id+ts) | ~30/event |
152
+ | 51+ | Rollup string | ~10/event |
146
153
 
147
154
  ---
148
155
 
149
156
  ## Use Cases
150
157
 
151
- ### 1. MEV BotTransaction Ordering Proof
158
+ ### 1. Claude Code Workflow Amnesia Prevention
152
159
 
153
- **Problem**: You got front-run. You know it happened. You can't prove it mempool timestamps are per-node, unsigned, and non-authoritative. No evidence, no recourse.
160
+ **Problem**: A 20-agent Dynamic Workflow refactors a 500K-line codebase over hours. After each context compression, agents have no memory of what they already processed. Duplicate work. State corruption.
154
161
 
155
- **Solution**: Call `pot_generate` before submitting every transaction. The PoT receipt is cryptographically signed by three independent time sources (NIST, Google, Cloudflare), hashed on-chain to Base Sepolia TTT ERC-1155. If front-running occurs, you have a timestamped, on-chain-anchored record of your original submission that predates the attacker's block inclusion.
162
+ **Solution**: Each agent stamps its steps with `pot_generate(eventId, prevEventId)`. After compression, it calls `pot_query(eventId)` to recover its exact action history what ran, when, in what order from the external server. The server is outside Claude's context window; compression never touches it.
156
163
 
157
164
  ```typescript
158
- // Before tx submission
159
- const pot = await client.callTool({ name: "pot_generate", arguments: { txHash: pendingTxHash, chainId: 8453 } });
160
- // Store pot.potHash alongside your trade log
161
- // If front-run: pot.potHash is your evidence, timestamped by NIST+Google+Cloudflare
162
- ```
163
-
164
- **V2 path**: When builder staking goes live, `S(V) ≥ V − c₀` makes reordering economically irrational for any V. Not just evidence — prevention.
165
-
166
- ---
165
+ // Agent starts a workflow step
166
+ const pot = await client.callTool({
167
+ name: "pot_generate",
168
+ arguments: {
169
+ eventId: "refactor_auth_module_step3",
170
+ prevEventId: "refactor_auth_module_step2"
171
+ }
172
+ });
173
+ // pot.potHash — cryptographic proof this step happened at this time
167
174
 
168
- ### 2. DEX Protocol AdaptiveSwitch Sandwich Deterrence
175
+ // After context compression, agent recovers its history:
176
+ const history = await client.callTool({
177
+ name: "pot_query",
178
+ arguments: { eventId: "refactor_auth_module_step3" }
179
+ });
180
+ // history.local[0] — exact record: timestamp, prevEventId, potHash
181
+ // history.found: true — O(1) lookup, collision probability 2⁻²⁵⁶
169
182
 
170
- **Problem**: Small-to-mid value sandwich attacks (V < ~$87) are constant background noise on any AMM. Each one is individually too small to litigate, collectively significant. No governance mechanism moves fast enough to respond.
183
+ // Traverse full causal chain:
184
+ const chain = await client.callTool({
185
+ name: "pot_graph",
186
+ arguments: { eventId: "refactor_auth_module_step3", depth: 20 }
187
+ });
188
+ // chain.backwardChain — all ancestor steps in chronological order
189
+ // chain.forwardChain — steps that follow this one
190
+ ```
171
191
 
172
- **Solution**: Integrate `TTTHookSimple` (Uniswap V4 hook, Base Sepolia: `0x8C633b05b833a476925F7d9818da6E215760F2c7`). Honest builders who preserve PoT-verified ordering get `turbo` mode (~50ms path). Builders who tamper are flagged to `full` mode (~127ms + exponential backoff up to 320 blocks). The 77ms throughput differential makes reordering cost exceed opportunity value for the V* range. No vote. No committee. Economics.
192
+ **Before a long task or every ~100 events create a checkpoint:**
173
193
 
174
194
  ```typescript
175
- // Query current switch state for a pool
176
- const status = await client.callTool({ name: "pot_stats", arguments: { poolAddress: "0x..." } });
177
- // status.adaptiveMode: "turbo" | "full"
178
- // status.currentV_star: estimated MEV threshold being deterred
195
+ // Compress workflow history before context fills up — by causal range:
196
+ const checkpoint = await client.callTool({
197
+ name: "pot_checkpoint",
198
+ arguments: {
199
+ fromEventId: "refactor_auth_module_step1",
200
+ toEventId: "refactor_auth_module_step3"
201
+ }
202
+ });
203
+ // checkpoint.checkpointId — store this; resume from it after compression
204
+ // checkpoint.rollupSummary — depth-adaptive compressed history (10–200 tokens/event)
205
+ // checkpoint.chainIntact: true — causal chain verified unbroken
206
+ // checkpoint.nextCheckpointHint: 87 — suggested events before next checkpoint
207
+
208
+ // Or compress by time window with a token budget:
209
+ const checkpoint = await client.callTool({
210
+ name: "pot_checkpoint",
211
+ arguments: {
212
+ startTime: Date.now() - 3_600_000, // last 1 hour
213
+ maxTokens: 1500
214
+ }
215
+ });
216
+
217
+ // After context compression, restore from checkpoint instead of re-querying all events:
218
+ const history = await client.callTool({
219
+ name: "pot_query",
220
+ arguments: { eventId: checkpoint.checkpointId }
221
+ });
222
+ // Full causal context restored in a single call
179
223
  ```
180
224
 
181
- **Outcome**: ~80% reduction in sub-threshold sandwich attacks. Provable per-block audit trail.
225
+ **Outcome**: Zero duplicate work. Full workflow timeline recoverable even after complete context resets.
182
226
 
183
227
  ---
184
228
 
185
- ### 3. Hedge Fund / Prop Desk MiFIR Art.22c Compliance
229
+ ### 2. MEV BotTransaction Ordering Proof
186
230
 
187
- **Problem**: MiFIR Article 22c / RTS 25 requires microsecond-precision UTC-synchronized timestamps for every trade on regulated venues. The standard hardware solution (PTP/IEEE 1588 appliances) costs $50K–$500K and requires dedicated ops. Most DeFi-adjacent funds run manual reconciliation between two separate timestamp systems.
231
+ **Problem**: You got front-run. You can't prove it mempool timestamps are per-node, unsigned, non-authoritative.
188
232
 
189
- **Solution**: `pot_generate` produces an Ed25519-signed timestamp with uncertainty bound, confidence score, and multi-source attestation. The output is structurally compatible with RTS 25 audit record requirements. No hardware appliance. No dedicated ops. One API call per trade.
233
+ **Solution**: Call `pot_generate` before every submission. The PoT receipt is cryptographically signed by three independent time sources (NIST, Google, Cloudflare), anchored on Base Sepolia TTT ERC-1155. If front-running occurs, you have a timestamped, on-chain record predating the attacker's block inclusion.
190
234
 
191
235
  ```typescript
192
- const audit = await client.callTool({
236
+ const pot = await client.callTool({
193
237
  name: "pot_generate",
194
- arguments: { txHash: tradeHash, chainId: 8453, metadata: { desk: "MACRO-1", trader: "algo-07" } }
238
+ arguments: { txHash: pendingTxHash, chainId: 8453, poolAddress: "0x..." }
195
239
  });
196
- // audit.timestamp: nanosecond precision
197
- // audit.uncertainty: +/- ms bound (required field in RTS 25 record)
198
- // audit.confidence: fraction of sources that agreed
199
- // audit.ed25519_sig: non-repudiation signature
200
- // Export to your compliance system — same format, every trade
240
+ // pot.potHash your evidence, timestamped by NIST+Google+Cloudflare
201
241
  ```
202
242
 
203
- **Outcome**: MiFIR-grade audit trail at ~$0.04/1K calls (DEX tier). Replaces $50K+ hardware setup. IETF standardized via `draft-helmprotocol-tttps-00`.
204
-
205
243
  ---
206
244
 
207
- ### 4. Liquidity ProviderPosition Timeline for Dispute Resolution
245
+ ### 3. DEX ProtocolSandwich Deterrence
208
246
 
209
- **Problem**: LP enters and exits positions based on market conditions. When impermanent loss occurs due to a suspected protocol exploit or ordering manipulation, proving the sequence of events (position entry → exploit event → position exit) requires timestamped evidence that the current stack doesn't provide.
210
-
211
- **Solution**: Stamp every LP action (add liquidity, remove liquidity, fee harvest) with a PoT receipt. The receipt chain creates an unforgeable causal timeline: each action's potHash references the previous, anchored on Base Sepolia. Legally defensible for tax documentation, insurance claims, and protocol dispute resolution.
212
-
213
- ```typescript
214
- // On liquidity add
215
- const entryPot = await client.callTool({ name: "pot_generate", arguments: { txHash: addLiqTx, chainId: 8453 } });
216
-
217
- // On liquidity remove
218
- const exitPot = await client.callTool({ name: "pot_generate", arguments: { txHash: removeLiqTx, chainId: 8453 } });
219
-
220
- // Verify the causal chain
221
- const chain = await client.callTool({ name: "pot_verify", arguments: { potHash: exitPot.potHash, precedingHash: entryPot.potHash } });
222
- // chain.valid: true means exit cryptographically followed entry
223
- ```
247
+ **Solution**: Integrate `TTTHookSimple` (Uniswap V4 hook, Base Sepolia: `0x8C633b05b833a476925F7d9818da6E215760F2c7`). Honest builders get `turbo` mode. Tampered sequences get `full` mode (exponential backoff). Economics, not governance.
224
248
 
225
249
  ---
226
250
 
227
- ### 5. AI Agent CoordinationMulti-Agent Causal Ordering
251
+ ### 4. Hedge Fund / Prop Desk MiFIR Art.22c Compliance
228
252
 
229
- **Problem**: When multiple AI agents interact in a pipeline (Agent A signals → Agent B acts → Agent C settles), the causal order matters for debugging, auditing, and liability. Agent logs are unverifiable—any agent can claim any timestamp.
253
+ **Problem**: MiFIR Article 22c / RTS 25 requires microsecond-precision UTC-synchronized timestamps. Hardware PTP appliances cost $50K–$500K.
230
254
 
231
- **Solution**: Each agent calls `pot_generate` before acting. The resulting potHash chain is independently verifiable: "Agent A's signal at T₁ preceded Agent B's action at T₂" can be proven without trusting either agent's self-reported logs. The on-chain anchor makes the ordering dispute-proof.
255
+ **Solution**: `pot_generate` produces an Ed25519-signed timestamp with uncertainty bound and multi-source attestation. Structurally compatible with RTS 25 audit record requirements. One API call per trade.
232
256
 
233
257
  ```typescript
234
- // Agent A (signal generator)
235
- const signalPot = await client.callTool({ name: "pot_generate", arguments: { txHash: signalId } });
236
-
237
- // Agent B (executor) — references Agent A's pot
238
- const execPot = await client.callTool({
258
+ const audit = await client.callTool({
239
259
  name: "pot_generate",
240
- arguments: { txHash: execId, precedingPotHash: signalPot.potHash }
260
+ arguments: { txHash: tradeHash, chainId: 8453 }
241
261
  });
242
-
243
- // Any third party can verify the causal chain
244
- const verified = await client.callTool({ name: "pot_verify", arguments: { potHash: execPot.potHash, precedingHash: signalPot.potHash } });
262
+ // audit.timestamp: nanosecond precision
263
+ // audit.uncertainty: ±ms bound (RTS 25 required field)
264
+ // audit.confidence: fraction of sources that agreed
245
265
  ```
246
266
 
247
- **Outcome**: Unforgeable causal chain across autonomous agents. Useful for multi-agent DeFi strategies, audit compliance, and cross-agent dispute resolution.
267
+ **Outcome**: MiFIR-grade audit trail. IETF standardized via `draft-helmprotocol-tttps-00`.
248
268
 
249
269
  ---
250
270
 
251
- ## TypeScript: MEV Bot Integration
252
-
253
- ```typescript
254
- import { McpClient } from "@modelcontextprotocol/sdk/client/mcp.js";
255
-
256
- // Generate a Proof of Time for a transaction
257
- const result = await client.callTool({
258
- name: "pot_generate",
259
- arguments: {
260
- txHash: "0xabc123...",
261
- chainId: 8453,
262
- poolAddress: "0xdef456..."
263
- }
264
- });
265
- // Returns: { potHash, timestamp, stratum, grg_shards }
266
- ```
271
+ ### 5. Multi-Agent Coordination — Causal Order Proof
267
272
 
268
- ## Python: Hedge Fund Audit
273
+ **Problem**: When multiple AI agents interact in a pipeline, the causal order matters for debugging and audit. Agent logs are unverifiable.
269
274
 
270
- ```python
271
- import subprocess, json
275
+ **Solution**: Each agent stamps its action with `pot_generate`. The potHash chain is independently verifiable. `pot_graph` reconstructs who did what and in what order.
272
276
 
273
- result = subprocess.run(
274
- ["npx", "-y", "@helm-protocol/ttt-mcp"],
275
- input=json.dumps({
276
- "tool": "pot_verify",
277
- "potHash": "0x...",
278
- "expectedChainId": 8453
279
- }),
280
- capture_output=True, text=True
281
- )
282
- ```
277
+ ---
283
278
 
284
279
  ## Rate Limits & Pricing
285
280
 
286
281
  ```
287
282
  Free Tier: 100 calls/day per IP — no API key needed
288
- Paid Tier: Set TTT_API_KEY env var — unlimited
289
- Commercial: peter@kenosian.com (hedge funds, DEX protocols, OTC desks)
283
+ BOT Tier: $199/mo — unlimited, SLA
284
+ DEX Tier: $499/mo unlimited, priority support
285
+ FUND Tier: $2K+/mo — enterprise, dedicated infra
290
286
  ```
291
287
 
288
+ Contact: heime.jorgen@proton.me
289
+
290
+ ---
291
+
292
+ ## Requirements
293
+
294
+ - Node.js >= 18
295
+ - Network access for time synthesis (HTTPS to time.nist.gov, time.google.com, time.cloudflare.com)
296
+
297
+ ---
298
+
292
299
  ## Learn More
293
300
 
294
301
  - [OpenTTT SDK](https://www.npmjs.com/package/openttt) — The underlying SDK
@@ -299,7 +306,6 @@ Commercial: peter@kenosian.com (hedge funds, DEX protocols, OTC desks)
299
306
 
300
307
  BSL-1.1 — free for non-commercial use.
301
308
 
302
- **Commercial use** (production bots, hedge funds, prop desks) requires a license.
303
- → [kenosian.com/pricing](https://kenosian.com/pricing)
309
+ **Commercial use** (production bots, hedge funds, prop desks) requires a license.
304
310
 
305
311
  Change Date: 2029-05-28 → Apache 2.0
package/dist/index.js CHANGED
@@ -7,188 +7,183 @@ var import_http = require("http");
7
7
  var import_zod = require("zod");
8
8
  var import_tools = require("./tools");
9
9
  var import_auth = require("./auth");
10
- const IS_STDIO = !process.env.PORT;
11
- const server = new import_mcp.McpServer({
12
- name: "ttt-mcp",
13
- version: "0.1.0"
14
- });
15
- server.tool(
16
- "pot_generate",
17
- "Generate a Proof of Time for a transaction. Returns potHash, timestamp, stratum, and GRG integrity shards.",
18
- {
19
- txHash: import_zod.z.string().describe("Transaction hash (hex with 0x prefix)"),
20
- chainId: import_zod.z.number().describe("Chain ID (e.g. 8453 for Base, 84532 for Base Sepolia)"),
21
- poolAddress: import_zod.z.string().describe("DEX pool contract address")
22
- },
23
- async (args) => {
24
- if (IS_STDIO) {
25
- const apiKey = (0, import_auth.resolveApiKey)();
26
- const rl = (0, import_auth.checkRateLimit)(apiKey, "stdio");
27
- if (!rl.allowed) {
28
- return {
29
- content: [{ type: "text", text: JSON.stringify({
30
- error: "rate_limit_exceeded",
31
- message: `Free tier: ${import_auth.FREE_TIER_LIMIT} calls/day. Set TTT_API_KEY env var for unlimited access. \u2192 kenosian.com/pricing`,
32
- remaining: 0,
33
- tier: "free"
34
- }) }],
35
- isError: true
36
- };
10
+ async function restoreDAGFromRedis() {
11
+ try {
12
+ await Promise.race([
13
+ import_tools.redis.connect(),
14
+ new Promise((_, reject) => setTimeout(() => reject(new Error("timeout")), 3e3))
15
+ ]);
16
+ } catch {
17
+ return 0;
18
+ }
19
+ let cursor = "0";
20
+ let restored = 0;
21
+ const entries = [];
22
+ try {
23
+ do {
24
+ const [next, keys] = await import_tools.redis.scan(cursor, "MATCH", "dag:*", "COUNT", "200");
25
+ cursor = next;
26
+ if (keys.length === 0) continue;
27
+ const values = await import_tools.redis.mget(...keys);
28
+ for (const v of values) {
29
+ if (!v) continue;
30
+ try {
31
+ const entry = JSON.parse(v);
32
+ if (entry.eventId) entries.push(entry);
33
+ } catch {
34
+ }
37
35
  }
38
- }
36
+ } while (cursor !== "0");
37
+ } catch {
38
+ return 0;
39
+ }
40
+ entries.sort((a, b) => a.createdAt - b.createdAt);
41
+ for (const e of entries) {
39
42
  try {
40
- const result = await (0, import_tools.potGenerate)(args);
41
- return {
42
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
43
- };
44
- } catch (err) {
45
- const message = err instanceof Error ? err.message : String(err);
46
- return {
47
- content: [{ type: "text", text: `Error: ${message}` }],
48
- isError: true
49
- };
43
+ (0, import_tools.restoreDagEntry)({
44
+ eventId: e.eventId,
45
+ prevEventId: e.prevEventId,
46
+ potHash: e.potHash,
47
+ timestamp: e.timestamp,
48
+ stratum: e.stratum,
49
+ mode: e.mode,
50
+ createdAt: e.createdAt,
51
+ chainId: e.chainId,
52
+ poolAddress: e.poolAddress
53
+ });
54
+ restored++;
55
+ } catch {
50
56
  }
51
57
  }
52
- );
53
- server.tool(
54
- "pot_verify",
55
- "Verify a Proof of Time using its hash and GRG shards. Returns validity, mode (turbo/full), and timestamp.",
56
- {
57
- potHash: import_zod.z.string().describe("PoT hash to verify (hex with 0x prefix)"),
58
- grgShards: import_zod.z.array(import_zod.z.string()).describe("Array of hex-encoded GRG integrity shards"),
59
- chainId: import_zod.z.number().describe("EVM chain ID (e.g. 84532 for Base Sepolia)"),
60
- poolAddress: import_zod.z.string().describe("Uniswap V4 pool address (0x-prefixed)")
61
- },
62
- async (args) => {
63
- if (IS_STDIO) {
64
- const apiKey = (0, import_auth.resolveApiKey)();
65
- const rl = (0, import_auth.checkRateLimit)(apiKey, "stdio");
66
- if (!rl.allowed) {
67
- return {
68
- content: [{ type: "text", text: JSON.stringify({
69
- error: "rate_limit_exceeded",
70
- message: `Free tier: ${import_auth.FREE_TIER_LIMIT} calls/day. Set TTT_API_KEY env var for unlimited access. \u2192 kenosian.com/pricing`,
71
- remaining: 0,
72
- tier: "free"
73
- }) }],
74
- isError: true
75
- };
58
+ if (restored > 0) {
59
+ console.error(`[ttt-mcp] DAG restored from Redis: ${restored} entries`);
60
+ }
61
+ return restored;
62
+ }
63
+ function buildMcpServer() {
64
+ const s = new import_mcp.McpServer({ name: "ttt-mcp", version: "0.1.0" });
65
+ s.tool(
66
+ "pot_generate",
67
+ "Generate a cryptographic Proof of Time timestamp. For Claude Code workflows: use eventId + prevEventId to build a causal chain. For DeFi: use txHash + chainId + poolAddress. Either eventId or txHash is required.",
68
+ {
69
+ eventId: import_zod.z.string().optional().describe("Workflow step identifier (Claude Code). E.g. 'refactor_auth_step1'"),
70
+ prevEventId: import_zod.z.string().optional().describe("Previous step's eventId \u2014 links steps into a causal chain"),
71
+ txHash: import_zod.z.string().optional().describe("Transaction hash (DeFi, hex with 0x prefix)"),
72
+ chainId: import_zod.z.number().optional().describe("EVM chain ID (DeFi, e.g. 8453 for Base)"),
73
+ poolAddress: import_zod.z.string().optional().describe("DEX pool contract address (DeFi)")
74
+ },
75
+ async (args) => {
76
+ try {
77
+ const result = await (0, import_tools.potGenerate)(args);
78
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
79
+ } catch (err) {
80
+ return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
76
81
  }
77
82
  }
78
- try {
79
- const result = await (0, import_tools.potVerify)(args);
80
- return {
81
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
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
- };
83
+ );
84
+ s.tool(
85
+ "pot_verify",
86
+ "Verify a Proof of Time using its hash and integrity shards. Returns validity, mode (turbo/full), and timestamp.",
87
+ {
88
+ potHash: import_zod.z.string().describe("PoT hash to verify (hex with 0x prefix)"),
89
+ grgShards: import_zod.z.array(import_zod.z.string()).describe("Array of hex-encoded cryptographic integrity shards"),
90
+ chainId: import_zod.z.number().describe("EVM chain ID (e.g. 84532 for Base Sepolia)"),
91
+ poolAddress: import_zod.z.string().describe("Uniswap V4 pool address (0x-prefixed)")
92
+ },
93
+ async (args) => {
94
+ try {
95
+ const result = await (0, import_tools.potVerify)(args);
96
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
97
+ } catch (err) {
98
+ return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
99
+ }
89
100
  }
90
- }
91
- );
92
- server.tool(
93
- "pot_query",
94
- "Query Proof of Time history from local log and on-chain subgraph.",
95
- {
96
- startTime: import_zod.z.number().optional().describe("Start time (unix ms). Default: 24h ago"),
97
- endTime: import_zod.z.number().optional().describe("End time (unix ms). Default: now"),
98
- limit: import_zod.z.number().optional().describe("Max entries to return. Default: 100, max: 1000")
99
- },
100
- async (args) => {
101
- if (IS_STDIO) {
102
- const apiKey = (0, import_auth.resolveApiKey)();
103
- const rl = (0, import_auth.checkRateLimit)(apiKey, "stdio");
104
- if (!rl.allowed) {
105
- return {
106
- content: [{ type: "text", text: JSON.stringify({
107
- error: "rate_limit_exceeded",
108
- message: `Free tier: ${import_auth.FREE_TIER_LIMIT} calls/day. Set TTT_API_KEY env var for unlimited access. \u2192 kenosian.com/pricing`,
109
- remaining: 0,
110
- tier: "free"
111
- }) }],
112
- isError: true
113
- };
101
+ );
102
+ s.tool(
103
+ "pot_query",
104
+ "Query Proof of Time records. Use eventId for exact O(1) lookup of a specific workflow step (collision probability 2^-256). Use startTime/endTime for time-range queries.",
105
+ {
106
+ eventId: import_zod.z.string().optional().describe("Exact eventId lookup \u2014 call this at workflow start to restore action history after context compression"),
107
+ startTime: import_zod.z.number().optional().describe("Start time (unix ms). Default: 24h ago"),
108
+ endTime: import_zod.z.number().optional().describe("End time (unix ms). Default: now"),
109
+ limit: import_zod.z.number().optional().describe("Max entries to return. Default: 100, max: 1000")
110
+ },
111
+ async (args) => {
112
+ try {
113
+ const result = await (0, import_tools.potQuery)(args);
114
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
115
+ } catch (err) {
116
+ return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
114
117
  }
115
118
  }
116
- try {
117
- const result = await (0, import_tools.potQuery)(args);
118
- return {
119
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
120
- };
121
- } catch (err) {
122
- const message = err instanceof Error ? err.message : String(err);
123
- return {
124
- content: [{ type: "text", text: `Error: ${message}` }],
125
- isError: true
126
- };
119
+ );
120
+ s.tool(
121
+ "pot_graph",
122
+ "Traverse the causal chain of workflow steps. Given an eventId, returns the full backward chain (ancestors via prevEventId) and forward chain (steps that follow). Use after context compression to reconstruct the complete workflow timeline.",
123
+ {
124
+ eventId: import_zod.z.string().describe("The workflow step to start traversal from"),
125
+ depth: import_zod.z.number().optional().describe("Max backward traversal depth. Default: 10, max: 100")
126
+ },
127
+ async (args) => {
128
+ try {
129
+ const result = await (0, import_tools.potGraph)(args);
130
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
131
+ } catch (err) {
132
+ return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
133
+ }
127
134
  }
128
- }
129
- );
130
- server.tool(
131
- "pot_stats",
132
- "Get PoT statistics: total swaps, turbo/full counts, and turbo ratio for a given period.",
133
- {
134
- period: import_zod.z.enum(["day", "week", "month"]).describe("Time period for statistics")
135
- },
136
- async (args) => {
137
- if (IS_STDIO) {
138
- const apiKey = (0, import_auth.resolveApiKey)();
139
- const rl = (0, import_auth.checkRateLimit)(apiKey, "stdio");
140
- if (!rl.allowed) {
141
- return {
142
- content: [{ type: "text", text: JSON.stringify({
143
- error: "rate_limit_exceeded",
144
- message: `Free tier: ${import_auth.FREE_TIER_LIMIT} calls/day. Set TTT_API_KEY env var for unlimited access. \u2192 kenosian.com/pricing`,
145
- remaining: 0,
146
- tier: "free"
147
- }) }],
148
- isError: true
149
- };
135
+ );
136
+ s.tool(
137
+ "pot_stats",
138
+ "Get PoT statistics: total swaps, turbo/full counts, and turbo ratio for a given period.",
139
+ { period: import_zod.z.enum(["day", "week", "month"]).describe("Time period for statistics") },
140
+ async (args) => {
141
+ try {
142
+ const result = await (0, import_tools.potStats)(args);
143
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
144
+ } catch (err) {
145
+ return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
150
146
  }
151
147
  }
152
- try {
153
- const result = await (0, import_tools.potStats)(args);
154
- return {
155
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
156
- };
157
- } catch (err) {
158
- const message = err instanceof Error ? err.message : String(err);
159
- return {
160
- content: [{ type: "text", text: `Error: ${message}` }],
161
- isError: true
162
- };
148
+ );
149
+ s.tool(
150
+ "pot_health",
151
+ "Check PoT system health: time source status, subgraph sync, server uptime, and current mode.",
152
+ {},
153
+ async () => {
154
+ try {
155
+ const result = await (0, import_tools.potHealth)();
156
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
157
+ } catch (err) {
158
+ return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
159
+ }
163
160
  }
164
- }
165
- );
166
- server.tool(
167
- "pot_health",
168
- "Check PoT system health: time source status, subgraph sync, server uptime, and current mode.",
169
- {},
170
- async () => {
171
- try {
172
- const result = await (0, import_tools.potHealth)();
173
- return {
174
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
175
- };
176
- } catch (err) {
177
- const message = err instanceof Error ? err.message : String(err);
178
- return {
179
- content: [{ type: "text", text: `Error: ${message}` }],
180
- isError: true
181
- };
161
+ );
162
+ s.tool(
163
+ "pot_checkpoint",
164
+ "Create a compressed rollup checkpoint of workflow history. Call this periodically to prevent token explosion when recovering from context compression. Returns checkpointId, compressed event history, chainIntact status, and nextCheckpointHint (recommended events before next checkpoint).",
165
+ {
166
+ fromEventId: import_zod.z.string().optional().describe("Start of range by eventId (optional, use with toEventId)"),
167
+ toEventId: import_zod.z.string().optional().describe("End of range by eventId (optional, use with fromEventId)"),
168
+ startTime: import_zod.z.number().optional().describe("Unix ms start time (optional, default: 1h ago)"),
169
+ endTime: import_zod.z.number().optional().describe("Unix ms end time (optional, default: now)"),
170
+ maxTokens: import_zod.z.number().optional().describe("Approximate max tokens for rollup (default: 2000)")
171
+ },
172
+ async (args) => {
173
+ try {
174
+ const result = await (0, import_tools.potCheckpoint)(args);
175
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
176
+ } catch (err) {
177
+ return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
178
+ }
182
179
  }
183
- }
184
- );
180
+ );
181
+ return s;
182
+ }
185
183
  async function main() {
184
+ await restoreDAGFromRedis();
186
185
  const port = process.env.PORT ? parseInt(process.env.PORT, 10) : null;
187
186
  if (port) {
188
- const transport = new import_streamableHttp.StreamableHTTPServerTransport({
189
- sessionIdGenerator: void 0
190
- });
191
- await server.connect(transport);
192
187
  const httpServer = (0, import_http.createServer)(async (req, res) => {
193
188
  if (req.method === "GET" && (req.url === "/health" || req.url === "/ping")) {
194
189
  res.writeHead(200, { "Content-Type": "application/json" });
@@ -209,7 +204,7 @@ async function main() {
209
204
  res.end(
210
205
  JSON.stringify({
211
206
  error: "rate_limit_exceeded",
212
- message: "Free tier limit reached (100 calls/day). Add X-API-Key header for unlimited access.",
207
+ message: "Free tier: 100 calls/day reached. Contact heime.jorgen@proton.me for commercial access.",
213
208
  tier: "free"
214
209
  })
215
210
  );
@@ -233,6 +228,9 @@ async function main() {
233
228
  }
234
229
  }
235
230
  try {
231
+ const reqServer = buildMcpServer();
232
+ const transport = new import_streamableHttp.StreamableHTTPServerTransport({ sessionIdGenerator: void 0 });
233
+ await reqServer.connect(transport);
236
234
  await transport.handleRequest(req, res);
237
235
  } catch (err) {
238
236
  if (!res.headersSent) {
@@ -245,8 +243,9 @@ async function main() {
245
243
  console.error(`[ttt-mcp] OpenTTT MCP Server (HTTP) on port ${port}`);
246
244
  });
247
245
  } else {
246
+ const stdioServer = buildMcpServer();
248
247
  const transport = new import_stdio.StdioServerTransport();
249
- await server.connect(transport);
248
+ await stdioServer.connect(transport);
250
249
  console.error("[ttt-mcp] OpenTTT MCP Server running on stdio");
251
250
  }
252
251
  }
package/dist/tools.js CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,24 +17,52 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
  var tools_exports = {};
20
30
  __export(tools_exports, {
31
+ potCheckpoint: () => potCheckpoint,
21
32
  potGenerate: () => potGenerate,
33
+ potGraph: () => potGraph,
22
34
  potHealth: () => potHealth,
23
35
  potQuery: () => potQuery,
24
36
  potStats: () => potStats,
25
- potVerify: () => potVerify
37
+ potVerify: () => potVerify,
38
+ redis: () => redis,
39
+ restoreDagEntry: () => restoreDagEntry
26
40
  });
27
41
  module.exports = __toCommonJS(tools_exports);
28
42
  var import_openttt = require("openttt");
29
43
  var import_telemetry = require("./telemetry");
44
+ var import_ioredis = __toESM(require("ioredis"));
45
+ const GrgPipeline = (
46
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
47
+ require("openttt").GrgPipeline ?? null
48
+ );
49
+ const redis = new import_ioredis.default(process.env.REDIS_URL ?? "redis://127.0.0.1:6379", {
50
+ lazyConnect: true,
51
+ enableOfflineQueue: false,
52
+ maxRetriesPerRequest: 1
53
+ });
54
+ redis.on("error", () => {
55
+ });
30
56
  const timeSynth = new import_openttt.TimeSynthesis();
31
57
  const adaptiveSwitch = new import_openttt.AdaptiveSwitch();
32
58
  const potSigner = new import_openttt.PotSigner();
33
59
  const startedAt = Date.now();
34
60
  const POT_LOG_MAX = 1e4;
35
61
  const potLog = [];
62
+ const potByEventId = /* @__PURE__ */ new Map();
63
+ const potByPrevEventId = /* @__PURE__ */ new Map();
64
+ const evictedEventIds = /* @__PURE__ */ new Set();
65
+ const EVICTED_MAX = 1e3;
36
66
  function bigintReplacer(_key, value) {
37
67
  return typeof value === "bigint" ? value.toString() : value;
38
68
  }
@@ -40,26 +70,181 @@ function serialize(obj) {
40
70
  return JSON.parse(JSON.stringify(obj, bigintReplacer));
41
71
  }
42
72
  const SUBGRAPH_URL = "https://api.studio.thegraph.com/query/1744392/openttt-base-sepolia/v0.1.0";
73
+ function compressEntry(entry, depth, depthThreshold) {
74
+ const t = depthThreshold ?? { compact: 5, minimal: 20, rollup: 50 };
75
+ if (depth <= t.compact) return entry;
76
+ if (depth <= t.minimal) return {
77
+ // compact
78
+ eventId: entry.eventId,
79
+ potHash: entry.potHash,
80
+ timestamp: entry.timestamp,
81
+ prevEventId: entry.prevEventId
82
+ };
83
+ if (depth <= t.rollup) return {
84
+ // minimal
85
+ eventId: entry.eventId,
86
+ timestamp: entry.timestamp
87
+ };
88
+ return `${entry.eventId ?? "?"}@${entry.timestamp}`;
89
+ }
90
+ function depthThresholdFromTokens(maxTokens, entryCount) {
91
+ const charBudget = maxTokens * 4;
92
+ const fullBudget = charBudget * 0.6;
93
+ const compactBudget = charBudget * 0.25;
94
+ const minimalBudget = charBudget * 0.1;
95
+ const fullDepth = Math.max(1, Math.floor(fullBudget / 300));
96
+ const compactDepth = fullDepth + Math.max(0, Math.floor(compactBudget / 120));
97
+ const minimalDepth = compactDepth + Math.max(0, Math.floor(minimalBudget / 60));
98
+ return {
99
+ compact: Math.min(fullDepth, entryCount),
100
+ minimal: Math.min(compactDepth, entryCount),
101
+ rollup: Math.min(minimalDepth, entryCount)
102
+ };
103
+ }
104
+ function restoreDagEntry(entry) {
105
+ if (potByEventId.has(entry.eventId)) return;
106
+ const e = {
107
+ potHash: entry.potHash,
108
+ timestamp: entry.timestamp,
109
+ stratum: entry.stratum,
110
+ mode: entry.mode,
111
+ chainId: entry.chainId ?? void 0,
112
+ poolAddress: entry.poolAddress ?? void 0,
113
+ eventId: entry.eventId,
114
+ prevEventId: entry.prevEventId ?? void 0,
115
+ createdAt: entry.createdAt
116
+ };
117
+ if (potLog.length >= POT_LOG_MAX) {
118
+ const evicted = potLog.shift();
119
+ if (evicted.eventId) {
120
+ potByEventId.delete(evicted.eventId);
121
+ evictedEventIds.add(evicted.eventId);
122
+ if (evictedEventIds.size > EVICTED_MAX) {
123
+ const first = evictedEventIds.values().next().value;
124
+ if (first !== void 0) evictedEventIds.delete(first);
125
+ }
126
+ }
127
+ if (evicted.prevEventId) {
128
+ const siblings = potByPrevEventId.get(evicted.prevEventId);
129
+ if (siblings) {
130
+ const filtered = siblings.filter((s) => s !== evicted);
131
+ if (filtered.length === 0) potByPrevEventId.delete(evicted.prevEventId);
132
+ else potByPrevEventId.set(evicted.prevEventId, filtered);
133
+ }
134
+ }
135
+ }
136
+ potLog.push(e);
137
+ potByEventId.set(entry.eventId, e);
138
+ if (entry.prevEventId) {
139
+ const bucket = potByPrevEventId.get(entry.prevEventId) ?? [];
140
+ bucket.push(e);
141
+ potByPrevEventId.set(entry.prevEventId, bucket);
142
+ }
143
+ }
43
144
  async function potGenerate(args) {
145
+ if (!args.eventId && !args.txHash) {
146
+ throw new Error("Either eventId (Claude Code) or txHash (DeFi) is required");
147
+ }
44
148
  (0, import_telemetry.telemetryIncrement)("pot_generate");
45
- const pot = await timeSynth.generateProofOfTime();
149
+ let pot;
150
+ let isOfflineFallback = false;
151
+ try {
152
+ pot = await timeSynth.generateProofOfTime();
153
+ } catch {
154
+ isOfflineFallback = true;
155
+ const nowMs = BigInt(Date.now());
156
+ pot = {
157
+ timestamp: nowMs * 1000000n,
158
+ stratum: 16,
159
+ uncertainty: 999999999,
160
+ confidence: 0,
161
+ sources: 0,
162
+ nonce: Buffer.from(crypto.getRandomValues(new Uint8Array(16))).toString("hex"),
163
+ expiresAt: (nowMs + 300000n) * 1000000n,
164
+ sourceReadings: []
165
+ };
166
+ }
46
167
  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);
168
+ let grgShards = [];
169
+ let currentMode;
170
+ if (args.txHash && args.chainId != null && args.poolAddress) {
171
+ const txData = new TextEncoder().encode(args.txHash);
172
+ grgShards = GrgPipeline.processForward(txData, args.chainId, args.poolAddress).map((s) => Buffer.from(s).toString("hex"));
173
+ const syntheticBlock = {
174
+ timestamp: Number(pot.timestamp / 1000000n),
175
+ // ns → ms
176
+ txs: [args.txHash],
177
+ data: new TextEncoder().encode(args.txHash)
178
+ };
179
+ const syntheticTTTRecord = {
180
+ time: Number(pot.timestamp / 1000000n),
181
+ txOrder: [args.txHash],
182
+ grgPayload: []
183
+ };
184
+ currentMode = adaptiveSwitch.verifyBlock(syntheticBlock, syntheticTTTRecord, args.chainId, args.poolAddress);
185
+ } else {
186
+ currentMode = adaptiveSwitch.getCurrentMode();
187
+ }
49
188
  const signature = potSigner.signPot(potHash);
50
189
  const entry = {
51
190
  potHash,
52
191
  timestamp: pot.timestamp.toString(),
53
192
  stratum: pot.stratum,
54
- mode: adaptiveSwitch.getCurrentMode(),
193
+ mode: currentMode,
55
194
  chainId: args.chainId,
56
195
  poolAddress: args.poolAddress,
196
+ eventId: args.eventId,
197
+ prevEventId: args.prevEventId,
57
198
  createdAt: Date.now()
58
199
  };
200
+ if (potLog.length >= POT_LOG_MAX) {
201
+ const evicted = potLog.shift();
202
+ if (evicted.eventId) {
203
+ potByEventId.delete(evicted.eventId);
204
+ evictedEventIds.add(evicted.eventId);
205
+ if (evictedEventIds.size > EVICTED_MAX) {
206
+ const first = evictedEventIds.values().next().value;
207
+ if (first !== void 0) evictedEventIds.delete(first);
208
+ }
209
+ }
210
+ if (evicted.prevEventId) {
211
+ const siblings = potByPrevEventId.get(evicted.prevEventId);
212
+ if (siblings) {
213
+ const filtered = siblings.filter((e) => e !== evicted);
214
+ if (filtered.length === 0) potByPrevEventId.delete(evicted.prevEventId);
215
+ else potByPrevEventId.set(evicted.prevEventId, filtered);
216
+ }
217
+ }
218
+ }
59
219
  potLog.push(entry);
60
- if (potLog.length > POT_LOG_MAX) potLog.shift();
220
+ if (args.eventId) potByEventId.set(args.eventId, entry);
221
+ if (args.prevEventId) {
222
+ const bucket = potByPrevEventId.get(args.prevEventId) ?? [];
223
+ bucket.push(entry);
224
+ potByPrevEventId.set(args.prevEventId, bucket);
225
+ }
226
+ if (args.eventId) {
227
+ redis.setex(
228
+ `dag:${args.eventId}`,
229
+ 90 * 86400,
230
+ JSON.stringify({
231
+ eventId: args.eventId,
232
+ prevEventId: args.prevEventId ?? null,
233
+ potHash,
234
+ timestamp: pot.timestamp.toString(),
235
+ stratum: pot.stratum,
236
+ mode: currentMode,
237
+ createdAt: entry.createdAt,
238
+ chainId: args.chainId ?? null,
239
+ poolAddress: args.poolAddress ?? null
240
+ })
241
+ ).catch(() => {
242
+ });
243
+ }
61
244
  return serialize({
62
245
  potHash,
246
+ eventId: args.eventId ?? null,
247
+ prevEventId: args.prevEventId ?? null,
63
248
  timestamp: pot.timestamp.toString(),
64
249
  stratum: pot.stratum,
65
250
  uncertainty: pot.uncertainty,
@@ -67,7 +252,8 @@ async function potGenerate(args) {
67
252
  sources: pot.sources,
68
253
  nonce: pot.nonce,
69
254
  expiresAt: pot.expiresAt.toString(),
70
- grgShards: grgShards.map((s) => Buffer.from(s).toString("hex")),
255
+ ...isOfflineFallback && { mode: "local" },
256
+ ...grgShards.length > 0 && { grgShards },
71
257
  signature: {
72
258
  issuerPubKey: signature.issuerPubKey,
73
259
  signature: signature.signature,
@@ -81,7 +267,7 @@ async function potVerify(args) {
81
267
  let valid = false;
82
268
  let reconstructedSize = 0;
83
269
  try {
84
- const recovered = import_openttt.GrgPipeline.processInverse(shards, 0, args.chainId, args.poolAddress);
270
+ const recovered = GrgPipeline.processInverse(shards, 0, args.chainId, args.poolAddress);
85
271
  valid = recovered.length > 0;
86
272
  reconstructedSize = recovered.length;
87
273
  } catch {
@@ -98,6 +284,16 @@ async function potVerify(args) {
98
284
  }
99
285
  async function potQuery(args) {
100
286
  (0, import_telemetry.telemetryIncrement)("pot_query");
287
+ if (args.eventId) {
288
+ const entry = potByEventId.get(args.eventId);
289
+ return serialize({
290
+ local: entry ? [entry] : [],
291
+ subgraph: [],
292
+ found: !!entry,
293
+ totalLocal: potLog.length,
294
+ query: { eventId: args.eventId }
295
+ });
296
+ }
101
297
  const limit = Math.min(args.limit ?? 100, 1e3);
102
298
  const now = Date.now();
103
299
  const startTime = args.startTime ?? now - 864e5;
@@ -140,6 +336,35 @@ async function potQuery(args) {
140
336
  query: { startTime, endTime, limit }
141
337
  });
142
338
  }
339
+ async function potGraph(args) {
340
+ (0, import_telemetry.telemetryIncrement)("pot_graph");
341
+ const maxDepth = Math.min(args.depth ?? 10, 100);
342
+ const backwardChain = [];
343
+ let cursor = potByEventId.get(args.eventId);
344
+ let d = 0;
345
+ while (cursor && d < maxDepth) {
346
+ backwardChain.unshift(cursor);
347
+ cursor = cursor.prevEventId ? potByEventId.get(cursor.prevEventId) : void 0;
348
+ d++;
349
+ }
350
+ const forwardChain = potByPrevEventId.get(args.eventId) ?? [];
351
+ const found = potByEventId.has(args.eventId);
352
+ const chainBroken = backwardChain.some((e) => e.eventId && evictedEventIds.has(e.eventId)) || cursor?.prevEventId != null && potByEventId.get(cursor.prevEventId) == null;
353
+ const chainRoot = backwardChain.length > 0 ? backwardChain[0] : null;
354
+ const isServerRestart = chainBroken && chainRoot !== null && chainRoot.prevEventId != null && !potByEventId.has(chainRoot.prevEventId) && !evictedEventIds.has(chainRoot.prevEventId);
355
+ const brokenAt = chainBroken ? isServerRestart ? "server_restart" : backwardChain[0]?.eventId ?? null : null;
356
+ const compressedBackward = backwardChain.map((e, i) => compressEntry(e, i + 1));
357
+ return serialize({
358
+ eventId: args.eventId,
359
+ found,
360
+ backwardChain: compressedBackward,
361
+ forwardChain,
362
+ chainLength: backwardChain.length + forwardChain.length,
363
+ reachableDepth: backwardChain.length,
364
+ chainBroken,
365
+ brokenAt
366
+ });
367
+ }
143
368
  async function potStats(args) {
144
369
  (0, import_telemetry.telemetryIncrement)("pot_stats");
145
370
  const now = Date.now();
@@ -220,11 +445,54 @@ async function potHealth() {
220
445
  }
221
446
  });
222
447
  }
448
+ async function potCheckpoint(args) {
449
+ (0, import_telemetry.telemetryIncrement)("pot_checkpoint");
450
+ const now = Date.now();
451
+ const startTime = args.startTime ?? now - 36e5;
452
+ const endTime = args.endTime ?? now;
453
+ let entries;
454
+ if (args.fromEventId && args.toEventId) {
455
+ entries = [];
456
+ let cursor = potByEventId.get(args.fromEventId);
457
+ const maxDepth = 1e3;
458
+ let d = 0;
459
+ while (cursor && d < maxDepth) {
460
+ entries.push(cursor);
461
+ if (cursor.eventId === args.toEventId) break;
462
+ const nexts = potByPrevEventId.get(cursor.eventId ?? "") ?? [];
463
+ cursor = nexts[0];
464
+ d++;
465
+ }
466
+ } else {
467
+ entries = potLog.filter((e) => e.createdAt >= startTime && e.createdAt <= endTime);
468
+ }
469
+ const eventCount = entries.length;
470
+ const depthThreshold = args.maxTokens ? depthThresholdFromTokens(args.maxTokens, entries.length) : void 0;
471
+ const compressed = entries.map((e, i) => compressEntry(e, i + 1, depthThreshold));
472
+ const chainIntact = !entries.some((e) => e.eventId && evictedEventIds.has(e.eventId));
473
+ const nextCheckpointHint = Math.max(10, 100 - eventCount % 100);
474
+ const checkpointId = `ckpt_${now}_${eventCount}`;
475
+ const firstTs = entries[0]?.timestamp ?? null;
476
+ const lastTs = entries[entries.length - 1]?.timestamp ?? null;
477
+ return serialize({
478
+ checkpointId,
479
+ eventCount,
480
+ chainIntact,
481
+ nextCheckpointHint,
482
+ rollup: compressed,
483
+ summary: `${eventCount} events from ${firstTs ?? "?"} to ${lastTs ?? "?"}`,
484
+ generatedAt: now
485
+ });
486
+ }
223
487
  // Annotate the CommonJS export names for ESM import in node:
224
488
  0 && (module.exports = {
489
+ potCheckpoint,
225
490
  potGenerate,
491
+ potGraph,
226
492
  potHealth,
227
493
  potQuery,
228
494
  potStats,
229
- potVerify
495
+ potVerify,
496
+ redis,
497
+ restoreDagEntry
230
498
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@helm-protocol/ttt-mcp",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "description": "MCP Server for OpenTTT — Proof of Time tools for AI agents",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -13,7 +13,23 @@
13
13
  "scripts": {
14
14
  "build": "esbuild index.ts tools.ts telemetry.ts auth.ts --platform=node --target=node18 --format=cjs --outdir=dist",
15
15
  "start": "node dist/index.js",
16
- "dev": "npx ts-node index.ts"
16
+ "dev": "npx ts-node index.ts",
17
+ "test": "jest --forceExit"
18
+ },
19
+ "jest": {
20
+ "preset": "ts-jest",
21
+ "testEnvironment": "node",
22
+ "testMatch": ["**/__tests__/**/*.test.ts"],
23
+ "moduleNameMapper": {
24
+ "^openttt$": "<rootDir>/node_modules/openttt/dist/index.js"
25
+ },
26
+ "transform": {
27
+ "^.+\\.tsx?$": ["ts-jest", {
28
+ "tsconfig": {
29
+ "module": "CommonJS"
30
+ }
31
+ }]
32
+ }
17
33
  },
18
34
  "keywords": [
19
35
  "mcp",
@@ -36,12 +52,16 @@
36
52
  },
37
53
  "dependencies": {
38
54
  "@modelcontextprotocol/sdk": "^1.12.1",
39
- "openttt": "^0.2.6",
55
+ "ioredis": "^5.11.0",
56
+ "openttt": "^0.2.13",
40
57
  "zod": "^3.25.0"
41
58
  },
42
59
  "devDependencies": {
60
+ "@types/jest": "^30.0.0",
43
61
  "@types/node": "^20.11.19",
44
62
  "esbuild": "^0.27.4",
63
+ "jest": "^30.4.2",
64
+ "ts-jest": "^29.4.11",
45
65
  "typescript": "^5.3.3"
46
66
  },
47
67
  "mcpName": "io.github.Helm-Protocol/openttt-pot"