show_ordinals
Fetch and display Bitcoin Ordinals inscription data, saving image files locally for further analysis. Essential first step in a structured two-response workflow for processing inscriptions.
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
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
MUST be used for extracting textual/visual data from the image.
Takes
local_file_pathas input.
π MANDATORY TWO-STEP RESPONSE WORKFLOW (FOR IMAGES)
π RESPONSE 1: Display Attempt + Technical Details (NON-TERMINAL)
β Execute
β Return an using the local_file_path as the src attribute.
β Follow with detailed technical metadata.
β STOP HERE. NO ANALYSIS YET.
β RESPONSE FORMAT (STRICTLY ENFORCED):
πΉ 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
β 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):
β No hallucinations. No AI-generated assumptions.
π FALLBACK MECHANISM (IF OCR TOOLS FAIL)
If BOTH
image_processorANDvisionfail, 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 β Attempt Image Display (using local path) + Metadata. (Response intended as Non-Terminal) β Step 2: Execute β Analysis Description. (Separate Response)
π‘ This ensures clarity, prevents AI hallucinations, and provides structured responses. π
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| content_type_filter | No | Optional. Filter inscriptions by content type, e.g., 'image/png', 'text/plain', 'application/json'. | |
| inscription_index | No | Optional. The specific index (starting from 0) of the inscription to retrieve within the transaction, if there are multiple. | |
| txid | Yes | The unique 64-character hexadecimal identifier of the Bitcoin transaction to inspect for Ordinals. Example: 'f1d2d3...a8b9c0'. |
Implementation Reference
- src/servers/base.ts:565-639 (handler)Primary handler for 'show_ordinals' tool call. Validates input, delegates to OrdinalsClient.decodeWitness with 'file' option for image handling, processes images by saving to local path and generating HTML img tag response, formats non-image content (JSON/text).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[], }; } }
- src/servers/base.ts:129-152 (registration)Registers the 'show_ordinals' tool with the MCP server in listToolsRequestHandler, including detailed workflow instructions for image handling 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, ], }));
- src/mcp_inscription_types.ts:188-193 (schema)Zod schema used for input validation in the show_ordinals handler (note: registration schema has additional optional fields not validated here).// Schema for decoding witness with image formatting option export const DecodeWitnessSchema = z.object({ txid: z.string().length(64, "Invalid transaction ID"), formatImageOption: z.enum(["base64", "file"]).optional().default("base64"), });
- src/ordinals_client.ts:396-457 (helper)Delegated core implementation: extracts all Ordinal inscriptions from tx witness data using extractOrdinalContent/extractAllOrdinals, formats content per type (saves images to ~/.cache/mcp-inscription/ if 'file', base64 otherwise; parses JSON/text), returns formatted strings/paths.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); } }
- src/ordinals_client.ts:119-167 (helper)Key utility to parse Ordinal inscription envelope from witness script buffer and extract raw contentType and data buffer.private extractOrdinalContent(scriptBuffer: Buffer): { contentType: string; content: Buffer } | null { try { const decompiled = bitcoin.script.decompile(scriptBuffer); if (!decompiled) { return null; } const ifIndex = decompiled.findIndex((op) => op === bitcoin.opcodes.OP_IF); if (ifIndex === -1 || ifIndex === 0 || decompiled[ifIndex - 1] !== bitcoin.opcodes.OP_FALSE) { return null; } const endifIndex = decompiled.findIndex((op, idx) => idx > ifIndex && op === bitcoin.opcodes.OP_ENDIF); if (endifIndex === -1) { return null; } const ordPushIndex = decompiled.findIndex((op, idx) => idx > ifIndex && idx < endifIndex && isBuffer(op) && op.toString() === 'ord'); if (ordPushIndex === -1) { return null; } let contentTypeIndex = -1; let contentType: string | null = null; if (ordPushIndex + 1 < endifIndex && (decompiled[ordPushIndex + 1] === bitcoin.opcodes.OP_1 || decompiled[ordPushIndex + 1] === 1)) { if (ordPushIndex + 2 < endifIndex && isBuffer(decompiled[ordPushIndex + 2])) { contentTypeIndex = ordPushIndex + 2; contentType = (decompiled[contentTypeIndex] as Buffer).toString('utf8'); } } else { if (ordPushIndex + 1 < endifIndex && isBuffer(decompiled[ordPushIndex + 1])) { contentTypeIndex = ordPushIndex + 1; contentType = (decompiled[contentTypeIndex] as Buffer).toString('utf8'); } } if (contentTypeIndex === -1 || !contentType) { return null; } let separatorIndex = -1; if (contentTypeIndex + 1 < endifIndex) { const nextOp = decompiled[contentTypeIndex + 1]; if (nextOp === bitcoin.opcodes.OP_0) { separatorIndex = contentTypeIndex + 1; } else if (isBuffer(nextOp) && nextOp.length === 1 && nextOp[0] === 0) { separatorIndex = contentTypeIndex + 1; } } if (separatorIndex === -1) { return null; } const dataChunks: Buffer[] = []; for (let i = separatorIndex + 1; i < endifIndex; i++) { const op = decompiled[i]; if (isBuffer(op)) { dataChunks.push(op); } else { logger.warn(`Unexpected opcode ${bitcoin.script.toASM([op])} found within data section at index ${i}.`); } } const fullContent = Buffer.concat(dataChunks); logger.info(`Successfully extracted inscription: Type=${contentType}, Content Size=${fullContent.length}`); return { contentType, content: fullContent }; } catch (error: unknown) { if (error instanceof Error) { logger.error({ error: error.message, stack: error.stack, bufferHex: scriptBuffer.toString('hex') }, "Error during ordinal content extraction"); } else { logger.error({ error: String(error), bufferHex: scriptBuffer.toString('hex') }, "Unknown error during ordinal content extraction"); } return null; }