@helm-protocol/ttt-mcp 0.1.8 → 0.2.1

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/LICENSE CHANGED
@@ -1,21 +1,22 @@
1
- MIT License
1
+ Business Source License 1.1
2
2
 
3
- Copyright (c) 2026 Helm Protocol Foundation
3
+ Parameters
4
4
 
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
5
+ Licensor: Heime-Jorgen (Peter Jang)
6
+ Licensed Work: @helm-protocol/ttt-mcp
7
+ Change Date: 2029-05-28
8
+ Change License: Apache License, Version 2.0
11
9
 
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
10
+ Additional Use Grant:
11
+ The following uses are permitted without a commercial license:
12
+ - Non-commercial use (research, education, personal projects)
13
+ - Commercial use generating less than $10,000 USD/year in revenue
14
+ - Individual MEV bots or arbitrage bots operated for personal account
14
15
 
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
16
+ For all other commercial use, a license is required. See:
17
+ https://kenosian.com/pricing
18
+
19
+ ---
20
+
21
+ Business Source License 1.1 terms apply. The full text is available at:
22
+ https://mariadb.com/bsl11/
package/README.md CHANGED
@@ -144,6 +144,47 @@ Config file locations:
144
144
  - Node.js >= 18
145
145
  - Network access for time synthesis (HTTPS to time.nist.gov, time.google.com, time.cloudflare.com)
146
146
 
147
+ ## TypeScript: MEV Bot Integration
148
+
149
+ ```typescript
150
+ import { McpClient } from "@modelcontextprotocol/sdk/client/mcp.js";
151
+
152
+ // Generate a Proof of Time for a transaction
153
+ const result = await client.callTool({
154
+ name: "pot_generate",
155
+ arguments: {
156
+ txHash: "0xabc123...",
157
+ chainId: 8453,
158
+ poolAddress: "0xdef456..."
159
+ }
160
+ });
161
+ // Returns: { potHash, timestamp, stratum, grg_shards }
162
+ ```
163
+
164
+ ## Python: Hedge Fund Audit
165
+
166
+ ```python
167
+ import subprocess, json
168
+
169
+ result = subprocess.run(
170
+ ["npx", "-y", "@helm-protocol/ttt-mcp"],
171
+ input=json.dumps({
172
+ "tool": "pot_verify",
173
+ "potHash": "0x...",
174
+ "expectedChainId": 8453
175
+ }),
176
+ capture_output=True, text=True
177
+ )
178
+ ```
179
+
180
+ ## Rate Limits & Pricing
181
+
182
+ ```
183
+ Free Tier: 100 calls/day per IP — no API key needed
184
+ Paid Tier: Set TTT_API_KEY env var — unlimited
185
+ Commercial: peter@kenosian.com (hedge funds, DEX protocols, OTC desks)
186
+ ```
187
+
147
188
  ## Learn More
148
189
 
149
190
  - [OpenTTT SDK](https://www.npmjs.com/package/openttt) — The underlying SDK
@@ -152,4 +193,4 @@ Config file locations:
152
193
 
153
194
  ## License
154
195
 
155
- MIT
196
+ BSL-1.1
package/dist/auth.js CHANGED
@@ -1,9 +1,7 @@
1
1
  "use strict";
2
- var __create = Object.create;
3
2
  var __defProp = Object.defineProperty;
4
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
6
  var __export = (target, all) => {
9
7
  for (var name in all)
@@ -17,241 +15,52 @@ var __copyProps = (to, from, except, desc) => {
17
15
  }
18
16
  return to;
19
17
  };
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
- ));
28
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
19
  var auth_exports = {};
30
20
  __export(auth_exports, {
31
- AUTH_ERROR_MESSAGE: () => AUTH_ERROR_MESSAGE,
32
- verifyHelmApiKey: () => verifyHelmApiKey
21
+ checkRateLimit: () => checkRateLimit,
22
+ resolveApiKey: () => resolveApiKey
33
23
  });
34
24
  module.exports = __toCommonJS(auth_exports);
35
- var import_crypto = require("crypto");
36
- var https = __toESM(require("https"));
37
- var import_crypto2 = require("crypto");
38
- const HELM_PUBLIC_KEY_B64 = process.env.HELM_PUBLIC_KEY ?? "PLACEHOLDER_REPLACE_BEFORE_DEPLOY";
39
- const HELM_AUTH_SERVER_URL = process.env.HELM_AUTH_SERVER_URL;
40
- if (HELM_AUTH_SERVER_URL !== void 0) {
41
- if (!HELM_AUTH_SERVER_URL.startsWith("https://")) {
42
- throw new Error(
43
- "[ttt-mcp] HELM_AUTH_SERVER_URL must use HTTPS (got: " + HELM_AUTH_SERVER_URL + "). Refusing to start to protect API keys in transit."
44
- );
45
- }
46
- }
47
- const CLIENT_VERSION = "0.2.0";
48
- const authCache = /* @__PURE__ */ new Map();
49
- function getCached(apiKey, toolName) {
50
- const key = `${apiKey}:${toolName}`;
51
- const entry = authCache.get(key);
52
- if (!entry) return null;
53
- if (Date.now() > entry.expiresAt) {
54
- authCache.delete(key);
55
- return null;
56
- }
57
- return entry;
58
- }
59
- function setCached(apiKey, toolName, allowed, planTier, cacheTtlMs) {
60
- const key = `${apiKey}:${toolName}`;
61
- authCache.set(key, {
62
- allowed,
63
- planTier,
64
- expiresAt: Date.now() + cacheTtlMs
65
- });
66
- }
67
- const CIRCUIT_FAILURE_THRESHOLD = 3;
68
- const CIRCUIT_OPEN_DURATION_MS = 5 * 60 * 1e3;
69
- const circuit = {
70
- state: "CLOSED",
71
- consecutiveFailures: 0,
72
- openedAt: 0
73
- };
74
- function circuitRecordSuccess() {
75
- circuit.consecutiveFailures = 0;
76
- circuit.state = "CLOSED";
77
- }
78
- function circuitRecordFailure() {
79
- circuit.consecutiveFailures += 1;
80
- if (circuit.consecutiveFailures >= CIRCUIT_FAILURE_THRESHOLD) {
81
- circuit.state = "OPEN";
82
- circuit.openedAt = Date.now();
83
- }
84
- }
85
- function circuitCanAttempt() {
86
- if (circuit.state === "CLOSED") return true;
87
- if (circuit.state === "OPEN") {
88
- if (Date.now() - circuit.openedAt >= CIRCUIT_OPEN_DURATION_MS) {
89
- circuit.state = "HALF_OPEN";
90
- return true;
91
- }
92
- return false;
93
- }
94
- return true;
25
+ const FREE_TIER_LIMIT = parseInt(process.env.FREE_TIER_LIMIT ?? "100", 10);
26
+ const buckets = /* @__PURE__ */ new Map();
27
+ function nextMidnightUtc() {
28
+ const now = /* @__PURE__ */ new Date();
29
+ const midnight = new Date(
30
+ Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() + 1)
31
+ );
32
+ return midnight.getTime();
95
33
  }
96
- function callAuthServer(serverUrl, apiKey, toolName) {
97
- return new Promise((resolve, reject) => {
98
- const requestId = (0, import_crypto2.randomUUID)();
99
- const body = JSON.stringify({
100
- api_key: apiKey,
101
- tool_name: toolName,
102
- request_id: requestId,
103
- timestamp_ms: Date.now(),
104
- client_version: CLIENT_VERSION
105
- });
106
- const url = new URL("/v1/auth/verify", serverUrl);
107
- const options = {
108
- hostname: url.hostname,
109
- port: url.port || 443,
110
- path: url.pathname,
111
- method: "POST",
112
- headers: {
113
- "Content-Type": "application/json",
114
- "Content-Length": Buffer.byteLength(body),
115
- "X-Request-ID": requestId
116
- }
117
- };
118
- const req = https.request(options, (res) => {
119
- let data = "";
120
- res.on("data", (chunk) => {
121
- data += chunk.toString();
122
- });
123
- res.on("end", () => {
124
- try {
125
- const parsed = JSON.parse(data);
126
- resolve(parsed);
127
- } catch {
128
- reject(new Error("Auth server returned non-JSON response"));
129
- }
130
- });
131
- });
132
- req.setTimeout(3e3, () => {
133
- req.destroy(new Error("Auth server request timed out (3000ms)"));
134
- });
135
- req.on("error", (err) => {
136
- reject(err);
137
- });
138
- req.write(body);
139
- req.end();
140
- });
141
- }
142
- async function serverSideGate(apiKey, toolName) {
143
- if (!HELM_AUTH_SERVER_URL) {
144
- return null;
145
- }
146
- const cached = getCached(apiKey, toolName);
147
- if (cached !== null) {
148
- if (!cached.allowed) {
149
- return `Access denied by Helm Auth Server (plan: ${cached.planTier}, cached)`;
150
- }
151
- return null;
152
- }
153
- if (!circuitCanAttempt()) {
154
- return "Helm Auth Server is temporarily unreachable. Access denied (fail-closed). Please retry in a few minutes.";
155
- }
156
- try {
157
- const resp = await callAuthServer(HELM_AUTH_SERVER_URL, apiKey, toolName);
158
- const ttl = typeof resp.cache_ttl_ms === "number" && resp.cache_ttl_ms > 0 ? resp.cache_ttl_ms : 3e5;
159
- setCached(apiKey, toolName, resp.allowed, resp.plan_tier ?? "unknown", ttl);
160
- circuitRecordSuccess();
161
- if (!resp.allowed) {
162
- const reason = resp.reason ? ` Reason: ${resp.reason}` : "";
163
- return `Access denied by Helm Auth Server (plan: ${resp.plan_tier}).${reason}`;
164
- }
165
- return null;
166
- } catch {
167
- circuitRecordFailure();
168
- return "Helm Auth Server is unreachable. Access denied (fail-closed). Please retry later.";
34
+ function checkRateLimit(apiKey, clientIp) {
35
+ if (apiKey && apiKey.trim().length > 0) {
36
+ return { allowed: true, remaining: -1, tier: "paid" };
169
37
  }
170
- }
171
- async function verifyHelmApiKey(apiKey, opts) {
172
- if (!apiKey) {
173
- return { valid: false, error: "No API key provided" };
174
- }
175
- if (!apiKey.startsWith("hk-")) {
176
- return { valid: false, error: "Invalid key format" };
177
- }
178
- if (HELM_PUBLIC_KEY_B64 === "PLACEHOLDER_REPLACE_BEFORE_DEPLOY") {
179
- if (process.env.HELM_DEV_MODE === "true") {
180
- return { valid: true, tier: 0, orgId: "dev-mode" };
181
- }
182
- return { valid: false, error: "Server misconfiguration: HELM_PUBLIC_KEY not set" };
38
+ const bucketKey = `ip:${clientIp}`;
39
+ const now = Date.now();
40
+ let entry = buckets.get(bucketKey);
41
+ if (!entry || now >= entry.resetAt) {
42
+ entry = { count: 0, resetAt: nextMidnightUtc() };
43
+ buckets.set(bucketKey, entry);
183
44
  }
184
- const jwt = apiKey.slice(3);
185
- const parts = jwt.split(".");
186
- if (parts.length !== 3) {
187
- return { valid: false, error: "Malformed JWT" };
188
- }
189
- try {
190
- const payload = JSON.parse(
191
- Buffer.from(parts[1], "base64url").toString("utf8")
192
- );
193
- if (typeof payload.exp !== "number" || payload.exp < Math.floor(Date.now() / 1e3)) {
194
- return { valid: false, error: "Key expired" };
195
- }
196
- if (payload.iss !== "helm-protocol") {
197
- return { valid: false, error: "Invalid issuer" };
198
- }
199
- const publicKey = (0, import_crypto.createPublicKey)({
200
- key: Buffer.from(HELM_PUBLIC_KEY_B64, "base64"),
201
- format: "der",
202
- type: "spki"
203
- });
204
- const sigInput = Buffer.from(`${parts[0]}.${parts[1]}`);
205
- const sig = Buffer.from(parts[2], "base64url");
206
- const isValid = (0, import_crypto.verify)(null, sigInput, publicKey, sig);
207
- if (!isValid) {
208
- return { valid: false, error: "Invalid signature" };
209
- }
210
- if (opts?.redisClient && payload.type === "short" && payload.jti) {
211
- const redisKey = `helm:jti:${payload.jti}`;
212
- const ttl = Math.max(
213
- 1,
214
- payload.exp - Math.floor(Date.now() / 1e3)
215
- );
216
- const isNew = await opts.redisClient.set(redisKey, "1", {
217
- NX: true,
218
- EX: ttl
219
- });
220
- if (isNew === null) {
221
- opts.onReplayDetected?.(payload.jti);
222
- return { valid: false, error: "Replay detected" };
223
- }
224
- }
225
- if (opts?.toolName) {
226
- const gateError = await serverSideGate(apiKey, opts.toolName);
227
- if (gateError !== null) {
228
- return { valid: false, error: gateError };
229
- }
230
- }
45
+ if (entry.count >= FREE_TIER_LIMIT) {
231
46
  return {
232
- valid: true,
233
- tier: typeof payload.tier === "number" ? payload.tier : 0,
234
- orgId: payload.sub
47
+ allowed: false,
48
+ remaining: 0,
49
+ tier: "free"
235
50
  };
236
- } catch {
237
- return { valid: false, error: "Verification error" };
238
51
  }
52
+ entry.count += 1;
53
+ return {
54
+ allowed: true,
55
+ remaining: FREE_TIER_LIMIT - entry.count,
56
+ tier: "free"
57
+ };
58
+ }
59
+ function resolveApiKey(headerValue) {
60
+ return headerValue?.trim() || process.env.TTT_API_KEY?.trim() || void 0;
239
61
  }
240
- const AUTH_ERROR_MESSAGE = `This tool requires a Helm Protocol API key.
241
-
242
- To get access:
243
- 1. Contact enterprise@helmprotocol.io
244
- 2. Add the key to your MCP config:
245
- { "env": { "HELM_API_KEY": "hk-..." } }
246
-
247
- Optional server-side validation:
248
- Set HELM_AUTH_SERVER_URL=https://auth.helm-protocol.com for additional
249
- server-side gate (quota enforcement, revocation, plan tier checks).
250
- HTTPS is required \u2014 HTTP URLs are rejected at startup.
251
-
252
- Free tools available without a key: pot_health, pot_stats`;
253
62
  // Annotate the CommonJS export names for ESM import in node:
254
63
  0 && (module.exports = {
255
- AUTH_ERROR_MESSAGE,
256
- verifyHelmApiKey
64
+ checkRateLimit,
65
+ resolveApiKey
257
66
  });