@rel-time/mcp-server 1.2.3 → 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/index.js +84 -12
- package/package.json +2 -1
package/index.js
CHANGED
|
@@ -18,6 +18,7 @@ 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) {
|
|
@@ -55,9 +56,38 @@ let LAST_INVOICE_PACK = null;
|
|
|
55
56
|
|
|
56
57
|
const SERVER_INFO = {
|
|
57
58
|
name: "rel-time-mcp-server",
|
|
58
|
-
version: "1.2.
|
|
59
|
+
version: "1.2.4",
|
|
59
60
|
};
|
|
60
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
|
+
|
|
61
91
|
/**
|
|
62
92
|
* Loads saved API key from ~/.reltime/api_key.json if not provided via env/args.
|
|
63
93
|
*/
|
|
@@ -203,7 +233,7 @@ function openSystemBrowser(url) {
|
|
|
203
233
|
* Rel-Time WebAuthn Passkey Bridge in the user's browser so they can touch
|
|
204
234
|
* TouchID / FaceID / YubiKey.
|
|
205
235
|
*/
|
|
206
|
-
function launchBrowserPasskeyBridge(timeoutMs =
|
|
236
|
+
function launchBrowserPasskeyBridge(timeoutMs = 300000) {
|
|
207
237
|
return new Promise((resolve, reject) => {
|
|
208
238
|
const server = http.createServer((req, res) => {
|
|
209
239
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
@@ -503,7 +533,7 @@ const TOOLS = [
|
|
|
503
533
|
{
|
|
504
534
|
name: "reltime_provision_agent_key",
|
|
505
535
|
description:
|
|
506
|
-
"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!",
|
|
507
537
|
inputSchema: {
|
|
508
538
|
type: "object",
|
|
509
539
|
properties: {
|
|
@@ -576,7 +606,7 @@ async function executeTool(name, args = {}) {
|
|
|
576
606
|
"Before anchoring files, a Rel-Time API key is required. Ask the user which pack they want to provision first:"
|
|
577
607
|
);
|
|
578
608
|
}
|
|
579
|
-
const filePath =
|
|
609
|
+
const filePath = resolveLocalFilePath(args.file_path);
|
|
580
610
|
if (!fs.existsSync(filePath)) {
|
|
581
611
|
throw new Error(`File not found at path: ${filePath}`);
|
|
582
612
|
}
|
|
@@ -696,7 +726,7 @@ async function executeTool(name, args = {}) {
|
|
|
696
726
|
}
|
|
697
727
|
|
|
698
728
|
if (name === "reltime_stamp_file") {
|
|
699
|
-
const filePath =
|
|
729
|
+
const filePath = resolveLocalFilePath(args.file_path);
|
|
700
730
|
if (!fs.existsSync(filePath)) {
|
|
701
731
|
throw new Error(`File not found at path: ${filePath}`);
|
|
702
732
|
}
|
|
@@ -853,7 +883,40 @@ async function executeTool(name, args = {}) {
|
|
|
853
883
|
pack: args.pack,
|
|
854
884
|
}),
|
|
855
885
|
});
|
|
856
|
-
|
|
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;
|
|
857
920
|
} else if (args.action === "claim_invoice") {
|
|
858
921
|
if (!args.invoice_id) throw new Error("invoice_id is required when action is 'claim_invoice'.");
|
|
859
922
|
const resolvedPack = args.pack || LAST_INVOICE_PACK || undefined;
|
|
@@ -933,16 +996,25 @@ rl.on("line", async (line) => {
|
|
|
933
996
|
const toolArgs = params?.arguments || {};
|
|
934
997
|
try {
|
|
935
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
|
+
});
|
|
936
1013
|
sendJsonRpc({
|
|
937
1014
|
jsonrpc: "2.0",
|
|
938
1015
|
id,
|
|
939
1016
|
result: {
|
|
940
|
-
content:
|
|
941
|
-
{
|
|
942
|
-
type: "text",
|
|
943
|
-
text: JSON.stringify(resultObj, null, 2),
|
|
944
|
-
},
|
|
945
|
-
],
|
|
1017
|
+
content: contentItems,
|
|
946
1018
|
},
|
|
947
1019
|
});
|
|
948
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
|
}
|