Skip to main content
Glama

tokens_send_ft

Send fungible tokens (e.g., USDC, USDT, WNEAR) from one account to another using the specified token contract, amount, and network.

Instructions

Send Fungible Tokens (FT) like USDC native, USDT, WNEAR, etc. based on the NEP-141 and NEP-148 standards to an account. The signer account is the sender of the tokens, and the receiver account is the recipient of the tokens. Ensure the contract account id exists and is in the same network as the signer and receiver accounts.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
signerAccountIdYesThe account that will send the tokens.
receiverAccountIdYesThe account that will receive the tokens.
networkIdNomainnet
fungibleTokenContractAccountIdYesThe account id of the fungible token contract. Ensure the contract account id exists and is in the same network as the signer and receiver accounts.
amountYesThe amount of tokens to send in the fungible token contract. e.g. 1 USDC, 0.33 USDT, 1.5 WNEAR, etc.

Implementation Reference

  • The tool 'tokens_send_ft' is registered using mcp.tool() on line 1683. This is the MCP tool registration call.
    mcp.tool(
      'tokens_send_ft',
      noLeadingWhitespace`
      Send Fungible Tokens (FT) like USDC native, USDT, WNEAR, etc. based on the NEP-141 and NEP-148 standards to an account.
      The signer account is the sender of the tokens, and the receiver account is the
      recipient of the tokens. Ensure the contract account id exists and is in the same network as the signer and receiver accounts.`,
      {
        signerAccountId: z
          .string()
          .describe('The account that will send the tokens.'),
        receiverAccountId: z
          .string()
          .describe('The account that will receive the tokens.'),
        networkId: z.enum(['mainnet']).default('mainnet'),
        fungibleTokenContractAccountId: z
          .string()
          .describe(
            'The account id of the fungible token contract. Ensure the contract account id exists and is in the same network as the signer and receiver accounts.',
          ),
        amount: z
          .number()
          .describe(
            'The amount of tokens to send in the fungible token contract. e.g. 1 USDC, 0.33 USDT, 1.5 WNEAR, etc.',
          ),
      },
      async (args, _) => {
        const connection = await connect({
          networkId: args.networkId,
          keyStore: keystore,
          nodeUrl: getEndpointsByNetwork(args.networkId)[0]!,
        });
    
        // check that the fungible token contract exists by getting
        // the metadata of the contract
        const fungibleTokenContractMetadataResult: Result<
          FungibleTokenMetadata,
          Error
        > = await getFungibleTokenContractMetadataResult(
          args.fungibleTokenContractAccountId,
          connection,
        );
        if (!fungibleTokenContractMetadataResult.ok) {
          return {
            content: [
              {
                type: 'text',
                text: `Error: ${fungibleTokenContractMetadataResult.error}`,
              },
            ],
          };
        }
    
        // convert the amount into the decimals of the fungible token
        const amountInDecimals = BigInt(
          args.amount * 10 ** fungibleTokenContractMetadataResult.value.decimals,
        );
    
        // call the transfer function of the fungible token contract
        const transferResult: Result<FinalExecutionOutcome, Error> =
          await (async () => {
            try {
              const fungibleTokenContractResult = await getAccount(
                args.fungibleTokenContractAccountId,
                connection,
              );
              if (!fungibleTokenContractResult.ok) {
                return fungibleTokenContractResult;
              }
              const fungibleTokenContract = fungibleTokenContractResult.value;
    
              const senderAccount = await connection.account(
                args.signerAccountId,
              );
              const receiverAccount = await connection.account(
                args.receiverAccountId,
              );
    
              return {
                ok: true,
                value: await senderAccount.functionCall({
                  contractId: fungibleTokenContract.accountId,
                  methodName: 'ft_transfer',
                  args: {
                    receiver_id: receiverAccount.accountId,
                    amount: amountInDecimals.toString(),
                  },
                  gas: DEFAULT_GAS,
                  attachedDeposit:
                    NearToken.parse_yocto_near('1').as_yocto_near(),
                }),
              };
            } catch (e) {
              return { ok: false, error: new Error(e as string) };
            }
          })();
        if (!transferResult.ok) {
          return {
            content: [{ type: 'text', text: `Error: ${transferResult.error}` }],
          };
        }
    
        return {
          content: [
            {
              type: 'text',
              text: `Transaction sent: ${stringify_bigint(transferResult.value)}`,
            },
          ],
        };
      },
    );
  • The input schema for 'tokens_send_ft' defines required parameters: signerAccountId, receiverAccountId, networkId (default 'mainnet'), fungibleTokenContractAccountId, and amount.
    {
      signerAccountId: z
        .string()
        .describe('The account that will send the tokens.'),
      receiverAccountId: z
        .string()
        .describe('The account that will receive the tokens.'),
      networkId: z.enum(['mainnet']).default('mainnet'),
      fungibleTokenContractAccountId: z
        .string()
        .describe(
          'The account id of the fungible token contract. Ensure the contract account id exists and is in the same network as the signer and receiver accounts.',
        ),
      amount: z
        .number()
        .describe(
          'The amount of tokens to send in the fungible token contract. e.g. 1 USDC, 0.33 USDT, 1.5 WNEAR, etc.',
        ),
    },
  • The handler function for 'tokens_send_ft'. It connects to the NEAR network, validates the fungible token contract by fetching its metadata, converts the amount to the correct decimal precision, and calls ft_transfer on the token contract.
      async (args, _) => {
        const connection = await connect({
          networkId: args.networkId,
          keyStore: keystore,
          nodeUrl: getEndpointsByNetwork(args.networkId)[0]!,
        });
    
        // check that the fungible token contract exists by getting
        // the metadata of the contract
        const fungibleTokenContractMetadataResult: Result<
          FungibleTokenMetadata,
          Error
        > = await getFungibleTokenContractMetadataResult(
          args.fungibleTokenContractAccountId,
          connection,
        );
        if (!fungibleTokenContractMetadataResult.ok) {
          return {
            content: [
              {
                type: 'text',
                text: `Error: ${fungibleTokenContractMetadataResult.error}`,
              },
            ],
          };
        }
    
        // convert the amount into the decimals of the fungible token
        const amountInDecimals = BigInt(
          args.amount * 10 ** fungibleTokenContractMetadataResult.value.decimals,
        );
    
        // call the transfer function of the fungible token contract
        const transferResult: Result<FinalExecutionOutcome, Error> =
          await (async () => {
            try {
              const fungibleTokenContractResult = await getAccount(
                args.fungibleTokenContractAccountId,
                connection,
              );
              if (!fungibleTokenContractResult.ok) {
                return fungibleTokenContractResult;
              }
              const fungibleTokenContract = fungibleTokenContractResult.value;
    
              const senderAccount = await connection.account(
                args.signerAccountId,
              );
              const receiverAccount = await connection.account(
                args.receiverAccountId,
              );
    
              return {
                ok: true,
                value: await senderAccount.functionCall({
                  contractId: fungibleTokenContract.accountId,
                  methodName: 'ft_transfer',
                  args: {
                    receiver_id: receiverAccount.accountId,
                    amount: amountInDecimals.toString(),
                  },
                  gas: DEFAULT_GAS,
                  attachedDeposit:
                    NearToken.parse_yocto_near('1').as_yocto_near(),
                }),
              };
            } catch (e) {
              return { ok: false, error: new Error(e as string) };
            }
          })();
        if (!transferResult.ok) {
          return {
            content: [{ type: 'text', text: `Error: ${transferResult.error}` }],
          };
        }
    
        return {
          content: [
            {
              type: 'text',
              text: `Transaction sent: ${stringify_bigint(transferResult.value)}`,
            },
          ],
        };
      },
    );
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description must fully disclose behavior. It states the signer is sender and receiver is recipient, and emphasizes network alignment. However, it fails to disclose critical traits: that this is a write/mutation operation, irreversibility, authorization requirements, or any limitations. The behavioral transparency is insufficient.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is three sentences with no wasted words. It is concise and to the point, though it lacks structural elements like bullet points or sections. The information is front-loaded with the primary action.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given no output schema, the description should mention return values (e.g., transaction receipt). It also lacks error conditions, decimal handling, and standard implications. While it covers network and contract existence, it omits important contextual details for a tool with 5 parameters and many siblings.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema covers 80% of parameters with descriptions; the description adds context for signer, receiver, contract, and amount (e.g., examples like '1 USDC'). However, it does not add meaning for networkId. The added value is modest but acceptable given high schema coverage, earning a baseline score of 3.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool sends fungible tokens like USDC, USDT, WNEAR, referencing NEP-141 and NEP-148 standards. It specifies the verb 'send' and the resource 'fungible tokens' with examples. However, it does not explicitly differentiate from the sibling tool tokens_send_near, which sends native NEAR tokens, leaving the distinction implicit.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives basic prerequisites (ensure contract exists and same network) but provides no guidance on when to use this tool versus alternatives like tokens_send_near or ref_finance_execute_swap. There is no mention of when not to use it or context for selecting this tool over others.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other 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/nearai/near-mcp'

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