@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.
Files changed (3) hide show
  1. package/README.md +8 -7
  2. package/index.js +100 -46
  3. 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` or `.cursor/mcp.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
- *(Note: `reltime_link_passkey`, `reltime_get_sun_stamp`, `reltime_check_proof`, and `reltime_provision_agent_key` work immediately even without an API key configured!)*
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
- const API_BASE = (process.env.RELTIME_API_URL || "https://rel-time.com/api/v1").replace(/\/+$/, "");
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.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 && CURRENT_API_KEY.trim()) {
65
+ if (isValidApiKeyCandidate(CURRENT_API_KEY)) {
42
66
  return CURRENT_API_KEY.trim();
43
67
  }
44
- if (process.env.RELTIME_API_KEY && process.env.RELTIME_API_KEY.trim()) {
45
- return process.env.RELTIME_API_KEY.trim();
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 && parsed.api_key.trim()) {
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: "Optional override for RELTIME_API_KEY.",
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
- const key = (args.api_key || loadSavedApiKey() || "").trim();
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
- throw new Error(
663
- "Missing RELTIME_API_KEY. Please ask the user which API key pack they would like to provision first before calling `reltime_provision_agent_key`: 1) Agent Launch Pack (`pack: \"launch\"`, $5 / 50 Hashes · 10-Min Queue · Standard Mode only) or 2) Autonomous Agents (`pack: \"standard\"`, $50 / 500 Hashes · 6-Hr Queue · Temporal & Standard Modes)."
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
- throw new Error("Missing RELTIME_API_KEY.");
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 data = await res.json();
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
- status: "pack_selection_required",
766
- message:
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.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": {