contract_get_function_args
Retrieves function call arguments by parsing the contract ABI or checking recent execution results via nearblocks.io API. Experimental.
Instructions
Get the arguments of a function call by parsing the contract's ABI or by using the nearblocks.io API (as a fallback). This function API checks recent execution results of the contract's method being queried to determine the likely arguments of the function call. Warning: This tool is experimental and is not garunteed to get the correct arguments.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| contractId | Yes | ||
| methodName | Yes | ||
| networkId | No | mainnet |
Implementation Reference
- src/services.ts:1843-1993 (handler)The MCP tool registration and handler for 'contract_get_function_args'. It first tries to parse the contract's ABI to find method args; if no ABI, falls back to nearblocks.io API to parse recent execution results and infer args via JSON-to-Zod conversion.
mcp.tool( 'contract_get_function_args', noLeadingWhitespace` Get the arguments of a function call by parsing the contract's ABI or by using the nearblocks.io API (as a fallback). This function API checks recent execution results of the contract's method being queried to determine the likely arguments of the function call. Warning: This tool is experimental and is not garunteed to get the correct arguments.`, { contractId: z.string(), methodName: z.string(), networkId: z.enum(['testnet', 'mainnet']).default('mainnet'), }, async (args, _) => { const connection = await connect({ networkId: args.networkId, nodeUrl: getEndpointsByNetwork(args.networkId)[0]!, }); const contractAccountResult: Result<Account, Error> = await getAccount( args.contractId, connection, ); if (!contractAccountResult.ok) { return { content: [ { type: 'text', text: `Error: ${contractAccountResult.error}` }, ], }; } const contractMethods = await getContractMethods( args.contractId, connection, ); if (!contractMethods.ok) { return { content: [ { type: 'text', text: `Error: ${contractMethods.error}`, }, ], }; } if (contractMethods.value.length === 0) { return { content: [ { type: 'text', text: `No methods found for contract ${args.contractId}`, }, ], }; } if (!contractMethods.value.includes(args.methodName)) { return { content: [ { type: 'text', text: `Method ${args.methodName} not found for contract ${args.contractId}`, }, ], }; } const parsedContractABIResult = await getContractABI( contractAccountResult.value, args.contractId, ); // if the contract ABI is not found, ignore, only return if // the contract ABI is found if (parsedContractABIResult.ok) { const abi = parsedContractABIResult.value; const method = abi.body.functions.find( (method) => method.name === args.methodName, ); if (!method) { return { content: [ { type: 'text', text: `Method ${args.methodName} not found in contract ${args.contractId}`, }, ], }; } return { content: [ { type: 'text', text: stringify_bigint({ ...method, args: method.params?.args || {}, }), }, ], }; } // TODO: This function uses near blocks api which is rate limited // and will fail if we call it too many times. We should // use another method to get the contract methods. const parsedContractMethodsResult: Result<JsonSchema7Type, Error> = await (async () => { try { const parsedMethod = await getParsedContractMethod( args.contractId, args.methodName, ); if (!parsedMethod.ok) { return parsedMethod; } const zodArgsResult = json_to_zod( parsedMethod.value.action.length > 0 ? parsedMethod.value.action[0]?.args.args_json : {}, ); if (!zodArgsResult.ok) { return zodArgsResult; } const jsonSchema = zodToJsonSchema(zodArgsResult.value); return { ok: true, value: jsonSchema, }; } catch (e) { return { ok: false, error: new Error(e as string) }; } })(); if (!parsedContractMethodsResult.ok) { return { content: [ { type: 'text', text: `Error Parsing Contract Methods: ${parsedContractMethodsResult.error}`, }, ], }; } const parsedContractMethods = parsedContractMethodsResult.value; return { content: [ { type: 'text', text: stringify_bigint(parsedContractMethods), }, ], }; }, ); - src/services.ts:1850-1854 (schema)Input schema for contract_get_function_args: contractId (string), methodName (string), networkId (enum testnet/mainnet, default mainnet).
{ contractId: z.string(), methodName: z.string(), networkId: z.enum(['testnet', 'mainnet']).default('mainnet'), }, - src/services.ts:1843-1993 (registration)The tool is registered via mcp.tool() call in the createMcpServer function (line 411). The tool name is 'contract_get_function_args'.
mcp.tool( 'contract_get_function_args', noLeadingWhitespace` Get the arguments of a function call by parsing the contract's ABI or by using the nearblocks.io API (as a fallback). This function API checks recent execution results of the contract's method being queried to determine the likely arguments of the function call. Warning: This tool is experimental and is not garunteed to get the correct arguments.`, { contractId: z.string(), methodName: z.string(), networkId: z.enum(['testnet', 'mainnet']).default('mainnet'), }, async (args, _) => { const connection = await connect({ networkId: args.networkId, nodeUrl: getEndpointsByNetwork(args.networkId)[0]!, }); const contractAccountResult: Result<Account, Error> = await getAccount( args.contractId, connection, ); if (!contractAccountResult.ok) { return { content: [ { type: 'text', text: `Error: ${contractAccountResult.error}` }, ], }; } const contractMethods = await getContractMethods( args.contractId, connection, ); if (!contractMethods.ok) { return { content: [ { type: 'text', text: `Error: ${contractMethods.error}`, }, ], }; } if (contractMethods.value.length === 0) { return { content: [ { type: 'text', text: `No methods found for contract ${args.contractId}`, }, ], }; } if (!contractMethods.value.includes(args.methodName)) { return { content: [ { type: 'text', text: `Method ${args.methodName} not found for contract ${args.contractId}`, }, ], }; } const parsedContractABIResult = await getContractABI( contractAccountResult.value, args.contractId, ); // if the contract ABI is not found, ignore, only return if // the contract ABI is found if (parsedContractABIResult.ok) { const abi = parsedContractABIResult.value; const method = abi.body.functions.find( (method) => method.name === args.methodName, ); if (!method) { return { content: [ { type: 'text', text: `Method ${args.methodName} not found in contract ${args.contractId}`, }, ], }; } return { content: [ { type: 'text', text: stringify_bigint({ ...method, args: method.params?.args || {}, }), }, ], }; } // TODO: This function uses near blocks api which is rate limited // and will fail if we call it too many times. We should // use another method to get the contract methods. const parsedContractMethodsResult: Result<JsonSchema7Type, Error> = await (async () => { try { const parsedMethod = await getParsedContractMethod( args.contractId, args.methodName, ); if (!parsedMethod.ok) { return parsedMethod; } const zodArgsResult = json_to_zod( parsedMethod.value.action.length > 0 ? parsedMethod.value.action[0]?.args.args_json : {}, ); if (!zodArgsResult.ok) { return zodArgsResult; } const jsonSchema = zodToJsonSchema(zodArgsResult.value); return { ok: true, value: jsonSchema, }; } catch (e) { return { ok: false, error: new Error(e as string) }; } })(); if (!parsedContractMethodsResult.ok) { return { content: [ { type: 'text', text: `Error Parsing Contract Methods: ${parsedContractMethodsResult.error}`, }, ], }; } const parsedContractMethods = parsedContractMethodsResult.value; return { content: [ { type: 'text', text: stringify_bigint(parsedContractMethods), }, ], }; }, ); - src/utils.ts:396-429 (helper)Helper that fetches recent transaction actions for a contract method from nearblocks.io API, used as fallback when contract ABI is not available.
export const getParsedContractMethod = async ( contractId: string, methodName: string, ): Promise<Result<ParsedMethod, Error>> => { try { const url = `https://api.nearblocks.io/v1/account/${contractId}/contract/${methodName}`; const response = await fetch(url); const responseJson = (await response.json()) as unknown; const parsedMethod = ParsedMethodSchema.safeParse(responseJson); if (!parsedMethod.success) { return { ok: false, error: new Error( `Error parsing args for contract ${contractId}, method ${methodName}. Got: ${JSON.stringify( responseJson, null, 2, )}`, ), }; } return { ok: true, value: parsedMethod.data, }; } catch (error) { return { ok: false, error: new Error( `Error parsing contract method ${methodName} for contract ${contractId}: ${String(error)}`, ), }; } }; - src/utils.ts:450-575 (helper)Helper that converts a JSON object into a Zod schema, used to infer argument types from the fallback API response.
export const json_to_zod = ( json: unknown, options: JsonToZodConfig = {}, ): Result<z.ZodType<unknown>, Error> => { const { convertTuples = false, zodValueOverrides = {} } = options; const seen = new WeakSet(); function parse(value: unknown, schemaName = 'schema'): z.ZodType<unknown> { // Handle null and undefined if (value === null) return z.null(); if (value === undefined) return z.undefined(); // Handle primitive types switch (typeof value) { case 'string': return z.string(); case 'number': return Number.isInteger(value) ? z.number().int() : z.number(); case 'bigint': return z.bigint(); case 'boolean': return z.boolean(); case 'function': throw new Error('Functions are not supported'); case 'symbol': return z.unknown(); case 'object': { // Prevent circular references if (seen.has(value as object)) { throw new Error('Circular objects are not supported'); } seen.add(value as object); // Handle arrays if (Array.isArray(value)) { // Empty array if (value.length === 0) { throw new Error('Cannot infer schema for empty array'); } // Handle as tuple if requested if (convertTuples) { if (value.length === 0) { throw new Error('Cannot create a tuple from an empty array'); } return z.tuple( value.map((item) => parse(item, schemaName)) as [ z.ZodTypeAny, ...z.ZodTypeAny[], ], ); } // Process array items and find unique schema types const itemSchemas = value.map((item) => parse(item, schemaName)); // Extract unique schemas based on their structure // This is a simplified approach - a more robust solution would require // deeper comparison of schema structures const uniqueSchemas: z.ZodTypeAny[] = []; const schemaTypes = new Set<string>(); for (const schema of itemSchemas) { // Use a simple type identification method let typeId = 'unknown'; if (schema instanceof z.ZodString) typeId = 'string'; else if (schema instanceof z.ZodNumber) typeId = 'number'; else if (schema instanceof z.ZodBoolean) typeId = 'boolean'; else if (schema instanceof z.ZodNull) typeId = 'null'; else if (schema instanceof z.ZodObject) typeId = 'object'; // Add more type checks as needed if (!schemaTypes.has(typeId)) { schemaTypes.add(typeId); uniqueSchemas.push(schema); } } // Create appropriate array schema based on unique item types if (uniqueSchemas.length === 1) { const schema = uniqueSchemas[0]; if (!schema) { throw new Error('Schema is undefined'); } return z.array(schema); } else if (uniqueSchemas.length > 1) { return z.array( z.union( uniqueSchemas as [ z.ZodTypeAny, z.ZodTypeAny, ...z.ZodTypeAny[], ], ), ); } else { return z.array(z.unknown()); } } // Handle objects const schemaObj: Record<string, z.ZodTypeAny> = {}; for (const [key, val] of Object.entries(value)) { const overrideKey = key.toLowerCase(); // Check for overrides if (zodValueOverrides?.[schemaName]?.[overrideKey]) { schemaObj[key] = zodValueOverrides[schemaName][overrideKey]; } else { schemaObj[key] = parse(val, schemaName); } } return z.object(schemaObj); } default: return z.unknown(); } } try { return { ok: true, value: parse(json) }; } catch (error) { return { ok: false, error: error as Error }; } };