get_nft_info
Retrieve ERC721 NFT details including metadata URI by providing contract address and token ID on EVM-compatible networks.
Instructions
Get information about an ERC721 NFT including metadata URI
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| contractAddress | Yes | The NFT contract address | |
| tokenId | Yes | The NFT token ID | |
| network | No | Network name or chain ID. Defaults to Ethereum mainnet. |
Implementation Reference
- src/core/tools.ts:1401-1478 (registration)Registration of the 'get_nft_info' tool on the MCP server, including input schema and inline handler function.'get_nft_info', 'Get detailed information about a specific NFT (ERC721 token), including collection name, symbol, token URI, and current owner if available.', { tokenAddress: z .string() .describe( "The contract address of the NFT collection (e.g., '0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D' for Bored Ape Yacht Club)" ), tokenId: z .string() .describe("The ID of the specific NFT token to query (e.g., '1234')"), network: z .string() .optional() .describe( "Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', 'polygon') or chain ID. Most NFTs are on Ethereum mainnet, which is the default." ) }, async ({ tokenAddress, tokenId, network = 'ethereum' }) => { try { const nftInfo = await services.getERC721TokenMetadata( tokenAddress as Address, BigInt(tokenId), network ); // Check ownership separately let owner = null; try { // This may fail if tokenId doesn't exist owner = await services.getPublicClient(network).readContract({ address: tokenAddress as Address, abi: [ { inputs: [{ type: 'uint256' }], name: 'ownerOf', outputs: [{ type: 'address' }], stateMutability: 'view', type: 'function' } ], functionName: 'ownerOf', args: [BigInt(tokenId)] }); } catch (e) { // Ownership info not available } return { content: [ { type: 'text', text: JSON.stringify( { contract: tokenAddress, tokenId, network, ...nftInfo, owner: owner || 'Unknown' }, null, 2 ) } ] }; } catch (error) { return { content: [ { type: 'text', text: `Error fetching NFT info: ${error instanceof Error ? error.message : String(error)}` } ], isError: true }; } }
- src/core/tools.ts:1401-1478 (handler)Handler function that executes the tool logic, calling getERC721TokenMetadata and ownerOf to fetch NFT details.'get_nft_info', 'Get detailed information about a specific NFT (ERC721 token), including collection name, symbol, token URI, and current owner if available.', { tokenAddress: z .string() .describe( "The contract address of the NFT collection (e.g., '0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D' for Bored Ape Yacht Club)" ), tokenId: z .string() .describe("The ID of the specific NFT token to query (e.g., '1234')"), network: z .string() .optional() .describe( "Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', 'polygon') or chain ID. Most NFTs are on Ethereum mainnet, which is the default." ) }, async ({ tokenAddress, tokenId, network = 'ethereum' }) => { try { const nftInfo = await services.getERC721TokenMetadata( tokenAddress as Address, BigInt(tokenId), network ); // Check ownership separately let owner = null; try { // This may fail if tokenId doesn't exist owner = await services.getPublicClient(network).readContract({ address: tokenAddress as Address, abi: [ { inputs: [{ type: 'uint256' }], name: 'ownerOf', outputs: [{ type: 'address' }], stateMutability: 'view', type: 'function' } ], functionName: 'ownerOf', args: [BigInt(tokenId)] }); } catch (e) { // Ownership info not available } return { content: [ { type: 'text', text: JSON.stringify( { contract: tokenAddress, tokenId, network, ...nftInfo, owner: owner || 'Unknown' }, null, 2 ) } ] }; } catch (error) { return { content: [ { type: 'text', text: `Error fetching NFT info: ${error instanceof Error ? error.message : String(error)}` } ], isError: true }; } }
- src/core/tools.ts:1404-1418 (schema)Zod input schema for validating tool parameters: tokenAddress, tokenId, network.tokenAddress: z .string() .describe( "The contract address of the NFT collection (e.g., '0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D' for Bored Ape Yacht Club)" ), tokenId: z .string() .describe("The ID of the specific NFT token to query (e.g., '1234')"), network: z .string() .optional() .describe( "Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', 'polygon') or chain ID. Most NFTs are on Ethereum mainnet, which is the default." ) },
- src/core/services/tokens.ts:118-146 (helper)Core helper function that reads name, symbol, and tokenURI from ERC721 contract using viem's getContract.export async function getERC721TokenMetadata( tokenAddress: Address, tokenId: bigint, network: string = 'ethereum' ): Promise<{ name: string; symbol: string; tokenURI: string; }> { const publicClient = getPublicClient(network); const contract = getContract({ address: tokenAddress, abi: erc721Abi, client: publicClient, }); const [name, symbol, tokenURI] = await Promise.all([ contract.read.name(), contract.read.symbol(), contract.read.tokenURI([tokenId]) ]); return { name, symbol, tokenURI }; }
- src/core/services/tokens.ts:43-64 (helper)ERC721 ABI used for reading NFT metadata (name, symbol, tokenURI).const erc721Abi = [ { inputs: [], name: 'name', outputs: [{ type: 'string' }], stateMutability: 'view', type: 'function' }, { inputs: [], name: 'symbol', outputs: [{ type: 'string' }], stateMutability: 'view', type: 'function' }, { inputs: [{ type: 'uint256', name: 'tokenId' }], name: 'tokenURI', outputs: [{ type: 'string' }], stateMutability: 'view', type: 'function' }