coinopai-mcp 1.2.9 → 1.2.10

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.
Files changed (3) hide show
  1. package/index.js +37 -7
  2. package/package.json +3 -3
  3. package/server.json +2 -2
package/index.js CHANGED
@@ -5,7 +5,7 @@ const { Server } = require("@modelcontextprotocol/sdk/server/index.js");
5
5
  const { StdioServerTransport } = require("@modelcontextprotocol/sdk/server/stdio.js");
6
6
  const { CallToolRequestSchema, ListToolsRequestSchema } = require("@modelcontextprotocol/sdk/types.js");
7
7
  const { x402Client, x402HTTPClient } = require("@x402/core/client");
8
- const { registerExactEvmScheme } = require("@x402/evm/exact/client");
8
+ const { ExactEvmScheme } = require("@x402/evm/exact/client");
9
9
  const { toClientEvmSigner } = require("@x402/evm");
10
10
  const { privateKeyToAccount } = require("viem/accounts");
11
11
  const { createPublicClient, createWalletClient, http, parseAbi } = require("viem");
@@ -36,6 +36,7 @@ const IMAGEGEN_URL = "https://imagegen.coinopai.com";
36
36
  const PYRIMID_PRODUCTS_IMAGEGEN = {
37
37
  "/generate": { productId: 3n, priceUsdc: 100000n },
38
38
  };
39
+ const BASE_RPC_URL = process.env.BASE_RPC_URL || "https://mainnet.base.org";
39
40
 
40
41
  const TOOLS = [
41
42
  {
@@ -163,20 +164,42 @@ function buildHttpClient() {
163
164
  if (!key) throw new Error("WALLET_PRIVATE_KEY required — set a Base wallet private key with USDC funded");
164
165
  const pk = key.startsWith("0x") ? key : "0x" + key;
165
166
  const account = privateKeyToAccount(pk);
166
- const signer = toClientEvmSigner(account);
167
- const coreClient = registerExactEvmScheme(new x402Client(), { signer });
167
+ const coreClient = new x402Client().register("eip155:*", new ExactEvmScheme(toClientEvmSigner(account)));
168
168
  return { httpClient: new x402HTTPClient(coreClient), account };
169
169
  }
170
170
 
171
171
  const sleep = (ms) => new Promise(r => setTimeout(r, ms));
172
172
 
173
+ async function createChainTimedPaymentPayload(httpClient, paymentRequired) {
174
+ try {
175
+ const publicClient = createPublicClient({ chain: base, transport: http(BASE_RPC_URL) });
176
+ const block = await publicClient.getBlock();
177
+ const chainNow = Number(block.timestamp);
178
+ const originalNow = Date.now;
179
+ const localNow = Math.floor(originalNow() / 1000);
180
+ const timeout = Number(paymentRequired.accepts?.[0]?.maxTimeoutSeconds || 300);
181
+ const lowerBound = localNow + 30 - timeout;
182
+ const upperBound = chainNow + 600;
183
+ const signingNow = Math.min(Math.max(chainNow, lowerBound), upperBound);
184
+ // x402 derives EIP-3009 validity windows from Date.now; choose a timestamp valid for both Base block time and facilitator wall-clock checks.
185
+ Date.now = () => signingNow * 1000;
186
+ try {
187
+ return await httpClient.createPaymentPayload(paymentRequired);
188
+ } finally {
189
+ Date.now = originalNow;
190
+ }
191
+ } catch (_) {
192
+ return httpClient.createPaymentPayload(paymentRequired);
193
+ }
194
+ }
195
+
173
196
  // Pyrimid affiliate flow — approve + routePayment on-chain, then retry with tx hash
174
197
  async function callPyrimid(account, path, affiliateId, baseUrl, products) {
175
198
  const pathname = new URL(path, baseUrl).pathname;
176
199
  const product = products[pathname];
177
200
  if (!product) throw new Error(`No Pyrimid product registered for ${pathname}`);
178
201
  const url = baseUrl + path;
179
- const transport = http();
202
+ const transport = http(BASE_RPC_URL);
180
203
  const publicClient = createPublicClient({ chain: base, transport });
181
204
  const walletClient = createWalletClient({ account, chain: base, transport });
182
205
 
@@ -234,7 +257,7 @@ async function callPaid(ctx, path, affiliateId, opts = {}) {
234
257
  const paymentRequired = httpClient.getPaymentRequiredResponse(
235
258
  (name) => res.headers.get(name), body
236
259
  );
237
- const paymentPayload = await httpClient.createPaymentPayload(paymentRequired);
260
+ const paymentPayload = await createChainTimedPaymentPayload(httpClient, paymentRequired);
238
261
  const paidRes = await fetch(url, {
239
262
  method,
240
263
  headers: { ...bodyHeaders, ...httpClient.encodePaymentSignatureHeader(paymentPayload), ...extraHeaders },
@@ -244,7 +267,14 @@ async function callPaid(ctx, path, affiliateId, opts = {}) {
244
267
  const errBody = await paidRes.text().catch(() => paidRes.statusText);
245
268
  throw new Error(`HTTP ${paidRes.status}: ${errBody.slice(0, 200)}`);
246
269
  }
247
- return paidRes.json();
270
+ const data = await paidRes.json();
271
+ try {
272
+ const settleResponse = httpClient.getPaymentSettleResponse((name) => paidRes.headers.get(name));
273
+ if (settleResponse && data && typeof data === "object" && !Array.isArray(data)) {
274
+ return { ...data, _payment: settleResponse };
275
+ }
276
+ } catch (_) {}
277
+ return data;
248
278
  }
249
279
 
250
280
  if (!res.ok) {
@@ -262,7 +292,7 @@ async function main() {
262
292
  }
263
293
 
264
294
  const server = new Server(
265
- { name: "coinopai-mcp", version: "1.2.9" },
295
+ { name: "coinopai-mcp", version: "1.2.10" },
266
296
  { capabilities: { tools: {} } }
267
297
  );
268
298
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "coinopai-mcp",
3
- "version": "1.2.9",
3
+ "version": "1.2.10",
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",
@@ -48,8 +48,8 @@
48
48
  },
49
49
  "dependencies": {
50
50
  "@modelcontextprotocol/sdk": "^1.10.1",
51
- "@x402/core": "^2.12.0",
52
- "@x402/evm": "^2.12.0",
51
+ "@x402/core": "2.11.0",
52
+ "@x402/evm": "2.11.0",
53
53
  "viem": "^2.0.0"
54
54
  }
55
55
  }
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": "1.2.9",
9
+ "version": "1.2.10",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "coinopai-mcp",
14
- "version": "1.2.9",
14
+ "version": "1.2.10",
15
15
  "transport": {
16
16
  "type": "stdio"
17
17
  },