Skip to main content
Glama

1inch Cross-Chain Swap MCP Server

by vaibhavgeek
mcpServer.js12.8 kB
const { McpServer } = require("@modelcontextprotocol/sdk/server/mcp.js"); const { StdioServerTransport } = require("@modelcontextprotocol/sdk/server/stdio.js"); const { z } = require("zod"); const { executeCrossChainSwap } = require('./index.js'); const https = require('https'); const env = require('dotenv'); const process = env.config().parsed; // Create an MCP server instance with a name and version. const server = new McpServer({ name: "1inch-CrossChain-Swap", version: "1.0.0" }); // Helper function to make API requests to the 1inch Portfolio API. // It builds the URL with chain_id and any extra query parameters, sends an HTTPS GET request, // and returns a promise that resolves with the parsed JSON data. const makePortfolioApiRequest = (endpoint, chainId, queryParams = {}) => { return new Promise((resolve, reject) => { const url = new URL(`https://api.1inch.dev/portfolio/portfolio/v4${endpoint}`); // Append chain_id and additional query parameters to the URL. url.searchParams.append('chain_id', chainId); for (const [key, value] of Object.entries(queryParams)) { url.searchParams.append(key, value); } const options = { headers: { 'Authorization': `Bearer ${process.env.INCH_API_KEY || ''}`, 'accept': 'application/json', 'content-type': 'application/json' } }; const req = https.get(url, options, (res) => { let data = ''; res.on('data', (chunk) => { data += chunk; }); res.on('end', () => { if (res.statusCode >= 200 && res.statusCode < 300) { try { resolve(JSON.parse(data)); } catch (error) { reject(new Error(`Failed to parse API response: ${error.message}`)); } } else { reject(new Error(`API request failed with status ${res.statusCode}: ${data}`)); } }); }); req.on('error', (error) => { reject(error); }); req.end(); }); }; /* * Tool: swap * * Description: * Initiates a cross-chain token swap by calling the executeCrossChainSwap function. * Parameters include source and destination chain IDs, token addresses, amount, * and an optional invert flag. The amount is expected in base units. * * NOTE: This tool includes a conversion check so that if a user passes a human-readable * amount (e.g., "1" for 1 USDC), it multiplies by 10^6 to ensure the amount is in the * correct 6-decimal base (i.e., 1 USDC becomes 1000000). */ server.tool( "swap", { srcChainId: z.number().optional().default(8453), dstChainId: z.number().optional().default(42161), // Example: Arbitrum chain ID. srcTokenAddress: z.string().optional().default('0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'), dstTokenAddress: z.string().optional().default('0xaf88d065e77c8cC2239327C5EDb3A432268e5831'), // Default set to "1000000" so that it represents 1 USDC in 6-decimal format. amount: z.string().optional().default('1000000'), invert: z.boolean().optional().default(false) }, async (params) => { try { // Convert the provided amount if it appears to be in human-readable format. // For example, if the user enters "1" (which is less than 1e6), convert it to "1000000". let amount = params.amount; if (!isNaN(Number(amount)) && Number(amount) < 1e6) { amount = (Number(amount) * 1e6).toString(); } // Update the params object with the converted amount. params.amount = amount; const result = await executeCrossChainSwap(params); return { content: [{ type: "text", text: result ? `Swap initiated successfully! Order hash: ${result.orderHash}\n${result.message}` : `Swap failed: ${result.error}` }] }; } catch (error) { return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true }; } } ); /* * Resource: swap-status (via URI) * * Description: * Reads a JSON file (order-status.json) containing swap order statuses. * If an order hash is specified in the URI, it returns the status for that specific order. * Otherwise, it lists all swap orders. */ server.resource( "swap-status", "swaps://{orderHash}", async (uri) => { const orderHash = uri.pathname.replace(/^\//, ''); const path = require('path'); const fs = require('fs'); const statusFile = path.join(__dirname, 'order-status.json'); if (!fs.existsSync(statusFile)) { return { contents: [{ uri: uri.href, text: `No swap orders found.` }] }; } try { const statusData = JSON.parse(fs.readFileSync(statusFile, 'utf8')); // If no order hash is provided, return all orders. if (!orderHash) { const ordersText = statusData.orders.map(order => `Order: ${order.orderHash}\nStatus: ${order.status}\nStart Time: ${new Date(order.startTime).toLocaleString()}\nLast Updated: ${new Date(order.lastUpdated).toLocaleString()}` ).join('\n\n'); return { contents: [{ uri: uri.href, text: ordersText || 'No orders found.' }] }; } // Find specific order by hash. const order = statusData.orders.find(o => o.orderHash === orderHash); if (!order) { return { contents: [{ uri: uri.href, text: `Order ${orderHash} not found.` }] }; } return { contents: [{ uri: uri.href, text: `Order: ${orderHash}\nStatus: ${order.status}\nStart Time: ${new Date(order.startTime).toLocaleString()}\nLast Updated: ${new Date(order.lastUpdated).toLocaleString()}` }] }; } catch (error) { return { contents: [{ uri: uri.href, text: `Error reading swap status: ${error.message}` }] }; } } ); /* * Tool: portfolio-protocols-value * * Description: * Fetches the current value of various protocols from the 1inch Portfolio API. * Parameters include chain ID and addresses. */ server.tool( "portfolio-protocols-value", { chainId: z.number().default(1), addresses: z.string().optional(), use_cache: z.boolean().optional().default(false) }, async (params) => { try { const queryParams = { addresses: params.addresses, use_cache: params.use_cache.toString() }; const result = await makePortfolioApiRequest('/overview/protocols/current_value', params.chainId, queryParams); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; } catch (error) { return { content: [{ type: "text", text: `Error fetching protocols value: ${error.message}` }], isError: true }; } } ); /* * Tool: portfolio-tokens-details * * Description: * Retrieves detailed information about ERC-20 tokens from the 1inch Portfolio API. * Parameters include chain ID, addresses, timerange, closed positions flag, and threshold. */ server.tool( "portfolio-tokens-details", { chainId: z.number().default(1), addresses: z.string().optional(), timerange: z.enum(['1day', '1week', '1month', '1year', '3years']).optional().default('1day'), closed: z.boolean().default(true), closed_threshold: z.number().default(1), use_cache: z.boolean().optional().default(false) }, async (params) => { try { const queryParams = { addresses: params.addresses, timerange: params.timerange, closed: params.closed.toString(), closed_threshold: params.closed_threshold.toString(), use_cache: params.use_cache.toString() }; const result = await makePortfolioApiRequest('/overview/erc20/details', params.chainId, queryParams); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; } catch (error) { return { content: [{ type: "text", text: `Error fetching tokens details: ${error.message}` }], isError: true }; } } ); /* * Tool: portfolio-general-value * * Description: * Fetches the general current portfolio value from the 1inch Portfolio API. * Parameters include chain ID, addresses and cache flag. */ server.tool( "portfolio-general-value", { chainId: z.number().default(1), addresses: z.string().optional(), use_cache: z.boolean().optional().default(false) }, async (params) => { try { const queryParams = { addresses: params.addresses, use_cache: params.use_cache.toString() }; const result = await makePortfolioApiRequest('/general/current_value', params.chainId, queryParams); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; } catch (error) { return { content: [{ type: "text", text: `Error fetching general value: ${error.message}` }], isError: true }; } } ); /* * Tool: portfolio-value-chart * * Description: * Retrieves chart data for the general portfolio value from the 1inch Portfolio API. * Parameters include chain ID, addresses, timerange and cache flag. */ server.tool( "portfolio-value-chart", { chainId: z.number().default(1), addresses: z.string().optional(), timerange: z.enum(['1day', '1week', '1month', '1year', '3years']).optional().default('1month'), use_cache: z.boolean().optional().default(false) }, async (params) => { try { const queryParams = { addresses: params.addresses, timerange: params.timerange, use_cache: params.use_cache.toString() }; const result = await makePortfolioApiRequest('/general/value_chart', params.chainId, queryParams); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; } catch (error) { return { content: [{ type: "text", text: `Error fetching value chart: ${error.message}` }], isError: true }; } } ); /* * Tool: swap-status * * Description: * This tool (different from the URI resource) provides a way to check the status * of swap orders by reading the local 'order-status.json' file. If an order hash is provided, * it returns the details for that specific order. Otherwise, it lists all recorded orders. */ server.tool( "swap-status", { orderHash: z.string().optional() }, async (params) => { const path = require('path'); const fs = require('fs'); const statusFile = path.join(__dirname, 'order-status.json'); if (!fs.existsSync(statusFile)) { return { content: [{ type: "text", text: `No swap orders found.` }] }; } try { const statusData = JSON.parse(fs.readFileSync(statusFile, 'utf8')); // If no order hash is provided, display all orders. if (!params.orderHash) { if (statusData.orders.length === 0) { return { content: [{ type: "text", text: `No orders found.` }] }; } const ordersText = statusData.orders.map(order => `Order: ${order.orderHash}\nStatus: ${order.status}\nStart Time: ${new Date(order.startTime).toLocaleString()}\nLast Updated: ${new Date(order.lastUpdated).toLocaleString()}` ).join('\n\n'); return { content: [{ type: "text", text: ordersText }] }; } // Find specific order by hash. const order = statusData.orders.find(o => o.orderHash === params.orderHash); if (!order) { return { content: [{ type: "text", text: `Order ${params.orderHash} not found.` }] }; } return { content: [{ type: "text", text: `Order: ${params.orderHash}\nStatus: ${order.status}\nStart Time: ${new Date(order.startTime).toLocaleString()}\nLast Updated: ${new Date(order.lastUpdated).toLocaleString()}` }] }; } catch (error) { return { content: [{ type: "text", text: `Error reading swap status: ${error.message}` }], isError: true }; } } ); // Start the MCP server using the standard I/O transport. const transport = new StdioServerTransport(); (async () => { await server.connect(transport); })();

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/vaibhavgeek/one_inch_mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server