Skip to main content
Glama

chainlink-feeds-mcp

index.js8.13 kB
require('dotenv').config(); const { McpServer } = require('@modelcontextprotocol/sdk/server/mcp.js'); const { StdioServerTransport } = require('@modelcontextprotocol/sdk/server/stdio.js'); const { z } = require('zod'); const { ethers } = require('ethers'); // Load feeds.json const feedsData = require('./feeds.json'); // Full Chainlink AggregatorV3Interface ABI const priceFeedAbi = [ 'function decimals() view returns (uint8)', 'function description() view returns (string)', 'function version() view returns (uint256)', 'function getRoundData(uint80 _roundId) view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)', 'function latestRoundData() view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)' ]; // Create MCP server const server = new McpServer({ name: 'Chainlink Feeds', version: '1.0.0', capabilities: { tools: {} } }); // Tool schema for latest price const latestPriceSchema = z.object({ pair: z.string().describe('The price feed pair, e.g., FIL/ETH or FDUSD/USD'), chain: z.string().refine((val) => feedsData[val.toLowerCase()], { message: 'Unsupported chain' }).describe('The blockchain network, e.g., ethereum or base') }); // Tool: Get latest price server.tool( 'getLatestPrice', 'Fetches the latest price for a given pair on a specified chain', latestPriceSchema, async ({ pair, chain }) => { try { // Validate inputs const chainKey = chain.toLowerCase(); latestPriceSchema.parse({ pair, chain }); // Find feed by pair const feed = feedsData[chainKey].feeds.find((f) => f.name.toLowerCase() === pair.toLowerCase()); if (!feed) { throw new Error(`Pair ${pair} not found on chain ${chain}`); } // Initialize provider and contract const provider = new ethers.JsonRpcProvider(`${feedsData[chainKey].baseUrl}/${process.env.INFURA_API_KEY}`); const priceFeedContract = new ethers.Contract(feed.proxyAddress, priceFeedAbi, provider); // Fetch decimals and latest round data const [decimals, roundData] = await Promise.all([ priceFeedContract.decimals(), priceFeedContract.latestRoundData() ]); const price = ethers.formatUnits(roundData.answer, decimals); const timestamp = Number(roundData.updatedAt) * 1000; return { content: [{ type: 'text', text: JSON.stringify({ chain, pair, price: Number(price), decimals: Number(decimals), roundId: roundData.roundId.toString(), timestamp: new Date(timestamp).toISOString(), proxyAddress: feed.proxyAddress, feedCategory: feed.feedCategory }, null, 2) }] }; } catch (error) { return { content: [{ type: 'text', text: `Error: ${error.message}` }], isError: true }; } } ); // Tool schema for querying price by round ID const queryPriceSchema = z.object({ roundId: z.string().regex(/^\d+$/, 'Round ID must be a number').describe('The round ID for the price data'), pair: z.string().describe('The price feed pair, e.g., FIL/ETH or FDUSD/USD'), chain: z.string().refine((val) => feedsData[val.toLowerCase()], { message: 'Unsupported chain' }).describe('The blockchain network, e.g., ethereum or base') }); // Tool: Query price by round ID (placeholder) server.tool( 'queryPriceByRound', 'Queries the price for a given pair and round ID on a specified chain (placeholder due to historical data limitations)', queryPriceSchema, async ({ roundId, pair, chain }) => { try { // Validate inputs const chainKey = chain.toLowerCase(); queryPriceSchema.parse({ roundId, pair, chain }); // Find feed by pair const feed = feedsData[chainKey].feeds.find((f) => f.name.toLowerCase() === pair.toLowerCase()); if (!feed) { throw new Error(`Pair ${pair} not found on chain ${chain}`); } // Initialize provider and contract const provider = new ethers.JsonRpcProvider(`${feedsData[chainKey].baseUrl}/${process.env.INFURA_API_KEY}`); const priceFeedContract = new ethers.Contract(feed.proxyAddress, priceFeedAbi, provider); // Note: getRoundData may not be supported for historical rounds const decimals = await priceFeedContract.decimals(); const roundData = await priceFeedContract.latestRoundData(); // Placeholder const price = ethers.formatUnits(roundData.answer, decimals); const timestamp = Number(roundData.updatedAt) * 1000; return { content: [{ type: 'text', text: JSON.stringify({ chain, pair, price: Number(price), decimals: Number(decimals), roundId, timestamp: new Date(timestamp).toISOString(), proxyAddress: feed.proxyAddress, feedCategory: feed.feedCategory }, null, 2) }] }; } catch (error) { return { content: [{ type: 'text', text: `Error: ${error.message}` }], isError: true }; } } ); // Tool schema for listing supported chains const listSupportedChainsSchema = z.object({}).describe('No parameters required'); // Tool: List all supported chains server.tool( 'listSupportedChains', 'Returns a comma-separated list of all supported blockchain networks', listSupportedChainsSchema, async () => { try { // Get all chain names as comma-separated string const chains = Object.keys(feedsData).join(','); return { content: [{ type: 'text', text: chains }] }; } catch (error) { return { content: [{ type: 'text', text: `Error: ${error.message}` }], isError: true }; } } ); // Tool schema for listing feeds by chain const listSupportedFeedsByChainSchema = z.object({ chain: z.string().refine((val) => feedsData[val.toLowerCase()], { message: 'Unsupported chain' }).describe('The blockchain network, e.g., ethereum or base') }); // Tool: List feeds for a specific chain server.tool( 'listSupportedFeedsByChain', 'Returns a comma-separated list of price feed names for a specified blockchain network', listSupportedFeedsByChainSchema, async ({ chain }) => { try { // Validate inputs const chainKey = chain.toLowerCase(); listSupportedFeedsByChainSchema.parse({ chain }); // Get feed names as comma-separated string const feedNames = feedsData[chainKey].feeds.map((feed) => feed.name).join(','); return { content: [{ type: 'text', text: feedNames }] }; } catch (error) { return { content: [{ type: 'text', text: `Error: ${error.message}` }], isError: true }; } } ); // Tool schema for listing all supported feeds const listSupportedFeedsSchema = z.object({}).describe('No parameters required'); // Tool: List all supported chains and feeds server.tool( 'listSupportedFeeds', 'Returns a Markdown list of all supported chains and their price feed names', listSupportedFeedsSchema, async () => { try { // Prepare Markdown list const markdownList = Object.keys(feedsData).map((chain) => { const feedNames = feedsData[chain].feeds.map((feed) => feed.name).join(','); return `- ${chain}: ${feedNames}`; }).join('\n'); return { content: [{ type: 'text', text: markdownList }] }; } catch (error) { return { content: [{ type: 'text', text: `Error: ${error.message}` }], isError: true }; } } ); // Start server with Stdio transport async function startServer() { const transport = new StdioServerTransport(); await server.connect(transport); } startServer().catch(console.error);

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/kukapay/chainlink-feeds-mcp'

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