Skip to main content
Glama

get_sip009_token_info

Retrieve comprehensive SIP-009 NFT details including ownership, metadata URI, and content for specific token IDs on the Stacks blockchain network.

Instructions

Get complete information about a specific SIP-009 NFT including owner, metadata URI, and actual metadata content.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
contractAddressYesThe contract address
contractNameYesThe contract name of the SIP-009 NFT collection
networkYesThe Stacks network
tokenIdYesThe NFT token ID

Implementation Reference

  • The main execution handler for the 'get_sip009_token_info' tool. Fetches NFT owner and token URI using parallel Hiro API read-only contract calls, parses results, fetches and displays metadata, and returns formatted information.
    export const getSIP009TokenInfoTool: Tool<undefined, typeof SIP009TokenInfoScheme> = {
      name: "get_sip009_token_info",
      description: "Get complete information about a specific SIP-009 NFT including owner, metadata URI, and actual metadata content.",
      parameters: SIP009TokenInfoScheme,
      execute: async (args, context) => {
        try {
          await recordTelemetry({ action: "get_sip009_token_info" }, context);
          
          // Get NFT information in parallel
          const [ownerResult, uriResult] = await Promise.all([
            callReadOnlyFunction(
              args.contractAddress,
              args.contractName,
              "get-owner",
              [`0x${args.tokenId.toString(16).padStart(32, '0')}`],
              args.network
            ),
            callReadOnlyFunction(
              args.contractAddress,
              args.contractName,
              "get-token-uri",
              [`0x${args.tokenId.toString(16).padStart(32, '0')}`],
              args.network
            ),
          ]);
          
          // Parse results
          const owner = ownerResult.okay && ownerResult.result !== 'none' 
            ? ownerResult.result.replace(/[()]/g, '').replace('some ', '') 
            : 'No owner (token may not exist)';
          
          const metadataUri = uriResult.okay && uriResult.result !== 'none' 
            ? uriResult.result.replace(/[()]/g, '').replace('some ', '').replace(/"/g, '')
            : null;
          
          let metadata = null;
          if (metadataUri) {
            metadata = await fetchMetadata(metadataUri);
          }
          
          return `# SIP-009 NFT Information
    
    **Contract**: ${args.contractAddress}.${args.contractName}
    **Token ID**: ${args.tokenId}
    **Network**: ${args.network}
    
    ## Ownership
    - **Current Owner**: ${owner}
    
    ## Metadata
    - **URI**: ${metadataUri || 'No metadata URI'}
    
    ${metadata ? `
    ## Metadata Content
    - **Name**: ${metadata.name || 'N/A'}
    - **Description**: ${metadata.description || 'N/A'}
    - **Image**: ${metadata.image || 'N/A'}
    - **External URL**: ${metadata.external_url || 'N/A'}
    
    ${metadata.attributes ? `
    ### Attributes
    ${metadata.attributes.map((attr: any) => `- **${attr.trait_type}**: ${attr.value}`).join('\n')}
    ` : ''}
    ` : metadata === null && metadataUri ? `
    ⚠️ **Metadata fetch failed** - URI may be inaccessible
    ` : ''}
    
    ## Contract Verification
    ✅ This contract implements the SIP-009 NFT standard trait.
    
    ## Transfer Notes
    - Post-conditions are REQUIRED for all transfers
    - Only the current owner can initiate transfers
    - Use native Stacks post-conditions for security`;
          
        } catch (error) {
          return `❌ Failed to get SIP-009 NFT info: ${error}`;
        }
      },
    };
  • Input schema (Zod) defining required parameters: contractAddress, contractName, tokenId, and network (mainnet/testnet/devnet).
    const SIP009TokenInfoScheme = z.object({
      contractAddress: z.string().describe("The contract address"),
      contractName: z.string().describe("The contract name of the SIP-009 NFT collection"),
      tokenId: z.number().describe("The NFT token ID"),
      network: NetworkScheme.describe("The Stacks network"),
    });
  • Registers the getSIP009TokenInfoTool with the FastMCP server instance.
    server.addTool(getSIP009TokenInfoTool);
  • Helper function to perform read-only contract function calls via Hiro API (stacks-blockchain explorer). Used to fetch 'get-owner' and 'get-token-uri'.
    async function callReadOnlyFunction(
      contractAddress: string,
      contractName: string,
      functionName: string,
      functionArgs: any[],
      network: string
    ): Promise<any> {
      const apiUrl = network === "mainnet" 
        ? "https://api.hiro.so" 
        : "https://api.testnet.hiro.so";
      
      try {
        const response = await fetch(
          `${apiUrl}/v2/contracts/call-read/${contractAddress}/${contractName}/${functionName}`,
          {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({
              sender: contractAddress,
              arguments: functionArgs,
            }),
          }
        );
        
        if (!response.ok) {
          const data: any = await response.json();
          throw new Error(data.error || `HTTP ${response.status}`);
        }
        
        return await response.json();
      } catch (error) {
        throw new Error(`Failed to call ${functionName}: ${error}`);
      }
    }
  • Helper function to fetch and parse NFT metadata JSON from the token URI, with IPFS support and 5-second timeout.
    async function fetchMetadata(uri: string): Promise<any> {
      try {
        // Handle IPFS URIs
        if (uri.startsWith('ipfs://')) {
          uri = uri.replace('ipfs://', 'https://ipfs.io/ipfs/');
        }
        
        const controller = new AbortController();
        const timeoutId = setTimeout(() => controller.abort(), 5000);
        
        const response = await fetch(uri, { signal: controller.signal });
        clearTimeout(timeoutId);
        
        if (!response.ok) {
          throw new Error(`HTTP ${response.status}`);
        }
        
        return await response.json();
      } catch (error) {
        return null;
      }
    }

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/exponentlabshq/stacks-clarity-mcp'

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