SkillAgentSearch skills...

opentrons-protocol-api

Python API v2 for Opentrons OT-2/Flex liquid handlers: protocols as Python files with metadata and run(); control pipettes, labware, and modules (thermocycler, heater-shaker, magnetic, temperature). Simulate via opentrons_simulate then upload.

Install / Use

npx skills add jaechang-hits/SciAgent-Skills --skill opentrons-protocol-api

Installs into whichever agent you are using.

About this skill
📄

SKILL.md

Installable skill definition

Quality Score

91/100

Supported Platforms

Universal

Our assessment of opentrons-protocol-api

opentrons-protocol-api scores 91/100 on our quality scale, 1166th of 4,619 Development & Engineering skills we index (top 26%).

Its SKILL.md is 30 KB long, well organised into 28 sections with 18 code examples: a thorough specification that gives an agent plenty to work with.

It has 367 GitHub stars, a meaningful sign that others use it.

Substance
30/30
Structure
20/20
Description
15/15
Adoption
11/20
Freshness
15/15

Maintenance, license and trust

  • The repository was last updated 37 days ago, so opentrons-protocol-api is actively maintained.
  • No license is declared. By default that means all rights are reserved: you can read it, but reusing or redistributing it is not clearly permitted. Ask the author before building on it commercially.
  • Its trust signals score 88/100, with 1 caution from licensing, adoption, age or documentation. These come from repository metadata, not a code audit — read the skill file before letting an agent act on it.

Safety scan

No issues found

Our scan of the whole file found no instruction hijacking, hidden characters, credential access, data exfiltration or destructive commands.

Automated pattern scan on 2026-10-05. It catches known dangerous patterns, not every risk — read a skill before letting an agent act on it.

opentrons-protocol-api compared with similar skills

All 4 of these similar skills score higher than opentrons-protocol-api; compare them before choosing.

SkillScoreStarsUpdatedFormat
opentrons-protocol-api (this skill)by jaechang-hits9136737d agoSKILL.md
Agent-Reachby Panniantong10090.8k19d agoCLAUDE.md
headroomby headroomlabs-ai10074.4ktodayCLAUDE.md
ai-job-searchby MadsLorentzen10045.0k1d agoCLAUDE.md
claude-howtoby luongnv8910041.7k4d agoCLAUDE.md

Frequently asked questions

How do I install opentrons-protocol-api?
Run npx skills add jaechang-hits/SciAgent-Skills --skill opentrons-protocol-api. The install tabs above show the steps for each supported agent.
Which AI agents does opentrons-protocol-api work with?
It is written for Universal, as a SKILL.md file. Other agents that read the same format can often use it too.
Is opentrons-protocol-api safe to use?
Our scan of the whole file found no instruction hijacking, hidden characters, credential access, data exfiltration or destructive commands. It declares no license and scores 88/100 on trust signals. Skills are instructions an agent will follow, so read the file before installing it and do not approve commands you do not understand.
Is opentrons-protocol-api still maintained?
The repository was last updated 37 days ago, so opentrons-protocol-api is actively maintained.

name: "opentrons-protocol-api" description: "Python API v2 for Opentrons OT-2/Flex liquid handlers: protocols as Python files with metadata and run(); control pipettes, labware, and modules (thermocycler, heater-shaker, magnetic, temperature). Simulate via opentrons_simulate then upload. Use PyLabRobot for vendor-agnostic scripts (Hamilton, Tecan)." license: "Apache-2.0"

Opentrons Python Protocol API

Overview

The Opentrons Protocol API v2 lets you write liquid handling protocols as plain Python files that run on OT-2 or Flex robots. Every protocol defines a metadata dictionary, an optional requirements dictionary, and a run(protocol) function. The ProtocolContext object passed to run() exposes all deck setup, pipette operations, module control, and utility methods. Protocols can be simulated on any computer with opentrons_simulate before uploading to the robot through the Opentrons App or HTTP API.

When to Use

  • Setting up PCR reactions: Distribute master mix from a tube rack into a thermocycler plate, add template DNA from individual tubes, then execute a PCR profile automatically.
  • Running serial dilutions: Programmatically step a multi-channel pipette across a 96-well plate to create 2-fold or custom dilution curves with defined diluent volumes.
  • Performing ELISA plate layouts: Add blocking buffer, primary antibody, secondary antibody, and substrate to defined wells with tip changes between each reagent.
  • Automating magnetic bead cleanups: Engage/disengage the magnetic module, aspirate supernatant, wash with ethanol, and elute — in a fully automated loop.
  • Plate reformatting and stamping: Transfer an entire 96-well plate to a destination plate with one command; reformat from tubes to plates.
  • Integrating hardware modules: Coordinate temperature control, shaking, and liquid handling steps in a single protocol with precise timing.
  • Use PyLabRobot instead when writing protocols that must run on Hamilton STAR, Tecan Freedom EVO, or other vendors without Opentrons-specific hardware; for Opentrons-only workflows the native Protocol API provides tighter integration and module support.
  • For retrieving and parsing published protocols before automation, use protocolsio-integration to search protocols.io alongside this skill.

Prerequisites

  • Python packages: opentrons
  • Robot types: OT-2 (slots 1-11, Gen2 pipettes) or Flex (slots A1-D3, Flex pipettes)
  • Environment: Python 3.10+; Opentrons App for uploading to physical robot
  • CLI tool: opentrons_simulate ships with the package for local testing
pip install opentrons
# Verify installation and simulate a protocol locally
opentrons_simulate my_protocol.py

Quick Start

A minimal protocol showing all required elements — metadata, labware, instrument, and a transfer:

from opentrons import protocol_api

metadata = {
    "protocolName": "Simple Reagent Distribution",
    "author": "Lab Automation Team",
    "apiLevel": "2.19",
}

def run(protocol: protocol_api.ProtocolContext):
    # Load labware onto deck slots
    tips    = protocol.load_labware("opentrons_96_tiprack_300ul", "1")
    source  = protocol.load_labware("nest_12_reservoir_15ml", "2")
    plate   = protocol.load_labware("corning_96_wellplate_360ul_flat", "3")

    # Load pipette and attach tip rack
    pipette = protocol.load_instrument("p300_single_gen2", "left", tip_racks=[tips])

    # Distribute 50 µL from reservoir A1 to first 12 wells using one tip
    pipette.distribute(50, source["A1"], plate.wells()[:12], new_tip="once")
    protocol.comment("Distribution complete")
# Simulate locally — no robot needed
opentrons_simulate simple_reagent_distribution.py

Core API

Module 1: Protocol Metadata and Deck Setup

Every protocol requires a metadata dict specifying at minimum apiLevel. The optional requirements dict sets the target robot type. All labware and instruments are loaded through the ProtocolContext.

from opentrons import protocol_api

# Minimum required metadata
metadata = {
    "protocolName": "My Assay Protocol",
    "author": "Jane Smith <jane@lab.org>",
    "description": "96-well assay setup with temperature control",
    "apiLevel": "2.19",
}

# Optional: target a specific robot type (Flex or OT-2)
requirements = {"robotType": "OT-2", "apiLevel": "2.19"}

def run(protocol: protocol_api.ProtocolContext):
    # OT-2: slots numbered 1-11 in a 3×4 grid
    tips_300 = protocol.load_labware("opentrons_96_tiprack_300ul", "1")
    tips_20  = protocol.load_labware("opentrons_96_tiprack_20ul",  "4")
    source   = protocol.load_labware("nest_12_reservoir_15ml",     "2", label="Buffer Reservoir")
    plate    = protocol.load_labware("corning_96_wellplate_360ul_flat", "3")
    tube_rack = protocol.load_labware("opentrons_24_tuberack_nest_1.5ml_snapcap", "5")

    # Load both pipettes (optional: one or two mounts)
    p300 = protocol.load_instrument("p300_single_gen2", "left",  tip_racks=[tips_300])
    p20  = protocol.load_instrument("p20_single_gen2",  "right", tip_racks=[tips_20])

    print(f"Deck has {len(protocol.deck)} slots; pipettes: {[p300.name, p20.name]}")

OT-2 deck layout (3 columns × 4 rows, numbered left-to-right, bottom-to-top):

Slot map (OT-2):         Slot map (Flex, A-D rows, 1-3 cols):
 10 | 11 | Trash          D1 | D2 | D3
  7 |  8 |  9             C1 | C2 | C3
  4 |  5 |  6             B1 | B2 | B3
  1 |  2 |  3             A1 | A2 | A3

Common OT-2 pipette names: p20_single_gen2, p300_single_gen2, p1000_single_gen2, p20_multi_gen2, p300_multi_gen2. Common Flex pipette names: p50_single_flex, p1000_single_flex, p50_multi_flex, p1000_multi_flex, flex_96channel_1000.

Module 2: Pipette Operations

Low-level aspirate/dispense/blow-out operations for precise step-by-step control.

def run(protocol: protocol_api.ProtocolContext):
    tips   = protocol.load_labware("opentrons_96_tiprack_300ul", "1")
    source = protocol.load_labware("nest_12_reservoir_15ml", "2")
    dest   = protocol.load_labware("corning_96_wellplate_360ul_flat", "3")
    p300   = protocol.load_instrument("p300_single_gen2", "left", tip_racks=[tips])

    p300.pick_up_tip()

    # Aspirate and dispense — basic liquid movement
    p300.aspirate(100, source["A1"])         # draw 100 µL from reservoir
    p300.dispense(100, dest["A1"])           # expel into plate well

    # Air gap to prevent dripping during transport
    p300.aspirate(80, source["A2"])
    p300.air_gap(20)                         # draw 20 µL air to cap the tip
    p300.dispense(100, dest["A2"])           # dispenses liquid + air

    # Mix in place (repetitions, volume)
    p300.mix(3, 60, dest["A1"])             # mix 60 µL × 3 times

    # Remove exterior droplets / expel residual
    p300.touch_tip(dest["A1"])              # wipe tip on well rim
    p300.blow_out(dest["A1"].top())         # expel last drop at top

    p300.drop_tip()
    protocol.comment("Low-level operations complete")
def run(protocol: protocol_api.ProtocolContext):
    tips = protocol.load_labware("opentrons_96_tiprack_300ul", "1")
    p300 = protocol.load_instrument("p300_single_gen2", "left", tip_racks=[tips])

    # Adjust flow rates (µL/s) for viscous or sensitive samples
    p300.flow_rate.aspirate = 50    # slow down for viscous liquids (default ~150)
    p300.flow_rate.dispense = 150   # default dispense speed
    p300.flow_rate.blow_out = 300   # fast blow-out for complete expulsion
    print(f"Aspirate rate: {p300.flow_rate.aspirate} µL/s")

Module 3: transfer() Shortcut

transfer(), distribute(), and consolidate() handle tip management automatically and accept mix, blow-out, and air-gap options.

def run(protocol: protocol_api.ProtocolContext):
    tips   = protocol.load_labware("opentrons_96_tiprack_300ul", "1")
    source = protocol.load_labware("corning_96_wellplate_360ul_flat", "2")
    dest   = protocol.load_labware("corning_96_wellplate_360ul_flat", "3")
    p300   = protocol.load_instrument("p300_single_gen2", "left", tip_racks=[tips])

    # transfer(): one source → one destination, with optional per-well tip changes
    p300.transfer(
        100,
        source["A1"],
        dest["A1"],
        new_tip="always",       # options: "always", "once", "never"
        mix_after=(3, 50),      # mix 50 µL × 3 reps after each dispense
        blow_out=True,
        touch_tip=True,
    )

    # transfer() with lists: pairwise source-destination mapping
    sources = source.wells()[:8]
    dests   = dest.wells()[:8]
    p300.transfer(75, sources, dests, new_tip="always")

    # distribute(): one source → many destinations (single tip, multi-dispense)
    p300.distribute(
        50,
        source["A1"],
        dest.wells()[:12],
        new_tip="once",         # use one tip for all destinations
        disposal_volume=10,     # extra volume drawn to ensure accuracy
    )

    # consolidate(): many sources → one destination (collect, then dispense)
    p300.consolidate(
        50,
        source.wells()[:8],
        dest["A1"],
        mix_after=(3, 100),
    )
    print("Compound transfer operations complete")

Module 4: Labware, Liquids, and Well Access

Load labware from the library, navigate wells by name/row/column, and define liquids for visual tracking in the Opentrons App.

def run(protocol: protocol_api.ProtocolContext):
    plate  = protocol.load_labware("corning_96_wellplate_360ul_flat", "1")
    p300   = protocol.load_instrument("p300_single_gen2", "left",
                                      tip_racks=[protocol.load_labware("opentrons_96_tiprack_300ul", "2")])

    # Access wells by alphanumeric name
    well_a1 = plate["A1"]

    # Access all wells (column-major order: A1, B1, C1, ..., H1, A2, ...)
    all_wells = plate.wells()
    print(f"Total wells: {len(all_wells)}")   # 96

    # Access by row (8 rows, A-H; each row has 12 wells)
    row_a = plate.rows()[0]    # [A1, A2, ..., A12]
    row_b = plate.rows()[1]    # [B1, B2, ..., B12]

    # Access by column (12 columns, 1-12; each column has 8 wells)
    col_1 = plate.columns()[0]   # [A1, B1, C1, D1, E1, F1, G1, H1]

    # Vertical position control within a well
    p300.pick_up_tip()
    p300.aspirate(80, well_a1.bottom(z=1))   # 1 mm above well bottom
    p300.dispense(80, well_a1.top(z=-2))     # 2 mm below well top
    p300.aspirate(80, well_a1.center())      # geometric center
    p300.drop_tip()
def run(protocol: protocol_api.ProtocolContext):
    reservoir = protocol.load_labware("nest_12_reservoir_15ml", "1")
    plate     = protocol.load_labware("corning_96_wellplate_360ul_flat", "2")

    # Define liquids for visual tracking in Opentrons App
    pbs     = protocol.define_liquid(name="1× PBS",    description="Phosphate buffered saline",    display_color="#0077BB")
    sample  = protocol.define_liquid(name="Sample",    description="Cell lysate, 1 mg/mL protein", display_color="#EE7733")

    # Assign liquids to wells with known starting volumes (µL)
    reservoir["A1"].load_liquid(liquid=pbs,    volume=10000)
    reservoir["A2"].load_liquid(liquid=sample, volume=5000)

    # Mark destination wells as empty
    for well in plate.wells():
        well.load_empty()

    print("Liquids defined and assigned")

Module 5: Hardware Modules

Control temperature, magnetic, thermocycler, and heater-shaker modules. Each module is loaded by its model name string and occupies specific deck slots.

def run(protocol: protocol_api.ProtocolContext):
    # --- Temperature Module (Gen2) ---
    temp_mod = protocol.load_module("temperature module gen2", "3")
    temp_plate = temp_mod.load_labware("corning_96_wellplate_360ul_flat")
    temp_mod.set_temperature(celsius=4)       # blocks until target reached
    print(f"Temp module: {temp_mod.temperature}°C")
    # temp_mod.deactiva

Truncated for display — read the full file on GitHub.

Related Skills

View on GitHub
GitHub Stars367
CategoryDevelopment
Updated1mo ago
Forks36

Languages

Python

Trust signals

88/100

From repository metadata: license, adoption, age and documentation. Not a code audit — see the Safety scan above for what the skill file itself contains.

1 medium