@rel-time/mcp-server 1.2.2 → 1.2.4
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 +183 -57
- package/package.json +2 -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
|
@@ -18,11 +18,36 @@ const path = require("path");
|
|
|
18
18
|
const readline = require("readline");
|
|
19
19
|
const { exec } = require("child_process");
|
|
20
20
|
const { PDFDocument } = require("pdf-lib");
|
|
21
|
+
const QRCode = require("qrcode");
|
|
21
22
|
const sharp = require("sharp");
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
function normalizeApiBase(rawUrl) {
|
|
25
|
+
const cleaned = (rawUrl || "https://rel-time.com/api/v1").trim().replace(/\/+$/, "");
|
|
26
|
+
if (
|
|
27
|
+
cleaned === "https://rel-time.com/v1" ||
|
|
28
|
+
cleaned === "https://rel-time.com" ||
|
|
29
|
+
cleaned.includes("api.rel-time.com")
|
|
30
|
+
) {
|
|
31
|
+
return "https://rel-time.com/api/v1";
|
|
32
|
+
}
|
|
33
|
+
return cleaned;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function isValidApiKeyCandidate(k) {
|
|
37
|
+
if (!k || typeof k !== "string") return false;
|
|
38
|
+
const trimmed = k.trim();
|
|
39
|
+
if (!trimmed || trimmed.length < 16) return false;
|
|
40
|
+
if (trimmed.includes("${") || trimmed.includes("...") || trimmed.includes("YOUR_") || trimmed.includes("<")) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
return trimmed.startsWith("rel_");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const API_BASE = normalizeApiBase(process.env.RELTIME_API_URL);
|
|
24
47
|
const WEB_BASE = (process.env.RELTIME_WEB_URL || "https://rel-time.com").replace(/\/+$/, "");
|
|
25
|
-
let CURRENT_API_KEY = process.env.RELTIME_API_KEY
|
|
48
|
+
let CURRENT_API_KEY = isValidApiKeyCandidate(process.env.RELTIME_API_KEY)
|
|
49
|
+
? process.env.RELTIME_API_KEY.trim()
|
|
50
|
+
: "";
|
|
26
51
|
|
|
27
52
|
const PASSKEY_CONFIG_DIR = path.join(os.homedir(), ".reltime");
|
|
28
53
|
const PASSKEY_CONFIG_PATH = path.join(PASSKEY_CONFIG_DIR, "passkey.json");
|
|
@@ -31,23 +56,53 @@ let LAST_INVOICE_PACK = null;
|
|
|
31
56
|
|
|
32
57
|
const SERVER_INFO = {
|
|
33
58
|
name: "rel-time-mcp-server",
|
|
34
|
-
version: "1.2.
|
|
59
|
+
version: "1.2.4",
|
|
35
60
|
};
|
|
36
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Resolves a local file path, automatically matching macOS screenshot filenames
|
|
64
|
+
* that contain Narrow No-Break Spaces (U+202F) or Non-Breaking Spaces (U+00A0)
|
|
65
|
+
* when the caller passes normal ASCII spaces.
|
|
66
|
+
*/
|
|
67
|
+
function resolveLocalFilePath(rawPath) {
|
|
68
|
+
const resolved = path.resolve(rawPath);
|
|
69
|
+
if (fs.existsSync(resolved)) {
|
|
70
|
+
return resolved;
|
|
71
|
+
}
|
|
72
|
+
const dir = path.dirname(resolved);
|
|
73
|
+
const targetBase = path.basename(resolved);
|
|
74
|
+
const normalizeSpaces = (s) => s.normalize("NFC").replace(/[\u00A0\u202F\u2007\u2009\s]+/g, " ");
|
|
75
|
+
const normalizedTarget = normalizeSpaces(targetBase);
|
|
76
|
+
try {
|
|
77
|
+
if (fs.existsSync(dir)) {
|
|
78
|
+
const entries = fs.readdirSync(dir);
|
|
79
|
+
for (const entry of entries) {
|
|
80
|
+
if (normalizeSpaces(entry) === normalizedTarget) {
|
|
81
|
+
return path.join(dir, entry);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
} catch (e) {
|
|
86
|
+
// Ignore directory read error
|
|
87
|
+
}
|
|
88
|
+
return resolved;
|
|
89
|
+
}
|
|
90
|
+
|
|
37
91
|
/**
|
|
38
92
|
* Loads saved API key from ~/.reltime/api_key.json if not provided via env/args.
|
|
39
93
|
*/
|
|
40
94
|
function loadSavedApiKey() {
|
|
41
|
-
if (CURRENT_API_KEY
|
|
95
|
+
if (isValidApiKeyCandidate(CURRENT_API_KEY)) {
|
|
42
96
|
return CURRENT_API_KEY.trim();
|
|
43
97
|
}
|
|
44
|
-
if (process.env.RELTIME_API_KEY
|
|
45
|
-
|
|
98
|
+
if (isValidApiKeyCandidate(process.env.RELTIME_API_KEY)) {
|
|
99
|
+
CURRENT_API_KEY = process.env.RELTIME_API_KEY.trim();
|
|
100
|
+
return CURRENT_API_KEY;
|
|
46
101
|
}
|
|
47
102
|
try {
|
|
48
103
|
if (fs.existsSync(API_KEY_CONFIG_PATH)) {
|
|
49
104
|
const parsed = JSON.parse(fs.readFileSync(API_KEY_CONFIG_PATH, "utf8"));
|
|
50
|
-
if (parsed.api_key
|
|
105
|
+
if (isValidApiKeyCandidate(parsed.api_key)) {
|
|
51
106
|
CURRENT_API_KEY = parsed.api_key.trim();
|
|
52
107
|
return CURRENT_API_KEY;
|
|
53
108
|
}
|
|
@@ -58,6 +113,41 @@ function loadSavedApiKey() {
|
|
|
58
113
|
return "";
|
|
59
114
|
}
|
|
60
115
|
|
|
116
|
+
function buildPackSelectionPrompt(contextMessage) {
|
|
117
|
+
return {
|
|
118
|
+
status: "pack_selection_required",
|
|
119
|
+
api_endpoint: API_BASE,
|
|
120
|
+
important_instruction_for_ai_agent:
|
|
121
|
+
"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'`.",
|
|
122
|
+
message:
|
|
123
|
+
contextMessage ||
|
|
124
|
+
"No Rel-Time API key is active yet. Ask the user which key pack they want to provision first:",
|
|
125
|
+
available_packs: [
|
|
126
|
+
{
|
|
127
|
+
pack: "launch",
|
|
128
|
+
name: "1. Agent Launch Pack ($5 / 50 Hashes)",
|
|
129
|
+
price_usd: "$5.00",
|
|
130
|
+
credits: 50,
|
|
131
|
+
settlement_queue: "Priority 10-Minute Bitcoin Queue",
|
|
132
|
+
hashing_modes: "Standard Mode only (Data Integrity + Biometric or Anonymous)",
|
|
133
|
+
outputs: "64-Ray Sun-Stamp SVG + 1-Click Stamped Copy (.PDF/.PNG) + .JSON Doomsday Proof",
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
pack: "standard",
|
|
137
|
+
name: "2. Autonomous Agents ($50 / 500 Hashes)",
|
|
138
|
+
price_usd: "$50.00",
|
|
139
|
+
credits: 500,
|
|
140
|
+
settlement_queue: "Dynamic 6-Hour Batch Queue",
|
|
141
|
+
hashing_modes: "Temporal Mode (T_index sub-second VDF sequencing) + Standard Mode (Biometric or Anonymous)",
|
|
142
|
+
outputs:
|
|
143
|
+
"64-Ray Sun-Stamp SVG + 1-Click Stamped Copy (.PDF/.PNG) + .JSON Doomsday Proof + Reloadable anytime (+500 credits / $50)",
|
|
144
|
+
},
|
|
145
|
+
],
|
|
146
|
+
next_step:
|
|
147
|
+
"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' })`.",
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
61
151
|
/**
|
|
62
152
|
* Saves provisioned API key to ~/.reltime/api_key.json with 0600 permissions.
|
|
63
153
|
*/
|
|
@@ -143,7 +233,7 @@ function openSystemBrowser(url) {
|
|
|
143
233
|
* Rel-Time WebAuthn Passkey Bridge in the user's browser so they can touch
|
|
144
234
|
* TouchID / FaceID / YubiKey.
|
|
145
235
|
*/
|
|
146
|
-
function launchBrowserPasskeyBridge(timeoutMs =
|
|
236
|
+
function launchBrowserPasskeyBridge(timeoutMs = 300000) {
|
|
147
237
|
return new Promise((resolve, reject) => {
|
|
148
238
|
const server = http.createServer((req, res) => {
|
|
149
239
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
@@ -428,13 +518,14 @@ const TOOLS = [
|
|
|
428
518
|
{
|
|
429
519
|
name: "reltime_account_status",
|
|
430
520
|
description:
|
|
431
|
-
"Checks the remaining Hash Credits, tier, and lifetime usage of the configured Rel-Time API key (`GET /v1/account`).",
|
|
521
|
+
"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
522
|
inputSchema: {
|
|
433
523
|
type: "object",
|
|
434
524
|
properties: {
|
|
435
525
|
api_key: {
|
|
436
526
|
type: "string",
|
|
437
|
-
description:
|
|
527
|
+
description:
|
|
528
|
+
"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
529
|
},
|
|
439
530
|
},
|
|
440
531
|
},
|
|
@@ -442,7 +533,7 @@ const TOOLS = [
|
|
|
442
533
|
{
|
|
443
534
|
name: "reltime_provision_agent_key",
|
|
444
535
|
description:
|
|
445
|
-
"Self-onboarding & Top-Up for Autonomous AI Agents via Bitcoin Lightning (`POST /v1/agent/invoice` & `POST /v1/agent/claim`). IMPORTANT
|
|
536
|
+
"Self-onboarding & Top-Up for Autonomous AI Agents via Bitcoin Lightning (`POST /v1/agent/invoice` & `POST /v1/agent/claim`). IMPORTANT:\n1. Before calling `create_invoice` for a new key, ALWAYS ask the user which pack they want first:\n - **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.\n - **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.\n2. When `create_invoice` returns, ALWAYS present BOTH the scannable QR code image (by calling Read on `qr_code_png_path` so the image renders inline in chat!) AND the full, un-truncated `ln_invoice` (`bolt11`) string in a copyable code block on the VERY FIRST try!",
|
|
446
537
|
inputSchema: {
|
|
447
538
|
type: "object",
|
|
448
539
|
properties: {
|
|
@@ -479,7 +570,11 @@ const TOOLS = [
|
|
|
479
570
|
];
|
|
480
571
|
|
|
481
572
|
async function executeTool(name, args = {}) {
|
|
482
|
-
|
|
573
|
+
if (args.api_key && isValidApiKeyCandidate(args.api_key)) {
|
|
574
|
+
CURRENT_API_KEY = args.api_key.trim();
|
|
575
|
+
saveApiKeyConfig(CURRENT_API_KEY, "custom");
|
|
576
|
+
}
|
|
577
|
+
const key = (isValidApiKeyCandidate(args.api_key) ? args.api_key.trim() : loadSavedApiKey()) || "";
|
|
483
578
|
|
|
484
579
|
if (name === "reltime_link_passkey") {
|
|
485
580
|
if (args.public_key_hex && args.passkey_signature) {
|
|
@@ -506,15 +601,15 @@ async function executeTool(name, args = {}) {
|
|
|
506
601
|
}
|
|
507
602
|
|
|
508
603
|
if (name === "reltime_anchor_file") {
|
|
509
|
-
const filePath = path.resolve(args.file_path);
|
|
510
|
-
if (!fs.existsSync(filePath)) {
|
|
511
|
-
throw new Error(`File not found at path: ${filePath}`);
|
|
512
|
-
}
|
|
513
604
|
if (!key) {
|
|
514
|
-
|
|
515
|
-
"
|
|
605
|
+
return buildPackSelectionPrompt(
|
|
606
|
+
"Before anchoring files, a Rel-Time API key is required. Ask the user which pack they want to provision first:"
|
|
516
607
|
);
|
|
517
608
|
}
|
|
609
|
+
const filePath = resolveLocalFilePath(args.file_path);
|
|
610
|
+
if (!fs.existsSync(filePath)) {
|
|
611
|
+
throw new Error(`File not found at path: ${filePath}`);
|
|
612
|
+
}
|
|
518
613
|
|
|
519
614
|
// 1. Compute SHA-256 locally (Zero-Knowledge: raw file never leaves the device)
|
|
520
615
|
const fileBuffer = fs.readFileSync(filePath);
|
|
@@ -631,7 +726,7 @@ async function executeTool(name, args = {}) {
|
|
|
631
726
|
}
|
|
632
727
|
|
|
633
728
|
if (name === "reltime_stamp_file") {
|
|
634
|
-
const filePath =
|
|
729
|
+
const filePath = resolveLocalFilePath(args.file_path);
|
|
635
730
|
if (!fs.existsSync(filePath)) {
|
|
636
731
|
throw new Error(`File not found at path: ${filePath}`);
|
|
637
732
|
}
|
|
@@ -659,8 +754,8 @@ async function executeTool(name, args = {}) {
|
|
|
659
754
|
|
|
660
755
|
if (name === "reltime_anchor_hash") {
|
|
661
756
|
if (!key) {
|
|
662
|
-
|
|
663
|
-
"
|
|
757
|
+
return buildPackSelectionPrompt(
|
|
758
|
+
"Before anchoring hashes, a Rel-Time API key is required. Ask the user which pack they want to provision first:"
|
|
664
759
|
);
|
|
665
760
|
}
|
|
666
761
|
const clientHash = (args.client_hash || "").trim().toLowerCase();
|
|
@@ -746,13 +841,26 @@ async function executeTool(name, args = {}) {
|
|
|
746
841
|
|
|
747
842
|
if (name === "reltime_account_status") {
|
|
748
843
|
if (!key) {
|
|
749
|
-
|
|
844
|
+
return buildPackSelectionPrompt(
|
|
845
|
+
"Rel-Time MCP is connected! No API key is saved yet. Ask the user which pack they would like to provision first:"
|
|
846
|
+
);
|
|
750
847
|
}
|
|
751
848
|
const res = await fetch(`${API_BASE}/account`, {
|
|
752
849
|
headers: { Authorization: `Bearer ${key}` },
|
|
753
850
|
});
|
|
754
|
-
const
|
|
851
|
+
const text = await res.text();
|
|
852
|
+
let data;
|
|
853
|
+
try {
|
|
854
|
+
data = JSON.parse(text);
|
|
855
|
+
} catch (e) {
|
|
856
|
+
throw new Error(`Rel-Time Account error (${res.status}) from ${API_BASE}/account: ${text.slice(0, 200)}`);
|
|
857
|
+
}
|
|
755
858
|
if (!res.ok) {
|
|
859
|
+
if (res.status === 401 || res.status === 403) {
|
|
860
|
+
return buildPackSelectionPrompt(
|
|
861
|
+
"The configured API key was not recognized. Ask the user which pack they would like to provision:"
|
|
862
|
+
);
|
|
863
|
+
}
|
|
756
864
|
throw new Error(`Rel-Time Account error (${res.status}): ${JSON.stringify(data)}`);
|
|
757
865
|
}
|
|
758
866
|
return data;
|
|
@@ -761,33 +869,9 @@ async function executeTool(name, args = {}) {
|
|
|
761
869
|
if (name === "reltime_provision_agent_key") {
|
|
762
870
|
if (args.action === "create_invoice") {
|
|
763
871
|
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
|
-
};
|
|
872
|
+
return buildPackSelectionPrompt(
|
|
873
|
+
"Please ask the user which Rel-Time Agent API Key pack they would like to provision before generating the Lightning invoice:"
|
|
874
|
+
);
|
|
791
875
|
}
|
|
792
876
|
|
|
793
877
|
LAST_INVOICE_PACK = args.pack;
|
|
@@ -799,7 +883,40 @@ async function executeTool(name, args = {}) {
|
|
|
799
883
|
pack: args.pack,
|
|
800
884
|
}),
|
|
801
885
|
});
|
|
802
|
-
|
|
886
|
+
const data = await res.json();
|
|
887
|
+
const bolt11 = data.ln_invoice || data.bolt11 || "";
|
|
888
|
+
if (bolt11) {
|
|
889
|
+
try {
|
|
890
|
+
if (!fs.existsSync(PASSKEY_CONFIG_DIR)) {
|
|
891
|
+
fs.mkdirSync(PASSKEY_CONFIG_DIR, { recursive: true, mode: 0o700 });
|
|
892
|
+
}
|
|
893
|
+
const qrPngBuffer = await QRCode.toBuffer(bolt11.toUpperCase(), {
|
|
894
|
+
type: "png",
|
|
895
|
+
width: 560,
|
|
896
|
+
margin: 2,
|
|
897
|
+
errorCorrectionLevel: "M",
|
|
898
|
+
});
|
|
899
|
+
const homeQrPath = path.join(PASSKEY_CONFIG_DIR, "lightning-invoice-qr.png");
|
|
900
|
+
const tmpQrPath = "/tmp/reltime-lightning-qr.png";
|
|
901
|
+
fs.writeFileSync(homeQrPath, qrPngBuffer);
|
|
902
|
+
try {
|
|
903
|
+
fs.writeFileSync(tmpQrPath, qrPngBuffer);
|
|
904
|
+
} catch (e) {
|
|
905
|
+
// Ignore /tmp write error
|
|
906
|
+
}
|
|
907
|
+
data.bolt11 = bolt11;
|
|
908
|
+
data.ln_invoice = bolt11;
|
|
909
|
+
data.qr_code_png_path = homeQrPath;
|
|
910
|
+
data.qr_code_tmp_png_path = tmpQrPath;
|
|
911
|
+
data.invoice_expiration = "60 minutes (3,600 seconds)";
|
|
912
|
+
data.mandatory_first_reply_instructions =
|
|
913
|
+
"CRITICAL INSTRUCTION FOR AI AGENT: On this very first reply, you MUST show the user BOTH: (1) the scannable QR code image (immediately call your Read tool on `qr_code_png_path` BEFORE sending your message so the QR code PNG renders inline in the chat window right now!) AND (2) the full, un-truncated `bolt11` (`lnbc...`) Lightning invoice string inside a copyable code block so the user can either scan with their phone or copy-paste on the first try without asking!";
|
|
914
|
+
data._mcp_image_base64 = qrPngBuffer.toString("base64");
|
|
915
|
+
} catch (qrErr) {
|
|
916
|
+
data.qr_error = qrErr.message;
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
return data;
|
|
803
920
|
} else if (args.action === "claim_invoice") {
|
|
804
921
|
if (!args.invoice_id) throw new Error("invoice_id is required when action is 'claim_invoice'.");
|
|
805
922
|
const resolvedPack = args.pack || LAST_INVOICE_PACK || undefined;
|
|
@@ -879,16 +996,25 @@ rl.on("line", async (line) => {
|
|
|
879
996
|
const toolArgs = params?.arguments || {};
|
|
880
997
|
try {
|
|
881
998
|
const resultObj = await executeTool(toolName, toolArgs);
|
|
999
|
+
const contentItems = [];
|
|
1000
|
+
if (resultObj && resultObj._mcp_image_base64) {
|
|
1001
|
+
const b64 = resultObj._mcp_image_base64;
|
|
1002
|
+
delete resultObj._mcp_image_base64;
|
|
1003
|
+
contentItems.push({
|
|
1004
|
+
type: "image",
|
|
1005
|
+
data: b64,
|
|
1006
|
+
mimeType: "image/png",
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
1009
|
+
contentItems.push({
|
|
1010
|
+
type: "text",
|
|
1011
|
+
text: JSON.stringify(resultObj, null, 2),
|
|
1012
|
+
});
|
|
882
1013
|
sendJsonRpc({
|
|
883
1014
|
jsonrpc: "2.0",
|
|
884
1015
|
id,
|
|
885
1016
|
result: {
|
|
886
|
-
content:
|
|
887
|
-
{
|
|
888
|
-
type: "text",
|
|
889
|
-
text: JSON.stringify(resultObj, null, 2),
|
|
890
|
-
},
|
|
891
|
-
],
|
|
1017
|
+
content: contentItems,
|
|
892
1018
|
},
|
|
893
1019
|
});
|
|
894
1020
|
} catch (toolErr) {
|
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.4",
|
|
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": {
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"pdf-lib": "^1.17.1",
|
|
35
|
+
"qrcode": "^1.5.4",
|
|
35
36
|
"sharp": "^0.35.5"
|
|
36
37
|
}
|
|
37
38
|
}
|