mj41-mcp 1.2.1 → 1.4.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 +254 -5
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -60,8 +60,59 @@ const RATE = {
|
|
|
60
60
|
RICHARD: 10, // investigate_wallet calls AI, expensive
|
|
61
61
|
FIRST_SIGNAL: 30,
|
|
62
62
|
};
|
|
63
|
+
// ── Copper Holiday: free until June 28, 2026 ─────────────────
|
|
64
|
+
const COPPER_HOLIDAY_END = new Date("2026-06-28T00:00:00-05:00");
|
|
65
|
+
function isCopperHoliday() {
|
|
66
|
+
return Date.now() < COPPER_HOLIDAY_END.getTime();
|
|
67
|
+
}
|
|
68
|
+
function copperHolidayNote() {
|
|
69
|
+
if (isCopperHoliday()) {
|
|
70
|
+
const diff = COPPER_HOLIDAY_END.getTime() - Date.now();
|
|
71
|
+
const days = Math.ceil(diff / 86_400_000);
|
|
72
|
+
return `COPPER HOLIDAY — All tools free until June 28, 2026 (${days} days remaining). After that: 41 RWACu/call or 82 RWACu equiv via USDC/Stripe. Pay with RWACu and save 50%.`;
|
|
73
|
+
}
|
|
74
|
+
return "Copper Holiday has ended. API key required. Register with the register tool.";
|
|
75
|
+
}
|
|
76
|
+
const BILLING_URL = "https://rwacu.com/api/billing";
|
|
63
77
|
const COPPERTRACE_URL = "https://coppertrace.vercel.app";
|
|
64
78
|
const FIRST_SIGNAL_URL = "https://aiwire.mj41.me";
|
|
79
|
+
// ── Billing gate ─────────────────────────────────────────────
|
|
80
|
+
// During Copper Holiday: all calls free, no key needed.
|
|
81
|
+
// After June 28: key required, balance deducted per call.
|
|
82
|
+
let storedApiKey = null;
|
|
83
|
+
async function billingGate(tool) {
|
|
84
|
+
if (isCopperHoliday()) {
|
|
85
|
+
return { allowed: true, message: copperHolidayNote() };
|
|
86
|
+
}
|
|
87
|
+
if (!storedApiKey) {
|
|
88
|
+
return {
|
|
89
|
+
allowed: false,
|
|
90
|
+
message: "API key required. Use the register tool to create one, then set_api_key to activate it.",
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
const res = await fetch(`${BILLING_URL}/use`, {
|
|
95
|
+
method: "POST",
|
|
96
|
+
headers: { "Content-Type": "application/json" },
|
|
97
|
+
body: JSON.stringify({ key: storedApiKey, tool }),
|
|
98
|
+
});
|
|
99
|
+
const data = await res.json();
|
|
100
|
+
if (!data.allowed) {
|
|
101
|
+
return {
|
|
102
|
+
allowed: false,
|
|
103
|
+
message: data.reason || "No calls remaining. Use buy_calls to purchase more.",
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
allowed: true,
|
|
108
|
+
message: data.balance >= 0 ? `${data.balance} calls remaining` : "",
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
// If billing API is down, allow the call (fail open during transition)
|
|
113
|
+
return { allowed: true, message: "Billing check unavailable — call allowed" };
|
|
114
|
+
}
|
|
115
|
+
}
|
|
65
116
|
const ORACLE_URLS = {
|
|
66
117
|
a: "https://classa.mj41.me",
|
|
67
118
|
b: "https://classb.mj41.me",
|
|
@@ -82,6 +133,9 @@ server.tool("get_copper_price", "Get the current copper price from a ZJ Industri
|
|
|
82
133
|
.enum(["a", "b", "c"])
|
|
83
134
|
.describe("Copper class: 'a' for COMEX spot, 'b' for scrap, 'c' for industrial"),
|
|
84
135
|
}, async ({ class: copperClass }) => {
|
|
136
|
+
const gate = await billingGate("get_copper_price");
|
|
137
|
+
if (!gate.allowed)
|
|
138
|
+
return { content: [{ type: "text", text: gate.message }] };
|
|
85
139
|
try {
|
|
86
140
|
checkRate("copper", RATE.COPPER);
|
|
87
141
|
const url = ORACLE_URLS[copperClass];
|
|
@@ -112,7 +166,7 @@ server.tool("get_copper_price", "Get the current copper price from a ZJ Industri
|
|
|
112
166
|
status.update_mode ? `Update mode: ${status.update_mode} (${status.update_frequency})` : "",
|
|
113
167
|
``,
|
|
114
168
|
`Note: Price value available via ${url}/api/v1/price`,
|
|
115
|
-
|
|
169
|
+
gate.message,
|
|
116
170
|
``,
|
|
117
171
|
`— mj41, LLC | ZJ Industries (zjindustries.com)`,
|
|
118
172
|
]
|
|
@@ -131,6 +185,9 @@ server.tool("get_copper_price", "Get the current copper price from a ZJ Industri
|
|
|
131
185
|
});
|
|
132
186
|
// ── get_all_copper_prices ───────────────────────────────────
|
|
133
187
|
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 () => {
|
|
188
|
+
const gate = await billingGate("get_all_copper_prices");
|
|
189
|
+
if (!gate.allowed)
|
|
190
|
+
return { content: [{ type: "text", text: gate.message }] };
|
|
134
191
|
const results = [
|
|
135
192
|
"ZJ Industries x MJ41 — Copper Oracle Suite",
|
|
136
193
|
"Three on-chain copper price feeds on Base mainnet",
|
|
@@ -166,8 +223,7 @@ server.tool("get_all_copper_prices", "Get status of all three ZJ Industries copp
|
|
|
166
223
|
results.push("");
|
|
167
224
|
}
|
|
168
225
|
}
|
|
169
|
-
results.push(
|
|
170
|
-
results.push("API key requests: api@zjindustries.com");
|
|
226
|
+
results.push(copperHolidayNote());
|
|
171
227
|
results.push("");
|
|
172
228
|
results.push("— mj41, LLC | ZJ Industries (zjindustries.com)");
|
|
173
229
|
return {
|
|
@@ -180,6 +236,9 @@ server.tool("get_oracle_status", "Check if a ZJ Industries copper oracle is oper
|
|
|
180
236
|
.enum(["a", "b", "c"])
|
|
181
237
|
.describe("Copper class: 'a' for COMEX spot, 'b' for scrap, 'c' for industrial"),
|
|
182
238
|
}, async ({ class: copperClass }) => {
|
|
239
|
+
const gate = await billingGate("get_oracle_status");
|
|
240
|
+
if (!gate.allowed)
|
|
241
|
+
return { content: [{ type: "text", text: gate.message }] };
|
|
183
242
|
try {
|
|
184
243
|
checkRate("copper", RATE.COPPER);
|
|
185
244
|
const url = ORACLE_URLS[copperClass];
|
|
@@ -214,6 +273,9 @@ server.tool("investigate_wallet", "Investigate a blockchain wallet address or tr
|
|
|
214
273
|
.string()
|
|
215
274
|
.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...')"),
|
|
216
275
|
}, async ({ query }) => {
|
|
276
|
+
const gate = await billingGate("investigate_wallet");
|
|
277
|
+
if (!gate.allowed)
|
|
278
|
+
return { content: [{ type: "text", text: gate.message }] };
|
|
217
279
|
try {
|
|
218
280
|
checkRate("richard", RATE.RICHARD);
|
|
219
281
|
// No cache — investigations are unique per query
|
|
@@ -252,6 +314,9 @@ server.tool("investigate_wallet", "Investigate a blockchain wallet address or tr
|
|
|
252
314
|
});
|
|
253
315
|
// ── get_eth_price ───────────────────────────────────────────
|
|
254
316
|
server.tool("get_eth_price", "Get the current price of Ethereum in USD. Powered by mj41, LLC.", {}, async () => {
|
|
317
|
+
const gate = await billingGate("get_eth_price");
|
|
318
|
+
if (!gate.allowed)
|
|
319
|
+
return { content: [{ type: "text", text: gate.message }] };
|
|
255
320
|
try {
|
|
256
321
|
checkRate("richard", RATE.RICHARD);
|
|
257
322
|
const data = await cachedFetch("eth-price", async () => {
|
|
@@ -285,6 +350,9 @@ server.tool("get_latest_news", "Get the latest stories from The First Signal, an
|
|
|
285
350
|
.default(5)
|
|
286
351
|
.describe("Number of stories to return (default 5, max 20)"),
|
|
287
352
|
}, async ({ limit }) => {
|
|
353
|
+
const gate = await billingGate("get_latest_news");
|
|
354
|
+
if (!gate.allowed)
|
|
355
|
+
return { content: [{ type: "text", text: gate.message }] };
|
|
288
356
|
try {
|
|
289
357
|
checkRate("first-signal", RATE.FIRST_SIGNAL);
|
|
290
358
|
const cap = Math.min(limit, 20);
|
|
@@ -342,6 +410,9 @@ server.tool("get_news_by_beat", "Get stories from a specific beat on The First S
|
|
|
342
410
|
.default(5)
|
|
343
411
|
.describe("Number of stories (default 5)"),
|
|
344
412
|
}, async ({ beat, limit }) => {
|
|
413
|
+
const gate = await billingGate("get_news_by_beat");
|
|
414
|
+
if (!gate.allowed)
|
|
415
|
+
return { content: [{ type: "text", text: gate.message }] };
|
|
345
416
|
try {
|
|
346
417
|
checkRate("first-signal", RATE.FIRST_SIGNAL);
|
|
347
418
|
const cap = Math.min(limit, 20);
|
|
@@ -393,6 +464,9 @@ server.tool("get_story", "Read the full text of a specific story from The First
|
|
|
393
464
|
.string()
|
|
394
465
|
.describe("The story ID (UUID)"),
|
|
395
466
|
}, async ({ id }) => {
|
|
467
|
+
const gate = await billingGate("get_story");
|
|
468
|
+
if (!gate.allowed)
|
|
469
|
+
return { content: [{ type: "text", text: gate.message }] };
|
|
396
470
|
try {
|
|
397
471
|
checkRate("first-signal", RATE.FIRST_SIGNAL);
|
|
398
472
|
const story = await cachedFetch(`news-story-${id}`, async () => {
|
|
@@ -434,6 +508,9 @@ server.tool("submit_story_idea", "Submit a story idea or investigation request t
|
|
|
434
508
|
topic: z.string().describe("The story topic or headline"),
|
|
435
509
|
description: z.string().describe("Details about what should be investigated"),
|
|
436
510
|
}, async ({ topic, description }) => {
|
|
511
|
+
const gate = await billingGate("submit_story_idea");
|
|
512
|
+
if (!gate.allowed)
|
|
513
|
+
return { content: [{ type: "text", text: gate.message }] };
|
|
437
514
|
try {
|
|
438
515
|
checkRate("first-signal", RATE.FIRST_SIGNAL);
|
|
439
516
|
// No cache — write operation
|
|
@@ -480,6 +557,177 @@ server.tool("submit_story_idea", "Submit a story idea or investigation request t
|
|
|
480
557
|
}
|
|
481
558
|
});
|
|
482
559
|
// ════════════════════════════════════════════════════════════
|
|
560
|
+
// BILLING — Register, Set Key, Buy Calls, Check Balance
|
|
561
|
+
// ════════════════════════════════════════════════════════════
|
|
562
|
+
// ── register ────────────────────────────────────────────────
|
|
563
|
+
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.", {
|
|
564
|
+
label: z
|
|
565
|
+
.string()
|
|
566
|
+
.optional()
|
|
567
|
+
.describe("Optional label for this key (e.g. your name or bot name)"),
|
|
568
|
+
}, async ({ label }) => {
|
|
569
|
+
try {
|
|
570
|
+
const res = await fetch(`${BILLING_URL}/register`, {
|
|
571
|
+
method: "POST",
|
|
572
|
+
headers: { "Content-Type": "application/json" },
|
|
573
|
+
body: JSON.stringify({ label: label || "" }),
|
|
574
|
+
});
|
|
575
|
+
const data = await res.json();
|
|
576
|
+
if (data.error) {
|
|
577
|
+
return { content: [{ type: "text", text: `Registration failed: ${data.error}` }] };
|
|
578
|
+
}
|
|
579
|
+
// Auto-store the key for this session
|
|
580
|
+
storedApiKey = data.key;
|
|
581
|
+
return {
|
|
582
|
+
content: [
|
|
583
|
+
{
|
|
584
|
+
type: "text",
|
|
585
|
+
text: [
|
|
586
|
+
"MJ41 API KEY REGISTERED",
|
|
587
|
+
"═══════════════════════",
|
|
588
|
+
"",
|
|
589
|
+
`Key: ${data.key}`,
|
|
590
|
+
`Balance: ${data.balance} free calls`,
|
|
591
|
+
"",
|
|
592
|
+
"SAVE THIS KEY — it cannot be recovered.",
|
|
593
|
+
"Key has been activated for this session.",
|
|
594
|
+
"",
|
|
595
|
+
"Pricing (after Copper Holiday ends June 28):",
|
|
596
|
+
" 41 RWACu / call — pay with RWACu, save 50%",
|
|
597
|
+
" 82 RWACu equiv / call — pay with USDC or Stripe",
|
|
598
|
+
"",
|
|
599
|
+
"Use buy_calls to purchase more when your balance runs low.",
|
|
600
|
+
"",
|
|
601
|
+
copperHolidayNote(),
|
|
602
|
+
"",
|
|
603
|
+
"— mj41, LLC",
|
|
604
|
+
].join("\n"),
|
|
605
|
+
},
|
|
606
|
+
],
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
catch (err) {
|
|
610
|
+
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
611
|
+
return { content: [{ type: "text", text: `Registration error: ${msg}` }] };
|
|
612
|
+
}
|
|
613
|
+
});
|
|
614
|
+
// ── set_api_key ─────────────────────────────────────────────
|
|
615
|
+
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.", {
|
|
616
|
+
key: z.string().describe("Your mj41 API key (starts with mj41_)"),
|
|
617
|
+
}, async ({ key }) => {
|
|
618
|
+
try {
|
|
619
|
+
const res = await fetch(`${BILLING_URL}/balance?key=${encodeURIComponent(key)}`);
|
|
620
|
+
const data = await res.json();
|
|
621
|
+
if (data.error) {
|
|
622
|
+
return { content: [{ type: "text", text: `Invalid key: ${data.error}` }] };
|
|
623
|
+
}
|
|
624
|
+
storedApiKey = key;
|
|
625
|
+
return {
|
|
626
|
+
content: [
|
|
627
|
+
{
|
|
628
|
+
type: "text",
|
|
629
|
+
text: [
|
|
630
|
+
"API key activated for this session.",
|
|
631
|
+
"",
|
|
632
|
+
`Balance: ${data.balance} calls remaining`,
|
|
633
|
+
`Total used: ${data.total_used}`,
|
|
634
|
+
`Total purchased: ${data.total_purchased}`,
|
|
635
|
+
"",
|
|
636
|
+
copperHolidayNote(),
|
|
637
|
+
"",
|
|
638
|
+
"— mj41, LLC",
|
|
639
|
+
].join("\n"),
|
|
640
|
+
},
|
|
641
|
+
],
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
catch (err) {
|
|
645
|
+
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
646
|
+
return { content: [{ type: "text", text: `Key validation error: ${msg}` }] };
|
|
647
|
+
}
|
|
648
|
+
});
|
|
649
|
+
// ── check_balance ───────────────────────────────────────────
|
|
650
|
+
server.tool("check_balance", "Check your remaining call balance on the mj41 MCP server.", {}, async () => {
|
|
651
|
+
if (!storedApiKey) {
|
|
652
|
+
return {
|
|
653
|
+
content: [
|
|
654
|
+
{
|
|
655
|
+
type: "text",
|
|
656
|
+
text: isCopperHoliday()
|
|
657
|
+
? `No API key set — but all calls are free during the Copper Holiday.\n\n${copperHolidayNote()}`
|
|
658
|
+
: "No API key set. Use register to create one or set_api_key to activate an existing key.",
|
|
659
|
+
},
|
|
660
|
+
],
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
try {
|
|
664
|
+
const res = await fetch(`${BILLING_URL}/balance?key=${encodeURIComponent(storedApiKey)}`);
|
|
665
|
+
const data = await res.json();
|
|
666
|
+
return {
|
|
667
|
+
content: [
|
|
668
|
+
{
|
|
669
|
+
type: "text",
|
|
670
|
+
text: [
|
|
671
|
+
"MJ41 ACCOUNT",
|
|
672
|
+
"═════════════",
|
|
673
|
+
"",
|
|
674
|
+
`Balance: ${data.balance} calls remaining`,
|
|
675
|
+
`Total used: ${data.total_used}`,
|
|
676
|
+
`Total purchased: ${data.total_purchased}`,
|
|
677
|
+
data.copper_holiday ? "Status: COPPER HOLIDAY — all calls free until June 28" : "",
|
|
678
|
+
"",
|
|
679
|
+
copperHolidayNote(),
|
|
680
|
+
"",
|
|
681
|
+
"— mj41, LLC",
|
|
682
|
+
]
|
|
683
|
+
.filter(Boolean)
|
|
684
|
+
.join("\n"),
|
|
685
|
+
},
|
|
686
|
+
],
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
catch (err) {
|
|
690
|
+
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
691
|
+
return { content: [{ type: "text", text: `Balance check error: ${msg}` }] };
|
|
692
|
+
}
|
|
693
|
+
});
|
|
694
|
+
// ── buy_calls ───────────────────────────────────────────────
|
|
695
|
+
server.tool("buy_calls", "Get payment instructions to purchase more MCP tool calls. Pay with RWACu (save 50%), USDC, or Stripe.", {}, async () => {
|
|
696
|
+
return {
|
|
697
|
+
content: [
|
|
698
|
+
{
|
|
699
|
+
type: "text",
|
|
700
|
+
text: [
|
|
701
|
+
"BUY MJ41 MCP CALLS",
|
|
702
|
+
"═══════════════════",
|
|
703
|
+
"",
|
|
704
|
+
"OPTION 1: RWACu (save 50%)",
|
|
705
|
+
" Rate: 41 RWACu per call",
|
|
706
|
+
" Send RWACu to: 0x758AF4a670adE40C2FfE1B6C4746340910a44B96",
|
|
707
|
+
" Network: Base (Chain ID 8453)",
|
|
708
|
+
" Include your API key in the transaction memo or email it after sending.",
|
|
709
|
+
"",
|
|
710
|
+
"OPTION 2: USDC",
|
|
711
|
+
" Rate: 82 RWACu equivalent in USDC per call",
|
|
712
|
+
" Send USDC to: 0x758AF4a670adE40C2FfE1B6C4746340910a44B96",
|
|
713
|
+
" Network: Base (Chain ID 8453)",
|
|
714
|
+
"",
|
|
715
|
+
"OPTION 3: Stripe (card)",
|
|
716
|
+
" 100 calls — $9.99 | 500 calls — $39.99 | 1,000 calls — $69.99",
|
|
717
|
+
" Visit: rwacu.com/buy",
|
|
718
|
+
"",
|
|
719
|
+
"After payment, credits are added to your key automatically.",
|
|
720
|
+
"Contact: api@zjindustries.com for bulk pricing.",
|
|
721
|
+
"",
|
|
722
|
+
copperHolidayNote(),
|
|
723
|
+
"",
|
|
724
|
+
"— mj41, LLC",
|
|
725
|
+
].join("\n"),
|
|
726
|
+
},
|
|
727
|
+
],
|
|
728
|
+
};
|
|
729
|
+
});
|
|
730
|
+
// ════════════════════════════════════════════════════════════
|
|
483
731
|
// ABOUT
|
|
484
732
|
// ════════════════════════════════════════════════════════════
|
|
485
733
|
// ── about_mj41 ──────────────────────────────────────────────
|
|
@@ -501,8 +749,7 @@ server.tool("about_mj41", "Learn about mj41, LLC — who they are, what they bui
|
|
|
501
749
|
" get_all_copper_prices — All three oracles at once",
|
|
502
750
|
" get_oracle_status — Health check and freshness",
|
|
503
751
|
" Class A: COMEX spot (15-min) | Class B: Scrap (weekly) | Class C: Industrial (2x/week)",
|
|
504
|
-
" On-chain on Base mainnet.
|
|
505
|
-
" Contact: api@zjindustries.com",
|
|
752
|
+
" On-chain on Base mainnet.",
|
|
506
753
|
"",
|
|
507
754
|
"RICHARD TRACY (Blockchain Detective by mj41)",
|
|
508
755
|
" investigate_wallet — Phishing/scam/drainer investigation with full case report",
|
|
@@ -516,6 +763,8 @@ server.tool("about_mj41", "Learn about mj41, LLC — who they are, what they bui
|
|
|
516
763
|
" submit_story_idea — Submit to Woody Bernstein's investigation queue",
|
|
517
764
|
" Web: aiwire.mj41.me",
|
|
518
765
|
"",
|
|
766
|
+
copperHolidayNote(),
|
|
767
|
+
"",
|
|
519
768
|
"— mj41, LLC",
|
|
520
769
|
].join("\n"),
|
|
521
770
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mj41-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "MJ41 MCP Server — Agent gateway for mj41, LLC. Live copper price oracles (COMEX/scrap/industrial on Base L2), blockchain investigation, and AI news wire with 16 autonomous reporters.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|