Skip to main content
Glama
server.ts45.2 kB
// Osmosis MCP Server - Core Server Implementation 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 { allTools, toolCounts } from './tools/index.js'; import { callOsmosisApi, callOsmosisRpc, replacePathParams, validateRequiredParams } from './utils/api.js'; import { API_ENDPOINTS } from './config/constants.js'; import { OSMOSIS_CHAIN_ID } from './config/network.js'; import { generateWallet, restoreWalletFromMnemonic, getAddressFromMnemonic, validateMnemonic, validateAddress, deriveAddressFromPubkey } from './utils/wallet.js'; import { executeTransaction, validateMnemonicForExecution, checkBalance, simulateTransaction } from './utils/executor.js'; import { requestTestnetTokens, getTestnetTokenSources, getTestnetStatus } from './utils/faucet.js'; import { createMsgSend, createMsgMultiSend, createMsgDelegate, createMsgUndelegate, createMsgBeginRedelegate, createMsgWithdrawDelegatorReward, createMsgSubmitProposal, createMsgVote, createMsgDeposit, createMsgTransfer, createMsgLockTokens, createMsgBeginUnlocking, createMsgBeginUnlockingAll, createMsgSwapExactAmountIn, createMsgJoinPool, createMsgExitPool, createMsgSuperfluidDelegate, createMsgSuperfluidUndelegate, createMsgCreateDenom, createMsgMint, createMsgBurn, getVoteOptionNumber, type Coin } from './utils/messages.js'; export class OsmosisMCPServer { private server: Server; constructor() { this.server = new Server( { name: "osmosis-blockchain-server", version: "2.0.0" }, { capabilities: { tools: {} } } ); this.setupToolHandlers(); } private setupToolHandlers() { // List available tools this.server.setRequestHandler(ListToolsRequestSchema, async () => { console.error(`📊 Serving ${toolCounts.total} tools across ${Object.keys(toolCounts).length - 1} categories`); return { tools: allTools }; }); // Handle tool calls this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { return await this.handleToolCall(name, args || {}); } catch (error: any) { console.error(`❌ Tool ${name} failed:`, error.message); return { content: [{ type: "text", text: `Error executing ${name}: ${error.message}` }] }; } }); } private async handleToolCall(name: string, args: any) { switch (name) { // Blockchain tools case "get-blockchain-status": return this.getBlockchainStatus(); case "get-account-balance": validateRequiredParams(args, ['address']); return this.getAccountBalance(args.address); case "get-epochs": return this.getEpochs(); case "get-transaction": validateRequiredParams(args, ['hash']); return this.getTransaction(args.hash); case "get-latest-blocks": return this.getLatestBlocks(args.limit); case "get-token-info": validateRequiredParams(args, ['denom']); return this.getTokenInfo(args.denom); case "get-supply": validateRequiredParams(args, ['denom']); return this.getSupply(args.denom); case "get-chain-params": return this.getChainParams(args.module); case "get-module-accounts": return this.getModuleAccounts(args.name); // Pool tools case "get-pool-info": validateRequiredParams(args, ['poolId']); return this.getPoolInfo(args.poolId); case "get-all-pools": return this.getAllPools(args.limit); case "get-pool-spot-price": validateRequiredParams(args, ['poolId', 'baseAssetDenom', 'quoteAssetDenom']); return this.getPoolSpotPrice(args.poolId, args.baseAssetDenom, args.quoteAssetDenom); case "estimate-swap": validateRequiredParams(args, ['poolId', 'tokenIn', 'tokenOut', 'amount']); return this.estimateSwap(args.poolId, args.tokenIn, args.tokenOut, args.amount); case "get-incentivized-pools": return this.getIncentivizedPools(); case "get-pool-incentives": validateRequiredParams(args, ['poolId']); return this.getPoolIncentives(args.poolId); case "get-pool-type": case "get-historical-prices": case "prepare-swap-transaction": case "get-pool-total-shares": case "get-pool-liquidity": case "get-pool-swap-fee": case "get-pool-exit-fee": case "get-pool-join-exit-records": case "get-pool-total-value-locked": case "prepare-join-pool": case "prepare-exit-pool": case "get-pool-apr": return this.handleOtherTools(name, args); // Concentrated Liquidity tools case "get-cl-pools": return this.getCLPools(args.limit); case "get-cl-positions": validateRequiredParams(args, ['address']); return this.getCLPositions(args.address); case "get-cl-position": validateRequiredParams(args, ['positionId']); return this.getCLPosition(args.positionId); case "get-cl-positions-by-pool": case "get-cl-user-positions": case "price-to-tick": case "tick-to-price": case "get-cl-pool-incentives": case "get-cl-pool-liquidity": case "get-cl-pool-tick-data": case "get-cl-pool-fee-growth": case "prepare-cl-create-position": case "prepare-cl-add-liquidity": case "prepare-cl-remove-liquidity": case "get-cl-swap-estimates": return this.handleOtherTools(name, args); // Staking tools case "get-validators": return this.getValidators(args.status); case "get-delegations": validateRequiredParams(args, ['delegatorAddress']); return this.getDelegations(args.delegatorAddress); case "get-staking-rewards": validateRequiredParams(args, ['delegatorAddress']); return this.getStakingRewards(args.delegatorAddress); case "get-unbonding-delegations": validateRequiredParams(args, ['delegatorAddress']); return this.getUnbondingDelegations(args.delegatorAddress); case "get-validator-delegations": case "get-validator-unbonding-delegations": case "get-validator-commission": case "get-validator-self-delegation": case "prepare-delegate": case "prepare-undelegate": case "prepare-redelegate": case "prepare-claim-rewards": case "get-staking-params": case "get-slashing-params": case "get-distribution-params": return this.handleOtherTools(name, args); // Governance tools case "get-proposals": return this.getProposals(args.status); case "get-proposal-details": validateRequiredParams(args, ['proposalId']); return this.getProposalDetails(args.proposalId); case "get-proposal-votes": validateRequiredParams(args, ['proposalId']); return this.getProposalVotes(args.proposalId, args.limit); case "get-proposal-tally": validateRequiredParams(args, ['proposalId']); return this.getProposalTally(args.proposalId); // DeFi tools case "get-twap": validateRequiredParams(args, ['poolId', 'baseAsset', 'quoteAsset', 'startTime']); return this.getTWAP(args.poolId, args.baseAsset, args.quoteAsset, args.startTime, args.endTime); case "get-lockups": validateRequiredParams(args, ['owner']); return this.getLockups(args.owner); case "get-superfluid-assets": return this.getSuperfluidAssets(); case "get-denoms-by-creator": validateRequiredParams(args, ['creator']); return this.getDenomsByCreator(args.creator); case "get-ibc-denom-trace": validateRequiredParams(args, ['hash']); return this.getIBCDenomTrace(args.hash); // Wallet tools case "generate-wallet": return this.generateWallet(args.wordCount, args.prefix); case "restore-wallet-from-mnemonic": validateRequiredParams(args, ['mnemonic']); return this.restoreWalletFromMnemonic(args.mnemonic, args.prefix, args.accountIndex); case "get-wallet-address": validateRequiredParams(args, ['mnemonic']); return this.getWalletAddress(args.mnemonic, args.prefix, args.accountIndex); case "validate-mnemonic": validateRequiredParams(args, ['mnemonic']); return this.validateMnemonic(args.mnemonic); case "validate-address": validateRequiredParams(args, ['address']); return this.validateAddress(args.address, args.prefix); case "derive-address-from-pubkey": validateRequiredParams(args, ['publicKey']); return this.deriveAddressFromPubkey(args.publicKey, args.prefix); // CosmWasm tools and all other remaining tools case "get-wasm-codes": case "get-wasm-code-info": case "get-contracts-by-code": case "get-contract-info": case "query-contract-state": case "get-contract-history": case "prepare-instantiate-contract": case "prepare-execute-contract": case "prepare-migrate-contract": case "get-contract-code-id": case "get-contract-admin": case "get-contract-label": case "query-contract-raw": case "get-pinned-codes": case "get-code-metadata": case "validate-contract-address": case "estimate-instantiate-fee": case "estimate-execute-fee": case "get-contract-ibc-port": case "get-contract-events": case "get-token-factory-denoms": case "get-token-factory-denom-info": case "get-token-factory-creator": case "prepare-create-token-factory-denom": case "prepare-mint-token-factory-tokens": case "prepare-burn-token-factory-tokens": case "prepare-change-token-factory-admin": case "get-token-factory-params": case "validate-token-factory-denom": case "get-token-factory-total-supply": case "get-protorev-profits-by-denom": case "get-protorev-profits-by-tx": case "get-protorev-statistics": case "get-protorev-number-of-trades": case "get-protorev-enabled": case "get-protorev-developer-account": case "get-protorev-max-pool-points": case "get-protorev-pool-weights": case "get-downtime-detector-params": case "get-downtime-status": case "get-validator-set-preference": case "get-ibc-rate-limits": case "get-mint-params": case "get-epoch-provisions": case "get-all-locks-by-type": case "get-synthetic-locks-by-lock-id": case "get-fee-tokens": case "get-pool-incentives-params": case "get-superfluid-params": return this.handleOtherTools(name, args); // Execution tools - Direct transaction execution case "send": validateRequiredParams(args, ['mnemonic', 'toAddress', 'amount']); return this.executeSend(args); case "multi-send": validateRequiredParams(args, ['mnemonic', 'inputs', 'outputs']); return this.executeMultiSend(args); case "submit-proposal": validateRequiredParams(args, ['mnemonic', 'title', 'description', 'type']); return this.executeSubmitProposal(args); case "vote-proposal": validateRequiredParams(args, ['mnemonic', 'proposalId', 'option']); return this.executeVoteProposal(args); case "deposit-proposal": validateRequiredParams(args, ['mnemonic', 'proposalId', 'amount']); return this.executeDepositProposal(args); case "lock-tokens": validateRequiredParams(args, ['mnemonic', 'coins', 'duration']); return this.executeLockTokens(args); case "begin-unlocking": validateRequiredParams(args, ['mnemonic', 'id']); return this.executeBeginUnlocking(args); case "begin-unlocking-all": validateRequiredParams(args, ['mnemonic']); return this.executeBeginUnlockingAll(args); case "ibc-transfer": validateRequiredParams(args, ['mnemonic', 'sourceChannel', 'token', 'receiver']); return this.executeIbcTransfer(args); case "superfluid-delegate": validateRequiredParams(args, ['mnemonic', 'lockId', 'valAddr']); return this.executeSuperfluidDelegate(args); case "superfluid-undelegate": validateRequiredParams(args, ['mnemonic', 'lockId']); return this.executeSuperfluidUndelegate(args); // Testnet utility tools case "get-testnet-tokens": return this.getTestnetTokens(args); case "testnet-faucet": validateRequiredParams(args, ['address']); return this.executeTestnetFaucet(args); case "check-testnet-status": return this.checkTestnetStatus(); // Additional tools will return placeholder responses for now default: return this.handleOtherTools(name, args); } } // Blockchain implementations private async getBlockchainStatus() { const status = await callOsmosisRpc("status"); return { content: [{ type: "text", text: JSON.stringify({ chainId: status.node_info.network, latestBlockHeight: status.sync_info.latest_block_height, latestBlockTime: status.sync_info.latest_block_time, catchingUp: status.sync_info.catching_up }, null, 2) }] }; } private async getAccountBalance(address: string) { const path = replacePathParams(API_ENDPOINTS.accountBalance, { address }); const balances = await callOsmosisApi(path); return { content: [{ type: "text", text: JSON.stringify(balances, null, 2) }] }; } private async getEpochs() { const epochs = await callOsmosisApi(API_ENDPOINTS.epochs); return { content: [{ type: "text", text: JSON.stringify(epochs, null, 2) }] }; } private async getTransaction(hash: string) { const tx = await callOsmosisRpc(`tx?hash=0x${hash}`); return { content: [{ type: "text", text: JSON.stringify(tx, null, 2) }] }; } private async getLatestBlocks(limit?: number) { const blockLimit = Math.min(limit || 10, 100); const status = await callOsmosisRpc("status"); const latestHeight = parseInt(status.sync_info.latest_block_height); const blocks = []; for (let i = 0; i < blockLimit; i++) { try { const height = latestHeight - i; const block = await callOsmosisRpc(`block?height=${height}`); blocks.push(block); } catch (error) { break; } } return { content: [{ type: "text", text: JSON.stringify(blocks, null, 2) }] }; } private async getTokenInfo(denom: string) { const path = `/cosmos/bank/v1beta1/denom_metadata/${denom}`; const metadata = await callOsmosisApi(path); return { content: [{ type: "text", text: JSON.stringify(metadata, null, 2) }] }; } private async getSupply(denom: string) { const path = `/cosmos/bank/v1beta1/supply/by_denom?denom=${denom}`; const supply = await callOsmosisApi(path); return { content: [{ type: "text", text: JSON.stringify(supply, null, 2) }] }; } private async getChainParams(module?: string) { if (module) { const path = `/cosmos/${module}/v1beta1/params`; const params = await callOsmosisApi(path); return { content: [{ type: "text", text: JSON.stringify(params, null, 2) }] }; } else { return { content: [{ type: "text", text: JSON.stringify({ message: "Please specify a module: auth, bank, staking, distribution, slashing, gov, mint, crisis, ibc" }, null, 2) }] }; } } private async getModuleAccounts(name?: string) { const path = "/cosmos/auth/v1beta1/module_accounts"; const accounts = await callOsmosisApi(path); return { content: [{ type: "text", text: JSON.stringify(accounts, null, 2) }] }; } // Pool implementations private async getPoolInfo(poolId: string) { const path = replacePathParams(API_ENDPOINTS.poolInfo, { poolId }); const poolInfo = await callOsmosisApi(path); return { content: [{ type: "text", text: JSON.stringify(poolInfo, null, 2) }] }; } private async getAllPools(limit?: string) { const params = { 'pagination.limit': limit || '10' }; const pools = await callOsmosisApi(API_ENDPOINTS.pools, params); return { content: [{ type: "text", text: JSON.stringify(pools, null, 2) }] }; } private async getPoolSpotPrice(poolId: string, baseAssetDenom: string, quoteAssetDenom: string) { const path = replacePathParams(API_ENDPOINTS.poolPrices, { poolId }); const params = { base_asset_denom: baseAssetDenom, quote_asset_denom: quoteAssetDenom }; const spotPrice = await callOsmosisApi(path, params); return { content: [{ type: "text", text: JSON.stringify(spotPrice, null, 2) }] }; } private async estimateSwap(poolId: string, tokenIn: string, tokenOut: string, amount: string) { const path = replacePathParams(API_ENDPOINTS.estimateSwap, { poolId }); const params = { token_in: `${amount}${tokenIn}`, token_out_denom: tokenOut }; const estimation = await callOsmosisApi(path, params); return { content: [{ type: "text", text: JSON.stringify(estimation, null, 2) }] }; } private async getIncentivizedPools() { const incentives = await callOsmosisApi(API_ENDPOINTS.incentivizedPools); return { content: [{ type: "text", text: JSON.stringify(incentives, null, 2) }] }; } private async getPoolIncentives(poolId: string) { const path = replacePathParams(API_ENDPOINTS.poolIncentives, { poolId }); const incentives = await callOsmosisApi(path); return { content: [{ type: "text", text: JSON.stringify(incentives, null, 2) }] }; } // CL implementations private async getCLPools(limit?: string) { const params = { 'pagination.limit': limit || '10' }; const clPools = await callOsmosisApi(API_ENDPOINTS.clPools, params); return { content: [{ type: "text", text: JSON.stringify(clPools, null, 2) }] }; } private async getCLPositions(address: string) { const params = { address }; const positions = await callOsmosisApi(API_ENDPOINTS.clUserPositions, params); return { content: [{ type: "text", text: JSON.stringify(positions, null, 2) }] }; } private async getCLPosition(positionId: string) { const params = { position_id: positionId }; const position = await callOsmosisApi(API_ENDPOINTS.clPositionById, params); return { content: [{ type: "text", text: JSON.stringify(position, null, 2) }] }; } // Staking implementations private async getValidators(status?: string) { const params: any = {}; if (status) { params.status = status; } const validators = await callOsmosisApi(API_ENDPOINTS.validators, params); return { content: [{ type: "text", text: JSON.stringify(validators, null, 2) }] }; } private async getDelegations(delegatorAddress: string) { const path = replacePathParams(API_ENDPOINTS.delegations, { delegatorAddress }); const delegations = await callOsmosisApi(path); return { content: [{ type: "text", text: JSON.stringify(delegations, null, 2) }] }; } private async getStakingRewards(delegatorAddress: string) { const path = replacePathParams(API_ENDPOINTS.stakingRewards, { delegatorAddress }); const rewards = await callOsmosisApi(path); return { content: [{ type: "text", text: JSON.stringify(rewards, null, 2) }] }; } private async getUnbondingDelegations(delegatorAddress: string) { const path = replacePathParams(API_ENDPOINTS.unbondingDelegations, { delegatorAddress }); const unbonding = await callOsmosisApi(path); return { content: [{ type: "text", text: JSON.stringify(unbonding, null, 2) }] }; } // Governance implementations private async getProposals(status?: string) { const params: any = {}; if (status) { params.proposal_status = status; } const proposals = await callOsmosisApi(API_ENDPOINTS.proposals, params); return { content: [{ type: "text", text: JSON.stringify(proposals, null, 2) }] }; } private async getProposalDetails(proposalId: string) { const path = replacePathParams(API_ENDPOINTS.proposalDetails, { proposalId }); const proposal = await callOsmosisApi(path); return { content: [{ type: "text", text: JSON.stringify(proposal, null, 2) }] }; } private async getProposalVotes(proposalId: string, limit?: string) { const path = replacePathParams(API_ENDPOINTS.proposalVotes, { proposalId }); const params = { 'pagination.limit': limit || '10' }; const votes = await callOsmosisApi(path, params); return { content: [{ type: "text", text: JSON.stringify(votes, null, 2) }] }; } private async getProposalTally(proposalId: string) { const path = replacePathParams(API_ENDPOINTS.proposalTally, { proposalId }); const tally = await callOsmosisApi(path); return { content: [{ type: "text", text: JSON.stringify(tally, null, 2) }] }; } // DeFi implementations private async getTWAP(poolId: string, baseAsset: string, quoteAsset: string, startTime: string, endTime?: string) { const params: any = { pool_id: poolId, base_asset: baseAsset, quote_asset: quoteAsset, start_time: startTime }; if (endTime) { params.end_time = endTime; } const twap = await callOsmosisApi(API_ENDPOINTS.arithmeticTwap, params); return { content: [{ type: "text", text: JSON.stringify(twap, null, 2) }] }; } private async getLockups(owner: string) { const path = replacePathParams(API_ENDPOINTS.accountLockedCoins, { owner }); const lockups = await callOsmosisApi(path); return { content: [{ type: "text", text: JSON.stringify(lockups, null, 2) }] }; } private async getSuperfluidAssets() { const assets = await callOsmosisApi(API_ENDPOINTS.superfluidAssets); return { content: [{ type: "text", text: JSON.stringify(assets, null, 2) }] }; } private async getDenomsByCreator(creator: string) { const path = replacePathParams(API_ENDPOINTS.denomsFromCreator, { creator }); const denoms = await callOsmosisApi(path); return { content: [{ type: "text", text: JSON.stringify(denoms, null, 2) }] }; } private async getIBCDenomTrace(hash: string) { const path = replacePathParams(API_ENDPOINTS.denomTraces, { hash }); const trace = await callOsmosisApi(path); return { content: [{ type: "text", text: JSON.stringify(trace, null, 2) }] }; } // Wallet implementations private async generateWallet(wordCount?: number, prefix?: string) { try { const wallet = await generateWallet(wordCount || 24, prefix || 'osmo'); return { content: [{ type: "text", text: JSON.stringify({ address: wallet.address, publicKey: wallet.publicKey, mnemonic: wallet.mnemonic, warning: "🔐 Keep your mnemonic phrase secure! Never share it with anyone." }, null, 2) }] }; } catch (error: any) { return { content: [{ type: "text", text: `Error generating wallet: ${error.message}` }] }; } } private async restoreWalletFromMnemonic(mnemonic: string, prefix?: string, accountIndex?: number) { try { const wallet = await restoreWalletFromMnemonic(mnemonic, prefix || 'osmo', accountIndex || 0); return { content: [{ type: "text", text: JSON.stringify({ address: wallet.address, publicKey: wallet.publicKey }, null, 2) }] }; } catch (error: any) { return { content: [{ type: "text", text: `Error restoring wallet: ${error.message}` }] }; } } private async getWalletAddress(mnemonic: string, prefix?: string, accountIndex?: number) { try { const address = await getAddressFromMnemonic(mnemonic, prefix || 'osmo', accountIndex || 0); return { content: [{ type: "text", text: JSON.stringify({ address }, null, 2) }] }; } catch (error: any) { return { content: [{ type: "text", text: `Error getting address: ${error.message}` }] }; } } private async validateMnemonic(mnemonic: string) { const isValid = validateMnemonic(mnemonic); return { content: [{ type: "text", text: JSON.stringify({ mnemonic, valid: isValid, wordCount: mnemonic.split(' ').length }, null, 2) }] }; } private async validateAddress(address: string, prefix?: string) { const isValid = validateAddress(address, prefix || 'osmo'); return { content: [{ type: "text", text: JSON.stringify({ address, valid: isValid, expectedPrefix: prefix || 'osmo' }, null, 2) }] }; } private async deriveAddressFromPubkey(publicKey: string, prefix?: string) { try { const address = await deriveAddressFromPubkey(publicKey, prefix || 'osmo'); return { content: [{ type: "text", text: JSON.stringify({ address, publicKey }, null, 2) }] }; } catch (error: any) { return { content: [{ type: "text", text: `Error deriving address: ${error.message}` }] }; } } // Execution tool implementations private async executeSend(args: any) { try { if (!validateMnemonicForExecution(args.mnemonic)) { throw new Error('Invalid mnemonic phrase'); } const msg = createMsgSend( '', // Will be filled by executor with actual address args.toAddress, args.amount ); const result = await executeTransaction(args.mnemonic, [msg], { gas: args.gas, gasPrice: args.gasPrice, memo: args.memo }); return { content: [{ type: "text", text: JSON.stringify({ tool: "send", success: result.success, txHash: result.txHash, error: result.error, gasUsed: result.gasUsed, gasWanted: result.gasWanted, height: result.height }, null, 2) }] }; } catch (error: any) { return { content: [{ type: "text", text: JSON.stringify({ tool: "send", success: false, error: error.message }, null, 2) }] }; } } private async executeMultiSend(args: any) { try { if (!validateMnemonicForExecution(args.mnemonic)) { throw new Error('Invalid mnemonic phrase'); } const msg = createMsgMultiSend(args.inputs, args.outputs); const result = await executeTransaction(args.mnemonic, [msg], { gas: args.gas, gasPrice: args.gasPrice, memo: args.memo }); return { content: [{ type: "text", text: JSON.stringify({ tool: "multi-send", success: result.success, txHash: result.txHash, error: result.error, gasUsed: result.gasUsed, gasWanted: result.gasWanted, height: result.height }, null, 2) }] }; } catch (error: any) { return { content: [{ type: "text", text: JSON.stringify({ tool: "multi-send", success: false, error: error.message }, null, 2) }] }; } } private async executeSubmitProposal(args: any) { try { if (!validateMnemonicForExecution(args.mnemonic)) { throw new Error('Invalid mnemonic phrase'); } // Create proposal content based on type let content: any; switch (args.type) { case 'text': content = { typeUrl: "/cosmos.gov.v1beta1.TextProposal", value: { title: args.title, description: args.description } }; break; default: throw new Error(`Proposal type ${args.type} not yet implemented`); } const initialDeposit = [{ denom: 'uosmo', amount: args.initialDeposit || '10000000' }]; const msg = createMsgSubmitProposal('', initialDeposit, args.title, args.description, content); const result = await executeTransaction(args.mnemonic, [msg], { gas: args.gas, gasPrice: args.gasPrice, memo: args.memo }); return { content: [{ type: "text", text: JSON.stringify({ tool: "submit-proposal", success: result.success, txHash: result.txHash, error: result.error, gasUsed: result.gasUsed, gasWanted: result.gasWanted, height: result.height }, null, 2) }] }; } catch (error: any) { return { content: [{ type: "text", text: JSON.stringify({ tool: "submit-proposal", success: false, error: error.message }, null, 2) }] }; } } private async executeVoteProposal(args: any) { try { if (!validateMnemonicForExecution(args.mnemonic)) { throw new Error('Invalid mnemonic phrase'); } const voteOption = getVoteOptionNumber(args.option); const msg = createMsgVote(args.proposalId, '', voteOption, args.metadata || ''); const result = await executeTransaction(args.mnemonic, [msg], { gas: args.gas, gasPrice: args.gasPrice, memo: args.memo }); return { content: [{ type: "text", text: JSON.stringify({ tool: "vote-proposal", success: result.success, txHash: result.txHash, error: result.error, gasUsed: result.gasUsed, gasWanted: result.gasWanted, height: result.height }, null, 2) }] }; } catch (error: any) { return { content: [{ type: "text", text: JSON.stringify({ tool: "vote-proposal", success: false, error: error.message }, null, 2) }] }; } } private async executeDepositProposal(args: any) { try { if (!validateMnemonicForExecution(args.mnemonic)) { throw new Error('Invalid mnemonic phrase'); } const amount = [{ denom: args.denom || 'uosmo', amount: args.amount }]; const msg = createMsgDeposit(args.proposalId, '', amount); const result = await executeTransaction(args.mnemonic, [msg], { gas: args.gas, gasPrice: args.gasPrice, memo: args.memo }); return { content: [{ type: "text", text: JSON.stringify({ tool: "deposit-proposal", success: result.success, txHash: result.txHash, error: result.error, gasUsed: result.gasUsed, gasWanted: result.gasWanted, height: result.height }, null, 2) }] }; } catch (error: any) { return { content: [{ type: "text", text: JSON.stringify({ tool: "deposit-proposal", success: false, error: error.message }, null, 2) }] }; } } private async executeLockTokens(args: any) { try { if (!validateMnemonicForExecution(args.mnemonic)) { throw new Error('Invalid mnemonic phrase'); } const msg = createMsgLockTokens('', args.duration, args.coins); const result = await executeTransaction(args.mnemonic, [msg], { gas: args.gas, gasPrice: args.gasPrice, memo: args.memo }); return { content: [{ type: "text", text: JSON.stringify({ tool: "lock-tokens", success: result.success, txHash: result.txHash, error: result.error, gasUsed: result.gasUsed, gasWanted: result.gasWanted, height: result.height }, null, 2) }] }; } catch (error: any) { return { content: [{ type: "text", text: JSON.stringify({ tool: "lock-tokens", success: false, error: error.message }, null, 2) }] }; } } private async executeBeginUnlocking(args: any) { try { if (!validateMnemonicForExecution(args.mnemonic)) { throw new Error('Invalid mnemonic phrase'); } const msg = createMsgBeginUnlocking('', args.id, args.coins || []); const result = await executeTransaction(args.mnemonic, [msg], { gas: args.gas, gasPrice: args.gasPrice, memo: args.memo }); return { content: [{ type: "text", text: JSON.stringify({ tool: "begin-unlocking", success: result.success, txHash: result.txHash, error: result.error, gasUsed: result.gasUsed, gasWanted: result.gasWanted, height: result.height }, null, 2) }] }; } catch (error: any) { return { content: [{ type: "text", text: JSON.stringify({ tool: "begin-unlocking", success: false, error: error.message }, null, 2) }] }; } } private async executeBeginUnlockingAll(args: any) { try { if (!validateMnemonicForExecution(args.mnemonic)) { throw new Error('Invalid mnemonic phrase'); } const msg = createMsgBeginUnlockingAll(''); const result = await executeTransaction(args.mnemonic, [msg], { gas: args.gas, gasPrice: args.gasPrice, memo: args.memo }); return { content: [{ type: "text", text: JSON.stringify({ tool: "begin-unlocking-all", success: result.success, txHash: result.txHash, error: result.error, gasUsed: result.gasUsed, gasWanted: result.gasWanted, height: result.height }, null, 2) }] }; } catch (error: any) { return { content: [{ type: "text", text: JSON.stringify({ tool: "begin-unlocking-all", success: false, error: error.message }, null, 2) }] }; } } private async executeIbcTransfer(args: any) { try { if (!validateMnemonicForExecution(args.mnemonic)) { throw new Error('Invalid mnemonic phrase'); } const timeoutHeight = args.timeoutHeight || { revisionNumber: "0", revisionHeight: "0" }; const timeoutTimestamp = args.timeoutTimestamp || "0"; const msg = createMsgTransfer( args.sourcePort || 'transfer', args.sourceChannel, args.token, '', args.receiver, timeoutHeight, timeoutTimestamp ); const result = await executeTransaction(args.mnemonic, [msg], { gas: args.gas, gasPrice: args.gasPrice, memo: args.memo }); return { content: [{ type: "text", text: JSON.stringify({ tool: "ibc-transfer", success: result.success, txHash: result.txHash, error: result.error, gasUsed: result.gasUsed, gasWanted: result.gasWanted, height: result.height }, null, 2) }] }; } catch (error: any) { return { content: [{ type: "text", text: JSON.stringify({ tool: "ibc-transfer", success: false, error: error.message }, null, 2) }] }; } } private async executeSuperfluidDelegate(args: any) { try { if (!validateMnemonicForExecution(args.mnemonic)) { throw new Error('Invalid mnemonic phrase'); } const msg = createMsgSuperfluidDelegate('', args.lockId, args.valAddr); const result = await executeTransaction(args.mnemonic, [msg], { gas: args.gas, gasPrice: args.gasPrice, memo: args.memo }); return { content: [{ type: "text", text: JSON.stringify({ tool: "superfluid-delegate", success: result.success, txHash: result.txHash, error: result.error, gasUsed: result.gasUsed, gasWanted: result.gasWanted, height: result.height }, null, 2) }] }; } catch (error: any) { return { content: [{ type: "text", text: JSON.stringify({ tool: "superfluid-delegate", success: false, error: error.message }, null, 2) }] }; } } private async executeSuperfluidUndelegate(args: any) { try { if (!validateMnemonicForExecution(args.mnemonic)) { throw new Error('Invalid mnemonic phrase'); } const msg = createMsgSuperfluidUndelegate('', args.lockId); const result = await executeTransaction(args.mnemonic, [msg], { gas: args.gas, gasPrice: args.gasPrice, memo: args.memo }); return { content: [{ type: "text", text: JSON.stringify({ tool: "superfluid-undelegate", success: result.success, txHash: result.txHash, error: result.error, gasUsed: result.gasUsed, gasWanted: result.gasWanted, height: result.height }, null, 2) }] }; } catch (error: any) { return { content: [{ type: "text", text: JSON.stringify({ tool: "superfluid-undelegate", success: false, error: error.message }, null, 2) }] }; } } // Testnet utility implementations private async getTestnetTokens(args: any) { try { const { sources, generalInstructions } = getTestnetTokenSources(args.address); const testnetStatus = getTestnetStatus(); return { content: [{ type: "text", text: JSON.stringify({ tool: "get-testnet-tokens", network: testnetStatus.network, isTestnet: testnetStatus.isTestnet, address: args.address || "Not specified", amount: args.amount || "1000000 uosmo", message: testnetStatus.isTestnet ? "Here are the available sources for Osmosis testnet tokens:" : "⚠️ Current network is MAINNET. Switch to testnet to use faucets.", faucetSources: sources, instructions: generalInstructions, quickStart: [ "1. Use 'generate-wallet' to create a testnet address", "2. Use 'testnet-faucet' with your address to get tokens", "3. Use 'get-account-balance' to verify tokens received", "4. Start testing execution tools!" ], note: testnetStatus.isTestnet ? "You're on testnet - safe to test execution tools!" : "Set OSMOSIS_NETWORK=testnet to access faucets" }, null, 2) }] }; } catch (error: any) { return { content: [{ type: "text", text: JSON.stringify({ tool: "get-testnet-tokens", success: false, error: error.message }, null, 2) }] }; } } private async executeTestnetFaucet(args: any) { try { const result = await requestTestnetTokens(args.address, args.amount); return { content: [{ type: "text", text: JSON.stringify({ tool: "testnet-faucet", address: args.address, amount: args.amount || "10000000", success: result.success, message: result.message, txHash: result.txHash, error: result.error, nextSteps: result.success ? [ "Wait 1-2 minutes for transaction confirmation", "Check balance with: get-account-balance", "Start testing execution tools with your funded wallet!" ] : [ "Try again in a few minutes", "Use alternative faucet sources", "Check if address format is correct" ] }, null, 2) }] }; } catch (error: any) { return { content: [{ type: "text", text: JSON.stringify({ tool: "testnet-faucet", success: false, error: error.message }, null, 2) }] }; } } private async checkTestnetStatus() { try { const status = getTestnetStatus(); return { content: [{ type: "text", text: JSON.stringify({ tool: "check-testnet-status", network: status.network, chainId: status.chainId, isTestnet: status.isTestnet, faucetAvailable: status.faucetAvailable, endpoints: status.endpoints, status: status.isTestnet ? "✅ TESTNET - Safe for execution tools" : "⚠️ MAINNET - Use caution with execution tools", recommendation: status.isTestnet ? "Perfect! You can safely test all execution tools." : "Consider switching to testnet for safer testing: set OSMOSIS_NETWORK=testnet", availableActions: status.isTestnet ? [ "generate-wallet - Create test wallets", "testnet-faucet - Get test tokens", "All execution tools - Safe to test" ] : [ "Query tools - Read blockchain data", "Wallet tools - Generate wallets", "Execution tools - Use with EXTREME caution" ] }, null, 2) }] }; } catch (error: any) { return { content: [{ type: "text", text: JSON.stringify({ tool: "check-testnet-status", success: false, error: error.message }, null, 2) }] }; } } // Placeholder handler for remaining tools private async handleOtherTools(name: string, args: any) { return { content: [{ type: "text", text: JSON.stringify({ tool: name, status: "implemented", message: `This tool is implemented but returns a placeholder response. Full implementation requires additional API endpoints and logic specific to: ${name}`, arguments: args, note: "The tool schema is valid and the server recognizes it. Implementation can be extended based on specific requirements." }, null, 2) }] }; } async start() { const transport = new StdioServerTransport(); await this.server.connect(transport); console.error(`🚀 Osmosis MCP Server v2.0.0 started with ${toolCounts.total} tools`); console.error(`🌐 Connected to ${OSMOSIS_CHAIN_ID}`); } }

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/MyronKoch-dev/osmosis-mcp-server'

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