Skip to main content
Glama

show_ordinals

Retrieve Bitcoin Ordinal inscription data and save images locally for analysis. Use this tool to fetch technical metadata and prepare files for OCR processing.

Instructions

šŸ”¹ Bitcoin Ordinals Expert Assistant

šŸš€ MANDATORY TWO-RESPONSE PROCESS FOR IMAGES

I strictly follow a two-step structured workflow for processing Bitcoin Ordinal inscriptions. STRICT ADHERENCE IS REQUIRED.


šŸ“Œ Required Tools & Workflow

šŸ” Tools Used:

1ļøāƒ£ show_ordinals (MANDATORY FIRST STEP)

  • Fetches inscription data.

  • If the inscription is an image, it MUST:

    • Save it locally and return the local_file_path.

    • (Code currently does NOT return a separate displayable source, only the local path)

2ļøāƒ£ image_processor / vision (OCR & Visual Analysis ONLY)

  • MUST be used for extracting textual/visual data from the image.

  • Takes local_file_path as input.


šŸš€ MANDATORY TWO-STEP RESPONSE WORKFLOW (FOR IMAGES)

šŸ“Œ RESPONSE 1: Display Attempt + Technical Details (NON-TERMINAL)

āœ” Execute show_ordinals FIRST.
āœ” Return an <img> tag using the local_file_path provided by show_ordinals as the src attribute. āœ” Follow with detailed technical metadata.
āœ” STOP HERE. NO ANALYSIS YET.

āœ… RESPONSE FORMAT (STRICTLY ENFORCED):

<!-- Attempting display using local path. Note: May not render in standard browsers due to security restrictions. -->
<img src="[local_file_path from show_ordinals]" alt="Ordinal Image Inscription" style="max-width: 100%; height: auto;" />

### šŸ“Š Technical Details  
* **MIME Type:** [MIME type]
* **Size:** [Size]
* **Transaction ID:** [Tx ID]
* **Status:** [Status]
* **Inscription Input:** [Input]
* **Local Path (for analysis):** `[local_file_path from show_ordinals]`
* [Other available details]

šŸ”¹ End FIRST response here. No OCR, No AI-generated descriptions. This response is intended to be NON-TERMINAL to allow for Step 2.


šŸ“Œ RESPONSE 2: OCR & Content Analysis (Using Designated Tools ONLY)

āœ” AFTER Response 1, consult available tools. āœ” Execute image_processor OR vision using the local_file_path from Response 1. āœ” Strictly use ONLY tool-generated analysis.
āœ” If BOTH tools fail, state failure explicitly.
āœ” Return a new markdown response with the extracted data.

āœ… RESPONSE FORMAT (STRICTLY ENFORCED):

### šŸ“ Detailed Image Analysis (Result from '[Executed Tool Name]')

[Extracted OCR text, visual description, or failure message from the tool ONLY]

⚠ No hallucinations. No AI-generated assumptions.


šŸ“Œ FALLBACK MECHANISM (IF OCR TOOLS FAIL)

  • If BOTH image_processor AND vision fail, use a verified fallback OCR tool (if available).

  • If no fallback exists, explicitly state failure instead of hallucinating content.


šŸŽÆ Summary: Strict 2-Response Workflow

āœ… Step 1: Execute show_ordinals → Attempt Image Display (using local path) + Metadata. (Response intended as Non-Terminal) āœ… Step 2: Execute image_processor / vision → Analysis Description. (Separate Response)


šŸ’” This ensures clarity, prevents AI hallucinations, and provides structured responses. šŸš€

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
txidYesThe unique 64-character hexadecimal identifier of the Bitcoin transaction to inspect for Ordinals. Example: 'f1d2d3...a8b9c0'.
inscription_indexNoOptional. The specific index (starting from 0) of the inscription to retrieve within the transaction, if there are multiple.
content_type_filterNoOptional. Filter inscriptions by content type, e.g., 'image/png', 'text/plain', 'application/json'.

Implementation Reference

  • Main handler function for the 'show_ordinals' tool. Validates input, fetches and decodes Ordinal inscription from transaction witness data using OrdinalsClient, handles image saving and HTML display, formats text/JSON content.
    protected async handleDecodeWitness(args: unknown) {
      const result = DecodeWitnessSchema.safeParse(args);
      if (!result.success) {
        throw new McpError(
          ErrorCode.InvalidParams,
          `Invalid parameters: ${result.error.message}`
        );
      }
    
      const txid = result.data.txid;
      logger.info(`Processing Ordinals for transaction: ${txid}`);
      
      try {
        // Call decodeWitness method with file option to store images in ~/.cache
        const decodedWitness = await this.client.decodeWitness(txid, "file");
        
        // Check if an image is present in the results
        // Image paths now never have file:// prefix
        const images = decodedWitness.filter(content => 
          content.startsWith('data:image/') || 
          (content.length > 0 && !content.startsWith('{') && !content.startsWith('['))
        );
        
        // If we have an image, process it for display
        if (images.length > 0) {
          const imageData = images[0];
          const txInfo = await this.client.getTransaction(txid, false);
          
          // Mark this txid as successfully decoded BEFORE processing the image
          // to avoid timing issues with get_transaction call
          this.markSuccessfulDecoding(txid);
          logger.info(`Transaction ${txid} marked as successfully decoded before image processing`);
          
          // Use processAndSaveImage method for display
          return await this.processAndSaveImage(imageData, txid, txInfo);
        }
        
        // For other content types (JSON, text, etc.)
        const formattedContent = decodedWitness.map(content => {
          if (content.startsWith('{') || content.startsWith('[')) {
            try {
              const jsonData = JSON.parse(content);
              return `\`\`\`json\n${JSON.stringify(jsonData, null, 2)}\n\`\`\``;
            } catch (e) {
              return content;
            }
          }
          return content;
        }).join('\n\n');
        
        return {
          content: [
            {
              type: "text",
              text: formattedContent
            }
          ] as TextContent[],
        };
      } catch (error) {
        // In case of error during the decoding process
        logger.error({ error }, `Error decoding Ordinals for ${txid}`);
        
        return {
          content: [
            {
              type: "text",
              text: `Error decoding Ordinals for transaction ${txid}:\n` +
                    `${error instanceof Error ? error.message : String(error)}\n\n` +
                    "This may be due to a blockchain API access issue, an unsupported transaction format, " +
                    "or the transaction may not exist."
            }
          ] as TextContent[],
        };
      }
    }
  • Zod schema defining input validation for the 'show_ordinals' tool: requires txid (64-char hex), optional formatImageOption ('base64' or 'file'). Matches the inline tool inputSchema.
    export const DecodeWitnessSchema = z.object({
      txid: z.string().length(64, "Invalid transaction ID"),
      formatImageOption: z.enum(["base64", "file"]).optional().default("base64"),
    });
  • Tool registration in ListToolsRequestSchema handler. Defines 'show_ordinals' name, detailed usage instructions for Ordinals analysis workflow, and input schema.
        {
          name: "show_ordinals",
          description: "## šŸ”¹ Bitcoin Ordinals Expert Assistant  \n\n### **šŸš€ MANDATORY TWO-RESPONSE PROCESS FOR IMAGES**  \nI strictly follow a **two-step structured workflow** for processing Bitcoin Ordinal inscriptions. **STRICT ADHERENCE IS REQUIRED.**  \n\n---\n\n## šŸ“Œ **Required Tools & Workflow**  \n\n### **šŸ” Tools Used:**  \n1ļøāƒ£ **`show_ordinals` (MANDATORY FIRST STEP)**  \n   - Fetches **inscription data**.  \n   - If the inscription is an **image**, it **MUST**:  \n     - **Save it locally** and return the **`local_file_path`**.\n     - *(Code currently does NOT return a separate displayable source, only the local path)*\n\n2ļøāƒ£ **`image_processor` / `vision` (OCR & Visual Analysis ONLY)**  \n   - **MUST be used** for extracting textual/visual data from the image.\n   - Takes **`local_file_path`** as input.\n\n---\n\n## **šŸš€ MANDATORY TWO-STEP RESPONSE WORKFLOW (FOR IMAGES)**  \n\n### **šŸ“Œ RESPONSE 1: Display Attempt + Technical Details (NON-TERMINAL)**  \nāœ” **Execute `show_ordinals` FIRST.**  \nāœ” **Return an `<img>` tag** using the **`local_file_path` provided by `show_ordinals`** as the `src` attribute.\nāœ” **Follow with detailed technical metadata.**  \nāœ” **STOP HERE. NO ANALYSIS YET.**  \n\n**āœ… RESPONSE FORMAT (STRICTLY ENFORCED):**  \n```html\n<!-- Attempting display using local path. Note: May not render in standard browsers due to security restrictions. -->\n<img src=\"[local_file_path from show_ordinals]\" alt=\"Ordinal Image Inscription\" style=\"max-width: 100%; height: auto;\" />\n\n### šŸ“Š Technical Details  \n* **MIME Type:** [MIME type]\n* **Size:** [Size]\n* **Transaction ID:** [Tx ID]\n* **Status:** [Status]\n* **Inscription Input:** [Input]\n* **Local Path (for analysis):** `[local_file_path from show_ordinals]`\n* [Other available details]\n```  \nšŸ”¹ **End FIRST response here.** **No OCR, No AI-generated descriptions. This response is intended to be NON-TERMINAL to allow for Step 2.**  \n\n---\n\n### **šŸ“Œ RESPONSE 2: OCR & Content Analysis (Using Designated Tools ONLY)**  \nāœ” **AFTER Response 1, consult available tools.**\nāœ” **Execute `image_processor` OR `vision` using the `local_file_path` from Response 1.**\nāœ” **Strictly use ONLY tool-generated analysis.**  \nāœ” **If BOTH tools fail, state failure explicitly.**  \nāœ” **Return a new markdown response with the extracted data.**  \n\n**āœ… RESPONSE FORMAT (STRICTLY ENFORCED):**  \n```markdown\n### šŸ“ Detailed Image Analysis (Result from '[Executed Tool Name]')\n\n[Extracted OCR text, visual description, or failure message from the tool ONLY]\n```\n⚠ **No hallucinations. No AI-generated assumptions.**  \n\n---\n\n### **šŸ“Œ FALLBACK MECHANISM (IF OCR TOOLS FAIL)**  \n- If **BOTH** `image_processor` AND `vision` **fail**, use a **verified fallback OCR tool** (if available).\n- If **no fallback exists**, explicitly **state failure** instead of hallucinating content.\n\n---\n\n### **šŸŽÆ Summary: Strict 2-Response Workflow**  \nāœ… **Step 1:** **Execute `show_ordinals`** → Attempt Image Display (using local path) + Metadata. (Response intended as Non-Terminal)\nāœ… **Step 2:** **Execute `image_processor` / `vision`** → Analysis Description. (Separate Response)\n\n---\n\nšŸ’” **This ensures clarity, prevents AI hallucinations, and provides structured responses.** šŸš€",
          inputSchema: {
            type: "object",
            properties: {
              txid: {
                type: "string",
                description: "The unique 64-character hexadecimal identifier of the Bitcoin transaction to inspect for Ordinals. Example: 'f1d2d3...a8b9c0'."
              },
              inscription_index: {
                type: "integer",
                description: "Optional. The specific index (starting from 0) of the inscription to retrieve within the transaction, if there are multiple."
              },
              content_type_filter: {
                type: "string",
                description: "Optional. Filter inscriptions by content type, e.g., 'image/png', 'text/plain', 'application/json'."
              }
            },
            required: ["txid"]
          }
        } as Tool,
      ],
    }));
  • Supporting function called by handler for image inscriptions. Saves image to local temp dir, computes metadata, returns formatted response with <img src="local_path" /> and instructions.
    protected async processAndSaveImage(
      imageData: string,
      txid: string,
      txInfo: TransactionWithOrdinal
    ): Promise<{ content: TextContent[], endProcessing?: boolean }> {
      try {
        let filePath: string;
        let mimeType: string;
        let buffer: Buffer;
    
        // Get the client's configured image directory (always ~/.cache/mcp-inscription)
        const imageTempDir = this.client.getConfig().imageTempDir;
        logger.debug(`Using image directory from client config: ${imageTempDir}`);
    
        // Process file path
        if (imageData.startsWith('file://')) {
          // Convert file:// URI to OS-compatible path
          const filePathRaw = imageData.substring(7);
          
          // Normalize path based on operating system
          filePath = path.normalize(decodeURIComponent(filePathRaw));
          
          // On Windows, if path starts with /, add drive letter
          if (process.platform === 'win32' && filePath.startsWith('/')) {
            filePath = filePath.substring(1);
          }
          
          if (!fs.existsSync(filePath)) {
            logger.warn(`File not found: ${filePath}, attempting to retrieve raw data`);
            // Fallback - try to use inscription data directly from txInfo
            if (txInfo.ordinal?.content && txInfo.ordinal.content.type.startsWith('image/')) {
              const contentBuffer = Buffer.from(txInfo.ordinal.content.data, 'hex');
              const extension = this.getMimeExtension(txInfo.ordinal.content.type);
              const filename = `${txid}${extension}`;
              
              // Create directory if it doesn't exist
              if (!fs.existsSync(imageTempDir)) {
                fs.mkdirSync(imageTempDir, { recursive: true });
              }
              
              filePath = path.join(imageTempDir, filename);
              
              // Write the file
              fs.writeFileSync(filePath, contentBuffer);
              buffer = contentBuffer;
              mimeType = txInfo.ordinal.content.type;
            } else {
              throw new Error(`File not found and unable to retrieve image data: ${filePath}`);
            }
          } else {
            buffer = fs.readFileSync(filePath);
            mimeType = this.getMimeTypeFromPath(filePath);
          }
        } 
        // Process data URI
        else if (imageData.startsWith('data:image/')) {
          const mimeMatch = imageData.match(/^data:([^;]+);base64,(.*)$/);
          if (!mimeMatch) {
            throw new Error("Invalid Data URI format");
          }
          
          mimeType = mimeMatch[1];
          const base64Data = mimeMatch[2];
          const extension = this.getMimeExtension(mimeType);
          const filename = `${txid}${extension}`;
          
          // Create directory if it doesn't exist
          if (!fs.existsSync(imageTempDir)) {
            fs.mkdirSync(imageTempDir, { recursive: true });
          }
          
          filePath = path.join(imageTempDir, filename);
          buffer = Buffer.from(base64Data, 'base64');
          
          // Write the file
          fs.writeFileSync(filePath, buffer);
        } 
        // Fallback - try to use inscription data directly
        else if (txInfo.ordinal?.content && txInfo.ordinal.content.type.startsWith('image/')) {
          const contentBuffer = Buffer.from(txInfo.ordinal.content.data, 'hex');
          mimeType = txInfo.ordinal.content.type;
          const extension = this.getMimeExtension(mimeType);
          const filename = `${txid}${extension}`;
          
          // Create directory if it doesn't exist
          if (!fs.existsSync(imageTempDir)) {
            fs.mkdirSync(imageTempDir, { recursive: true });
          }
          
          filePath = path.join(imageTempDir, filename);
          
          // Write the file
          fs.writeFileSync(filePath, contentBuffer);
          buffer = contentBuffer;
        } else {
          throw new Error("Unsupported image data format");
        }
    
        // Path to display in metadata - use filePath directly without file:// prefix
        let displayPath: string = filePath;
        
        // CRITICAL: Ensure the path used in HTML is the absolute path with correct slashes
        // and NEVER add file:// prefix as it causes issues with image display
        let imgSrc = filePath.replace(/\\/g, '/');
        // Explicitly remove any file:// prefix if present
        imgSrc = imgSrc.replace(/^file:\/\/+/, '');
    
        // Return formatted response with specific format to identify non-empty result
        // and include detailed inscription information
        const fileSizeKB = Math.round(buffer.length / 1024);
        const inputInfo = txInfo.inputs
          .filter(input => input.witness && input.witness.length > 0)
          .map(input => input.txid ? `${input.txid}:${input.vout}` : 'coinbase')[0] || "Unknown";
    
        // Log the paths to help with debugging
        logger.debug({
          filePath,
          displayPath,
          imgSrc,
          mimeType,
          size: fileSizeKB,
        }, "Image processing complete");
    
        return {
          content: [
            {
              type: "text",
              text: `DECODE_WITNESS_RESULT={ "status": "SUCCESS", "txid": "${txid}", "content_type": "image", "size": "${fileSizeKB} KB", "mime_type": "${mimeType}" }\n\n` +
                    `### Ordinal Inscription Details ###\n` +
                    `• Type: Ordinal Inscription (${mimeType})\n` +
                    `• Size: ${fileSizeKB} KB\n` +
                    `• Transaction ID: ${txid}\n` +
                    `• Status: ${txInfo.status.confirmed ? `Confirmed in block #${txInfo.status.blockHeight}` : "Unconfirmed"}\n` +
                    `• Transaction Size: ${txInfo.size} bytes\n` +
                    `• Fee: ${txInfo.fee} sats\n` +
                    `• Input containing inscription: ${inputInfo}\n` +
                    `• Image Path: ${displayPath}\n\n` +
                    `<img src="${imgSrc}" />\n\n` +
                    `Please describe this Ordinal inscription image in detail. What does it show? What are its main visual elements? Include the image again in your response using:\n\n\`\`\`html\n<img src="${imgSrc}" />\n\`\`\``,
              isComplete: true,
              isTerminal: true
            }
          ] as TextContent[],
          endProcessing: true
        };
      } catch (error) {
        logger.error({ error, txid }, "Error processing image");
        
        // In case of error, try to return a data URI if available
        if (imageData.startsWith('data:image/')) {
          const mimeMatch = imageData.match(/^data:([^;]+);base64,/);
          if (mimeMatch) {
            const mimeType = mimeMatch[1];
            const fileSizeEstimateKB = Math.round((imageData.length - imageData.indexOf(',') - 1) * 3 / 4 / 1024);
            const inputInfo = txInfo.inputs
              .filter(input => input.witness && input.witness.length > 0)
              .map(input => input.txid ? `${input.txid}:${input.vout}` : 'coinbase')[0] || "Unknown";
    
            logger.debug("Using data URI directly in error handler");
    
            return {
              content: [
                {
                  type: "text",
                  text: `DECODE_WITNESS_RESULT={ "status": "SUCCESS", "txid": "${txid}", "content_type": "image", "size": "${fileSizeEstimateKB} KB", "mime_type": "${mimeType}" }\n\n` +
                        `### Ordinal Inscription Details ###\n` +
                        `• Type: Ordinal Inscription (${mimeType})\n` +
                        `• Size: ~${fileSizeEstimateKB} KB\n` +
                        `• Transaction ID: ${txid}\n` +
                        `• Status: ${txInfo.status.confirmed ? `Confirmed in block #${txInfo.status.blockHeight}` : "Unconfirmed"}\n` +
                        `• Transaction Size: ${txInfo.size} bytes\n` +
                        `• Fee: ${txInfo.fee} sats\n` +
                        `• Input containing inscription: ${inputInfo}\n` +
                        `• Format: Data URI\n\n` +
                        `<img src="${imageData}" />\n\n` +
                        `Please describe this Ordinal inscription image in detail. What does it show? What are its main visual elements? Include the image again in your response using:\n\n\`\`\`html\n<img src="${imageData}" />\n\`\`\``,
                  isComplete: true,
                  isTerminal: true
                }
              ] as TextContent[],
              endProcessing: true
            };
          }
        }
        
        // If all fails, throw the error to be handled by the caller
        throw error;
      }
    }
  • Core utility in OrdinalsClient that performs witness decoding for inscriptions. Called by handler with 'file' option. Extracts content, saves images to cache dir, returns formatted strings (paths or data URIs).
    async decodeWitness(txid: string, formatImageOption: FormatImageOption = "base64"): Promise<string[]> {
      try {
        logger.debug(`Decoding witness (Ordinals) for transaction: ${txid}, formatImageOption=${formatImageOption}`);
        
        // Check the cache first
        const cached = this.transactionCache.get(txid);
        
        let extractedOrdinals: ExtractedOrdinal[] = [];
        
        if (cached) {
          logger.debug(`Using cached ordinals data for tx ${txid}`);
          extractedOrdinals = cached.extractedOrdinals;
        } else {
          // If not in cache, we need to retrieve the transaction first
          // getTransaction will extract all inscriptions and put them in cache
          await this.getTransaction(txid, false);
          
          // Now we can get the data from the cache
          const newCached = this.transactionCache.get(txid);
          if (newCached) {
            extractedOrdinals = newCached.extractedOrdinals;
          } else {
            logger.warn(`Transaction ${txid} not found in cache after getTransaction call`);
          }
        }
        
        // Format and return the found inscriptions
        const decodedData: string[] = [];
        
        if (extractedOrdinals.length === 0) {
          logger.info(`No Ordinal inscriptions found in transaction ${txid}`);
          return decodedData;
        }
        
        // Convert each extracted inscription to the requested format
        extractedOrdinals.forEach((ordinal, index) => {
          const formattedContent = this.formatInscriptionContent(
            ordinal.contentType,
            ordinal.content,
            formatImageOption,
            txid,
            index
          );
          
          if (formattedContent) {
            decodedData.push(formattedContent);
          }
        });
        
        logger.info(`Successfully decoded ${decodedData.length} ordinal inscription(s) from tx ${txid}`);
        return decodedData;
      } catch (error: unknown) {
        logger.error({ 
          error: error instanceof Error ? error.message : String(error),
          stack: error instanceof Error ? error.stack : undefined,
          txid 
        }, "Failed to decode witness data");
        
        if (error instanceof BitcoinError) throw error;
        throw handleBlockchainError(error, "Witness Data Decode", txid);
      }
    }
Behavior4/5

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

With no annotations provided, the description carries full burden and does disclose important behavioral traits: it fetches inscription data, saves images locally, returns a local_file_path, and is part of a mandatory two-response workflow. It explains that code 'currently does NOT return a separate displayable source, only the local path' which is valuable behavioral context. However, it doesn't mention error conditions, rate limits, or authentication requirements.

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

Conciseness1/5

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

The description is extremely verbose (over 800 words) and poorly structured for a tool definition. It's front-loaded with workflow instructions rather than tool purpose. Most content is about the mandatory two-response process, fallback mechanisms, and response formats rather than concisely describing the tool itself. Every sentence does not earn its place in a tool description context.

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

Completeness3/5

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

Given 3 parameters with 100% schema coverage but no annotations and no output schema, the description provides extensive workflow context but lacks critical tool-specific information. It doesn't explain what the tool returns (beyond mentioning local_file_path for images), error conditions, or performance characteristics. The workflow instructions are comprehensive but don't compensate for missing tool behavior documentation.

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

Parameters3/5

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

Schema description coverage is 100%, so the schema already documents all three parameters thoroughly. The description adds no parameter-specific information beyond what's in the schema. It mentions the tool fetches inscription data but doesn't explain how parameters affect this process. Baseline 3 is appropriate when schema does the heavy lifting.

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

Purpose2/5

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

The description states 'Fetches inscription data' which is a vague purpose statement. While it mentions Bitcoin Ordinals context, it doesn't specify what data is fetched (metadata, content, both) or how it differs from other potential tools. The description is primarily workflow instructions rather than a clear tool purpose definition.

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

Usage Guidelines5/5

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

The description provides explicit, detailed guidance on when to use this tool: 'MANDATORY FIRST STEP' for processing Bitcoin Ordinal inscriptions, specifically for images where it must save locally and return the local_file_path. It clearly states this is step 1 of a two-step workflow and specifies when to use sibling tools (image_processor/vision) in step 2.

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/Laz1mov/mcp-inscription'

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