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'
          }
        )
      )
    }
Install Server

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