mj41-mcp 1.1.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.
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,463 @@
1
+ #!/usr/bin/env node
2
+ // ============================================================
3
+ // MJ41 MCP Server
4
+ // Official agent gateway for mj41, LLC
5
+ // Infrastructure partner: ZJ Industries (zjindustries.com)
6
+ //
7
+ // Tools:
8
+ // Copper Oracles (ZJ Industries x MJ41)
9
+ // - get_copper_price — live price for any class (A/B/C)
10
+ // - get_all_copper_prices — all three classes at once
11
+ // - get_oracle_status — health check for any class
12
+ //
13
+ // Richard Tracy (Blockchain Detective by mj41)
14
+ // - investigate_wallet — phishing/scam investigation
15
+ // - get_eth_price — current ETH price
16
+ //
17
+ // The First Signal (AI News Wire by mj41)
18
+ // - get_latest_news — latest stories from the wire
19
+ // - get_news_by_beat — stories filtered by beat
20
+ // - get_story — read a single story
21
+ // - submit_story_idea — submit to Woody's investigation queue
22
+ // ============================================================
23
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
24
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
25
+ import { z } from "zod";
26
+ const COPPERTRACE_URL = "https://coppertrace.vercel.app";
27
+ const FIRST_SIGNAL_URL = "https://aiwire.mj41.me";
28
+ const ORACLE_URLS = {
29
+ a: "https://classa.mj41.me",
30
+ b: "https://classb.mj41.me",
31
+ c: "https://classc.mj41.me",
32
+ };
33
+ const CLASS_LABELS = {
34
+ a: "Class A — COMEX Spot Copper (Grade A cathode, 99.99% purity, COMEX HG futures)",
35
+ b: "Class B — Scrap Copper (yard call-arounds + COMEX reference)",
36
+ c: "Class C — Industrial Manufactured Copper (alloys C2182, C180, C110, verified invoices)",
37
+ };
38
+ const server = new McpServer({
39
+ name: "mj41",
40
+ version: "1.0.0",
41
+ });
42
+ // ── get_copper_price ────────────────────────────────────────
43
+ server.tool("get_copper_price", "Get the current copper price from a ZJ Industries on-chain oracle on Base. Class A = COMEX spot (updates every 15 min), Class B = scrap copper (weekly), Class C = industrial manufactured copper (2x/week). Powered by ZJ Industries x mj41, LLC.", {
44
+ class: z
45
+ .enum(["a", "b", "c"])
46
+ .describe("Copper class: 'a' for COMEX spot, 'b' for scrap, 'c' for industrial"),
47
+ }, async ({ class: copperClass }) => {
48
+ try {
49
+ const url = ORACLE_URLS[copperClass];
50
+ const res = await fetch(`${url}/api/v1/status`);
51
+ if (!res.ok)
52
+ throw new Error(`HTTP ${res.status}`);
53
+ const status = await res.json();
54
+ const label = CLASS_LABELS[copperClass];
55
+ const fresh = status.is_fresh ? "FRESH" : "STALE";
56
+ const lastUpdate = new Date(status.last_updated).toLocaleString("en-US", {
57
+ timeZone: "America/New_York",
58
+ dateStyle: "medium",
59
+ timeStyle: "short",
60
+ });
61
+ return {
62
+ content: [
63
+ {
64
+ type: "text",
65
+ text: [
66
+ `ZJ Industries Copper Oracle — ${label}`,
67
+ ``,
68
+ `Status: ${status.status} (${fresh})`,
69
+ `Last updated: ${lastUpdate} ET`,
70
+ `Chain: Base mainnet (8453)`,
71
+ `Contract: ${status.contract}`,
72
+ status.update_mode ? `Update mode: ${status.update_mode} (${status.update_frequency})` : "",
73
+ ``,
74
+ `Note: Price value available via ${url}/api/v1/price`,
75
+ `Open Trial — all endpoints free. Request a key at api@zjindustries.com`,
76
+ ``,
77
+ `— mj41, LLC | ZJ Industries (zjindustries.com)`,
78
+ ]
79
+ .filter(Boolean)
80
+ .join("\n"),
81
+ },
82
+ ],
83
+ };
84
+ }
85
+ catch (err) {
86
+ const msg = err instanceof Error ? err.message : "Unknown error";
87
+ return {
88
+ content: [{ type: "text", text: `Oracle read error: ${msg}` }],
89
+ };
90
+ }
91
+ });
92
+ // ── get_all_copper_prices ───────────────────────────────────
93
+ 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 () => {
94
+ const results = [
95
+ "ZJ Industries x MJ41 — Copper Oracle Suite",
96
+ "Three on-chain copper price feeds on Base mainnet",
97
+ "═══════════════════════════════════════════════",
98
+ "",
99
+ ];
100
+ for (const [cls, url] of Object.entries(ORACLE_URLS)) {
101
+ try {
102
+ const res = await fetch(`${url}/api/v1/status`);
103
+ const status = await res.json();
104
+ const fresh = status.is_fresh ? "FRESH" : "STALE";
105
+ const lastUpdate = new Date(status.last_updated).toLocaleString("en-US", {
106
+ timeZone: "America/New_York",
107
+ dateStyle: "medium",
108
+ timeStyle: "short",
109
+ });
110
+ results.push(`${CLASS_LABELS[cls]}`);
111
+ results.push(` Status: ${status.status} (${fresh})`);
112
+ results.push(` Last updated: ${lastUpdate} ET`);
113
+ results.push(` Contract: ${status.contract}`);
114
+ if (status.update_frequency) {
115
+ results.push(` Frequency: ${status.update_frequency}`);
116
+ }
117
+ results.push("");
118
+ }
119
+ catch (err) {
120
+ const msg = err instanceof Error ? err.message : "Unknown error";
121
+ results.push(`${CLASS_LABELS[cls]}`);
122
+ results.push(` Error: ${msg}`);
123
+ results.push("");
124
+ }
125
+ }
126
+ results.push("Open Trial — all endpoints free during trial period");
127
+ results.push("API key requests: api@zjindustries.com");
128
+ results.push("");
129
+ results.push("— mj41, LLC | ZJ Industries (zjindustries.com)");
130
+ return {
131
+ content: [{ type: "text", text: results.join("\n") }],
132
+ };
133
+ });
134
+ // ── get_oracle_status ───────────────────────────────────────
135
+ server.tool("get_oracle_status", "Check if a ZJ Industries copper oracle is operational, fresh, and what update mode it's in. No API key needed. Powered by ZJ Industries x mj41, LLC.", {
136
+ class: z
137
+ .enum(["a", "b", "c"])
138
+ .describe("Copper class: 'a' for COMEX spot, 'b' for scrap, 'c' for industrial"),
139
+ }, async ({ class: copperClass }) => {
140
+ try {
141
+ const url = ORACLE_URLS[copperClass];
142
+ const res = await fetch(`${url}/api/v1/status`);
143
+ if (!res.ok)
144
+ throw new Error(`HTTP ${res.status}`);
145
+ const data = await res.json();
146
+ return {
147
+ content: [
148
+ {
149
+ type: "text",
150
+ text: JSON.stringify(data, null, 2),
151
+ },
152
+ ],
153
+ };
154
+ }
155
+ catch (err) {
156
+ const msg = err instanceof Error ? err.message : "Unknown error";
157
+ return {
158
+ content: [{ type: "text", text: `Status check error: ${msg}` }],
159
+ };
160
+ }
161
+ });
162
+ // ════════════════════════════════════════════════════════════
163
+ // RICHARD TRACY — Blockchain Detective (mj41, LLC)
164
+ // ════════════════════════════════════════════════════════════
165
+ // ── investigate_wallet ──────────────────────────────────────
166
+ server.tool("investigate_wallet", "Investigate a blockchain wallet address or transaction for phishing, scams, wallet drainers, or suspicious activity. Returns a detailed case report with executive summary, incident reconstruction, suspicious addresses, funds trail, confidence ratings, and next steps. Powered by Richard Tracy — Blockchain Detective by mj41, LLC.", {
167
+ query: z
168
+ .string()
169
+ .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...')"),
170
+ }, async ({ query }) => {
171
+ try {
172
+ const res = await fetch(`${COPPERTRACE_URL}/api/investigate`, {
173
+ method: "POST",
174
+ headers: { "Content-Type": "application/json" },
175
+ body: JSON.stringify({ userPrompt: query }),
176
+ });
177
+ if (!res.ok) {
178
+ const errText = await res.text();
179
+ return {
180
+ content: [
181
+ {
182
+ type: "text",
183
+ text: `Investigation failed (HTTP ${res.status}): ${errText}`,
184
+ },
185
+ ],
186
+ };
187
+ }
188
+ const data = await res.json();
189
+ return {
190
+ content: [
191
+ {
192
+ type: "text",
193
+ text: `${data.report}\n\n— Richard Tracy, Blockchain Detective | mj41, LLC`,
194
+ },
195
+ ],
196
+ };
197
+ }
198
+ catch (err) {
199
+ const msg = err instanceof Error ? err.message : "Unknown error";
200
+ return {
201
+ content: [{ type: "text", text: `Investigation error: ${msg}` }],
202
+ };
203
+ }
204
+ });
205
+ // ── get_eth_price ───────────────────────────────────────────
206
+ server.tool("get_eth_price", "Get the current price of Ethereum in USD. Powered by mj41, LLC.", {}, async () => {
207
+ try {
208
+ const res = await fetch(`${COPPERTRACE_URL}/api/eth-price`);
209
+ const data = await res.json();
210
+ return {
211
+ content: [
212
+ {
213
+ type: "text",
214
+ text: `Current ETH price: $${data.price}\n\n— mj41, LLC`,
215
+ },
216
+ ],
217
+ };
218
+ }
219
+ catch (err) {
220
+ const msg = err instanceof Error ? err.message : "Unknown error";
221
+ return {
222
+ content: [{ type: "text", text: `Price fetch error: ${msg}` }],
223
+ };
224
+ }
225
+ });
226
+ // ════════════════════════════════════════════════════════════
227
+ // THE FIRST SIGNAL — AI-Native Agentic News Wire (mj41, LLC)
228
+ // ════════════════════════════════════════════════════════════
229
+ // ── get_latest_news ─────────────────────────────────────────
230
+ server.tool("get_latest_news", "Get the latest stories from The First Signal, an AI-native agentic news wire with 15 AI reporters covering commodities, fintech, AI, cybersecurity, retail, crime, sports, weather, transportation, and more. Powered by mj41, LLC. Live at aiwire.mj41.me.", {
231
+ limit: z
232
+ .number()
233
+ .optional()
234
+ .default(5)
235
+ .describe("Number of stories to return (default 5, max 20)"),
236
+ }, async ({ limit }) => {
237
+ try {
238
+ const cap = Math.min(limit, 20);
239
+ const res = await fetch(`${FIRST_SIGNAL_URL}/api/stories?limit=${cap}`);
240
+ if (!res.ok)
241
+ throw new Error(`HTTP ${res.status}`);
242
+ const data = await res.json();
243
+ const stories = data.stories || data;
244
+ if (!stories.length) {
245
+ return {
246
+ content: [{ type: "text", text: "No stories on the wire right now." }],
247
+ };
248
+ }
249
+ const lines = [
250
+ "THE FIRST SIGNAL — AI News Wire",
251
+ "by mj41, LLC | aiwire.mj41.me",
252
+ "════════════════════════════════",
253
+ "",
254
+ ];
255
+ for (const s of stories) {
256
+ const date = new Date(s.created_at).toLocaleDateString("en-US", {
257
+ month: "short",
258
+ day: "numeric",
259
+ });
260
+ lines.push(`[${s.beat?.toUpperCase() || "WIRE"}] ${s.headline}`);
261
+ lines.push(` ${date} | ${s.byline || "Staff"} | Confidence: ${s.confidence || "N/A"}`);
262
+ if (s.summary)
263
+ lines.push(` ${s.summary.slice(0, 200)}${s.summary.length > 200 ? "..." : ""}`);
264
+ lines.push("");
265
+ }
266
+ lines.push(`— The First Signal | mj41, LLC`);
267
+ return {
268
+ content: [{ type: "text", text: lines.join("\n") }],
269
+ };
270
+ }
271
+ catch (err) {
272
+ const msg = err instanceof Error ? err.message : "Unknown error";
273
+ return {
274
+ content: [{ type: "text", text: `News wire error: ${msg}` }],
275
+ };
276
+ }
277
+ });
278
+ // ── get_news_by_beat ────────────────────────────────────────
279
+ server.tool("get_news_by_beat", "Get stories from a specific beat on The First Signal news wire. Beats: cora (commodities), finn (fintech), mahesh (AI/agents), dalton (cybersecurity), becky (retail), richard (crime), sammy (sports/betting), algo (business), riplo (stocks), blaise (traffic), bambi (weather), roofus (transport/logistics), rex (metals pricing), woody (investigative), aisao (editorial/opinion). Powered by mj41, LLC.", {
280
+ beat: z
281
+ .string()
282
+ .describe("Reporter beat ID: cora, finn, mahesh, dalton, becky, richard, sammy, algo, riplo, blaise, bambi, roofus, rex, woody, or aisao"),
283
+ limit: z
284
+ .number()
285
+ .optional()
286
+ .default(5)
287
+ .describe("Number of stories (default 5)"),
288
+ }, async ({ beat, limit }) => {
289
+ try {
290
+ const cap = Math.min(limit, 20);
291
+ const res = await fetch(`${FIRST_SIGNAL_URL}/api/stories?beat=${beat}&limit=${cap}`);
292
+ if (!res.ok)
293
+ throw new Error(`HTTP ${res.status}`);
294
+ const data = await res.json();
295
+ const stories = data.stories || data;
296
+ if (!stories.length) {
297
+ return {
298
+ content: [{ type: "text", text: `No stories found for beat: ${beat}` }],
299
+ };
300
+ }
301
+ const lines = [
302
+ `THE FIRST SIGNAL — ${beat.toUpperCase()} Beat`,
303
+ "════════════════════════════════",
304
+ "",
305
+ ];
306
+ for (const s of stories) {
307
+ const date = new Date(s.created_at).toLocaleDateString("en-US", {
308
+ month: "short",
309
+ day: "numeric",
310
+ });
311
+ lines.push(`${s.headline}`);
312
+ lines.push(` ${date} | ${s.byline || "Staff"} | Confidence: ${s.confidence || "N/A"}`);
313
+ if (s.summary)
314
+ lines.push(` ${s.summary.slice(0, 200)}${s.summary.length > 200 ? "..." : ""}`);
315
+ lines.push("");
316
+ }
317
+ lines.push(`— The First Signal | mj41, LLC`);
318
+ return {
319
+ content: [{ type: "text", text: lines.join("\n") }],
320
+ };
321
+ }
322
+ catch (err) {
323
+ const msg = err instanceof Error ? err.message : "Unknown error";
324
+ return {
325
+ content: [{ type: "text", text: `News wire error: ${msg}` }],
326
+ };
327
+ }
328
+ });
329
+ // ── get_story ───────────────────────────────────────────────
330
+ server.tool("get_story", "Read the full text of a specific story from The First Signal news wire by its ID. Powered by mj41, LLC.", {
331
+ id: z
332
+ .string()
333
+ .describe("The story ID (UUID)"),
334
+ }, async ({ id }) => {
335
+ try {
336
+ const res = await fetch(`${FIRST_SIGNAL_URL}/api/stories/${id}`);
337
+ if (!res.ok)
338
+ throw new Error(`HTTP ${res.status}`);
339
+ const story = await res.json();
340
+ const lines = [
341
+ `THE FIRST SIGNAL`,
342
+ "════════════════════════════════",
343
+ "",
344
+ story.headline,
345
+ "",
346
+ `Beat: ${story.beat?.toUpperCase() || "WIRE"} | ${story.byline || "Staff"}`,
347
+ `Filed: ${new Date(story.created_at).toLocaleString("en-US", { timeZone: "America/New_York" })} ET`,
348
+ `Confidence: ${story.confidence || "N/A"} | Signal: ${story.signal_type || "N/A"}`,
349
+ story.caveat_required ? `CAVEAT: This story has caveated data` : "",
350
+ "",
351
+ story.summary || "",
352
+ "",
353
+ story.body || "",
354
+ "",
355
+ `— The First Signal | mj41, LLC | aiwire.mj41.me`,
356
+ ];
357
+ return {
358
+ content: [{ type: "text", text: lines.filter(Boolean).join("\n") }],
359
+ };
360
+ }
361
+ catch (err) {
362
+ const msg = err instanceof Error ? err.message : "Unknown error";
363
+ return {
364
+ content: [{ type: "text", text: `Story fetch error: ${msg}` }],
365
+ };
366
+ }
367
+ });
368
+ // ── submit_story_idea ───────────────────────────────────────
369
+ server.tool("submit_story_idea", "Submit a story idea or investigation request to Woody Bernstein's queue at The First Signal. Woody is the investigative reporter. Topics are reviewed and may be fast-tracked. Powered by mj41, LLC.", {
370
+ topic: z.string().describe("The story topic or headline"),
371
+ description: z.string().describe("Details about what should be investigated"),
372
+ }, async ({ topic, description }) => {
373
+ try {
374
+ const res = await fetch(`${FIRST_SIGNAL_URL}/api/requests`, {
375
+ method: "POST",
376
+ headers: { "Content-Type": "application/json" },
377
+ body: JSON.stringify({
378
+ topic,
379
+ description,
380
+ submitted_by: "mj41-mcp-agent",
381
+ }),
382
+ });
383
+ if (!res.ok) {
384
+ const errText = await res.text();
385
+ return {
386
+ content: [{ type: "text", text: `Submission failed: ${errText}` }],
387
+ };
388
+ }
389
+ const data = await res.json();
390
+ return {
391
+ content: [
392
+ {
393
+ type: "text",
394
+ text: [
395
+ "Story idea submitted to Woody Bernstein's queue.",
396
+ `Topic: ${topic}`,
397
+ `ID: ${data.id || "assigned"}`,
398
+ `Status: ${data.status || "pending"}`,
399
+ "",
400
+ "Woody reviews and prioritizes submissions.",
401
+ "Fast-track reviews available during Open Trial.",
402
+ "",
403
+ "— The First Signal | mj41, LLC",
404
+ ].join("\n"),
405
+ },
406
+ ],
407
+ };
408
+ }
409
+ catch (err) {
410
+ const msg = err instanceof Error ? err.message : "Unknown error";
411
+ return {
412
+ content: [{ type: "text", text: `Submission error: ${msg}` }],
413
+ };
414
+ }
415
+ });
416
+ // ════════════════════════════════════════════════════════════
417
+ // ABOUT
418
+ // ════════════════════════════════════════════════════════════
419
+ // ── about_mj41 ──────────────────────────────────────────────
420
+ server.tool("about_mj41", "Learn about mj41, LLC — who they are, what they build, and what tools are available through this MCP server.", {}, async () => {
421
+ return {
422
+ content: [
423
+ {
424
+ type: "text",
425
+ text: [
426
+ "MJ41 MCP Server — Official Agent Gateway",
427
+ "══════════════════════════════════════════",
428
+ "",
429
+ "Operator: mj41, LLC",
430
+ "",
431
+ "Available tools:",
432
+ "",
433
+ "COPPER ORACLES (ZJ Industries x MJ41)",
434
+ " get_copper_price — Live price for Class A, B, or C",
435
+ " get_all_copper_prices — All three oracles at once",
436
+ " get_oracle_status — Health check and freshness",
437
+ " Class A: COMEX spot (15-min) | Class B: Scrap (weekly) | Class C: Industrial (2x/week)",
438
+ " On-chain on Base mainnet. Open Trial — all endpoints free.",
439
+ " Contact: api@zjindustries.com",
440
+ "",
441
+ "RICHARD TRACY (Blockchain Detective by mj41)",
442
+ " investigate_wallet — Phishing/scam/drainer investigation with full case report",
443
+ " get_eth_price — Current ETH price in USD",
444
+ " Web: coppertrace.vercel.app",
445
+ "",
446
+ "THE FIRST SIGNAL (AI News Wire by mj41)",
447
+ " get_latest_news — Latest stories from 15 AI reporters",
448
+ " get_news_by_beat — Filter by beat (commodities, AI, cyber, sports, etc.)",
449
+ " get_story — Read full story by ID",
450
+ " submit_story_idea — Submit to Woody Bernstein's investigation queue",
451
+ " Web: aiwire.mj41.me",
452
+ "",
453
+ "— mj41, LLC",
454
+ ].join("\n"),
455
+ },
456
+ ],
457
+ };
458
+ });
459
+ async function main() {
460
+ const transport = new StdioServerTransport();
461
+ await server.connect(transport);
462
+ }
463
+ main().catch(console.error);
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "mj41-mcp",
3
+ "version": "1.1.0",
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
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "mj41-mcp": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsc",
15
+ "start": "node dist/index.js",
16
+ "prepublishOnly": "npm run build"
17
+ },
18
+ "keywords": [
19
+ "mcp",
20
+ "model-context-protocol",
21
+ "copper",
22
+ "oracle",
23
+ "commodities",
24
+ "blockchain",
25
+ "base",
26
+ "news",
27
+ "ai-agents",
28
+ "price-feed",
29
+ "comex",
30
+ "defi"
31
+ ],
32
+ "author": "mj41, LLC",
33
+ "license": "MIT",
34
+ "homepage": "https://github.com/mj41Fantastican/mj41-mcp",
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "https://github.com/mj41Fantastican/mj41-mcp"
38
+ },
39
+ "dependencies": {
40
+ "@modelcontextprotocol/sdk": "^1.12.1",
41
+ "zod": "^3.24.0"
42
+ },
43
+ "devDependencies": {
44
+ "typescript": "^5.8.0",
45
+ "@types/node": "^22.0.0"
46
+ }
47
+ }