Skip to main content
Glama

ValueRouter MCP Server

index.ts16.4 kB
#!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, Tool, } from '@modelcontextprotocol/sdk/types.js'; import { z } from 'zod'; import { config } from 'dotenv'; import { BridgeService } from './services/bridge.js'; import { QuoteService } from './services/quote.js'; import { StatusService } from './services/status.js'; import { BalanceService } from './services/balance.js'; import { QuoteRequestSchema, BridgeRequestSchema, SupportedTokensRequestSchema, BalanceQuerySchema, createSuccessResponse, createErrorResponse, MCPToolResult, SupportedChainId, } from './types/index.js'; import { CHAIN_INFO, NATIVE_TOKENS } from './constants/index.js'; // Load environment variables config(); class ValueRouterMCPServer { private server: Server; private bridgeService: BridgeService; private quoteService: QuoteService; private statusService: StatusService; private balanceService: BalanceService; constructor() { this.server = new Server( { name: 'valuerouter-mcp-server', version: '1.0.0', }, { capabilities: { tools: {}, }, } ); this.bridgeService = new BridgeService(); this.quoteService = new QuoteService(); this.statusService = new StatusService(); this.balanceService = new BalanceService(); this.setupHandlers(); } private setupHandlers() { this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: 'get_supported_chains', description: 'Get all supported chains for USDC bridging', inputSchema: { type: 'object', properties: { includeTestnets: { type: 'boolean', description: 'Whether to include testnet chains', default: false, }, }, additionalProperties: false, }, }, { name: 'get_supported_tokens', description: 'Get supported tokens for a specific chain or all chains', inputSchema: { type: 'object', properties: { chainId: { oneOf: [ { type: 'number' }, { type: 'string' }, ], description: 'Chain ID to get tokens for (optional)', }, includeTestnets: { type: 'boolean', description: 'Whether to include testnet tokens', default: false, }, }, additionalProperties: false, }, }, { name: 'get_bridge_quote', description: 'Get a quote for bridging USDC or other tokens between chains', inputSchema: { type: 'object', properties: { fromChainId: { oneOf: [ { type: 'number' }, { type: 'string' }, ], description: 'Source chain ID', }, toChainId: { oneOf: [ { type: 'number' }, { type: 'string' }, ], description: 'Destination chain ID', }, fromToken: { type: 'object', properties: { address: { type: 'string' }, chainId: { oneOf: [{ type: 'number' }, { type: 'string' }] }, symbol: { type: 'string' }, name: { type: 'string' }, decimals: { type: 'number' }, logoURI: { type: 'string' }, isNative: { type: 'boolean' }, }, required: ['address', 'chainId', 'symbol', 'name', 'decimals'], }, toToken: { type: 'object', properties: { address: { type: 'string' }, chainId: { oneOf: [{ type: 'number' }, { type: 'string' }] }, symbol: { type: 'string' }, name: { type: 'string' }, decimals: { type: 'number' }, logoURI: { type: 'string' }, isNative: { type: 'boolean' }, }, required: ['address', 'chainId', 'symbol', 'name', 'decimals'], }, amount: { type: 'string', description: 'Amount to bridge in smallest unit (wei, lamports, etc.)', }, slippageBps: { type: 'number', description: 'Slippage tolerance in basis points (100 = 1%)', default: 100, }, userAddress: { type: 'string', description: 'User address for better quote accuracy (optional)', }, }, required: ['fromChainId', 'toChainId', 'fromToken', 'toToken', 'amount'], additionalProperties: false, }, }, { name: 'execute_bridge', description: 'Execute a bridge transaction (simulation only - returns transaction data)', inputSchema: { type: 'object', properties: { fromChainId: { oneOf: [ { type: 'number' }, { type: 'string' }, ], description: 'Source chain ID', }, toChainId: { oneOf: [ { type: 'number' }, { type: 'string' }, ], description: 'Destination chain ID', }, fromToken: { type: 'object', properties: { address: { type: 'string' }, chainId: { oneOf: [{ type: 'number' }, { type: 'string' }] }, symbol: { type: 'string' }, name: { type: 'string' }, decimals: { type: 'number' }, logoURI: { type: 'string' }, isNative: { type: 'boolean' }, }, required: ['address', 'chainId', 'symbol', 'name', 'decimals'], }, toToken: { type: 'object', properties: { address: { type: 'string' }, chainId: { oneOf: [{ type: 'number' }, { type: 'string' }] }, symbol: { type: 'string' }, name: { type: 'string' }, decimals: { type: 'number' }, logoURI: { type: 'string' }, isNative: { type: 'boolean' }, }, required: ['address', 'chainId', 'symbol', 'name', 'decimals'], }, amount: { type: 'string', description: 'Amount to bridge in smallest unit', }, recipientAddress: { type: 'string', description: 'Recipient address on destination chain', }, userAddress: { type: 'string', description: 'User address initiating the transaction', }, slippageBps: { type: 'number', description: 'Slippage tolerance in basis points', default: 100, }, memo: { type: 'string', description: 'Memo for Cosmos chains (optional)', }, }, required: ['fromChainId', 'toChainId', 'fromToken', 'toToken', 'amount', 'recipientAddress', 'userAddress'], additionalProperties: false, }, }, { name: 'get_transaction_status', description: 'Get the status of a bridge transaction', inputSchema: { type: 'object', properties: { transactionHash: { type: 'string', description: 'Transaction hash to check status for', }, fromChainId: { oneOf: [ { type: 'number' }, { type: 'string' }, ], description: 'Source chain ID', }, toChainId: { oneOf: [ { type: 'number' }, { type: 'string' }, ], description: 'Destination chain ID', }, }, required: ['transactionHash', 'fromChainId', 'toChainId'], additionalProperties: false, }, }, { name: 'get_user_balance', description: 'Get user token balance on a specific chain', inputSchema: { type: 'object', properties: { chainId: { oneOf: [ { type: 'number' }, { type: 'string' }, ], description: 'Chain ID to check balance on', }, tokenAddress: { type: 'string', description: 'Token contract address', }, userAddress: { type: 'string', description: 'User address to check balance for', }, }, required: ['chainId', 'tokenAddress', 'userAddress'], additionalProperties: false, }, }, { name: 'estimate_bridge_fees', description: 'Estimate fees for a bridge transaction', inputSchema: { type: 'object', properties: { fromChainId: { oneOf: [ { type: 'number' }, { type: 'string' }, ], description: 'Source chain ID', }, toChainId: { oneOf: [ { type: 'number' }, { type: 'string' }, ], description: 'Destination chain ID', }, amount: { type: 'string', description: 'Amount to bridge', }, tokenAddress: { type: 'string', description: 'Token address (optional, defaults to USDC)', }, }, required: ['fromChainId', 'toChainId', 'amount'], additionalProperties: false, }, }, ] as Tool[], }; }); this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { switch (name) { case 'get_supported_chains': return await this.getSupportedChains(args); case 'get_supported_tokens': return await this.getSupportedTokens(args); case 'get_bridge_quote': return await this.getBridgeQuote(args); case 'execute_bridge': return await this.executeBridge(args); case 'get_transaction_status': return await this.getTransactionStatus(args); case 'get_user_balance': return await this.getUserBalance(args); case 'estimate_bridge_fees': return await this.estimateBridgeFees(args); default: return createErrorResponse( `Unknown tool: ${name}`, 'UNKNOWN_TOOL' ); } } catch (error) { return createErrorResponse( error instanceof Error ? error.message : String(error), 'TOOL_ERROR' ); } }); } private async getSupportedChains(args: any): Promise<MCPToolResult> { const { includeTestnets = false } = args; const chains = Object.values(CHAIN_INFO) .filter(chain => includeTestnets || !chain.isTestnet) .map(chain => ({ chainId: chain.chainId, name: chain.name, symbol: chain.symbol, decimals: chain.decimals, logoUrl: chain.logoUrl, explorerUrl: chain.explorerUrl, isTestnet: chain.isTestnet, networkType: chain.networkType, usdcAddress: chain.usdcAddress, })); return createSuccessResponse({ chains, count: chains.length, }); } private async getSupportedTokens(args: any): Promise<MCPToolResult> { try { const request = SupportedTokensRequestSchema.parse(args); const result = await this.quoteService.getSupportedTokens(request); return createSuccessResponse(result); } catch (error) { return createErrorResponse( error instanceof Error ? error.message : String(error), 'INVALID_REQUEST' ); } } private async getBridgeQuote(args: any): Promise<MCPToolResult> { try { const request = QuoteRequestSchema.parse(args); const result = await this.quoteService.getQuote(request); return createSuccessResponse(result); } catch (error) { return createErrorResponse( error instanceof Error ? error.message : String(error), 'QUOTE_ERROR' ); } } private async executeBridge(args: any): Promise<MCPToolResult> { try { const request = BridgeRequestSchema.parse(args); const result = await this.bridgeService.prepareBridgeTransaction(request); // Add a warning that this is simulation only return createSuccessResponse({ ...result, warning: 'This is a simulation only. To execute the transaction, use the returned transaction data with your wallet.', simulationOnly: true, }); } catch (error) { return createErrorResponse( error instanceof Error ? error.message : String(error), 'BRIDGE_ERROR' ); } } private async getTransactionStatus(args: any): Promise<MCPToolResult> { try { const { transactionHash, fromChainId, toChainId } = args; const result = await this.statusService.getTransactionStatus( transactionHash, fromChainId, toChainId ); return createSuccessResponse(result); } catch (error) { return createErrorResponse( error instanceof Error ? error.message : String(error), 'STATUS_ERROR' ); } } private async getUserBalance(args: any): Promise<MCPToolResult> { try { const request = BalanceQuerySchema.parse(args); const result = await this.balanceService.getBalance(request); return createSuccessResponse(result); } catch (error) { return createErrorResponse( error instanceof Error ? error.message : String(error), 'BALANCE_ERROR' ); } } private async estimateBridgeFees(args: any): Promise<MCPToolResult> { try { const { fromChainId, toChainId, amount, tokenAddress } = args; const result = await this.quoteService.estimateFees( fromChainId, toChainId, amount, tokenAddress ); return createSuccessResponse(result); } catch (error) { return createErrorResponse( error instanceof Error ? error.message : String(error), 'FEE_ESTIMATION_ERROR' ); } } async run() { const transport = new StdioServerTransport(); await this.server.connect(transport); } } // Start the server const server = new ValueRouterMCPServer(); server.run().catch((error) => { console.error('Server error:', error); process.exit(1); });

Latest Blog Posts

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/RWAValueRouter/MCP'

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