Skip to main content
Glama

HashPilot

by justmert
index.ts10.8 kB
#!/usr/bin/env node import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js"; import { ethers } from "ethers"; const HEDERA_NETWORKS = { mainnet: { chainId: 295, rpcUrl: "https://mainnet.hashio.io/api", name: "Hedera Mainnet" }, testnet: { chainId: 296, rpcUrl: "https://testnet.hashio.io/api", name: "Hedera Testnet" }, previewnet: { chainId: 297, rpcUrl: "https://previewnet.hashio.io/api", name: "Hedera Previewnet" } }; const DEFAULT_NETWORK = "testnet"; function getProvider(network: string = DEFAULT_NETWORK) { const networkConfig = HEDERA_NETWORKS[network as keyof typeof HEDERA_NETWORKS]; if (!networkConfig) { throw new Error(`Unknown network: ${network}`); } return new ethers.JsonRpcProvider(networkConfig.rpcUrl); } function convertHederaAccountToEvmAddress(accountId: string): string { const parts = accountId.split("."); if (parts.length !== 3) { throw new Error("Invalid Hedera account ID format. Expected: 0.0.12345"); } const accountNum = parseInt(parts[2]); return "0x" + accountNum.toString(16).padStart(40, "0"); } const server = new Server( { name: "hashpilot", version: "0.1.0", }, { capabilities: { tools: {}, }, } ); server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: "get_account_balance", description: "Get the HBAR balance of a Hedera account using JSON-RPC", inputSchema: { type: "object", properties: { accountId: { type: "string", description: "The Hedera account ID (e.g., 0.0.12345)", }, network: { type: "string", description: "Network to query (mainnet, testnet, previewnet). Default: testnet", enum: ["mainnet", "testnet", "previewnet"], }, }, required: ["accountId"], }, }, { name: "get_transaction_receipt", description: "Get transaction receipt by transaction hash", inputSchema: { type: "object", properties: { txHash: { type: "string", description: "The transaction hash (0x...)", }, network: { type: "string", description: "Network to query (mainnet, testnet, previewnet). Default: testnet", enum: ["mainnet", "testnet", "previewnet"], }, }, required: ["txHash"], }, }, { name: "get_block_info", description: "Get information about a specific block", inputSchema: { type: "object", properties: { blockNumber: { type: "string", description: "Block number or 'latest'", }, network: { type: "string", description: "Network to query (mainnet, testnet, previewnet). Default: testnet", enum: ["mainnet", "testnet", "previewnet"], }, }, required: ["blockNumber"], }, }, { name: "estimate_gas", description: "Estimate gas for a transaction", inputSchema: { type: "object", properties: { from: { type: "string", description: "From address (Hedera account ID or 0x address)", }, to: { type: "string", description: "To address (Hedera account ID or 0x address)", }, value: { type: "string", description: "Amount in HBAR to send (optional)", }, data: { type: "string", description: "Transaction data for contract calls (optional)", }, network: { type: "string", description: "Network to query (mainnet, testnet, previewnet). Default: testnet", enum: ["mainnet", "testnet", "previewnet"], }, }, required: ["from", "to"], }, }, { name: "get_network_info", description: "Get current network information (chain ID, gas price, block number)", inputSchema: { type: "object", properties: { network: { type: "string", description: "Network to query (mainnet, testnet, previewnet). Default: testnet", enum: ["mainnet", "testnet", "previewnet"], }, }, }, }, ], }; }); server.setRequestHandler(CallToolRequestSchema, async (request) => { try { switch (request.params.name) { case "get_account_balance": { const { accountId, network } = request.params.arguments as { accountId: string; network?: string; }; const provider = getProvider(network); const evmAddress = convertHederaAccountToEvmAddress(accountId); const balance = await provider.getBalance(evmAddress); const balanceInHbar = ethers.formatEther(balance); return { content: [ { type: "text", text: JSON.stringify({ accountId, evmAddress, balance: balanceInHbar + " HBAR", network: network || DEFAULT_NETWORK, }, null, 2), }, ], }; } case "get_transaction_receipt": { const { txHash, network } = request.params.arguments as { txHash: string; network?: string; }; const provider = getProvider(network); const receipt = await provider.getTransactionReceipt(txHash); if (!receipt) { return { content: [ { type: "text", text: "Transaction receipt not found", }, ], }; } return { content: [ { type: "text", text: JSON.stringify({ transactionHash: receipt.hash, blockNumber: receipt.blockNumber, from: receipt.from, to: receipt.to, gasUsed: receipt.gasUsed.toString(), status: receipt.status, network: network || DEFAULT_NETWORK, }, null, 2), }, ], }; } case "get_block_info": { const { blockNumber, network } = request.params.arguments as { blockNumber: string; network?: string; }; const provider = getProvider(network); const block = await provider.getBlock(blockNumber); if (!block) { return { content: [ { type: "text", text: "Block not found", }, ], }; } return { content: [ { type: "text", text: JSON.stringify({ number: block.number, hash: block.hash, timestamp: block.timestamp, gasLimit: block.gasLimit.toString(), gasUsed: block.gasUsed.toString(), transactions: block.transactions.length, network: network || DEFAULT_NETWORK, }, null, 2), }, ], }; } case "estimate_gas": { const { from, to, value, data, network } = request.params.arguments as { from: string; to: string; value?: string; data?: string; network?: string; }; const provider = getProvider(network); const fromAddress = from.startsWith("0x") ? from : convertHederaAccountToEvmAddress(from); const toAddress = to.startsWith("0x") ? to : convertHederaAccountToEvmAddress(to); const tx: ethers.TransactionRequest = { from: fromAddress, to: toAddress, value: value ? ethers.parseEther(value) : undefined, data: data, }; const gasEstimate = await provider.estimateGas(tx); const gasPrice = (await provider.getFeeData()).gasPrice; return { content: [ { type: "text", text: JSON.stringify({ gasLimit: gasEstimate.toString(), gasPrice: gasPrice ? ethers.formatUnits(gasPrice, "gwei") + " gwei" : "unknown", estimatedCost: gasPrice ? ethers.formatEther(gasEstimate * gasPrice) + " HBAR" : "unknown", network: network || DEFAULT_NETWORK, }, null, 2), }, ], }; } case "get_network_info": { const { network } = request.params.arguments as { network?: string; }; const provider = getProvider(network); const [blockNumber, feeData, chainId] = await Promise.all([ provider.getBlockNumber(), provider.getFeeData(), provider.getNetwork().then(n => n.chainId), ]); return { content: [ { type: "text", text: JSON.stringify({ network: network || DEFAULT_NETWORK, chainId: chainId.toString(), currentBlock: blockNumber, gasPrice: feeData.gasPrice ? ethers.formatUnits(feeData.gasPrice, "gwei") + " gwei" : "unknown", maxFeePerGas: feeData.maxFeePerGas ? ethers.formatUnits(feeData.maxFeePerGas, "gwei") + " gwei" : "unknown", rpcUrl: HEDERA_NETWORKS[network as keyof typeof HEDERA_NETWORKS || DEFAULT_NETWORK].rpcUrl, }, null, 2), }, ], }; } default: throw new Error(`Unknown tool: ${request.params.name}`); } } catch (error) { return { content: [ { type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}`, }, ], }; } }); async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error("HashPilot server running on stdio"); } main().catch((error) => { console.error("Fatal error:", error); process.exit(1); });

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/justmert/hashpilot'

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