@rel-time/mcp-server 1.2.1 → 1.2.3
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 +8 -7
- package/index.js +154 -19
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,25 +10,26 @@ Official Model Context Protocol (MCP) server for **[Rel-Time](https://rel-time.c
|
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
-
## Quick Setup (Claude Desktop / Cursor / Windsurf / Antigravity)
|
|
13
|
+
## Quick Setup (Claude Desktop / Claude Code / Cursor / Windsurf / Antigravity)
|
|
14
14
|
|
|
15
|
-
Add the following to your `claude_desktop_config.json
|
|
15
|
+
Add the following to your `.mcp.json`, `claude_desktop_config.json`, or `.cursor/mcp.json` (no API key or `.zshrc` environment variables needed to start!):
|
|
16
16
|
|
|
17
17
|
```json
|
|
18
18
|
{
|
|
19
19
|
"mcpServers": {
|
|
20
20
|
"rel-time": {
|
|
21
21
|
"command": "npx",
|
|
22
|
-
"args": ["-y", "@rel-time/mcp-server"]
|
|
23
|
-
"env": {
|
|
24
|
-
"RELTIME_API_KEY": "YOUR_RELTIME_API_KEY"
|
|
25
|
-
}
|
|
22
|
+
"args": ["-y", "@rel-time/mcp-server@latest"]
|
|
26
23
|
}
|
|
27
24
|
}
|
|
28
25
|
}
|
|
29
26
|
```
|
|
30
27
|
|
|
31
|
-
|
|
28
|
+
When you start a fresh chat and ask to anchor a file or provision a key, `@rel-time/mcp-server` automatically prompts your agent to ask which pack you want first:
|
|
29
|
+
1. **Agent Launch Pack (`pack: "launch"`, `$5 / 50 Hashes`):** Priority 10-Minute Bitcoin Queue · Standard Mode only (Data Integrity + Biometric or Anonymous) · Includes 64-Ray Sun-Stamp, 1-Click Stamped Copy (`.PDF`/`.PNG`), and `.JSON` Doomsday Proof.
|
|
30
|
+
2. **Autonomous Agents (`pack: "standard"`, `$50 / 500 Hashes`):** Dynamic 6-Hour Batch Queue · Supports both Temporal Mode (`T_index` sub-second VDF sequencing) and Standard Mode · Reloadable anytime (`+500` credits / `$50`).
|
|
31
|
+
|
|
32
|
+
Once paid via Lightning (or if you pass an existing `rel_key_...` key), the MCP server automatically saves your API key to `~/.reltime/api_key.json` (`0600` permissions) — no app restart or `.zshrc` edits required.
|
|
32
33
|
|
|
33
34
|
---
|
|
34
35
|
|
package/index.js
CHANGED
|
@@ -20,18 +20,123 @@ const { exec } = require("child_process");
|
|
|
20
20
|
const { PDFDocument } = require("pdf-lib");
|
|
21
21
|
const sharp = require("sharp");
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
function normalizeApiBase(rawUrl) {
|
|
24
|
+
const cleaned = (rawUrl || "https://rel-time.com/api/v1").trim().replace(/\/+$/, "");
|
|
25
|
+
if (
|
|
26
|
+
cleaned === "https://rel-time.com/v1" ||
|
|
27
|
+
cleaned === "https://rel-time.com" ||
|
|
28
|
+
cleaned.includes("api.rel-time.com")
|
|
29
|
+
) {
|
|
30
|
+
return "https://rel-time.com/api/v1";
|
|
31
|
+
}
|
|
32
|
+
return cleaned;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function isValidApiKeyCandidate(k) {
|
|
36
|
+
if (!k || typeof k !== "string") return false;
|
|
37
|
+
const trimmed = k.trim();
|
|
38
|
+
if (!trimmed || trimmed.length < 16) return false;
|
|
39
|
+
if (trimmed.includes("${") || trimmed.includes("...") || trimmed.includes("YOUR_") || trimmed.includes("<")) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
return trimmed.startsWith("rel_");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const API_BASE = normalizeApiBase(process.env.RELTIME_API_URL);
|
|
24
46
|
const WEB_BASE = (process.env.RELTIME_WEB_URL || "https://rel-time.com").replace(/\/+$/, "");
|
|
25
|
-
let CURRENT_API_KEY = process.env.RELTIME_API_KEY
|
|
47
|
+
let CURRENT_API_KEY = isValidApiKeyCandidate(process.env.RELTIME_API_KEY)
|
|
48
|
+
? process.env.RELTIME_API_KEY.trim()
|
|
49
|
+
: "";
|
|
26
50
|
|
|
27
51
|
const PASSKEY_CONFIG_DIR = path.join(os.homedir(), ".reltime");
|
|
28
52
|
const PASSKEY_CONFIG_PATH = path.join(PASSKEY_CONFIG_DIR, "passkey.json");
|
|
53
|
+
const API_KEY_CONFIG_PATH = path.join(PASSKEY_CONFIG_DIR, "api_key.json");
|
|
54
|
+
let LAST_INVOICE_PACK = null;
|
|
29
55
|
|
|
30
56
|
const SERVER_INFO = {
|
|
31
57
|
name: "rel-time-mcp-server",
|
|
32
|
-
version: "1.2.
|
|
58
|
+
version: "1.2.3",
|
|
33
59
|
};
|
|
34
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Loads saved API key from ~/.reltime/api_key.json if not provided via env/args.
|
|
63
|
+
*/
|
|
64
|
+
function loadSavedApiKey() {
|
|
65
|
+
if (isValidApiKeyCandidate(CURRENT_API_KEY)) {
|
|
66
|
+
return CURRENT_API_KEY.trim();
|
|
67
|
+
}
|
|
68
|
+
if (isValidApiKeyCandidate(process.env.RELTIME_API_KEY)) {
|
|
69
|
+
CURRENT_API_KEY = process.env.RELTIME_API_KEY.trim();
|
|
70
|
+
return CURRENT_API_KEY;
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
if (fs.existsSync(API_KEY_CONFIG_PATH)) {
|
|
74
|
+
const parsed = JSON.parse(fs.readFileSync(API_KEY_CONFIG_PATH, "utf8"));
|
|
75
|
+
if (isValidApiKeyCandidate(parsed.api_key)) {
|
|
76
|
+
CURRENT_API_KEY = parsed.api_key.trim();
|
|
77
|
+
return CURRENT_API_KEY;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
} catch (e) {
|
|
81
|
+
// Ignore corrupted file
|
|
82
|
+
}
|
|
83
|
+
return "";
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function buildPackSelectionPrompt(contextMessage) {
|
|
87
|
+
return {
|
|
88
|
+
status: "pack_selection_required",
|
|
89
|
+
api_endpoint: API_BASE,
|
|
90
|
+
important_instruction_for_ai_agent:
|
|
91
|
+
"DO NOT tell the user to edit ~/.zshrc, .mcp.json, or restart Claude/Cursor! The MCP server manages and persists API keys automatically in ~/.reltime/api_key.json. Simply ask the user right here in chat which of the two Agent API Key packs below they want to provision (or if they already have a `rel_key_...` key to paste), then call `reltime_provision_agent_key` with `action: 'create_invoice'` and `pack: 'launch'` or `pack: 'standard'`.",
|
|
92
|
+
message:
|
|
93
|
+
contextMessage ||
|
|
94
|
+
"No Rel-Time API key is active yet. Ask the user which key pack they want to provision first:",
|
|
95
|
+
available_packs: [
|
|
96
|
+
{
|
|
97
|
+
pack: "launch",
|
|
98
|
+
name: "1. Agent Launch Pack ($5 / 50 Hashes)",
|
|
99
|
+
price_usd: "$5.00",
|
|
100
|
+
credits: 50,
|
|
101
|
+
settlement_queue: "Priority 10-Minute Bitcoin Queue",
|
|
102
|
+
hashing_modes: "Standard Mode only (Data Integrity + Biometric or Anonymous)",
|
|
103
|
+
outputs: "64-Ray Sun-Stamp SVG + 1-Click Stamped Copy (.PDF/.PNG) + .JSON Doomsday Proof",
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
pack: "standard",
|
|
107
|
+
name: "2. Autonomous Agents ($50 / 500 Hashes)",
|
|
108
|
+
price_usd: "$50.00",
|
|
109
|
+
credits: 500,
|
|
110
|
+
settlement_queue: "Dynamic 6-Hour Batch Queue",
|
|
111
|
+
hashing_modes: "Temporal Mode (T_index sub-second VDF sequencing) + Standard Mode (Biometric or Anonymous)",
|
|
112
|
+
outputs:
|
|
113
|
+
"64-Ray Sun-Stamp SVG + 1-Click Stamped Copy (.PDF/.PNG) + .JSON Doomsday Proof + Reloadable anytime (+500 credits / $50)",
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
next_step:
|
|
117
|
+
"Ask the user: 'Would you like to start with the **Agent Launch Pack ($5 / 50 Hashes · 10-Min Queue · Standard Mode)** or the **Autonomous Agents Pack ($50 / 500 Hashes · 6-Hr Queue · Standard + Temporal Mode)**?' Once they answer, call `reltime_provision_agent_key({ action: 'create_invoice', pack: 'launch' | 'standard' })`.",
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Saves provisioned API key to ~/.reltime/api_key.json with 0600 permissions.
|
|
123
|
+
*/
|
|
124
|
+
function saveApiKeyConfig(apiKey, pack) {
|
|
125
|
+
if (!fs.existsSync(PASSKEY_CONFIG_DIR)) {
|
|
126
|
+
fs.mkdirSync(PASSKEY_CONFIG_DIR, { recursive: true, mode: 0o700 });
|
|
127
|
+
}
|
|
128
|
+
const payload = {
|
|
129
|
+
api_key: apiKey.trim(),
|
|
130
|
+
pack: pack || "standard",
|
|
131
|
+
saved_at: new Date().toISOString(),
|
|
132
|
+
};
|
|
133
|
+
fs.writeFileSync(API_KEY_CONFIG_PATH, JSON.stringify(payload, null, 2), {
|
|
134
|
+
encoding: "utf8",
|
|
135
|
+
mode: 0o600,
|
|
136
|
+
});
|
|
137
|
+
return payload;
|
|
138
|
+
}
|
|
139
|
+
|
|
35
140
|
/**
|
|
36
141
|
* Loads linked Passkey credentials from env vars or ~/.reltime/passkey.json.
|
|
37
142
|
*/
|
|
@@ -383,13 +488,14 @@ const TOOLS = [
|
|
|
383
488
|
{
|
|
384
489
|
name: "reltime_account_status",
|
|
385
490
|
description:
|
|
386
|
-
"Checks the remaining Hash Credits, tier, and lifetime usage of the configured Rel-Time API key (`GET /v1/account`).",
|
|
491
|
+
"Checks the remaining Hash Credits, tier, and lifetime usage of the configured Rel-Time API key (`GET /api/v1/account`). If no API key is configured yet, returns the two available Agent API Key packs (`launch` $5/50 hashes vs `standard` $50/500 hashes) so you can ask the user which pack they want to provision in chat (without needing any .zshrc or config edits).",
|
|
387
492
|
inputSchema: {
|
|
388
493
|
type: "object",
|
|
389
494
|
properties: {
|
|
390
495
|
api_key: {
|
|
391
496
|
type: "string",
|
|
392
|
-
description:
|
|
497
|
+
description:
|
|
498
|
+
"Optional: pass a `rel_key_...` API key to check its balance AND automatically save it to ~/.reltime/api_key.json for all future calls.",
|
|
393
499
|
},
|
|
394
500
|
},
|
|
395
501
|
},
|
|
@@ -397,7 +503,7 @@ const TOOLS = [
|
|
|
397
503
|
{
|
|
398
504
|
name: "reltime_provision_agent_key",
|
|
399
505
|
description:
|
|
400
|
-
"Self-onboarding & Top-Up for Autonomous AI Agents
|
|
506
|
+
"Self-onboarding & Top-Up for Autonomous AI Agents via Bitcoin Lightning (`POST /v1/agent/invoice` & `POST /v1/agent/claim`). IMPORTANT: Before calling `create_invoice` for a new key, ALWAYS ask the user which pack they want first:\n1. **Agent Launch Pack (`pack: 'launch'`, $5 / 50 Hashes):** Priority 10-Minute Bitcoin Queue · Standard Mode only (Data Integrity + Biometric or Anonymous) · Includes Sun-Stamp, 1-Click Stamped Copy (.PDF/.PNG), and .JSON Doomsday Proof.\n2. **Autonomous Agents (`pack: 'standard'`, $50 / 500 Hashes):** Dynamic 6-Hour Batch Queue · Supports both Temporal Mode (`T_index` sub-second sequencing) and Standard Mode · Reloadable anytime (+500 credits / $50) with zero key rotation.",
|
|
401
507
|
inputSchema: {
|
|
402
508
|
type: "object",
|
|
403
509
|
properties: {
|
|
@@ -411,7 +517,7 @@ const TOOLS = [
|
|
|
411
517
|
type: "string",
|
|
412
518
|
enum: ["launch", "standard"],
|
|
413
519
|
description:
|
|
414
|
-
"
|
|
520
|
+
"Which key pack the user selected: 'launch' (Agent Launch Pack — $5 for 50 Hash Credits, 10-Min Queue, Standard Mode only) or 'standard' (Autonomous Agents — $50 for 500 Hash Credits, 6-Hr Queue, Temporal & Standard Modes, reloadable). If omitted on 'create_invoice', the tool returns the two options so you can ask the user which pack they prefer.",
|
|
415
521
|
},
|
|
416
522
|
invoice_id: {
|
|
417
523
|
type: "string",
|
|
@@ -434,7 +540,11 @@ const TOOLS = [
|
|
|
434
540
|
];
|
|
435
541
|
|
|
436
542
|
async function executeTool(name, args = {}) {
|
|
437
|
-
|
|
543
|
+
if (args.api_key && isValidApiKeyCandidate(args.api_key)) {
|
|
544
|
+
CURRENT_API_KEY = args.api_key.trim();
|
|
545
|
+
saveApiKeyConfig(CURRENT_API_KEY, "custom");
|
|
546
|
+
}
|
|
547
|
+
const key = (isValidApiKeyCandidate(args.api_key) ? args.api_key.trim() : loadSavedApiKey()) || "";
|
|
438
548
|
|
|
439
549
|
if (name === "reltime_link_passkey") {
|
|
440
550
|
if (args.public_key_hex && args.passkey_signature) {
|
|
@@ -461,15 +571,15 @@ async function executeTool(name, args = {}) {
|
|
|
461
571
|
}
|
|
462
572
|
|
|
463
573
|
if (name === "reltime_anchor_file") {
|
|
574
|
+
if (!key) {
|
|
575
|
+
return buildPackSelectionPrompt(
|
|
576
|
+
"Before anchoring files, a Rel-Time API key is required. Ask the user which pack they want to provision first:"
|
|
577
|
+
);
|
|
578
|
+
}
|
|
464
579
|
const filePath = path.resolve(args.file_path);
|
|
465
580
|
if (!fs.existsSync(filePath)) {
|
|
466
581
|
throw new Error(`File not found at path: ${filePath}`);
|
|
467
582
|
}
|
|
468
|
-
if (!key) {
|
|
469
|
-
throw new Error(
|
|
470
|
-
"Missing RELTIME_API_KEY. Set RELTIME_API_KEY in your MCP config, pass `api_key`, or call `reltime_provision_agent_key` to get an Autonomous Agent key."
|
|
471
|
-
);
|
|
472
|
-
}
|
|
473
583
|
|
|
474
584
|
// 1. Compute SHA-256 locally (Zero-Knowledge: raw file never leaves the device)
|
|
475
585
|
const fileBuffer = fs.readFileSync(filePath);
|
|
@@ -614,7 +724,9 @@ async function executeTool(name, args = {}) {
|
|
|
614
724
|
|
|
615
725
|
if (name === "reltime_anchor_hash") {
|
|
616
726
|
if (!key) {
|
|
617
|
-
|
|
727
|
+
return buildPackSelectionPrompt(
|
|
728
|
+
"Before anchoring hashes, a Rel-Time API key is required. Ask the user which pack they want to provision first:"
|
|
729
|
+
);
|
|
618
730
|
}
|
|
619
731
|
const clientHash = (args.client_hash || "").trim().toLowerCase();
|
|
620
732
|
if (!/^[0-9a-f]{64}$/.test(clientHash)) {
|
|
@@ -699,13 +811,26 @@ async function executeTool(name, args = {}) {
|
|
|
699
811
|
|
|
700
812
|
if (name === "reltime_account_status") {
|
|
701
813
|
if (!key) {
|
|
702
|
-
|
|
814
|
+
return buildPackSelectionPrompt(
|
|
815
|
+
"Rel-Time MCP is connected! No API key is saved yet. Ask the user which pack they would like to provision first:"
|
|
816
|
+
);
|
|
703
817
|
}
|
|
704
818
|
const res = await fetch(`${API_BASE}/account`, {
|
|
705
819
|
headers: { Authorization: `Bearer ${key}` },
|
|
706
820
|
});
|
|
707
|
-
const
|
|
821
|
+
const text = await res.text();
|
|
822
|
+
let data;
|
|
823
|
+
try {
|
|
824
|
+
data = JSON.parse(text);
|
|
825
|
+
} catch (e) {
|
|
826
|
+
throw new Error(`Rel-Time Account error (${res.status}) from ${API_BASE}/account: ${text.slice(0, 200)}`);
|
|
827
|
+
}
|
|
708
828
|
if (!res.ok) {
|
|
829
|
+
if (res.status === 401 || res.status === 403) {
|
|
830
|
+
return buildPackSelectionPrompt(
|
|
831
|
+
"The configured API key was not recognized. Ask the user which pack they would like to provision:"
|
|
832
|
+
);
|
|
833
|
+
}
|
|
709
834
|
throw new Error(`Rel-Time Account error (${res.status}): ${JSON.stringify(data)}`);
|
|
710
835
|
}
|
|
711
836
|
return data;
|
|
@@ -713,22 +838,30 @@ async function executeTool(name, args = {}) {
|
|
|
713
838
|
|
|
714
839
|
if (name === "reltime_provision_agent_key") {
|
|
715
840
|
if (args.action === "create_invoice") {
|
|
841
|
+
if (args.pack !== "launch" && args.pack !== "standard") {
|
|
842
|
+
return buildPackSelectionPrompt(
|
|
843
|
+
"Please ask the user which Rel-Time Agent API Key pack they would like to provision before generating the Lightning invoice:"
|
|
844
|
+
);
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
LAST_INVOICE_PACK = args.pack;
|
|
716
848
|
const res = await fetch(`${API_BASE}/agent/invoice`, {
|
|
717
849
|
method: "POST",
|
|
718
850
|
headers: { "Content-Type": "application/json" },
|
|
719
851
|
body: JSON.stringify({
|
|
720
852
|
accept_terms: true,
|
|
721
|
-
pack: args.pack
|
|
853
|
+
pack: args.pack,
|
|
722
854
|
}),
|
|
723
855
|
});
|
|
724
856
|
return await res.json();
|
|
725
857
|
} else if (args.action === "claim_invoice") {
|
|
726
858
|
if (!args.invoice_id) throw new Error("invoice_id is required when action is 'claim_invoice'.");
|
|
859
|
+
const resolvedPack = args.pack || LAST_INVOICE_PACK || undefined;
|
|
727
860
|
const claimPayload = {
|
|
728
861
|
invoice_id: args.invoice_id,
|
|
729
|
-
pack:
|
|
862
|
+
pack: resolvedPack === "launch" ? "launch" : undefined,
|
|
730
863
|
};
|
|
731
|
-
const targetReloadKey = args.api_key || (args.reload_existing ?
|
|
864
|
+
const targetReloadKey = args.api_key || (args.reload_existing ? loadSavedApiKey() : "");
|
|
732
865
|
if (targetReloadKey && targetReloadKey.trim()) {
|
|
733
866
|
claimPayload.api_key = targetReloadKey.trim();
|
|
734
867
|
}
|
|
@@ -740,6 +873,8 @@ async function executeTool(name, args = {}) {
|
|
|
740
873
|
const data = await res.json();
|
|
741
874
|
if (data.api_key) {
|
|
742
875
|
CURRENT_API_KEY = data.api_key;
|
|
876
|
+
saveApiKeyConfig(data.api_key, resolvedPack || "standard");
|
|
877
|
+
data.saved_to_config = API_KEY_CONFIG_PATH;
|
|
743
878
|
}
|
|
744
879
|
return data;
|
|
745
880
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rel-time/mcp-server",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.3",
|
|
4
4
|
"description": "Official Model Context Protocol (MCP) server for Rel-Time (rel-time.com) — Zero-Knowledge Bitcoin & VDF Provenance, Local File Hashing, and Sun-Stamp Generation for Claude, Cursor, Windsurf, and Antigravity.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|