Skip to main content
Glama

poll_error_endpoint_and_fix

Fetch JSON error reports from Opentrons robots and automatically fix protocol files to resolve execution issues.

Instructions

Fetch specific JSON error report and automatically fix protocols

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
json_filenameNoName of JSON file to fetcherror_report_20250622_124746.json
original_protocol_pathNoPath to original protocol file/Users/gene/Developer/failed-protocol-5.py

Implementation Reference

  • Main handler function for 'poll_error_endpoint_and_fix' tool. Fetches error JSON from a local endpoint, stops any running Opentrons robot protocol, reads the original failed protocol file, uses Claude AI via Anthropic API to generate a fixed protocol, and returns the error report with the fixed code.
      async pollErrorEndpointAndFix(args) {
        const { json_filename = "error.json", original_protocol_path = "/Users/gene/Developer/failed-protocol-5.py" } = args;
        
        try {
          const axios = (await import('axios')).default;
          const baseUrl = 'http://192.168.0.145:8080';
          const jsonUrl = `${baseUrl}/${json_filename}`;
          
          console.error(`πŸ” Fetching JSON error report: ${jsonUrl}`);
          
          // Fetch the specific JSON file
          const response = await fetch(jsonUrl);
          if (!response.ok) {
            throw new Error(`Failed to fetch ${json_filename}: ${response.status} ${response.statusText}`);
          }
          
          let errorText = await response.text();
          
          // Validate and pretty-print JSON
          try {
            const parsed = JSON.parse(errorText);
            errorText = JSON.stringify(parsed, null, 2);
            console.error(`βœ… Successfully fetched and parsed ${json_filename}`);
          } catch (parseError) {
            throw new Error(`Invalid JSON in ${json_filename}: ${parseError.message}`);
          }
          
          // Get current run info and stop it
          const robotIp = "192.168.0.83";
          let stopStatus = "⚠️ No running protocol found";
          let currentRunId = null;
          let lastCompletedStep = null;
          
          try {
            const runsResponse = await axios.get(`http://${robotIp}:31950/runs`);
            const activeRun = runsResponse.data.data.find(run => 
              run.status === "running" || run.status === "paused"
            );
            
            if (activeRun) {
              currentRunId = activeRun.id;
              
              // Get detailed run info including protocol name and current status
              const runDetailResponse = await axios.get(`http://${robotIp}:31950/runs/${currentRunId}`);
              const runDetail = runDetailResponse.data.data;
              
              const protocolName = runDetail.protocolId || 'Unknown Protocol';
              const currentStatus = runDetail.status;
              const currentCommand = runDetail.current ? runDetail.current.command : 'None';
              
              if (runDetail.commands) {
                const completedCommands = runDetail.commands.filter(cmd => cmd.status === "succeeded");
                const failedCommands = runDetail.commands.filter(cmd => cmd.status === "failed");
                lastCompletedStep = completedCommands.length;
                
                console.log(`Protocol: ${protocolName}, Status: ${currentStatus}, Step: ${lastCompletedStep}`);
              }
              
              // Stop the run
              await axios.post(`http://${robotIp}:31950/runs/${currentRunId}/actions`, {
                data: { actionType: "stop" }
              });
              
              stopStatus = `βœ… Robot stopped
    Protocol: ${protocolName}
    Run ID: ${currentRunId}
    Status: ${currentStatus} β†’ stopped
    Completed steps: ${lastCompletedStep || 0}
    Current command: ${currentCommand}
    Failed commands: ${failedCommands?.length || 0}`;
            }
          } catch (stopError) {
            stopStatus = `❌ Stop failed: ${stopError.message}`;
          }
          
          // Read original protocol and generate fix
          const originalProtocol = fs.readFileSync(original_protocol_path, 'utf8');
          const fixedProtocol = await this.generateFixedProtocol(errorText, originalProtocol, lastCompletedStep, currentRunId);
          
          return {
            content: [{
              type: "text",
              text: `🚨 **JSON ERROR REPORT**: ${json_filename}\n\nπŸ“„ **CONTENT**:\n${errorText}\n\n${stopStatus}\n\nπŸ”§ **FIXED PROTOCOL**:\n\n\`\`\`python\n${fixedProtocol}\n\`\`\``
            }]
          };
          
        } catch (error) {
          return {
            content: [{
              type: "text",
              text: `❌ **Failed to fetch error report**: ${error.message}`
            }]
          };
        }
      }
  • Tool schema definition including name, description, and inputSchema with optional json_filename and original_protocol_path parameters (both with defaults).
    {
      name: "poll_error_endpoint_and_fix",
      description: "Fetch specific JSON error report and automatically fix protocols",
      inputSchema: {
        type: "object",
        properties: {
          json_filename: { type: "string", default: "error_report_20250622_124746.json", description: "Name of JSON file to fetch" },
          original_protocol_path: { type: "string", default: "/Users/gene/Developer/failed-protocol-5.py", description: "Path to original protocol file" }
        }
      }
  • index.js:268-269 (registration)
    Registration of the tool handler in the CallToolRequestSchema switch statement.
    case "poll_error_endpoint_and_fix":
      return this.pollErrorEndpointAndFix(args);
  • Supporting helper function that generates a fixed protocol by calling Anthropic Claude API (model claude-3-5-sonnet-20241022) with a detailed prompt including the error, original protocol, a working example, and run context. Uses ANTHROPIC_API_KEY env var.
      async generateFixedProtocol(errorText, originalProtocol, lastCompletedStep = null, currentRunId = null) {
        const anthropicKey = process.env.ANTHROPIC_API_KEY;
        if (!anthropicKey) {
          throw new Error("ANTHROPIC_API_KEY not configured");
        }
        
        const workingExample = `from opentrons import protocol_api
    
    metadata = {
        'protocolName': 'Pierce BCA Protein Assay Kit Aliquoting',
        'author': 'OpentronsAI',
        'description': 'Automated liquid handling for protein concentration determination using Pierce BCA Protein Assay Kit',
        'source': 'OpentronsAI'
    }
    
    requirements = {
        'robotType': 'Flex',
        'apiLevel': '2.22'
    }
    
    def run(protocol: protocol_api.ProtocolContext):
        # Load trash bin
        trash = protocol.load_trash_bin('A3')
        
        # Load labware
        reservoir = protocol.load_labware('nest_12_reservoir_15ml', 'D1', 'Source Reservoir')
        pcr_plate = protocol.load_labware('nest_96_wellplate_100ul_pcr_full_skirt', 'D2', 'PCR Plate')
        tiprack = protocol.load_labware('opentrons_flex_96_filtertiprack_50ul', 'D3', 'Filter Tips 50uL')
        
        # Load pipette
        p50_multi = protocol.load_instrument('flex_8channel_50', 'left', tip_racks=[tiprack])
        
        # Define liquid
        master_mix = protocol.define_liquid(
            name='Master Mix',
            description='Pierce BCA Protein Assay Master Mix',
            display_color='#0066CC'
        )
        
        # Load liquid into reservoir
        reservoir['A1'].load_liquid(liquid=master_mix, volume=1500)
        
        # Protocol steps
        protocol.comment("Starting Pierce BCA Protein Assay Kit aliquoting protocol")
        
        # Transfer 50 Β΅L from reservoir to first 16 wells of PCR plate
        source_well = reservoir['A1']
        destination_wells = pcr_plate.columns()[:2]  # First 2 columns = 16 wells
        
        protocol.comment("Transferring 50 Β΅L of master mix to first 16 wells of PCR plate")
        
        p50_multi.transfer(
            volume=50,
            source=source_well,
            dest=destination_wells,
            new_tip='once'
        )
        
        protocol.comment("Protocol completed successfully")`;
    
        let contextInfo = "";
        if (lastCompletedStep !== null && currentRunId !== null) {
          contextInfo = `\n\nRUN CONTEXT:
    - Run ID: ${currentRunId}
    - Successfully completed steps: ${lastCompletedStep}
    - The protocol should resume from or be modified to account for this point`;
        }
    
        const prompt = `Fix this Opentrons Flex protocol that failed with this error:
    
    ERROR: ${errorText}
    
    ORIGINAL FAILED PROTOCOL:
    ${originalProtocol}
    
    WORKING REFERENCE PROTOCOL:
    ${workingExample}${contextInfo}
    
    Generate a FIXED version of the original protocol that:
    1. Fixes the specific error mentioned
    2. Uses proper Flex deck positions (A1, B1, C1, D1, etc.)
    3. Uses proper Flex pipettes and labware
    4. Follows the working pattern from the reference
    5. Maintains the same general purpose as the original
    6. ${lastCompletedStep !== null ? `Accounts for the fact that ${lastCompletedStep} steps were already completed successfully` : 'Starts from the beginning'}
    
    Return ONLY the fixed Python code, no explanations or markdown.`;
    
        try {
          const response = await fetch("https://api.anthropic.com/v1/messages", {
            method: "POST",
            headers: {
              "x-api-key": anthropicKey,
              "Content-Type": "application/json",
              "anthropic-version": "2023-06-01"
            },
            body: JSON.stringify({
              model: "claude-3-5-sonnet-20241022",
              max_tokens: 2000,
              messages: [{ role: "user", content: prompt }]
            })
          });
          
          if (!response.ok) {
            throw new Error(`Claude API error: ${response.status}`);
          }
          
          const result = await response.json();
          return result.content[0].text;
          
        } catch (error) {
          return `❌ Failed to generate fix: ${error.message}\n\nORIGINAL PROTOCOL:\n${originalProtocol}`;
        }
      }

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/yerbymatey/opentrons-mcp'

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