Skip to main content
Glama
server.js54.7 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 } from './utils/executor.js'; import { requestTestnetTokens, getTestnetTokenSources, getTestnetStatus } from './utils/faucet.js'; import { createMsgSend, createMsgMultiSend, createMsgSubmitProposal, createMsgVote, createMsgDeposit, createMsgTransfer, createMsgLockTokens, createMsgBeginUnlocking, createMsgBeginUnlockingAll, createMsgSuperfluidDelegate, createMsgSuperfluidUndelegate, getVoteOptionNumber } from './utils/messages.js'; export class OsmosisMCPServer { server; constructor() { this.server = new Server({ name: "osmosis-blockchain-server", version: "2.0.0" }, { capabilities: { tools: {} } }); this.setupToolHandlers(); } 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) { console.error(`❌ Tool ${name} failed:`, error.message); return { content: [{ type: "text", text: `Error executing ${name}: ${error.message}` }] }; } }); } async handleToolCall(name, args) { 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 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) }] }; } async getAccountBalance(address) { const path = replacePathParams(API_ENDPOINTS.accountBalance, { address }); const balances = await callOsmosisApi(path); return { content: [{ type: "text", text: JSON.stringify(balances, null, 2) }] }; } async getEpochs() { const epochs = await callOsmosisApi(API_ENDPOINTS.epochs); return { content: [{ type: "text", text: JSON.stringify(epochs, null, 2) }] }; } async getTransaction(hash) { const tx = await callOsmosisRpc(`tx?hash=0x${hash}`); return { content: [{ type: "text", text: JSON.stringify(tx, null, 2) }] }; } async getLatestBlocks(limit) { 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) }] }; } async getTokenInfo(denom) { const path = `/cosmos/bank/v1beta1/denom_metadata/${denom}`; const metadata = await callOsmosisApi(path); return { content: [{ type: "text", text: JSON.stringify(metadata, null, 2) }] }; } async getSupply(denom) { 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) }] }; } async getChainParams(module) { 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) }] }; } } async getModuleAccounts(name) { const path = "/cosmos/auth/v1beta1/module_accounts"; const accounts = await callOsmosisApi(path); return { content: [{ type: "text", text: JSON.stringify(accounts, null, 2) }] }; } // Pool implementations async getPoolInfo(poolId) { const path = replacePathParams(API_ENDPOINTS.poolInfo, { poolId }); const poolInfo = await callOsmosisApi(path); return { content: [{ type: "text", text: JSON.stringify(poolInfo, null, 2) }] }; } async getAllPools(limit) { const params = { 'pagination.limit': limit || '10' }; const pools = await callOsmosisApi(API_ENDPOINTS.pools, params); return { content: [{ type: "text", text: JSON.stringify(pools, null, 2) }] }; } async getPoolSpotPrice(poolId, baseAssetDenom, quoteAssetDenom) { 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) }] }; } async estimateSwap(poolId, tokenIn, tokenOut, amount) { 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) }] }; } async getIncentivizedPools() { const incentives = await callOsmosisApi(API_ENDPOINTS.incentivizedPools); return { content: [{ type: "text", text: JSON.stringify(incentives, null, 2) }] }; } async getPoolIncentives(poolId) { const path = replacePathParams(API_ENDPOINTS.poolIncentives, { poolId }); const incentives = await callOsmosisApi(path); return { content: [{ type: "text", text: JSON.stringify(incentives, null, 2) }] }; } // CL implementations async getCLPools(limit) { const params = { 'pagination.limit': limit || '10' }; const clPools = await callOsmosisApi(API_ENDPOINTS.clPools, params); return { content: [{ type: "text", text: JSON.stringify(clPools, null, 2) }] }; } async getCLPositions(address) { const params = { address }; const positions = await callOsmosisApi(API_ENDPOINTS.clUserPositions, params); return { content: [{ type: "text", text: JSON.stringify(positions, null, 2) }] }; } async getCLPosition(positionId) { 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 async getValidators(status) { const params = {}; if (status) { params.status = status; } const validators = await callOsmosisApi(API_ENDPOINTS.validators, params); return { content: [{ type: "text", text: JSON.stringify(validators, null, 2) }] }; } async getDelegations(delegatorAddress) { const path = replacePathParams(API_ENDPOINTS.delegations, { delegatorAddress }); const delegations = await callOsmosisApi(path); return { content: [{ type: "text", text: JSON.stringify(delegations, null, 2) }] }; } async getStakingRewards(delegatorAddress) { const path = replacePathParams(API_ENDPOINTS.stakingRewards, { delegatorAddress }); const rewards = await callOsmosisApi(path); return { content: [{ type: "text", text: JSON.stringify(rewards, null, 2) }] }; } async getUnbondingDelegations(delegatorAddress) { const path = replacePathParams(API_ENDPOINTS.unbondingDelegations, { delegatorAddress }); const unbonding = await callOsmosisApi(path); return { content: [{ type: "text", text: JSON.stringify(unbonding, null, 2) }] }; } // Governance implementations async getProposals(status) { const params = {}; if (status) { params.proposal_status = status; } const proposals = await callOsmosisApi(API_ENDPOINTS.proposals, params); return { content: [{ type: "text", text: JSON.stringify(proposals, null, 2) }] }; } async getProposalDetails(proposalId) { const path = replacePathParams(API_ENDPOINTS.proposalDetails, { proposalId }); const proposal = await callOsmosisApi(path); return { content: [{ type: "text", text: JSON.stringify(proposal, null, 2) }] }; } async getProposalVotes(proposalId, limit) { 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) }] }; } async getProposalTally(proposalId) { const path = replacePathParams(API_ENDPOINTS.proposalTally, { proposalId }); const tally = await callOsmosisApi(path); return { content: [{ type: "text", text: JSON.stringify(tally, null, 2) }] }; } // DeFi implementations async getTWAP(poolId, baseAsset, quoteAsset, startTime, endTime) { const params = { 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) }] }; } async getLockups(owner) { const path = replacePathParams(API_ENDPOINTS.accountLockedCoins, { owner }); const lockups = await callOsmosisApi(path); return { content: [{ type: "text", text: JSON.stringify(lockups, null, 2) }] }; } async getSuperfluidAssets() { const assets = await callOsmosisApi(API_ENDPOINTS.superfluidAssets); return { content: [{ type: "text", text: JSON.stringify(assets, null, 2) }] }; } async getDenomsByCreator(creator) { const path = replacePathParams(API_ENDPOINTS.denomsFromCreator, { creator }); const denoms = await callOsmosisApi(path); return { content: [{ type: "text", text: JSON.stringify(denoms, null, 2) }] }; } async getIBCDenomTrace(hash) { const path = replacePathParams(API_ENDPOINTS.denomTraces, { hash }); const trace = await callOsmosisApi(path); return { content: [{ type: "text", text: JSON.stringify(trace, null, 2) }] }; } // Wallet implementations async generateWallet(wordCount, prefix) { 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) { return { content: [{ type: "text", text: `Error generating wallet: ${error.message}` }] }; } } async restoreWalletFromMnemonic(mnemonic, prefix, accountIndex) { 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) { return { content: [{ type: "text", text: `Error restoring wallet: ${error.message}` }] }; } } async getWalletAddress(mnemonic, prefix, accountIndex) { try { const address = await getAddressFromMnemonic(mnemonic, prefix || 'osmo', accountIndex || 0); return { content: [{ type: "text", text: JSON.stringify({ address }, null, 2) }] }; } catch (error) { return { content: [{ type: "text", text: `Error getting address: ${error.message}` }] }; } } async validateMnemonic(mnemonic) { const isValid = validateMnemonic(mnemonic); return { content: [{ type: "text", text: JSON.stringify({ mnemonic, valid: isValid, wordCount: mnemonic.split(' ').length }, null, 2) }] }; } async validateAddress(address, prefix) { const isValid = validateAddress(address, prefix || 'osmo'); return { content: [{ type: "text", text: JSON.stringify({ address, valid: isValid, expectedPrefix: prefix || 'osmo' }, null, 2) }] }; } async deriveAddressFromPubkey(publicKey, prefix) { try { const address = await deriveAddressFromPubkey(publicKey, prefix || 'osmo'); return { content: [{ type: "text", text: JSON.stringify({ address, publicKey }, null, 2) }] }; } catch (error) { return { content: [{ type: "text", text: `Error deriving address: ${error.message}` }] }; } } // Execution tool implementations async executeSend(args) { 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) { return { content: [{ type: "text", text: JSON.stringify({ tool: "send", success: false, error: error.message }, null, 2) }] }; } } async executeMultiSend(args) { 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) { return { content: [{ type: "text", text: JSON.stringify({ tool: "multi-send", success: false, error: error.message }, null, 2) }] }; } } async executeSubmitProposal(args) { try { if (!validateMnemonicForExecution(args.mnemonic)) { throw new Error('Invalid mnemonic phrase'); } // Create proposal content based on type let content; 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) { return { content: [{ type: "text", text: JSON.stringify({ tool: "submit-proposal", success: false, error: error.message }, null, 2) }] }; } } async executeVoteProposal(args) { 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) { return { content: [{ type: "text", text: JSON.stringify({ tool: "vote-proposal", success: false, error: error.message }, null, 2) }] }; } } async executeDepositProposal(args) { 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) { return { content: [{ type: "text", text: JSON.stringify({ tool: "deposit-proposal", success: false, error: error.message }, null, 2) }] }; } } async executeLockTokens(args) { 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) { return { content: [{ type: "text", text: JSON.stringify({ tool: "lock-tokens", success: false, error: error.message }, null, 2) }] }; } } async executeBeginUnlocking(args) { 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) { return { content: [{ type: "text", text: JSON.stringify({ tool: "begin-unlocking", success: false, error: error.message }, null, 2) }] }; } } async executeBeginUnlockingAll(args) { 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) { return { content: [{ type: "text", text: JSON.stringify({ tool: "begin-unlocking-all", success: false, error: error.message }, null, 2) }] }; } } async executeIbcTransfer(args) { 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) { return { content: [{ type: "text", text: JSON.stringify({ tool: "ibc-transfer", success: false, error: error.message }, null, 2) }] }; } } async executeSuperfluidDelegate(args) { 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) { return { content: [{ type: "text", text: JSON.stringify({ tool: "superfluid-delegate", success: false, error: error.message }, null, 2) }] }; } } async executeSuperfluidUndelegate(args) { 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) { return { content: [{ type: "text", text: JSON.stringify({ tool: "superfluid-undelegate", success: false, error: error.message }, null, 2) }] }; } } // Testnet utility implementations async getTestnetTokens(args) { 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) { return { content: [{ type: "text", text: JSON.stringify({ tool: "get-testnet-tokens", success: false, error: error.message }, null, 2) }] }; } } async executeTestnetFaucet(args) { 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) { return { content: [{ type: "text", text: JSON.stringify({ tool: "testnet-faucet", success: false, error: error.message }, null, 2) }] }; } } 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) { return { content: [{ type: "text", text: JSON.stringify({ tool: "check-testnet-status", success: false, error: error.message }, null, 2) }] }; } } // Placeholder handler for remaining tools async handleOtherTools(name, args) { 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