diff --git a/package-lock.json b/package-lock.json index 9a81ea5..b503975 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@injectivelabs/networks": "^1.14.27", "@injectivelabs/sdk-ts": "^1.14.27", "@injectivelabs/utils": "^1.14.27", + "@injectivelabs/x402": "^0.0.1", "@modelcontextprotocol/sdk": "^1.0.4", "decimal.js": "^10.4.3", "zod": "^3.22.0" @@ -750,6 +751,27 @@ "store2": "^2.14.4" } }, + "node_modules/@injectivelabs/x402": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@injectivelabs/x402/-/x402-0.0.1.tgz", + "integrity": "sha512-uHxTHz/bsX3kvIYPLXC/x0pZ6jcluRqowGLbXsEuFlVeG7rDNUXimrbNScnhRqZcPkMfjEHtMwiRuCOP6Kau4A==", + "license": "MIT", + "dependencies": { + "viem": "^2.39.3", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "express": "^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "express": { + "optional": true + } + } + }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", diff --git a/package.json b/package.json index 1d8c171..aafc7f3 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "@injectivelabs/networks": "^1.14.27", "@injectivelabs/sdk-ts": "^1.14.27", "@injectivelabs/utils": "^1.14.27", + "@injectivelabs/x402": "^0.0.1", "@modelcontextprotocol/sdk": "^1.0.4", "decimal.js": "^10.4.3", "zod": "^3.22.0" diff --git a/src/mcp/server.ts b/src/mcp/server.ts index c019868..8730c6f 100644 --- a/src/mcp/server.ts +++ b/src/mcp/server.ts @@ -25,6 +25,7 @@ import { debridge } from '../bridges/debridge.js' import { evm } from '../evm/index.js' import { eip712 } from '../evm/eip712.js' import { authz, TRADING_MSG_TYPES } from '../authz/index.js' +import { createInjectiveClient } from '@injectivelabs/x402/client' const injAddress = z.string().regex(/^inj1[a-z0-9]{38}$/, 'Must be a valid inj1... address (42 chars)') const numericString = z.string().regex(/^\d+(\.\d+)?$/, 'Must be a positive numeric string') @@ -809,6 +810,44 @@ server.tool( }, ) +// ─── x402 Payment Tools ──────────────────────────────────────────────────────── + +server.tool( + 'x402_fetch', + 'Fetch data from an x402-gated API endpoint. Automatically handles 402 Payment Required ' + + 'responses by signing a USDC payment using the Injective EVM wallet, submitting it to the facilitator, ' + + 'and retrying the request. IMPORTANT: Real on-chain payment with real funds.', + { + address: injAddress.describe('The inj1... address of your trading wallet.'), + password: z.string().describe('Keystore password to decrypt the private key for signing.'), + url: z.string().url().describe('The URL of the x402-gated API endpoint.'), + }, + async ({ address, password, url }) => { + const privateKeyHex = wallets.unlock(address, password) + const client = createInjectiveClient({ privateKey: privateKeyHex as `0x${string}` }) + const response = await client.fetch(url) + + const text = await response.text() + let data; + try { + data = JSON.parse(text) + } catch { + data = text + } + + return { + content: [{ + type: 'text', + text: JSON.stringify({ + status: response.status, + url: response.url, + data + }, null, 2), + }], + } + }, +) + // ─── Start ─────────────────────────────────────────────────────────────────── const transport = new StdioServerTransport()