Why REST APIs and SiLA2 Don't Talk - And How to Bridge Them
Why REST APIs and SiLA2 Don't Talk - And How to Bridge Them
REST APIs and SiLA2 cannot interoperate directly because SiLA2 is built on gRPC over HTTP/2 with Protocol Buffers, while REST is JSON over HTTP/1.1 - two fundamentally different wire protocols. The standard workaround in labs today is hand-authoring SiLA2 Feature Definition Language (FDL) XML to wrap each REST endpoint, which is slow, error-prone, and rarely maintained. The open-source tool openapi-to-sila2 (released April 2026 by QPillars) automates this: feed it an OpenAPI spec, get back validated FDL XML, gRPC stubs, and Python type classes - in one command.
The Mismatch - Two Standards That Don't Speak
SiLA2 (Standardization in Lab Automation, version 2) is the industry standard for laboratory instrument communication. It is governed by the SiLA Consortium, a non-profit whose board includes representatives from major IVD manufacturers, Tecan, Novartis, GSK, Takeda, Novo Nordisk, Fraunhofer IPA, and Zeiss Digital Innovation. If you integrate lab instruments at a serious pharma or biotech, SiLA2 is on the roadmap.
But SiLA2 is gRPC-native. The protocol runs over HTTP/2, payloads are encoded in Protocol Buffers, and instrument capabilities are described in an XML dialect called Feature Definition Language (FDL), validated against an official XSD schema.
Meanwhile, almost everything else in the modern lab stack ships REST/OpenAPI:
- LIMS and ELN platforms expose REST APIs (Benchling, LabVantage, STARLIMS)
- Cloud lab services (Strateos, Emerald Cloud Lab) ship REST
- New instrument vendors default to REST/OpenAPI before they even consider SiLA2
- Internal lab software written in the last five years is almost universally FastAPI, Express, or Spring Boot - all REST-first
The result: a SiLA2-compliant orchestrator cannot call a REST endpoint without a translation layer, and a REST-based LIMS cannot consume a SiLA2 instrument without a gRPC client. Two standards, no bridge.
Why SiLA2 Chose gRPC (And Why That Choice Matters)
SiLA2's choice of gRPC was not arbitrary. Lab instrument control has requirements that REST handles poorly:
Long-running commands with progress streams. A liquid handler's "run protocol" command can take 45 minutes. SiLA2 defines Observable Commands that stream intermediate state - current step, estimated remaining time, sub-task progress - over a single bi-directional gRPC stream. Doing this with REST means polling, webhooks, or Server-Sent Events, all of which are second-class citizens.
Strong typing across the wire. SiLA2 Features define exact data types (Real, Integer, String, Timestamp, custom Structures, Lists). gRPC + protobuf enforces these types at serialization time. REST/JSON has no native type enforcement - you bolt on JSON Schema validation, but it's optional and frequently skipped.
Discovery and self-description. A SiLA2 server publishes its FDL files. A client can query, parse, and statically validate every command before sending it. OpenAPI does the same conceptually, but FDL is purpose-built for lab instrument semantics (commands vs. properties, observable vs. unobservable, defined errors).
Bi-directional communication. Some instruments need to push events to the orchestrator (door opened, sample loaded, error condition). gRPC streaming handles this in one connection. REST requires a second channel.
These are real engineering wins for instrument control. They also create real friction with the REST-first world.
The Status Quo - Hand-Authoring FDL XML
Today, when a lab needs to expose a REST endpoint as a SiLA2 service, an engineer typically:
- Reads the OpenAPI spec (or the REST docs, if no spec exists).
- Designs a SiLA2 Feature - choosing identifiers, display names, descriptions.
- Maps each REST operation to a SiLA2 Command or Property.
- Translates each request/response schema into SiLA2 Data Types.
- Authors the FDL XML by hand, validating against the XSD.
- Runs
sila2-codegento generate.protoand gRPC stubs. - Writes a Python (or Java/.NET) wrapper that translates SiLA2 calls into REST calls.
Steps 2-5 take hours per Feature, days for a real instrument with 20+ endpoints, and the result drifts the moment the REST API changes. Most teams maintain these wrappers reluctantly. Some give up and write a custom HTTP-to-gRPC adapter that bypasses SiLA2 entirely - which defeats the purpose of having a standard.
The repetitive, mechanical nature of this work is exactly what code generation is for.
The Bridge - openapi-to-sila2
openapi-to-sila2 is an open-source (Apache 2.0) Python tool that automates the entire pipeline. Version 0.1.1 shipped to PyPI on April 8, 2026.
The pipeline is four stages, executed by a single CLI command:
Each stage is deterministic, validated, and reproducible:
- OpenAPI Parsing - reads JSON or YAML spec, validates structure.
- FDL Generation - emits SiLA2 Feature Definition XML, validated against the official XSD.
- gRPC Codegen - invokes the upstream
sila2-codegento produce.protofiles and gRPC stubs. - Python Type Classes - extracts protobuf types into idiomatic Python dataclasses for client-side use.
Install and Run
pip install openapi-to-sila2
openapi-to-sila2 generate \
--input plate-reader-api.openapi.json \
--output ./generated \
--codegen \
--types
That's it. Output: a fully wired SiLA2 Feature, ready to plug into a SiLA2 server skeleton.
How REST Concepts Map to SiLA2
The mapping is opinionated and consistent:
| OpenAPI concept | SiLA2 concept | Notes |
|---|---|---|
tags | Feature | Each tag becomes one SiLA2 Feature. |
GET (no parameters) | Property | Read-only value, fetched on demand. |
GET / POST / PUT / DELETE (with parameters) | Command | Parameterized operation. |
| Object schemas | Structure | Named collection of typed fields. |
| Arrays | List | Ordered collection. |
| HTTP error responses | ExecutionError | Mapped into SiLA2's defined error type. |
| Path + query + body params | Unified Parameters Structure | Combined into a single SiLA2 input. |
This is not a perfect 1:1 mapping - the two standards have genuinely different shapes - but it's faithful enough to use directly for most lab instrument and LIMS APIs.
Concrete Example - A Plate Reader
Consider a fictional plate reader that exposes a small REST API:
# plate-reader-api.openapi.yaml (excerpt)
openapi: 3.0.3
info:
title: AcmeReader API
version: 1.2.0
paths:
/status:
get:
tags: [Diagnostics]
operationId: getStatus
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/InstrumentStatus'
/measurements:
post:
tags: [Measurement]
operationId: runMeasurement
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/MeasurementRequest'
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/MeasurementResult'
components:
schemas:
InstrumentStatus:
type: object
properties:
ready: { type: boolean }
temperature_c: { type: number }
MeasurementRequest:
type: object
required: [wells, wavelength_nm]
properties:
wells:
type: array
items: { type: string, pattern: '^[A-H](?:[1-9]|1[0-2])$' }
wavelength_nm: { type: integer, minimum: 200, maximum: 1000 }
Running openapi-to-sila2 generate -i plate-reader-api.openapi.yaml -o ./generated --codegen --types produces, among other artifacts, an FDL XML that looks like this (abbreviated):
<?xml version="1.0" encoding="UTF-8"?>
<Feature xmlns="http://www.sila-standard.org"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.sila-standard.org
https://gitlab.com/SiLA2/sila_base/raw/master/schema/FeatureDefinition.xsd"
SiLA2Version="1.0" FeatureVersion="1.0"
Originator="com.acme" Category="instrument">
<Identifier>Measurement</Identifier>
<DisplayName>Measurement</DisplayName>
<Description>Operations from the Measurement tag of AcmeReader API.</Description>
<Command>
<Identifier>RunMeasurement</Identifier>
<DisplayName>Run Measurement</DisplayName>
<Description>Trigger a plate read at a specified wavelength.</Description>
<Observable>No</Observable>
<Parameter>
<Identifier>Parameters</Identifier>
<DisplayName>Parameters</DisplayName>
<Description>Combined request parameters.</Description>
<DataType>
<DataTypeIdentifier>MeasurementRequest</DataTypeIdentifier>
</DataType>
</Parameter>
<Response>
<Identifier>Result</Identifier>
<DisplayName>Result</DisplayName>
<Description>Measurement result payload.</Description>
<DataType>
<DataTypeIdentifier>MeasurementResult</DataTypeIdentifier>
</DataType>
</Response>
</Command>
<!-- DataType definitions follow ... -->
</Feature>
The companion .proto file (generated by sila2-codegen on the FDL) and Python dataclasses are produced in the same run. You implement one method - the actual REST call - and the rest is generated.
# Your implementation (the only hand-written part)
import httpx
from generated.measurement_pb2 import MeasurementRequest, MeasurementResult
async def run_measurement(req: MeasurementRequest) -> MeasurementResult:
async with httpx.AsyncClient(base_url="http://reader.local") as client:
resp = await client.post("/measurements", json={
"wells": list(req.wells),
"wavelength_nm": req.wavelength_nm,
})
resp.raise_for_status()
return MeasurementResult(**resp.json())
A 20-endpoint instrument that previously took two engineering days to wrap in SiLA2 is now a 30-minute job: run the generator, write the thin REST adapter functions, ship.
Known Limitations
The tool is honest about what it doesn't do:
- Complex unions (
allOf,oneOf,anyOf) collapse to SiLA2'sAnytype. SiLA2's type system does not have native sum types. - Multiple error schemas map to a single
ExecutionErrorper Feature - a SiLA2 limitation, not the tool's. - Streaming endpoints (Server-Sent Events, WebSocket upgrades) are not represented as SiLA2 Observable Commands. Generate the static surface, then add observability by hand for the few endpoints that need it.
- Dynamic objects without declared properties default to
Any.
These are upstream SiLA2 constraints, not bugs. The tool generates what is mappable and stays out of the way for the rest.
Where This Fits in the Modern Lab Stack
A 2026 lab is layered. Each layer has its own protocol, optimized for its own job:
- MCP (Model Context Protocol) is the AI agent layer. It is how language models discover and invoke tools, including lab instruments. We covered MCP in depth in our guide to connecting AI agents to lab instruments with MCP.
- SiLA2 is the instrument interop layer. It is what major IVD manufacturers, Tecan, and SiLA Consortium members converge on for cross-vendor orchestration.
- REST/OpenAPI is the everything-else layer - LIMS, ELN, scheduling, sample tracking, internal services, cloud lab APIs.
openapi-to-sila2 lives at the boundary between the bottom two layers. It lets a REST-native instrument or service join a SiLA2 orchestration without a hand-written adapter. Combined with an MCP wrapper on top, the same instrument is reachable from both classical orchestrators (SiLA2-aware schedulers like the SiLA2 Manager) and modern AI agents.
This is the architecture we use at QPillars on real client work - bridging instrument vendors who shipped REST years ago with SiLA2-native LIMS deployments at pharma customers. See our AI for Instruments and LiquidBridge services for more.
Frequently Asked Questions
Why doesn't SiLA2 just support REST natively?
Because the protocol's design goals - long-running observable commands with progress streams, strong cross-language typing, bi-directional communication, and a self-describing schema - are all easier to deliver on gRPC than on REST. SiLA2 chose the substrate that fit the lab instrument problem; bridging to REST is a separate, solvable problem.
What does openapi-to-sila2 actually generate?
A complete SiLA2 Feature pipeline: an XSD-validated FDL XML file describing the Feature, .proto files (via the upstream sila2-codegen tool), gRPC stubs, and idiomatic Python dataclasses for the request and response types. You write the thin function bodies that call the REST API; everything else is generated.
Can I use this in production today?
The tool is at version 0.1.1 (released April 2026), Apache 2.0 licensed, with a working test suite. It is appropriate for internal projects, prototypes, and non-regulated environments today. For GxP environments, treat the generated code as you would any third-party library - validate, version-pin, and document.
How is this different from just calling the REST API directly from Python?
Calling REST directly works inside one application. But if your orchestrator, LIMS, or scheduler is SiLA2-native (which is the case at most large pharma deployments), it speaks gRPC and expects a SiLA2 server endpoint, not an HTTP URL. openapi-to-sila2 produces that SiLA2 surface so the REST instrument or service joins the SiLA2 ecosystem without code changes on the consumer side.
How does this relate to MCP-based AI agent integration?
They are complementary. MCP is the protocol AI agents use to discover and invoke tools. SiLA2 is the protocol classical lab orchestrators use to drive instruments. A single instrument can have both an MCP server (for AI agents) and a SiLA2 server (for orchestrators) on top of one underlying REST API. openapi-to-sila2 generates the SiLA2 side; an MCP wrapper handles the agent side.
Key Takeaways
- REST and SiLA2 cannot interoperate directly - SiLA2 is gRPC over HTTP/2 with protobuf, REST is JSON over HTTP/1.1.
- SiLA2 chose gRPC for good reasons: observable commands, strong typing, bi-directional streams, native discovery - all hard on REST.
- The standard workaround - hand-authoring FDL XML per endpoint - costs hours per Feature and rots when the REST API changes.
- openapi-to-sila2 (v0.1.1, April 2026) automates the full pipeline: OpenAPI spec to validated FDL XML to gRPC stubs to Python types, in one command.
- In the 2026 lab stack, MCP serves AI agents, SiLA2 serves instrument orchestration, and REST serves everything else - openapi-to-sila2 bridges the bottom two layers so REST instruments and services can join SiLA2 deployments without hand-written adapters.
The tool is open source. Try it on one of your instrument APIs, file issues, send pull requests: github.com/qpillars/openapi-to-sila2.
Written by Iacob Marian, Technical Lead and Co-founder at QPillars. Published 2026-04-17.
Technical Lead & Co-founder at QPillars
Iacob builds intelligent software infrastructure for life sciences laboratories, with a focus on Rust for instrument control and agentic AI for lab automation.