Skip to main content
Glama

account_create_account

Create a new NEAR account funded by another account. Generate a random or custom account ID with the required suffix for mainnet or testnet.

Instructions

Create a new NEAR account with a new account ID. The initial balance of this account will be funded by the account that is calling this tool. This account will be created with a random public key. If no account ID is provided, a random one will be generated. Ensure that mainnet accounts are created with a .near suffix, and testnet accounts are created with a .testnet suffix.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
signerAccountIdYesThe account that will fund the new account.
newAccountIdNoThe account id of the new account. If not provided, a random one will be generated.
initialBalanceYesThe initial balance of the new account in NEAR. If not provided, the new account will be funded with 0.1 NEAR.
networkIdNomainnet

Implementation Reference

  • Registration of the 'account_create_account' tool on the MCP server via mcp.tool(). Defines the tool name, description, and input schema using Zod.
    mcp.tool(
      'account_create_account',
      noLeadingWhitespace`
      Create a new NEAR account with a new account ID. The initial balance of this account will be funded by the account that is calling this tool.
      This account will be created with a random public key. If no account ID is provided, a random one will be generated.
      Ensure that mainnet accounts are created with a .near suffix, and testnet accounts are created with a .testnet suffix.`,
      {
        signerAccountId: z
          .string()
          .describe('The account that will fund the new account.'),
        newAccountId: z
          .string()
          .optional()
          .describe(
            'The account id of the new account. If not provided, a random one will be generated.',
          ),
        initialBalance: z
          .number()
          .describe(
            'The initial balance of the new account in NEAR. If not provided, the new account will be funded with 0.1 NEAR.',
          ),
        networkId: z.enum(['testnet', 'mainnet']).default('mainnet'),
      },
      async (args, _) => {
        const rpcProvider = getProviderByNetwork(args.networkId);
    
        const signer: Result<MessageSigner, Error> = await getAccountSigner(
          args.signerAccountId,
          args.networkId,
          keystore,
        );
        if (!signer.ok) {
          return {
            content: [
              {
                type: 'text',
                text: `Error: ${signer.error}\n\nCannot find the account ${args.signerAccountId} in the keystore.`,
              },
            ],
          };
        }
    
        const newAccountId = (() => {
          if (!args.newAccountId) {
            const randomChars = Math.random().toString(36).substring(2, 10);
            const suffix = args.networkId === 'mainnet' ? '.near' : '.testnet';
            return randomChars + suffix;
          }
          return args.newAccountId;
        })();
        const keyPair = KeyPair.fromRandom('ed25519');
        await keystore.setKey(args.networkId, newAccountId, keyPair);
    
        const createAccountResult: Result<
          {
            outcome: FinalExecutionOutcome;
            result: SerializedReturnValue;
          },
          Error
        > = await (async () => {
          try {
            return {
              ok: true,
              value: await createTopLevelAccount({
                account: args.signerAccountId,
                contract: args.networkId,
                newAccount: newAccountId,
                newPublicKey: keyPair.getPublicKey().toString(),
                initialBalance: NearToken.parse_near(
                  args.initialBalance,
                ).as_yocto_near(),
                deps: { rpcProvider, signer: signer.value },
              }),
            };
          } catch (e) {
            return { ok: false, error: new Error(e as string) };
          }
        })();
        if (!createAccountResult.ok) {
          await keystore.removeKey(args.networkId, newAccountId);
          return {
            content: [
              {
                type: 'text',
                text: `Error: ${createAccountResult.error}\n\nFailed to create account ${newAccountId}`,
              },
            ],
          };
        }
    
        return {
          content: [
            {
              type: 'text',
              text: `Account creation result: ${stringify_bigint(
                createAccountResult.value,
              )}`,
            },
            {
              type: 'text',
              text: `Account created: ${newAccountId}`,
            },
          ],
        };
      },
    );
  • Input schema for account_create_account: signerAccountId (required), newAccountId (optional), initialBalance (number of NEAR), networkId (testnet/mainnet).
    {
      signerAccountId: z
        .string()
        .describe('The account that will fund the new account.'),
      newAccountId: z
        .string()
        .optional()
        .describe(
          'The account id of the new account. If not provided, a random one will be generated.',
        ),
      initialBalance: z
        .number()
        .describe(
          'The initial balance of the new account in NEAR. If not provided, the new account will be funded with 0.1 NEAR.',
        ),
      networkId: z.enum(['testnet', 'mainnet']).default('mainnet'),
    },
  • Handler function for account_create_account. Generates account ID (random if not provided), creates keypair, stores in keystore, calls createTopLevelAccount from @near-js/client, and returns the result. On failure, removes the key from keystore as cleanup.
    async (args, _) => {
      const rpcProvider = getProviderByNetwork(args.networkId);
    
      const signer: Result<MessageSigner, Error> = await getAccountSigner(
        args.signerAccountId,
        args.networkId,
        keystore,
      );
      if (!signer.ok) {
        return {
          content: [
            {
              type: 'text',
              text: `Error: ${signer.error}\n\nCannot find the account ${args.signerAccountId} in the keystore.`,
            },
          ],
        };
      }
    
      const newAccountId = (() => {
        if (!args.newAccountId) {
          const randomChars = Math.random().toString(36).substring(2, 10);
          const suffix = args.networkId === 'mainnet' ? '.near' : '.testnet';
          return randomChars + suffix;
        }
        return args.newAccountId;
      })();
      const keyPair = KeyPair.fromRandom('ed25519');
      await keystore.setKey(args.networkId, newAccountId, keyPair);
    
      const createAccountResult: Result<
        {
          outcome: FinalExecutionOutcome;
          result: SerializedReturnValue;
        },
        Error
      > = await (async () => {
        try {
          return {
            ok: true,
            value: await createTopLevelAccount({
              account: args.signerAccountId,
              contract: args.networkId,
              newAccount: newAccountId,
              newPublicKey: keyPair.getPublicKey().toString(),
              initialBalance: NearToken.parse_near(
                args.initialBalance,
              ).as_yocto_near(),
              deps: { rpcProvider, signer: signer.value },
            }),
          };
        } catch (e) {
          return { ok: false, error: new Error(e as string) };
        }
      })();
      if (!createAccountResult.ok) {
        await keystore.removeKey(args.networkId, newAccountId);
        return {
          content: [
            {
              type: 'text',
              text: `Error: ${createAccountResult.error}\n\nFailed to create account ${newAccountId}`,
            },
          ],
        };
      }
    
      return {
        content: [
          {
            type: 'text',
            text: `Account creation result: ${stringify_bigint(
              createAccountResult.value,
            )}`,
          },
          {
            type: 'text',
            text: `Account created: ${newAccountId}`,
          },
        ],
      };
    },
  • Helper imports: getProviderByNetwork and getSignerFromKeystore from '@near-js/client' are used by the handler to get the RPC provider and the message signer respectively.
      getEndpointsByNetwork,
      getProviderByNetwork,
      getSignerFromKeystore,
      type MessageSigner,
    } from '@near-js/client';
    import { type KeyPairString, type KeyType, PublicKey } from '@near-js/crypto';
    import {
      readKeyFile,
      UnencryptedFileSystemKeyStore,
    } from '@near-js/keystores-node';
    import {
      type ContractCodeView,
      type FinalExecutionOutcome,
      type SerializedReturnValue,
    } from '@near-js/types';
    import base58 from 'bs58';
    import express, { type Request, type Response } from 'express';
    import { writeFile } from 'fs/promises';
    import { type AbiRoot } from 'near-abi';
    import { type Account, connect, KeyPair, type Near } from 'near-api-js';
    import { homedir } from 'os';
    import path from 'path';
    import { z } from 'zod';
    import zodToJsonSchema, { type JsonSchema7Type } from 'zod-to-json-schema';
    import { ZSTDDecoder } from 'zstddec';
    
    import {
      curvePrefixToKeyType,
      DEFAULT_GAS,
      type FungibleTokenContract,
      getConfig as refGetConfig,
      getFungibleTokenContractInfo,
      getParsedContractMethod,
      getSmartRouteRefSwapEstimate,
      json_to_zod,
      keyTypeToCurvePrefix,
      mapSemaphore,
      MCP_SERVER_NAME,
      NearToken,
      noLeadingWhitespace,
      parsePool,
      type Pool,
      type PoolRPCView,
      type RefSwapByOutputAction,
      refSwapEstimateToActions,
      type Result,
      searchFungibleTokens,
      stringify_bigint,
    } from './utils';
    
    const getNetworkFromAccountId = (accountId: string): Result<string, Error> => {
      if (accountId.endsWith('.near')) {
        return { ok: true, value: 'mainnet' };
      }
      if (accountId.endsWith('.testnet')) {
        return { ok: true, value: 'testnet' };
      }
      return { ok: false, error: new Error('Invalid account id') };
    };
    
    const getAccount = async (
      accountId: string,
      connection: Near,
    ): Promise<Result<Account, Error>> => {
      try {
        const account = await connection.account(accountId);
        await account.getAccountBalance();
        return { ok: true, value: account };
      } catch (e) {
        return {
          ok: false,
          error: new Error(
            `Cannot find account by account id ${accountId}: ${e as string}`,
          ),
        };
      }
    };
    
    const getAccountKeyPair = async (
      accountId: string,
      networkId: string,
      keystore: UnencryptedFileSystemKeyStore,
    ): Promise<Result<KeyPair, Error>> => {
      try {
        const keyPair = await keystore.getKey(networkId, accountId);
        return { ok: true, value: keyPair };
      } catch (e) {
        return { ok: false, error: new Error(e as string) };
      }
    };
    
    const getAccountSigner = async (
Behavior3/5

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

Discloses that the account is funded by the signer, uses a random public key, and can generate a random account ID, but does not mention potential costs (gas), signing requirements, or failure conditions (e.g., duplicate ID) despite no annotations.

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

Conciseness5/5

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

Four sentences with no redundancy; key information is front-loaded and every sentence contributes directly to understanding tool usage.

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

Completeness4/5

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

Covers creation process, funding, and account ID rules well, but lacks details about return value and error scenarios; still adequate given no output schema.

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

Parameters4/5

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

Adds value beyond the schema by detailing the suffix rule for newAccountId and the default initial balance (0.1 NEAR), complementing the schema descriptions which already cover basic purpose.

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

Purpose5/5

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

The description clearly states the verb 'Create' and the resource 'new NEAR account', and distinguishes from siblings like account_create_implicit_account by specifying 'with a new account ID'.

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

Usage Guidelines4/5

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

Provides explicit guidance on suffix conventions for mainnet (.near) and testnet (.testnet), and implies usage for creating explicit accounts, but lacks explicit comparison to implicit account creation or when not to use.

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