@rel-time/mcp-server 1.2.2 → 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 +100 -46
- 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,9 +20,33 @@ 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");
|
|
@@ -31,23 +55,24 @@ let LAST_INVOICE_PACK = null;
|
|
|
31
55
|
|
|
32
56
|
const SERVER_INFO = {
|
|
33
57
|
name: "rel-time-mcp-server",
|
|
34
|
-
version: "1.2.
|
|
58
|
+
version: "1.2.3",
|
|
35
59
|
};
|
|
36
60
|
|
|
37
61
|
/**
|
|
38
62
|
* Loads saved API key from ~/.reltime/api_key.json if not provided via env/args.
|
|
39
63
|
*/
|
|
40
64
|
function loadSavedApiKey() {
|
|
41
|
-
if (CURRENT_API_KEY
|
|
65
|
+
if (isValidApiKeyCandidate(CURRENT_API_KEY)) {
|
|
42
66
|
return CURRENT_API_KEY.trim();
|
|
43
67
|
}
|
|
44
|
-
if (process.env.RELTIME_API_KEY
|
|
45
|
-
|
|
68
|
+
if (isValidApiKeyCandidate(process.env.RELTIME_API_KEY)) {
|
|
69
|
+
CURRENT_API_KEY = process.env.RELTIME_API_KEY.trim();
|
|
70
|
+
return CURRENT_API_KEY;
|
|
46
71
|
}
|
|
47
72
|
try {
|
|
48
73
|
if (fs.existsSync(API_KEY_CONFIG_PATH)) {
|
|
49
74
|
const parsed = JSON.parse(fs.readFileSync(API_KEY_CONFIG_PATH, "utf8"));
|
|
50
|
-
if (parsed.api_key
|
|
75
|
+
if (isValidApiKeyCandidate(parsed.api_key)) {
|
|
51
76
|
CURRENT_API_KEY = parsed.api_key.trim();
|
|
52
77
|
return CURRENT_API_KEY;
|
|
53
78
|
}
|
|
@@ -58,6 +83,41 @@ function loadSavedApiKey() {
|
|
|
58
83
|
return "";
|
|
59
84
|
}
|
|
60
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
|
+
|
|
61
121
|
/**
|
|
62
122
|
* Saves provisioned API key to ~/.reltime/api_key.json with 0600 permissions.
|
|
63
123
|
*/
|
|
@@ -428,13 +488,14 @@ const TOOLS = [
|
|
|
428
488
|
{
|
|
429
489
|
name: "reltime_account_status",
|
|
430
490
|
description:
|
|
431
|
-
"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).",
|
|
432
492
|
inputSchema: {
|
|
433
493
|
type: "object",
|
|
434
494
|
properties: {
|
|
435
495
|
api_key: {
|
|
436
496
|
type: "string",
|
|
437
|
-
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.",
|
|
438
499
|
},
|
|
439
500
|
},
|
|
440
501
|
},
|
|
@@ -479,7 +540,11 @@ const TOOLS = [
|
|
|
479
540
|
];
|
|
480
541
|
|
|
481
542
|
async function executeTool(name, args = {}) {
|
|
482
|
-
|
|
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()) || "";
|
|
483
548
|
|
|
484
549
|
if (name === "reltime_link_passkey") {
|
|
485
550
|
if (args.public_key_hex && args.passkey_signature) {
|
|
@@ -506,15 +571,15 @@ async function executeTool(name, args = {}) {
|
|
|
506
571
|
}
|
|
507
572
|
|
|
508
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
|
+
}
|
|
509
579
|
const filePath = path.resolve(args.file_path);
|
|
510
580
|
if (!fs.existsSync(filePath)) {
|
|
511
581
|
throw new Error(`File not found at path: ${filePath}`);
|
|
512
582
|
}
|
|
513
|
-
if (!key) {
|
|
514
|
-
throw new Error(
|
|
515
|
-
"Missing RELTIME_API_KEY. Please ask the user which API key pack they would like to provision first before calling `reltime_provision_agent_key`:\n1. **Agent Launch Pack (`pack: \"launch\"`, $5 / 50 Hashes):** Priority 10-Min Bitcoin Queue · Standard Mode only (Data Integrity + Biometric or Anonymous)\n2. **Autonomous Agents (`pack: \"standard\"`, $50 / 500 Hashes):** Dynamic 6-Hr Batch Queue · Temporal Mode (T_index sequencing) + Standard Mode · Reloadable anytime"
|
|
516
|
-
);
|
|
517
|
-
}
|
|
518
583
|
|
|
519
584
|
// 1. Compute SHA-256 locally (Zero-Knowledge: raw file never leaves the device)
|
|
520
585
|
const fileBuffer = fs.readFileSync(filePath);
|
|
@@ -659,8 +724,8 @@ async function executeTool(name, args = {}) {
|
|
|
659
724
|
|
|
660
725
|
if (name === "reltime_anchor_hash") {
|
|
661
726
|
if (!key) {
|
|
662
|
-
|
|
663
|
-
"
|
|
727
|
+
return buildPackSelectionPrompt(
|
|
728
|
+
"Before anchoring hashes, a Rel-Time API key is required. Ask the user which pack they want to provision first:"
|
|
664
729
|
);
|
|
665
730
|
}
|
|
666
731
|
const clientHash = (args.client_hash || "").trim().toLowerCase();
|
|
@@ -746,13 +811,26 @@ async function executeTool(name, args = {}) {
|
|
|
746
811
|
|
|
747
812
|
if (name === "reltime_account_status") {
|
|
748
813
|
if (!key) {
|
|
749
|
-
|
|
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
|
+
);
|
|
750
817
|
}
|
|
751
818
|
const res = await fetch(`${API_BASE}/account`, {
|
|
752
819
|
headers: { Authorization: `Bearer ${key}` },
|
|
753
820
|
});
|
|
754
|
-
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
|
+
}
|
|
755
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
|
+
}
|
|
756
834
|
throw new Error(`Rel-Time Account error (${res.status}): ${JSON.stringify(data)}`);
|
|
757
835
|
}
|
|
758
836
|
return data;
|
|
@@ -761,33 +839,9 @@ async function executeTool(name, args = {}) {
|
|
|
761
839
|
if (name === "reltime_provision_agent_key") {
|
|
762
840
|
if (args.action === "create_invoice") {
|
|
763
841
|
if (args.pack !== "launch" && args.pack !== "standard") {
|
|
764
|
-
return
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
"Please ask the user which Rel-Time Agent API Key pack they would like to provision before generating the Lightning invoice:",
|
|
768
|
-
available_packs: [
|
|
769
|
-
{
|
|
770
|
-
pack: "launch",
|
|
771
|
-
name: "Agent Launch Pack",
|
|
772
|
-
price_usd: "$5.00",
|
|
773
|
-
credits: 50,
|
|
774
|
-
settlement_queue: "Priority 10-Minute Bitcoin Queue",
|
|
775
|
-
hashing_modes: "Standard Mode only (Data Integrity + Biometric or Anonymous)",
|
|
776
|
-
outputs: "64-Ray Sun-Stamp SVG + 1-Click Stamped Copy (.PDF/.PNG) + .JSON Doomsday Proof",
|
|
777
|
-
},
|
|
778
|
-
{
|
|
779
|
-
pack: "standard",
|
|
780
|
-
name: "Autonomous Agents",
|
|
781
|
-
price_usd: "$50.00",
|
|
782
|
-
credits: 500,
|
|
783
|
-
settlement_queue: "Dynamic 6-Hour Batch Queue",
|
|
784
|
-
hashing_modes: "Temporal Mode (T_index sub-second VDF sequencing) + Standard Mode (Biometric or Anonymous)",
|
|
785
|
-
outputs: "64-Ray Sun-Stamp SVG + 1-Click Stamped Copy (.PDF/.PNG) + .JSON Doomsday Proof + Reloadable anytime (+500 credits / $50)",
|
|
786
|
-
},
|
|
787
|
-
],
|
|
788
|
-
next_step:
|
|
789
|
-
"Once the user chooses, call `reltime_provision_agent_key` with `action: 'create_invoice'` and `pack: 'launch'` or `pack: 'standard'`.",
|
|
790
|
-
};
|
|
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
|
+
);
|
|
791
845
|
}
|
|
792
846
|
|
|
793
847
|
LAST_INVOICE_PACK = args.pack;
|
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": {
|