mj41-mcp 1.3.0 → 1.5.0
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/dist/index.js +397 -2
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -10,6 +10,10 @@
|
|
|
10
10
|
// - get_all_copper_prices — all three classes at once
|
|
11
11
|
// - get_oracle_status — health check for any class
|
|
12
12
|
//
|
|
13
|
+
// Metals Oracles (ZJ Industries x MJ41)
|
|
14
|
+
// - get_metal_price — live price for gold, silver, aluminum, platinum, or palladium
|
|
15
|
+
// - get_all_metal_prices — all 8 oracles (3 copper + 5 metals) at once
|
|
16
|
+
//
|
|
13
17
|
// Richard Tracy (Blockchain Detective by mj41)
|
|
14
18
|
// - investigate_wallet — phishing/scam investigation
|
|
15
19
|
// - get_eth_price — current ETH price
|
|
@@ -73,13 +77,66 @@ function copperHolidayNote() {
|
|
|
73
77
|
}
|
|
74
78
|
return "Copper Holiday has ended. API key required. Register with the register tool.";
|
|
75
79
|
}
|
|
80
|
+
const BILLING_URL = "https://rwacu.com/api/billing";
|
|
76
81
|
const COPPERTRACE_URL = "https://coppertrace.vercel.app";
|
|
77
82
|
const FIRST_SIGNAL_URL = "https://aiwire.mj41.me";
|
|
83
|
+
// ── Billing gate ─────────────────────────────────────────────
|
|
84
|
+
// During Copper Holiday: all calls free, no key needed.
|
|
85
|
+
// After June 28: key required, balance deducted per call.
|
|
86
|
+
let storedApiKey = null;
|
|
87
|
+
async function billingGate(tool) {
|
|
88
|
+
if (isCopperHoliday()) {
|
|
89
|
+
return { allowed: true, message: copperHolidayNote() };
|
|
90
|
+
}
|
|
91
|
+
if (!storedApiKey) {
|
|
92
|
+
return {
|
|
93
|
+
allowed: false,
|
|
94
|
+
message: "API key required. Use the register tool to create one, then set_api_key to activate it.",
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
try {
|
|
98
|
+
const res = await fetch(`${BILLING_URL}/use`, {
|
|
99
|
+
method: "POST",
|
|
100
|
+
headers: { "Content-Type": "application/json" },
|
|
101
|
+
body: JSON.stringify({ key: storedApiKey, tool }),
|
|
102
|
+
});
|
|
103
|
+
const data = await res.json();
|
|
104
|
+
if (!data.allowed) {
|
|
105
|
+
return {
|
|
106
|
+
allowed: false,
|
|
107
|
+
message: data.reason || "No calls remaining. Use buy_calls to purchase more.",
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
allowed: true,
|
|
112
|
+
message: data.balance >= 0 ? `${data.balance} calls remaining` : "",
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
// If billing API is down, allow the call (fail open during transition)
|
|
117
|
+
return { allowed: true, message: "Billing check unavailable — call allowed" };
|
|
118
|
+
}
|
|
119
|
+
}
|
|
78
120
|
const ORACLE_URLS = {
|
|
79
121
|
a: "https://classa.mj41.me",
|
|
80
122
|
b: "https://classb.mj41.me",
|
|
81
123
|
c: "https://classc.mj41.me",
|
|
82
124
|
};
|
|
125
|
+
const METALS_BASE = "https://metals.mj41.me";
|
|
126
|
+
const METAL_URLS = {
|
|
127
|
+
gold: METALS_BASE,
|
|
128
|
+
silver: METALS_BASE,
|
|
129
|
+
aluminum: METALS_BASE,
|
|
130
|
+
platinum: METALS_BASE,
|
|
131
|
+
palladium: METALS_BASE,
|
|
132
|
+
};
|
|
133
|
+
const METAL_LABELS = {
|
|
134
|
+
gold: "Gold — COMEX GC=F (USD/troy oz)",
|
|
135
|
+
silver: "Silver — COMEX SI=F (USD/troy oz)",
|
|
136
|
+
aluminum: "Aluminum — COMEX ALI=F (USD/lb)",
|
|
137
|
+
platinum: "Platinum — NYMEX PL=F (USD/troy oz)",
|
|
138
|
+
palladium: "Palladium — NYMEX PA=F (USD/troy oz)",
|
|
139
|
+
};
|
|
83
140
|
const CLASS_LABELS = {
|
|
84
141
|
a: "Class A — COMEX Spot Copper (Grade A cathode, 99.99% purity, COMEX HG futures)",
|
|
85
142
|
b: "Class B — Scrap Copper (yard call-arounds + COMEX reference)",
|
|
@@ -95,6 +152,9 @@ server.tool("get_copper_price", "Get the current copper price from a ZJ Industri
|
|
|
95
152
|
.enum(["a", "b", "c"])
|
|
96
153
|
.describe("Copper class: 'a' for COMEX spot, 'b' for scrap, 'c' for industrial"),
|
|
97
154
|
}, async ({ class: copperClass }) => {
|
|
155
|
+
const gate = await billingGate("get_copper_price");
|
|
156
|
+
if (!gate.allowed)
|
|
157
|
+
return { content: [{ type: "text", text: gate.message }] };
|
|
98
158
|
try {
|
|
99
159
|
checkRate("copper", RATE.COPPER);
|
|
100
160
|
const url = ORACLE_URLS[copperClass];
|
|
@@ -125,7 +185,7 @@ server.tool("get_copper_price", "Get the current copper price from a ZJ Industri
|
|
|
125
185
|
status.update_mode ? `Update mode: ${status.update_mode} (${status.update_frequency})` : "",
|
|
126
186
|
``,
|
|
127
187
|
`Note: Price value available via ${url}/api/v1/price`,
|
|
128
|
-
|
|
188
|
+
gate.message,
|
|
129
189
|
``,
|
|
130
190
|
`— mj41, LLC | ZJ Industries (zjindustries.com)`,
|
|
131
191
|
]
|
|
@@ -144,6 +204,9 @@ server.tool("get_copper_price", "Get the current copper price from a ZJ Industri
|
|
|
144
204
|
});
|
|
145
205
|
// ── get_all_copper_prices ───────────────────────────────────
|
|
146
206
|
server.tool("get_all_copper_prices", "Get status of all three ZJ Industries copper oracles at once (Class A COMEX spot, Class B scrap, Class C industrial). Returns freshness, last update time, and contract addresses. Powered by ZJ Industries x mj41, LLC.", {}, async () => {
|
|
207
|
+
const gate = await billingGate("get_all_copper_prices");
|
|
208
|
+
if (!gate.allowed)
|
|
209
|
+
return { content: [{ type: "text", text: gate.message }] };
|
|
147
210
|
const results = [
|
|
148
211
|
"ZJ Industries x MJ41 — Copper Oracle Suite",
|
|
149
212
|
"Three on-chain copper price feeds on Base mainnet",
|
|
@@ -192,6 +255,9 @@ server.tool("get_oracle_status", "Check if a ZJ Industries copper oracle is oper
|
|
|
192
255
|
.enum(["a", "b", "c"])
|
|
193
256
|
.describe("Copper class: 'a' for COMEX spot, 'b' for scrap, 'c' for industrial"),
|
|
194
257
|
}, async ({ class: copperClass }) => {
|
|
258
|
+
const gate = await billingGate("get_oracle_status");
|
|
259
|
+
if (!gate.allowed)
|
|
260
|
+
return { content: [{ type: "text", text: gate.message }] };
|
|
195
261
|
try {
|
|
196
262
|
checkRate("copper", RATE.COPPER);
|
|
197
263
|
const url = ORACLE_URLS[copperClass];
|
|
@@ -218,6 +284,141 @@ server.tool("get_oracle_status", "Check if a ZJ Industries copper oracle is oper
|
|
|
218
284
|
}
|
|
219
285
|
});
|
|
220
286
|
// ════════════════════════════════════════════════════════════
|
|
287
|
+
// METALS ORACLES — ZJ Industries x MJ41
|
|
288
|
+
// ════════════════════════════════════════════════════════════
|
|
289
|
+
// ── get_metal_price ─────────────────────────────────────────
|
|
290
|
+
server.tool("get_metal_price", "Get the current price of a precious or industrial metal from a ZJ Industries on-chain oracle on Base. Available metals: gold (COMEX GC=F), silver (COMEX SI=F), aluminum (COMEX ALI=F), platinum (NYMEX PL=F), palladium (NYMEX PA=F). All prices update every 15 minutes during market hours. Powered by ZJ Industries x mj41, LLC.", {
|
|
291
|
+
metal: z
|
|
292
|
+
.enum(["gold", "silver", "aluminum", "platinum", "palladium"])
|
|
293
|
+
.describe("Metal to query: gold, silver, aluminum, platinum, or palladium"),
|
|
294
|
+
}, async ({ metal }) => {
|
|
295
|
+
const gate = await billingGate("get_metal_price");
|
|
296
|
+
if (!gate.allowed)
|
|
297
|
+
return { content: [{ type: "text", text: gate.message }] };
|
|
298
|
+
try {
|
|
299
|
+
checkRate("copper", RATE.COPPER);
|
|
300
|
+
const status = await cachedFetch(`metal-status-${metal}`, async () => {
|
|
301
|
+
const res = await fetch(`${METALS_BASE}/api/v1/${metal}/status`);
|
|
302
|
+
if (!res.ok)
|
|
303
|
+
throw new Error(`HTTP ${res.status}`);
|
|
304
|
+
return res.json();
|
|
305
|
+
}, TTL.COPPER_STATUS);
|
|
306
|
+
const label = METAL_LABELS[metal];
|
|
307
|
+
const fresh = status.is_fresh ? "FRESH" : "STALE";
|
|
308
|
+
const lastUpdate = new Date(status.last_updated).toLocaleString("en-US", {
|
|
309
|
+
timeZone: "America/New_York",
|
|
310
|
+
dateStyle: "medium",
|
|
311
|
+
timeStyle: "short",
|
|
312
|
+
});
|
|
313
|
+
return {
|
|
314
|
+
content: [
|
|
315
|
+
{
|
|
316
|
+
type: "text",
|
|
317
|
+
text: [
|
|
318
|
+
`ZJ Industries Metal Oracle — ${label}`,
|
|
319
|
+
``,
|
|
320
|
+
status.price ? `Price: $${status.price} ${status.unit}` : "",
|
|
321
|
+
`Status: ${status.status} (${fresh})`,
|
|
322
|
+
`Last updated: ${lastUpdate} ET`,
|
|
323
|
+
`Chain: Base mainnet (8453)`,
|
|
324
|
+
`Contract: ${status.contract}`,
|
|
325
|
+
status.update_mode ? `Update mode: ${status.update_mode} (${status.update_frequency})` : "",
|
|
326
|
+
``,
|
|
327
|
+
gate.message,
|
|
328
|
+
``,
|
|
329
|
+
`— mj41, LLC | ZJ Industries (zjindustries.com)`,
|
|
330
|
+
]
|
|
331
|
+
.filter(Boolean)
|
|
332
|
+
.join("\n"),
|
|
333
|
+
},
|
|
334
|
+
],
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
catch (err) {
|
|
338
|
+
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
339
|
+
return {
|
|
340
|
+
content: [{ type: "text", text: `Oracle read error: ${msg}` }],
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
// ── get_all_metal_prices ────────────────────────────────────
|
|
345
|
+
server.tool("get_all_metal_prices", "Get status of all 8 ZJ Industries on-chain oracles at once — 3 copper classes (A COMEX spot, B scrap, C industrial) plus 5 metals (gold, silver, aluminum, platinum, palladium). Returns freshness, last update time, and contract addresses for each. Powered by ZJ Industries x mj41, LLC.", {}, async () => {
|
|
346
|
+
const gate = await billingGate("get_all_metal_prices");
|
|
347
|
+
if (!gate.allowed)
|
|
348
|
+
return { content: [{ type: "text", text: gate.message }] };
|
|
349
|
+
const results = [
|
|
350
|
+
"ZJ Industries x MJ41 — Full Metals Oracle Suite",
|
|
351
|
+
"8 on-chain price feeds on Base mainnet",
|
|
352
|
+
"═══════════════════════════════════════════════",
|
|
353
|
+
"",
|
|
354
|
+
"COPPER ORACLES",
|
|
355
|
+
"──────────────",
|
|
356
|
+
"",
|
|
357
|
+
];
|
|
358
|
+
checkRate("copper", RATE.COPPER);
|
|
359
|
+
// Copper oracles
|
|
360
|
+
for (const [cls, url] of Object.entries(ORACLE_URLS)) {
|
|
361
|
+
try {
|
|
362
|
+
const status = await cachedFetch(`copper-status-${cls}`, async () => {
|
|
363
|
+
const res = await fetch(`${url}/api/v1/status`);
|
|
364
|
+
return res.json();
|
|
365
|
+
}, TTL.COPPER_STATUS);
|
|
366
|
+
const fresh = status.is_fresh ? "FRESH" : "STALE";
|
|
367
|
+
const lastUpdate = new Date(status.last_updated).toLocaleString("en-US", {
|
|
368
|
+
timeZone: "America/New_York",
|
|
369
|
+
dateStyle: "medium",
|
|
370
|
+
timeStyle: "short",
|
|
371
|
+
});
|
|
372
|
+
results.push(`${CLASS_LABELS[cls]}`);
|
|
373
|
+
results.push(` Status: ${status.status} (${fresh})`);
|
|
374
|
+
results.push(` Last updated: ${lastUpdate} ET`);
|
|
375
|
+
results.push(` Contract: ${status.contract}`);
|
|
376
|
+
results.push("");
|
|
377
|
+
}
|
|
378
|
+
catch (err) {
|
|
379
|
+
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
380
|
+
results.push(`${CLASS_LABELS[cls]}`);
|
|
381
|
+
results.push(` Error: ${msg}`);
|
|
382
|
+
results.push("");
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
results.push("METALS ORACLES");
|
|
386
|
+
results.push("──────────────");
|
|
387
|
+
results.push("");
|
|
388
|
+
// Metal oracles
|
|
389
|
+
for (const metal of Object.keys(METAL_URLS)) {
|
|
390
|
+
try {
|
|
391
|
+
const status = await cachedFetch(`metal-status-${metal}`, async () => {
|
|
392
|
+
const res = await fetch(`${METALS_BASE}/api/v1/${metal}/status`);
|
|
393
|
+
return res.json();
|
|
394
|
+
}, TTL.COPPER_STATUS);
|
|
395
|
+
const fresh = status.is_fresh ? "FRESH" : "STALE";
|
|
396
|
+
const lastUpdate = new Date(status.last_updated).toLocaleString("en-US", {
|
|
397
|
+
timeZone: "America/New_York",
|
|
398
|
+
dateStyle: "medium",
|
|
399
|
+
timeStyle: "short",
|
|
400
|
+
});
|
|
401
|
+
results.push(`${METAL_LABELS[metal]}`);
|
|
402
|
+
results.push(` Status: ${status.status} (${fresh})`);
|
|
403
|
+
results.push(` Last updated: ${lastUpdate} ET`);
|
|
404
|
+
results.push(` Contract: ${status.contract}`);
|
|
405
|
+
results.push("");
|
|
406
|
+
}
|
|
407
|
+
catch (err) {
|
|
408
|
+
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
409
|
+
results.push(`${METAL_LABELS[metal]}`);
|
|
410
|
+
results.push(` Error: ${msg}`);
|
|
411
|
+
results.push("");
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
results.push(copperHolidayNote());
|
|
415
|
+
results.push("");
|
|
416
|
+
results.push("— mj41, LLC | ZJ Industries (zjindustries.com)");
|
|
417
|
+
return {
|
|
418
|
+
content: [{ type: "text", text: results.join("\n") }],
|
|
419
|
+
};
|
|
420
|
+
});
|
|
421
|
+
// ════════════════════════════════════════════════════════════
|
|
221
422
|
// RICHARD TRACY — Blockchain Detective (mj41, LLC)
|
|
222
423
|
// ════════════════════════════════════════════════════════════
|
|
223
424
|
// ── investigate_wallet ──────────────────────────────────────
|
|
@@ -226,6 +427,9 @@ server.tool("investigate_wallet", "Investigate a blockchain wallet address or tr
|
|
|
226
427
|
.string()
|
|
227
428
|
.describe("The investigation query. Can be a wallet address, transaction hash, or incident description (e.g. 'I clicked a link and my wallet was drained, address: 0x...')"),
|
|
228
429
|
}, async ({ query }) => {
|
|
430
|
+
const gate = await billingGate("investigate_wallet");
|
|
431
|
+
if (!gate.allowed)
|
|
432
|
+
return { content: [{ type: "text", text: gate.message }] };
|
|
229
433
|
try {
|
|
230
434
|
checkRate("richard", RATE.RICHARD);
|
|
231
435
|
// No cache — investigations are unique per query
|
|
@@ -264,6 +468,9 @@ server.tool("investigate_wallet", "Investigate a blockchain wallet address or tr
|
|
|
264
468
|
});
|
|
265
469
|
// ── get_eth_price ───────────────────────────────────────────
|
|
266
470
|
server.tool("get_eth_price", "Get the current price of Ethereum in USD. Powered by mj41, LLC.", {}, async () => {
|
|
471
|
+
const gate = await billingGate("get_eth_price");
|
|
472
|
+
if (!gate.allowed)
|
|
473
|
+
return { content: [{ type: "text", text: gate.message }] };
|
|
267
474
|
try {
|
|
268
475
|
checkRate("richard", RATE.RICHARD);
|
|
269
476
|
const data = await cachedFetch("eth-price", async () => {
|
|
@@ -297,6 +504,9 @@ server.tool("get_latest_news", "Get the latest stories from The First Signal, an
|
|
|
297
504
|
.default(5)
|
|
298
505
|
.describe("Number of stories to return (default 5, max 20)"),
|
|
299
506
|
}, async ({ limit }) => {
|
|
507
|
+
const gate = await billingGate("get_latest_news");
|
|
508
|
+
if (!gate.allowed)
|
|
509
|
+
return { content: [{ type: "text", text: gate.message }] };
|
|
300
510
|
try {
|
|
301
511
|
checkRate("first-signal", RATE.FIRST_SIGNAL);
|
|
302
512
|
const cap = Math.min(limit, 20);
|
|
@@ -354,6 +564,9 @@ server.tool("get_news_by_beat", "Get stories from a specific beat on The First S
|
|
|
354
564
|
.default(5)
|
|
355
565
|
.describe("Number of stories (default 5)"),
|
|
356
566
|
}, async ({ beat, limit }) => {
|
|
567
|
+
const gate = await billingGate("get_news_by_beat");
|
|
568
|
+
if (!gate.allowed)
|
|
569
|
+
return { content: [{ type: "text", text: gate.message }] };
|
|
357
570
|
try {
|
|
358
571
|
checkRate("first-signal", RATE.FIRST_SIGNAL);
|
|
359
572
|
const cap = Math.min(limit, 20);
|
|
@@ -405,6 +618,9 @@ server.tool("get_story", "Read the full text of a specific story from The First
|
|
|
405
618
|
.string()
|
|
406
619
|
.describe("The story ID (UUID)"),
|
|
407
620
|
}, async ({ id }) => {
|
|
621
|
+
const gate = await billingGate("get_story");
|
|
622
|
+
if (!gate.allowed)
|
|
623
|
+
return { content: [{ type: "text", text: gate.message }] };
|
|
408
624
|
try {
|
|
409
625
|
checkRate("first-signal", RATE.FIRST_SIGNAL);
|
|
410
626
|
const story = await cachedFetch(`news-story-${id}`, async () => {
|
|
@@ -446,6 +662,9 @@ server.tool("submit_story_idea", "Submit a story idea or investigation request t
|
|
|
446
662
|
topic: z.string().describe("The story topic or headline"),
|
|
447
663
|
description: z.string().describe("Details about what should be investigated"),
|
|
448
664
|
}, async ({ topic, description }) => {
|
|
665
|
+
const gate = await billingGate("submit_story_idea");
|
|
666
|
+
if (!gate.allowed)
|
|
667
|
+
return { content: [{ type: "text", text: gate.message }] };
|
|
449
668
|
try {
|
|
450
669
|
checkRate("first-signal", RATE.FIRST_SIGNAL);
|
|
451
670
|
// No cache — write operation
|
|
@@ -492,6 +711,177 @@ server.tool("submit_story_idea", "Submit a story idea or investigation request t
|
|
|
492
711
|
}
|
|
493
712
|
});
|
|
494
713
|
// ════════════════════════════════════════════════════════════
|
|
714
|
+
// BILLING — Register, Set Key, Buy Calls, Check Balance
|
|
715
|
+
// ════════════════════════════════════════════════════════════
|
|
716
|
+
// ── register ────────────────────────────────────────────────
|
|
717
|
+
server.tool("register", "Register for an mj41 MCP API key. Returns a key with 10 free calls. Save the key — it cannot be recovered. After the Copper Holiday (June 28, 2026), an API key is required for all tool calls.", {
|
|
718
|
+
label: z
|
|
719
|
+
.string()
|
|
720
|
+
.optional()
|
|
721
|
+
.describe("Optional label for this key (e.g. your name or bot name)"),
|
|
722
|
+
}, async ({ label }) => {
|
|
723
|
+
try {
|
|
724
|
+
const res = await fetch(`${BILLING_URL}/register`, {
|
|
725
|
+
method: "POST",
|
|
726
|
+
headers: { "Content-Type": "application/json" },
|
|
727
|
+
body: JSON.stringify({ label: label || "" }),
|
|
728
|
+
});
|
|
729
|
+
const data = await res.json();
|
|
730
|
+
if (data.error) {
|
|
731
|
+
return { content: [{ type: "text", text: `Registration failed: ${data.error}` }] };
|
|
732
|
+
}
|
|
733
|
+
// Auto-store the key for this session
|
|
734
|
+
storedApiKey = data.key;
|
|
735
|
+
return {
|
|
736
|
+
content: [
|
|
737
|
+
{
|
|
738
|
+
type: "text",
|
|
739
|
+
text: [
|
|
740
|
+
"MJ41 API KEY REGISTERED",
|
|
741
|
+
"═══════════════════════",
|
|
742
|
+
"",
|
|
743
|
+
`Key: ${data.key}`,
|
|
744
|
+
`Balance: ${data.balance} free calls`,
|
|
745
|
+
"",
|
|
746
|
+
"SAVE THIS KEY — it cannot be recovered.",
|
|
747
|
+
"Key has been activated for this session.",
|
|
748
|
+
"",
|
|
749
|
+
"Pricing (after Copper Holiday ends June 28):",
|
|
750
|
+
" 41 RWACu / call — pay with RWACu, save 50%",
|
|
751
|
+
" 82 RWACu equiv / call — pay with USDC or Stripe",
|
|
752
|
+
"",
|
|
753
|
+
"Use buy_calls to purchase more when your balance runs low.",
|
|
754
|
+
"",
|
|
755
|
+
copperHolidayNote(),
|
|
756
|
+
"",
|
|
757
|
+
"— mj41, LLC",
|
|
758
|
+
].join("\n"),
|
|
759
|
+
},
|
|
760
|
+
],
|
|
761
|
+
};
|
|
762
|
+
}
|
|
763
|
+
catch (err) {
|
|
764
|
+
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
765
|
+
return { content: [{ type: "text", text: `Registration error: ${msg}` }] };
|
|
766
|
+
}
|
|
767
|
+
});
|
|
768
|
+
// ── set_api_key ─────────────────────────────────────────────
|
|
769
|
+
server.tool("set_api_key", "Set your mj41 API key for this session. Required after the Copper Holiday ends (June 28, 2026). If you already have a key, use this to activate it.", {
|
|
770
|
+
key: z.string().describe("Your mj41 API key (starts with mj41_)"),
|
|
771
|
+
}, async ({ key }) => {
|
|
772
|
+
try {
|
|
773
|
+
const res = await fetch(`${BILLING_URL}/balance?key=${encodeURIComponent(key)}`);
|
|
774
|
+
const data = await res.json();
|
|
775
|
+
if (data.error) {
|
|
776
|
+
return { content: [{ type: "text", text: `Invalid key: ${data.error}` }] };
|
|
777
|
+
}
|
|
778
|
+
storedApiKey = key;
|
|
779
|
+
return {
|
|
780
|
+
content: [
|
|
781
|
+
{
|
|
782
|
+
type: "text",
|
|
783
|
+
text: [
|
|
784
|
+
"API key activated for this session.",
|
|
785
|
+
"",
|
|
786
|
+
`Balance: ${data.balance} calls remaining`,
|
|
787
|
+
`Total used: ${data.total_used}`,
|
|
788
|
+
`Total purchased: ${data.total_purchased}`,
|
|
789
|
+
"",
|
|
790
|
+
copperHolidayNote(),
|
|
791
|
+
"",
|
|
792
|
+
"— mj41, LLC",
|
|
793
|
+
].join("\n"),
|
|
794
|
+
},
|
|
795
|
+
],
|
|
796
|
+
};
|
|
797
|
+
}
|
|
798
|
+
catch (err) {
|
|
799
|
+
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
800
|
+
return { content: [{ type: "text", text: `Key validation error: ${msg}` }] };
|
|
801
|
+
}
|
|
802
|
+
});
|
|
803
|
+
// ── check_balance ───────────────────────────────────────────
|
|
804
|
+
server.tool("check_balance", "Check your remaining call balance on the mj41 MCP server.", {}, async () => {
|
|
805
|
+
if (!storedApiKey) {
|
|
806
|
+
return {
|
|
807
|
+
content: [
|
|
808
|
+
{
|
|
809
|
+
type: "text",
|
|
810
|
+
text: isCopperHoliday()
|
|
811
|
+
? `No API key set — but all calls are free during the Copper Holiday.\n\n${copperHolidayNote()}`
|
|
812
|
+
: "No API key set. Use register to create one or set_api_key to activate an existing key.",
|
|
813
|
+
},
|
|
814
|
+
],
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
try {
|
|
818
|
+
const res = await fetch(`${BILLING_URL}/balance?key=${encodeURIComponent(storedApiKey)}`);
|
|
819
|
+
const data = await res.json();
|
|
820
|
+
return {
|
|
821
|
+
content: [
|
|
822
|
+
{
|
|
823
|
+
type: "text",
|
|
824
|
+
text: [
|
|
825
|
+
"MJ41 ACCOUNT",
|
|
826
|
+
"═════════════",
|
|
827
|
+
"",
|
|
828
|
+
`Balance: ${data.balance} calls remaining`,
|
|
829
|
+
`Total used: ${data.total_used}`,
|
|
830
|
+
`Total purchased: ${data.total_purchased}`,
|
|
831
|
+
data.copper_holiday ? "Status: COPPER HOLIDAY — all calls free until June 28" : "",
|
|
832
|
+
"",
|
|
833
|
+
copperHolidayNote(),
|
|
834
|
+
"",
|
|
835
|
+
"— mj41, LLC",
|
|
836
|
+
]
|
|
837
|
+
.filter(Boolean)
|
|
838
|
+
.join("\n"),
|
|
839
|
+
},
|
|
840
|
+
],
|
|
841
|
+
};
|
|
842
|
+
}
|
|
843
|
+
catch (err) {
|
|
844
|
+
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
845
|
+
return { content: [{ type: "text", text: `Balance check error: ${msg}` }] };
|
|
846
|
+
}
|
|
847
|
+
});
|
|
848
|
+
// ── buy_calls ───────────────────────────────────────────────
|
|
849
|
+
server.tool("buy_calls", "Get payment instructions to purchase more MCP tool calls. Pay with RWACu (save 50%), USDC, or Stripe.", {}, async () => {
|
|
850
|
+
return {
|
|
851
|
+
content: [
|
|
852
|
+
{
|
|
853
|
+
type: "text",
|
|
854
|
+
text: [
|
|
855
|
+
"BUY MJ41 MCP CALLS",
|
|
856
|
+
"═══════════════════",
|
|
857
|
+
"",
|
|
858
|
+
"OPTION 1: RWACu (save 50%)",
|
|
859
|
+
" Rate: 41 RWACu per call",
|
|
860
|
+
" Send RWACu to: 0x758AF4a670adE40C2FfE1B6C4746340910a44B96",
|
|
861
|
+
" Network: Base (Chain ID 8453)",
|
|
862
|
+
" Include your API key in the transaction memo or email it after sending.",
|
|
863
|
+
"",
|
|
864
|
+
"OPTION 2: USDC",
|
|
865
|
+
" Rate: 82 RWACu equivalent in USDC per call",
|
|
866
|
+
" Send USDC to: 0x758AF4a670adE40C2FfE1B6C4746340910a44B96",
|
|
867
|
+
" Network: Base (Chain ID 8453)",
|
|
868
|
+
"",
|
|
869
|
+
"OPTION 3: Stripe (card)",
|
|
870
|
+
" 100 calls — $9.99 | 500 calls — $39.99 | 1,000 calls — $69.99",
|
|
871
|
+
" Visit: rwacu.com/buy",
|
|
872
|
+
"",
|
|
873
|
+
"After payment, credits are added to your key automatically.",
|
|
874
|
+
"Contact: api@zjindustries.com for bulk pricing.",
|
|
875
|
+
"",
|
|
876
|
+
copperHolidayNote(),
|
|
877
|
+
"",
|
|
878
|
+
"— mj41, LLC",
|
|
879
|
+
].join("\n"),
|
|
880
|
+
},
|
|
881
|
+
],
|
|
882
|
+
};
|
|
883
|
+
});
|
|
884
|
+
// ════════════════════════════════════════════════════════════
|
|
495
885
|
// ABOUT
|
|
496
886
|
// ════════════════════════════════════════════════════════════
|
|
497
887
|
// ── about_mj41 ──────────────────────────────────────────────
|
|
@@ -510,11 +900,16 @@ server.tool("about_mj41", "Learn about mj41, LLC — who they are, what they bui
|
|
|
510
900
|
"",
|
|
511
901
|
"COPPER ORACLES (ZJ Industries x MJ41)",
|
|
512
902
|
" get_copper_price — Live price for Class A, B, or C",
|
|
513
|
-
" get_all_copper_prices — All three oracles at once",
|
|
903
|
+
" get_all_copper_prices — All three copper oracles at once",
|
|
514
904
|
" get_oracle_status — Health check and freshness",
|
|
515
905
|
" Class A: COMEX spot (15-min) | Class B: Scrap (weekly) | Class C: Industrial (2x/week)",
|
|
516
906
|
" On-chain on Base mainnet.",
|
|
517
907
|
"",
|
|
908
|
+
"METALS ORACLES (ZJ Industries x MJ41)",
|
|
909
|
+
" get_metal_price — Live price for gold, silver, aluminum, platinum, or palladium",
|
|
910
|
+
" get_all_metal_prices — All 8 oracles (3 copper + 5 metals) at once",
|
|
911
|
+
" All on-chain on Base mainnet, auto-updated every 15 min during market hours.",
|
|
912
|
+
"",
|
|
518
913
|
"RICHARD TRACY (Blockchain Detective by mj41)",
|
|
519
914
|
" investigate_wallet — Phishing/scam/drainer investigation with full case report",
|
|
520
915
|
" get_eth_price — Current ETH price in USD",
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mj41-mcp",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "MJ41 MCP Server — Agent gateway for mj41, LLC. Live
|
|
3
|
+
"version": "1.5.0",
|
|
4
|
+
"description": "MJ41 MCP Server — Agent gateway for mj41, LLC. Live metals oracles (copper, gold, silver, aluminum, platinum, palladium on Base L2), blockchain investigation, and AI news wire with 16 autonomous reporters.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"bin": {
|