Solana Model Context Protocol (MCP) Demo

Official
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { z } from "zod"; import { Connection, PublicKey, LAMPORTS_PER_SOL, clusterApiUrl } from "@solana/web3.js"; // Create an MCP server const server = new McpServer({ name: "Solana RPC Tools", version: "1.0.0", }); // Initialize Solana connection const connection = new Connection(clusterApiUrl("mainnet-beta"), "confirmed"); // Solana RPC Methods as Tools // Get Account Info server.tool( "getAccountInfo", "Used to look up account info by public key (32 byte base58 encoded address)", { publicKey: z.string() }, async ({ publicKey }) => { try { const pubkey = new PublicKey(publicKey); const accountInfo = await connection.getAccountInfo(pubkey); return { content: [{ type: "text", text: JSON.stringify(accountInfo, null, 2) }] }; } catch (error) { return { content: [{ type: "text", text: `Error: ${(error as Error).message}` }] }; } } ); // Get Balance server.tool( "getBalance", "Used to look up balance by public key (32 byte base58 encoded address)", { publicKey: z.string() }, async ({ publicKey }) => { try { const pubkey = new PublicKey(publicKey); const balance = await connection.getBalance(pubkey); return { content: [{ type: "text", text: `${balance / LAMPORTS_PER_SOL} SOL (${balance} lamports)` }] }; } catch (error) { return { content: [{ type: "text", text: `Error: ${(error as Error).message}` }] }; } } ); // Get Minimum Balance For Rent Exemption server.tool( "getMinimumBalanceForRentExemption", "Used to look up minimum balance required for rent exemption by data size", { dataSize: z.number() }, async ({ dataSize }) => { try { const minBalance = await connection.getMinimumBalanceForRentExemption(dataSize); return { content: [{ type: "text", text: `${minBalance / LAMPORTS_PER_SOL} SOL (${minBalance} lamports)` }] }; } catch (error) { return { content: [{ type: "text", text: `Error: ${(error as Error).message}` }] }; } } ); // Get Transaction server.tool("getTransaction", "Used to look up transaction by signature (64 byte base58 encoded string)", { signature: z.string() }, async ({ signature }) => { try { const transaction = await connection.getParsedTransaction(signature, { maxSupportedTransactionVersion: 0 }); return { content: [{ type: "text", text: JSON.stringify(transaction, null, 2) }] }; } catch (error) { return { content: [{ type: "text", text: `Error: ${(error as Error).message}` }] }; } } ); // Add a dynamic account info resource // Setup specific resources to read from solana.com/docs pages server.resource( "solanaDocsInstallation", new ResourceTemplate("solana://docs/intro/installation", { list: undefined }), async (uri) => { try { const response = await fetch(`https://raw.githubusercontent.com/solana-foundation/solana-com/main/content/docs/intro/installation.mdx`); const fileContent = await response.text(); return { contents: [{ uri: uri.href, text: fileContent }] }; } catch (error) { return { contents: [{ uri: uri.href, text: `Error: ${(error as Error).message}` }] }; } } ); server.resource( "solanaDocsClusters", new ResourceTemplate("solana://docs/references/clusters", { list: undefined }), async (uri) => { try { const response = await fetch(`https://raw.githubusercontent.com/solana-foundation/solana-com/main/content/docs/references/clusters.mdx`); const fileContent = await response.text(); return { contents: [{ uri: uri.href, text: fileContent }] }; } catch (error) { return { contents: [{ uri: uri.href, text: `Error: ${(error as Error).message}` }] }; } } ); server.prompt( 'calculate-storage-deposit', 'Calculate storage deposit for a specified number of bytes', { bytes: z.string() }, ({ bytes }) => ({ messages: [{ role: 'user', content: { type: 'text', text: `Calculate the SOL amount needed to store ${bytes} bytes of data on Solana using getMinimumBalanceForRentExemption.` } }] }) ); server.prompt( 'minimum-amount-of-sol-for-storage', 'Calculate the minimum amount of SOL needed for storing 0 bytes on-chain', () => ({ messages: [{ role: 'user', content: { type: 'text', text: `Calculate the amount of SOL needed to store 0 bytes of data on Solana using getMinimumBalanceForRentExemption & present it to the user as the minimum cost for storing any data on Solana.` } }] }) ); server.prompt( 'why-did-my-transaction-fail', 'Look up the given transaction and inspect its logs to figure out why it failed', { signature: z.string() }, ({ signature }) => ({ messages: [{ role: 'user', content: { type: 'text', text: `Look up the transaction with signature ${signature} and inspect its logs to figure out why it failed.` } }] }) ); server.prompt( 'how-much-did-this-transaction-cost', 'Fetch the transaction by signature, and break down cost & priority fees', { signature: z.string() }, ({ signature }) => ({ messages: [{ role: 'user', content: { type: 'text', text: `Calculate the network fee for the transaction with signature ${signature} by fetching it and inspecting the 'fee' field in 'meta'. Base fee is 0.000005 sol per signature (also provided as array at the end). So priority fee is fee - (numSignatures * 0.000005). Please provide the base fee and the priority fee.` } }] }) ); server.prompt('what-happened-in-transaction', 'Look up the given transaction and inspect its logs & instructions to figure out what happened', { signature: z.string() }, ({ signature }) => ({ messages: [{ role: 'user', content: { type: 'text', text: `Look up the transaction with signature ${signature} and inspect its logs & instructions to figure out what happened.` } }] }) ); // Start receiving messages on stdin and sending messages on stdout const transport = new StdioServerTransport(); server.connect(transport);