Skip to main content
Glama
kilkelly

Nano Currency MCP Server

by kilkelly

nano_send

Transfer a specified amount of Nano cryptocurrency from a predefined account to a designated Nano address using the Nano Currency MCP Server tool.

Instructions

Send a specified amount of Nano currency from a predefined account to a destination Nano address.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
amountYesAmount of Nano to send (max 0.05 by default)
destination_addressYesNano address to send the nano to

Implementation Reference

  • Handler function that executes the nano_send tool: validates inputs, checks balance, generates work, creates send block, processes it via RPC, returns result or error.
    async function (parameters) { NANO_PRIVATE_KEY_SCHEMA.parse(process.env.NANO_PRIVATE_KEY) try { let amountInRaw = convertNanoToRaw(parameters.amount) let sourceAddress = getAddress() let sourceAddressInfo = await getAccountInfo(sourceAddress) let balanceAfterSend = BigNumber(sourceAddressInfo.balance).minus(amountInRaw).toFixed() if (BigNumber(sourceAddressInfo.balance).lt(amountInRaw)) { throw new Error("Insufficient balance to perform Nano send transaction"); } if (!sourceAddressInfo.frontier) { throw new Error("Source account has no frontier (unopened account)"); } // ----- let work = ( await rpcCall( (process.env.NANO_WORK_GENERATION_URL ? NANO_WORK_GENERATION_URL_KEY : NANO_RPC_URL_KEY), 'work_generate', { hash: sourceAddressInfo.frontier }, 5 * ONE_MINUTE ) ).work z.string({ required_error: `Work is required`, }).refine(work_ => N.validateWork({ work: work_, blockHash: sourceAddressInfo.frontier }), { message: 'Computed Proof-of-Work for Nano transaction is not valid' }).parse(work) // ----- let { block } = N.createBlock(process.env.NANO_PRIVATE_KEY, { representative: sourceAddressInfo.representative, balance: balanceAfterSend, work, link: parameters.destination_address, previous: sourceAddressInfo.frontier }) let processJson = ( await rpcCall( NANO_RPC_URL_KEY, 'process', { json_block: 'true', subtype: 'send', block } ) ) return createTextResponse(JSON.stringify(processJson)) } catch (error) { console.error('[nano_send] Error:', error.message || error); return createErrorResponse(error) } }
  • Zod schema defining input parameters for nano_send: destination_address (validated Nano address) and amount (positive number <= max).
    const nano_send_parameters = { destination_address: z.string({ required_error: `Destination address is required`, }) .refine(address_ => N.checkAddress(address_), { message: 'Destination address is not valid' }) .describe('Nano address to send the nano to'), amount: z.string({ required_error: `Amount is required`, }) .refine(amount_ => !isNaN(Number(amount_)) && Number(amount_) > 0, { message: 'Amount must be a positive number' }) .transform(amount_ => Number(amount_)) .refine(amount_ => amount_ <= (process.env.NANO_MAX_SEND_AMOUNT || NANO_MAX_SEND_AMOUNT_DEFAULT), { message: 'Maximum send amount exceeded' }) .describe(`Amount of Nano to send (max ${(process.env.NANO_MAX_SEND_AMOUNT || NANO_MAX_SEND_AMOUNT_DEFAULT)} by default)`) }
  • Registration of the nano_send tool using server.tool(), including name, description, schema, and handler function.
    server.tool( 'nano_send', 'Send a specified amount of Nano currency from a predefined account to a destination Nano address.', nano_send_parameters, async function (parameters) { NANO_PRIVATE_KEY_SCHEMA.parse(process.env.NANO_PRIVATE_KEY) try { let amountInRaw = convertNanoToRaw(parameters.amount) let sourceAddress = getAddress() let sourceAddressInfo = await getAccountInfo(sourceAddress) let balanceAfterSend = BigNumber(sourceAddressInfo.balance).minus(amountInRaw).toFixed() if (BigNumber(sourceAddressInfo.balance).lt(amountInRaw)) { throw new Error("Insufficient balance to perform Nano send transaction"); } if (!sourceAddressInfo.frontier) { throw new Error("Source account has no frontier (unopened account)"); } // ----- let work = ( await rpcCall( (process.env.NANO_WORK_GENERATION_URL ? NANO_WORK_GENERATION_URL_KEY : NANO_RPC_URL_KEY), 'work_generate', { hash: sourceAddressInfo.frontier }, 5 * ONE_MINUTE ) ).work z.string({ required_error: `Work is required`, }).refine(work_ => N.validateWork({ work: work_, blockHash: sourceAddressInfo.frontier }), { message: 'Computed Proof-of-Work for Nano transaction is not valid' }).parse(work) // ----- let { block } = N.createBlock(process.env.NANO_PRIVATE_KEY, { representative: sourceAddressInfo.representative, balance: balanceAfterSend, work, link: parameters.destination_address, previous: sourceAddressInfo.frontier }) let processJson = ( await rpcCall( NANO_RPC_URL_KEY, 'process', { json_block: 'true', subtype: 'send', block } ) ) return createTextResponse(JSON.stringify(processJson)) } catch (error) { console.error('[nano_send] Error:', error.message || error); return createErrorResponse(error) } } )
  • Helper function rpcCall used by nano_send for making RPC calls to Nano node with timeout and error handling.
    async function rpcCall(envUrl, action, payload, timeout = ONE_MINUTE) { const controller = new AbortController() const timer = setTimeout(() => controller.abort(), timeout) try { const res = await fetch(process.env[envUrl], { ...FETCH_COMMON, signal: controller.signal, body: JSON.stringify({ action, ...payload }), }) if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`) const json = await res.json() if (json.error) throw new Error(`RPC Error: ${json.error}`) return json } catch (error) { throw new Error(`[${envUrl}] ${error.message}`) } finally { clearTimeout(timer) } }
  • Helper function getAccountInfo used by nano_send to fetch source account details before sending.
    async function getAccountInfo(address) { return ( await rpcCall( NANO_RPC_URL_KEY, 'account_info', { account: address, representative: 'true' } ) ) }

Other Tools

Related Tools

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/kilkelly/nano-currency-mcp-server'

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