// 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}`);
}
}