sendTransaction
Send ether or execute smart contract transactions on Ethereum networks through the MCP Ethers Wallet server, with options for simulation and custom network configuration.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| to | Yes | The Ethereum address to send to | |
| value | Yes | The amount to send in ether | |
| data | No | Optional. The hex data to include in the transaction | |
| mockMode | No | Optional. If true, just simulates the transaction without sending it. Default is false. | |
| provider | No | Optional. Either a network name or custom RPC URL. Use getAllNetworks to see available networks and their details, or getNetwork to get info about a specific network. You can use any network name returned by these tools as a provider value. | |
| chainId | No | Optional. The chain ID to use. If provided with a named network and they don't match, the RPC's chain ID will be used. |
Implementation Reference
- src/tools/core.ts:860-950 (handler)The handler function implements the sendTransaction tool logic. It checks for a wallet, creates a transaction object, simulates it in mockMode (generating fake hash and details), or rejects real sends. Uses ethersService for wallet and provider.async ({ to, value, data, mockMode = false, provider, chainId }) => { try { // First check if a wallet exists const wallet = await ethersService.getWallet(provider); if (!wallet) { return { isError: true, content: [{ type: "text", text: "No wallet available to send transaction. Please create or load a wallet first." }] }; } // Helper function to handle BigInt serialization const serializeData = (obj: any): any => { if (obj === null || obj === undefined) return null; if (typeof obj === 'bigint') return obj.toString(); if (typeof obj !== 'object') return obj; if (Array.isArray(obj)) { return obj.map(item => serializeData(item)); } const result: Record<string, any> = {}; for (const [key, value] of Object.entries(obj)) { result[key] = serializeData(value); } return result; }; // Create transaction object const tx = { to, value: ethers.parseEther(value), data: data || "0x" }; if (mockMode) { // Simulate the transaction without actually sending it const ethProvider = await ethersService.getProvider(provider, chainId); const feeData = await ethProvider.getFeeData(); const fromAddress = wallet.address; // Get nonce for the wallet const nonce = await ethProvider.getTransactionCount(fromAddress); const networkInfo = await ethProvider.getNetwork(); // Create a mock transaction response with all BigInt values converted to strings const mockTxResult = serializeData({ hash: `0x${Math.random().toString(16).substring(2).padStart(64, '0')}`, from: fromAddress, to, value: tx.value, nonce, gasLimit: 21000, // Basic ETH transfer gasPrice: feeData.gasPrice, maxFeePerGas: feeData.maxFeePerGas, maxPriorityFeePerGas: feeData.maxPriorityFeePerGas, data: data || "0x", chainId: networkInfo.chainId, type: 2, // EIP-1559 mockTransaction: true }); return { content: [{ type: "text", text: `MOCK TRANSACTION (not sent): \n${JSON.stringify(mockTxResult, null, 2)}` }] }; } // If not in mock mode, we should reject since we don't want to actually send transactions from tests return { isError: true, content: [{ type: "text", text: "Non-mock transactions are not supported in this implementation. Set mockMode: true to simulate transactions." }] }; } catch (error) { return { isError: true, content: [{ type: "text", text: `Error sending transaction: ${error instanceof Error ? error.message : String(error)}` }] }; } }
- src/tools/core.ts:842-859 (schema)Zod schema for sendTransaction tool inputs: to (address), value (ether amount), optional data, mockMode, provider, chainId.{ to: z.string().describe( "The Ethereum address to send to" ), value: z.string().describe( "The amount to send in ether" ), data: z.string().optional().describe( "Optional. The hex data to include in the transaction" ), mockMode: z.boolean().optional().default(false).describe( "Optional. If true, just simulates the transaction without sending it. Default is false." ), provider: z.string().optional().describe(PROVIDER_DESCRIPTION), chainId: z.number().optional().describe( "Optional. The chain ID to use. If provided with a named network and they don't match, the RPC's chain ID will be used." ) },
- src/tools/core.ts:840-951 (registration)Registers the sendTransaction tool on the MCP server with its schema and handler function.server.tool( "sendTransaction", { to: z.string().describe( "The Ethereum address to send to" ), value: z.string().describe( "The amount to send in ether" ), data: z.string().optional().describe( "Optional. The hex data to include in the transaction" ), mockMode: z.boolean().optional().default(false).describe( "Optional. If true, just simulates the transaction without sending it. Default is false." ), provider: z.string().optional().describe(PROVIDER_DESCRIPTION), chainId: z.number().optional().describe( "Optional. The chain ID to use. If provided with a named network and they don't match, the RPC's chain ID will be used." ) }, async ({ to, value, data, mockMode = false, provider, chainId }) => { try { // First check if a wallet exists const wallet = await ethersService.getWallet(provider); if (!wallet) { return { isError: true, content: [{ type: "text", text: "No wallet available to send transaction. Please create or load a wallet first." }] }; } // Helper function to handle BigInt serialization const serializeData = (obj: any): any => { if (obj === null || obj === undefined) return null; if (typeof obj === 'bigint') return obj.toString(); if (typeof obj !== 'object') return obj; if (Array.isArray(obj)) { return obj.map(item => serializeData(item)); } const result: Record<string, any> = {}; for (const [key, value] of Object.entries(obj)) { result[key] = serializeData(value); } return result; }; // Create transaction object const tx = { to, value: ethers.parseEther(value), data: data || "0x" }; if (mockMode) { // Simulate the transaction without actually sending it const ethProvider = await ethersService.getProvider(provider, chainId); const feeData = await ethProvider.getFeeData(); const fromAddress = wallet.address; // Get nonce for the wallet const nonce = await ethProvider.getTransactionCount(fromAddress); const networkInfo = await ethProvider.getNetwork(); // Create a mock transaction response with all BigInt values converted to strings const mockTxResult = serializeData({ hash: `0x${Math.random().toString(16).substring(2).padStart(64, '0')}`, from: fromAddress, to, value: tx.value, nonce, gasLimit: 21000, // Basic ETH transfer gasPrice: feeData.gasPrice, maxFeePerGas: feeData.maxFeePerGas, maxPriorityFeePerGas: feeData.maxPriorityFeePerGas, data: data || "0x", chainId: networkInfo.chainId, type: 2, // EIP-1559 mockTransaction: true }); return { content: [{ type: "text", text: `MOCK TRANSACTION (not sent): \n${JSON.stringify(mockTxResult, null, 2)}` }] }; } // If not in mock mode, we should reject since we don't want to actually send transactions from tests return { isError: true, content: [{ type: "text", text: "Non-mock transactions are not supported in this implementation. Set mockMode: true to simulate transactions." }] }; } catch (error) { return { isError: true, content: [{ type: "text", text: `Error sending transaction: ${error instanceof Error ? error.message : String(error)}` }] }; } } );
- src/tools/core.ts:875-889 (helper)Local helper function within the handler to serialize objects containing BigInt values to strings for JSON output.const serializeData = (obj: any): any => { if (obj === null || obj === undefined) return null; if (typeof obj === 'bigint') return obj.toString(); if (typeof obj !== 'object') return obj; if (Array.isArray(obj)) { return obj.map(item => serializeData(item)); } const result: Record<string, any> = {}; for (const [key, value] of Object.entries(obj)) { result[key] = serializeData(value); } return result; };