coinopai-mcp 2.0.2 → 2.2.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/README.md +25 -0
- package/ads.js +127 -0
- package/index.js +97 -2
- package/package.json +3 -1
- package/server.json +2 -2
package/README.md
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
[](https://nodejs.org)
|
|
9
9
|
[](https://x402.org)
|
|
10
10
|
[](https://base.org)
|
|
11
|
+
[](https://mcpservers.org/servers/forgemeshlabs/coinopai-mcp)
|
|
11
12
|
|
|
12
13
|
**Auditable market context with calibrated forecast ranges and outcome verification.**
|
|
13
14
|
|
|
@@ -167,6 +168,9 @@ Fresh audits can return `pending_window` until the evaluation window matures.
|
|
|
167
168
|
| `get_crypto_signals` | Model context for BTC, ETH, SOL, XRP, ADA | $0.05 | ✓ |
|
|
168
169
|
| `get_crypto_signal_history` | Up to 168h of context history for analysis | $0.05 | ✓ |
|
|
169
170
|
| `get_crypto_forecast` | Conformally-calibrated 80% price range (~0.80 empirical coverage) for BTC, ETH, SOL, XRP, ADA | $0.05 | ✓ |
|
|
171
|
+
| `get_perp_funding` | **Futures.** Live perp funding, mark/index, open interest, crowding (Kraken Futures + Hyperliquid) | $0.02 | — |
|
|
172
|
+
| `check_futures_risk` | **Futures.** Will this side / leverage / entry survive the calibrated range? Liquidation distance + verdict | $0.05 | — |
|
|
173
|
+
| `get_futures_decision` | **Futures.** LONG / SHORT / FLAT with stop and target on the calibrated range, leverage cap, liquidation, sizing, funding cost | $0.15 | — |
|
|
170
174
|
| `review_signal_anomaly` | Score signal features for unusual conditions; returns review labels, drivers, and component scores | $0.07 | — |
|
|
171
175
|
| `get_crypto_risk` | Market risk state and cooldown context | $0.02 | — |
|
|
172
176
|
| `search_agent_automations` | Search 819 agent automation prompts | $0.01 | — |
|
|
@@ -306,6 +310,27 @@ Route to an available symbol or wait 15 minutes for the next cycle.
|
|
|
306
310
|
|
|
307
311
|
---
|
|
308
312
|
|
|
313
|
+
## Sponsored cards (Lulu Ads)
|
|
314
|
+
|
|
315
|
+
Free-tier tool responses may carry a single disclosed sponsored card from [Lulu Ads](https://getlulu.dev). It is a plain, labelled data field on the JSON result — never text the model could read as an instruction:
|
|
316
|
+
|
|
317
|
+
```json
|
|
318
|
+
{
|
|
319
|
+
"results": [...],
|
|
320
|
+
"sponsored": { "label": "Sponsored", "text": "...", "url": "https://..." }
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
- **Eligible tools:** `search_agent_automations`, `list_automation_categories`, `get_agent_automation`, `check_trade_preflight` — and only when the response did **not** settle an x402 payment (no `_payment` receipt). A paid response never carries a card.
|
|
325
|
+
- **Current state:** one tool is free — `list_tools` (a plain fetch of `/menu`, no wallet, no payment) — so that is the only tool where a card can render today. The other four allowlisted tools are still paid, so no card attaches to them; the switch is server-side: when the operator makes one of those endpoints free (a plain `200` with no `402`), cards start appearing there with no client change.
|
|
326
|
+
- **Paid tools never carry it:** `get_crypto_signals`, `get_crypto_risk`, `get_crypto_signal_history`, `get_crypto_decision`, `audit_trade_decision`, `get_crypto_forecast`, `review_signal_anomaly`, `get_futures_decision`, `get_perp_funding`, `check_futures_risk` never touch the ads SDK.
|
|
327
|
+
- **Strip it:** `delete result.sponsored`.
|
|
328
|
+
- **Where the card comes from:** ForgeMesh's own publisher card is attached server-side to `GET https://x402.coinopai.com/menu`, so `list_tools` simply passes it through — this package ships **no** ad credentials and makes **no** calls to the ads network on its own.
|
|
329
|
+
- **Operator env vars (forks only):** `LULU_ADS_PUBLISHER_ID` and `LULU_ADS_API_KEY` let a fork attach its own publisher card client-side (both required; if either is missing the package makes zero calls to the ads network and responses are unchanged). `LULU_ADS_ENABLED=false` is a kill switch.
|
|
330
|
+
- **Fail-open:** any SDK error or timeout (hard budget 2s) returns the original response unchanged. The only external host contacted is `ads.getlulu.dev`.
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
309
334
|
## Affiliate Attribution (via Pyrimid)
|
|
310
335
|
|
|
311
336
|
High-value tools accept an optional `affiliate_id` parameter. When provided, payment routes through the [Pyrimid](https://pyrimid.xyz) affiliate network — the affiliate earns a commission split from within the listed price. **No extra cost to the caller.**
|
package/ads.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// Lulu Ads sponsored cards — disclosed data field, free-tier tools only.
|
|
4
|
+
//
|
|
5
|
+
// Contract:
|
|
6
|
+
// - Card attaches ONLY to tools in FREE_TIER_TOOLS, and only when the
|
|
7
|
+
// response carries no x402 `_payment` settlement receipt. A response
|
|
8
|
+
// that settled a payment never carries a card, even for an allowlisted
|
|
9
|
+
// tool. Every other tool (Kronos decision loop, anomaly review) is
|
|
10
|
+
// excluded by name and never touches the SDK.
|
|
11
|
+
// - The card is a plain data field `sponsored: {label, text, url}` on the
|
|
12
|
+
// tool's JSON result — never injected into prose the model could read
|
|
13
|
+
// as an instruction. Strip it with `delete result.sponsored`.
|
|
14
|
+
// - This package ships NO ad credentials. ForgeMesh's own card is attached
|
|
15
|
+
// server-side to GET /menu and passes through `list_tools` untouched
|
|
16
|
+
// (see "never clobber an upstream field" below). The env path here
|
|
17
|
+
// (LULU_ADS_PUBLISHER_ID, LULU_ADS_API_KEY) exists for forks that want
|
|
18
|
+
// their own publisher card client-side. Either missing → zero network
|
|
19
|
+
// calls, response unchanged. LULU_ADS_ENABLED=false is a kill switch.
|
|
20
|
+
// - Fail-open with a hard time budget: SDK error, timeout, or import
|
|
21
|
+
// failure → original response unchanged. Never throws.
|
|
22
|
+
//
|
|
23
|
+
// The SDK is deliberately NOT wired via `enableLuluAds`/`withLuluAds`
|
|
24
|
+
// from "lulu-ads/mcp": those wrap every registered tool, which would put
|
|
25
|
+
// cards on paid tools.
|
|
26
|
+
|
|
27
|
+
// Tools whose responses may carry a card. Everything else is paid-only.
|
|
28
|
+
//
|
|
29
|
+
// NOTE: `list_tools` is the one genuinely free tool (plain fetch of /menu,
|
|
30
|
+
// no callPaid, no `_payment`), so it is the only place a card renders today.
|
|
31
|
+
// The other four still go through callPaid and settle x402, so `_payment`
|
|
32
|
+
// is set and no card attaches; the switch for them is server-side — make the
|
|
33
|
+
// endpoint free (200 without a 402) and cards appear with no change here.
|
|
34
|
+
const FREE_TIER_TOOLS = new Set([
|
|
35
|
+
"list_tools",
|
|
36
|
+
"search_agent_automations",
|
|
37
|
+
"list_automation_categories",
|
|
38
|
+
"get_agent_automation",
|
|
39
|
+
"check_trade_preflight",
|
|
40
|
+
]);
|
|
41
|
+
|
|
42
|
+
// Ad category forwarded to ads-server per tool (allowlisted context key).
|
|
43
|
+
const TOOL_CATEGORY = {
|
|
44
|
+
list_tools: "agents.automation",
|
|
45
|
+
search_agent_automations: "agents.automation",
|
|
46
|
+
list_automation_categories: "agents.automation",
|
|
47
|
+
get_agent_automation: "agents.automation",
|
|
48
|
+
check_trade_preflight: "crypto.tools",
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// Hard ceiling on the whole slot call (SDK's own default is 1500ms warm /
|
|
52
|
+
// 3000ms cold). We pass SDK_TIMEOUT_MS explicitly so the SDK never picks the
|
|
53
|
+
// 3000ms cold budget, and race it against HARD_BUDGET_MS as a belt-and-braces
|
|
54
|
+
// guard in case the SDK's AbortSignal misbehaves.
|
|
55
|
+
const SDK_TIMEOUT_MS = 1500;
|
|
56
|
+
const HARD_BUDGET_MS = 2000;
|
|
57
|
+
|
|
58
|
+
let clientPromise = null; // lazily-built LuluAds instance (or null when inert)
|
|
59
|
+
|
|
60
|
+
// Test seam: `setAdsClientForTests(fake)` swaps the SDK client; `null` resets.
|
|
61
|
+
let injectedClient;
|
|
62
|
+
function setAdsClientForTests(client) {
|
|
63
|
+
injectedClient = client;
|
|
64
|
+
clientPromise = null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function adsEnabled(env = process.env) {
|
|
68
|
+
if (String(env.LULU_ADS_ENABLED || "").toLowerCase() === "false") return false;
|
|
69
|
+
return Boolean(env.LULU_ADS_PUBLISHER_ID && env.LULU_ADS_API_KEY);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// lulu-ads is ESM-only ("type": "module", `import` export only), so a
|
|
73
|
+
// CommonJS caller has to dynamic-import it. Done once, lazily, and only
|
|
74
|
+
// after the env guard passes — so no creds means the module is never even
|
|
75
|
+
// loaded, let alone warmed up (warmUp() would otherwise POST /telemetry/init
|
|
76
|
+
// with an empty key).
|
|
77
|
+
function getClient() {
|
|
78
|
+
if (injectedClient !== undefined) return Promise.resolve(injectedClient);
|
|
79
|
+
if (!clientPromise) {
|
|
80
|
+
clientPromise = import("lulu-ads")
|
|
81
|
+
.then(({ LuluAds }) => {
|
|
82
|
+
const client = new LuluAds({
|
|
83
|
+
publisherId: process.env.LULU_ADS_PUBLISHER_ID,
|
|
84
|
+
apiKey: process.env.LULU_ADS_API_KEY,
|
|
85
|
+
});
|
|
86
|
+
void client.warmUp(); // fire-and-forget, never throws
|
|
87
|
+
return client;
|
|
88
|
+
})
|
|
89
|
+
.catch(() => null);
|
|
90
|
+
}
|
|
91
|
+
return clientPromise;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function isFreeTierResponse(toolName, data) {
|
|
95
|
+
if (!FREE_TIER_TOOLS.has(toolName)) return false;
|
|
96
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) return false;
|
|
97
|
+
if (data._payment) return false; // x402 settled → paid response, no card
|
|
98
|
+
if ("sponsored" in data) return false; // never clobber an upstream field
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Returns `data` with a `sponsored` field appended when a card is available,
|
|
103
|
+
// otherwise the exact same object. Never throws, never exceeds HARD_BUDGET_MS.
|
|
104
|
+
async function attachSponsored(toolName, data) {
|
|
105
|
+
try {
|
|
106
|
+
if (!adsEnabled()) return data;
|
|
107
|
+
if (!isFreeTierResponse(toolName, data)) return data;
|
|
108
|
+
const client = await getClient();
|
|
109
|
+
if (!client) return data;
|
|
110
|
+
|
|
111
|
+
const slot = client.sponsoredSlot({
|
|
112
|
+
context: { tool: toolName, category: TOOL_CATEGORY[toolName] },
|
|
113
|
+
timeoutMs: SDK_TIMEOUT_MS,
|
|
114
|
+
});
|
|
115
|
+
let timer;
|
|
116
|
+
const budget = new Promise((resolve) => { timer = setTimeout(() => resolve(null), HARD_BUDGET_MS); });
|
|
117
|
+
const sponsored = await Promise.race([slot, budget]).catch(() => null);
|
|
118
|
+
clearTimeout(timer);
|
|
119
|
+
|
|
120
|
+
if (!sponsored || !sponsored.text || !sponsored.url) return data;
|
|
121
|
+
return { ...data, sponsored: { label: "Sponsored", text: String(sponsored.text), url: String(sponsored.url) } };
|
|
122
|
+
} catch (_) {
|
|
123
|
+
return data;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
module.exports = { attachSponsored, adsEnabled, isFreeTierResponse, FREE_TIER_TOOLS, TOOL_CATEGORY, setAdsClientForTests };
|
package/index.js
CHANGED
|
@@ -10,6 +10,7 @@ const { toClientEvmSigner } = require("@x402/evm");
|
|
|
10
10
|
const { privateKeyToAccount } = require("viem/accounts");
|
|
11
11
|
const { createPublicClient, createWalletClient, http, parseAbi } = require("viem");
|
|
12
12
|
const { base } = require("viem/chains");
|
|
13
|
+
const { attachSponsored, FREE_TIER_TOOLS } = require("./ads.js");
|
|
13
14
|
|
|
14
15
|
const BASE_URL = "https://x402.coinopai.com";
|
|
15
16
|
|
|
@@ -184,6 +185,63 @@ const TOOLS = [
|
|
|
184
185
|
required: ["decision_id"]
|
|
185
186
|
}
|
|
186
187
|
},
|
|
188
|
+
{
|
|
189
|
+
name: "get_futures_decision",
|
|
190
|
+
annotations: {
|
|
191
|
+
readOnlyHint: false,
|
|
192
|
+
destructiveHint: false,
|
|
193
|
+
idempotentHint: false,
|
|
194
|
+
openWorldHint: true,
|
|
195
|
+
},
|
|
196
|
+
description: "Kronos Futures: perpetual-futures decision package. Direction LONG / SHORT / FLAT from the Kronos signal, stop-loss and take-profit on the calibrated 80% range, leverage cap keeping liquidation outside 2x the stop, liquidation price, risk-based position size for your equity, and live perp funding cost. Market intelligence only; no exchange execution. Costs $0.15 USDC.",
|
|
197
|
+
inputSchema: {
|
|
198
|
+
type: "object",
|
|
199
|
+
properties: {
|
|
200
|
+
symbol: { type: "string", description: "Symbol: BTC, ETH, SOL, XRP, ADA (default: BTC)" },
|
|
201
|
+
equity: { type: "number", description: "Account equity in USD for position sizing (optional)" },
|
|
202
|
+
max_loss_pct: { type: "number", description: "Max loss per position as a fraction of equity (default 0.01)" },
|
|
203
|
+
max_leverage: { type: "number", description: "Your leverage ceiling, 1-5 (default 5)" }
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
name: "get_perp_funding",
|
|
209
|
+
annotations: {
|
|
210
|
+
readOnlyHint: true,
|
|
211
|
+
destructiveHint: false,
|
|
212
|
+
idempotentHint: false,
|
|
213
|
+
openWorldHint: true,
|
|
214
|
+
},
|
|
215
|
+
description: "Kronos Futures: live perpetual funding rates (1h, 8h, annualized), mark/index price, open interest, and crowding label (LONG_CROWDED / SHORT_CROWDED / BALANCED) for BTC, ETH, SOL, XRP, ADA from Kraken Futures and Hyperliquid. Costs $0.02 USDC.",
|
|
216
|
+
inputSchema: {
|
|
217
|
+
type: "object",
|
|
218
|
+
properties: {
|
|
219
|
+
symbol: { type: "string", description: "Optional single symbol; omit for all five" }
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
name: "check_futures_risk",
|
|
225
|
+
annotations: {
|
|
226
|
+
readOnlyHint: true,
|
|
227
|
+
destructiveHint: false,
|
|
228
|
+
idempotentHint: false,
|
|
229
|
+
openWorldHint: true,
|
|
230
|
+
},
|
|
231
|
+
description: "Kronos Futures: will a leveraged perp position survive the calibrated range? Returns liquidation price and distance, adverse range bound, verdict (SURVIVES_RANGE / THIN_BUFFER / LIQUIDATION_INSIDE_RANGE), max surviving leverage, P&L at both range bounds, and funding cost. Costs $0.05 USDC.",
|
|
232
|
+
inputSchema: {
|
|
233
|
+
type: "object",
|
|
234
|
+
properties: {
|
|
235
|
+
symbol: { type: "string", description: "Symbol: BTC, ETH, SOL, XRP, ADA (default: BTC)" },
|
|
236
|
+
side: { type: "string", description: "LONG or SHORT" },
|
|
237
|
+
leverage: { type: "number", description: "Leverage to test (1-125)" },
|
|
238
|
+
entry: { type: "number", description: "Entry price (defaults to current perp mark)" },
|
|
239
|
+
notional: { type: "number", description: "Position notional in USD for funding cost (optional)" },
|
|
240
|
+
horizon_hours: { type: "number", description: "Hours held for funding cost (optional)" }
|
|
241
|
+
},
|
|
242
|
+
required: ["side", "leverage"]
|
|
243
|
+
}
|
|
244
|
+
},
|
|
187
245
|
{
|
|
188
246
|
name: "get_crypto_forecast",
|
|
189
247
|
annotations: {
|
|
@@ -201,6 +259,17 @@ const TOOLS = [
|
|
|
201
259
|
}
|
|
202
260
|
}
|
|
203
261
|
},
|
|
262
|
+
{
|
|
263
|
+
name: "list_tools",
|
|
264
|
+
annotations: {
|
|
265
|
+
readOnlyHint: true,
|
|
266
|
+
destructiveHint: false,
|
|
267
|
+
idempotentHint: true,
|
|
268
|
+
openWorldHint: true,
|
|
269
|
+
},
|
|
270
|
+
description: "Free. Lists every CoinOpAI tool with its price, so an agent can pick before paying.",
|
|
271
|
+
inputSchema: { type: "object", properties: {} }
|
|
272
|
+
},
|
|
204
273
|
{
|
|
205
274
|
name: "review_signal_anomaly",
|
|
206
275
|
annotations: {
|
|
@@ -292,6 +361,13 @@ async function callPyrimid(account, path, affiliateId, baseUrl, products) {
|
|
|
292
361
|
return paidRes.json();
|
|
293
362
|
}
|
|
294
363
|
|
|
364
|
+
function queryString(params) {
|
|
365
|
+
const parts = Object.entries(params)
|
|
366
|
+
.filter(([, v]) => v !== undefined && v !== null && v !== "")
|
|
367
|
+
.map(([k, v]) => `${k}=${encodeURIComponent(String(v))}`);
|
|
368
|
+
return parts.length ? `?${parts.join("&")}` : "";
|
|
369
|
+
}
|
|
370
|
+
|
|
295
371
|
async function callPaid(ctx, path, affiliateId, opts = {}) {
|
|
296
372
|
const { httpClient, account } = ctx;
|
|
297
373
|
const baseUrl = opts.baseUrl || BASE_URL;
|
|
@@ -358,7 +434,7 @@ async function main() {
|
|
|
358
434
|
}
|
|
359
435
|
|
|
360
436
|
const server = new Server(
|
|
361
|
-
{ name: "coinopai-mcp", version: "1.
|
|
437
|
+
{ name: "coinopai-mcp", version: "2.1.0" },
|
|
362
438
|
{ capabilities: { tools: {} } }
|
|
363
439
|
);
|
|
364
440
|
|
|
@@ -369,9 +445,16 @@ async function main() {
|
|
|
369
445
|
try {
|
|
370
446
|
// Affiliate ID: tool arg takes precedence, then env fallback, then none
|
|
371
447
|
const affiliateId = args.affiliate_id || process.env.PYRIMID_AFFILIATE_ID || null;
|
|
372
|
-
|
|
448
|
+
// list_tools is free: plain fetch, no wallet, never touches callPaid
|
|
449
|
+
const paymentContext = name === "list_tools" ? null : getPaymentContext();
|
|
373
450
|
let data;
|
|
374
451
|
switch (name) {
|
|
452
|
+
case "list_tools": {
|
|
453
|
+
const r = await fetch(`${BASE_URL}/menu`);
|
|
454
|
+
if (!r.ok) throw new Error(`/menu returned ${r.status}`);
|
|
455
|
+
data = await r.json();
|
|
456
|
+
break;
|
|
457
|
+
}
|
|
375
458
|
// Low-value utility endpoints — no affiliate routing
|
|
376
459
|
case "search_agent_automations":
|
|
377
460
|
data = await callPaid(paymentContext, `/api/search?q=${encodeURIComponent(args.query || "")}&limit=${args.limit || 20}`, null);
|
|
@@ -404,6 +487,15 @@ async function main() {
|
|
|
404
487
|
case "get_crypto_forecast":
|
|
405
488
|
data = await callPaid(paymentContext, `/api/kronos/forecast?symbol=${encodeURIComponent(args.symbol || "BTC")}`, affiliateId);
|
|
406
489
|
break;
|
|
490
|
+
case "get_futures_decision":
|
|
491
|
+
data = await callPaid(paymentContext, `/api/kronos/futures/decision${queryString({ symbol: args.symbol || "BTC", equity: args.equity, max_loss_pct: args.max_loss_pct, max_leverage: args.max_leverage })}`, null);
|
|
492
|
+
break;
|
|
493
|
+
case "get_perp_funding":
|
|
494
|
+
data = await callPaid(paymentContext, `/api/kronos/futures/funding${queryString({ symbol: args.symbol })}`, null);
|
|
495
|
+
break;
|
|
496
|
+
case "check_futures_risk":
|
|
497
|
+
data = await callPaid(paymentContext, `/api/kronos/futures/risk${queryString({ symbol: args.symbol || "BTC", side: args.side, leverage: args.leverage, entry: args.entry, notional: args.notional, horizon_hours: args.horizon_hours })}`, null);
|
|
498
|
+
break;
|
|
407
499
|
case "review_signal_anomaly":
|
|
408
500
|
data = await callPaid(paymentContext, "/api/anomaly", null, {
|
|
409
501
|
method: "POST",
|
|
@@ -417,6 +509,9 @@ async function main() {
|
|
|
417
509
|
default:
|
|
418
510
|
throw new Error("Unknown tool: " + name);
|
|
419
511
|
}
|
|
512
|
+
// Sponsored card (Lulu Ads) — free-tier tools only, skipped when the
|
|
513
|
+
// response settled an x402 payment. Paid tools never reach this call.
|
|
514
|
+
if (FREE_TIER_TOOLS.has(name)) data = await attachSponsored(name, data);
|
|
420
515
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
421
516
|
} catch (e) {
|
|
422
517
|
return { content: [{ type: "text", text: "Error: " + e.message }], isError: true };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "coinopai-mcp",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"mcpName": "io.github.forgemeshlabs/coinopai-mcp",
|
|
5
5
|
"description": "Auditable market intelligence MCP with x402 payments, calibrated forecast ranges, anomaly review, risk context, and outcome audits",
|
|
6
6
|
"main": "index.js",
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"index.js",
|
|
12
|
+
"ads.js",
|
|
12
13
|
"README.md",
|
|
13
14
|
"server.json"
|
|
14
15
|
],
|
|
@@ -50,6 +51,7 @@
|
|
|
50
51
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
51
52
|
"@x402/core": "2.11.0",
|
|
52
53
|
"@x402/evm": "2.11.0",
|
|
54
|
+
"lulu-ads": "^0.9.19",
|
|
53
55
|
"viem": "^2.0.0"
|
|
54
56
|
}
|
|
55
57
|
}
|
package/server.json
CHANGED
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
"url": "https://github.com/forgemeshlabs/coinopai-mcp",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "2.0
|
|
9
|
+
"version": "2.1.0",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "coinopai-mcp",
|
|
14
|
-
"version": "2.0
|
|
14
|
+
"version": "2.1.0",
|
|
15
15
|
"transport": {
|
|
16
16
|
"type": "stdio"
|
|
17
17
|
},
|