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
| Name | Required | Description | Default |
|---|---|---|---|
| signerAccountId | Yes | The account that will fund the new account. | |
| newAccountId | No | The account id of the new account. If not provided, a random one will be generated. | |
| initialBalance | Yes | The initial balance of the new account in NEAR. If not provided, the new account will be funded with 0.1 NEAR. | |
| networkId | No | mainnet |
Implementation Reference
- src/services.ts:1193-1298 (registration)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}`, }, ], }; }, ); - src/services.ts:1199-1215 (schema)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'), }, - src/services.ts:1216-1297 (handler)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}`, }, ], }; }, - src/services.ts:7-98 (helper)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 (