The i3X API Gateway for OPC UA — REST, MCP & Real-Time Streaming
FastAPI gateway implementing the i3X API over OPC UA — exposes OPC UA address spaces as standardized REST and MCP endpoints with JSON responses, OpenAPI docs, and Server-Sent Event subscriptions. Built for teams that do not have deep OPC UA expertise.

Industrial Information Interoperability eXchange (i3X)
The Industrial Information Interoperability Exchange (i3X™) is an open, common API initiative proposed to address a growing interoperability challenge in modern manufacturing architectures: manufacturing data silo proliferation and API chaos. As manufacturers adopt heterogeneous software stacks from multiple vendors, the industry risks repeating past fragmentation seen with protocols, platforms, and namespaces - this time at the API layer.
Architecture Overview
flowchart LR
subgraph Consumer[consumer]
Client[i3X Client]
Agent[AI Agent]
end
subgraph Deployment[API Gateway]
Nginx[nginx]
end
subgraph Security[Security Control Plane]
Auth[(External AuthN/AuthZ)]
Certbot[Certbot / ACME Client]
CertStore[(Certificate Store)]
AcmeCA[(ACME CA)]
end
API[FastAPI App]
Client <--> Nginx
Agent <--> Nginx
Nginx --> API
Nginx -. enforces auth .-> Auth
Certbot -. ACME .-> AcmeCA
Certbot --> CertStore
Nginx --> CertStore
Certbot -. renews certs .-> Nginx
subgraph Core[i3x2ua-core]
Routers[Router-Layer /v1 /ua optional /mcp]
Deps[Dependency Layer]
Services[Application Services]
Model[Model Layer]
SubSvc[Subscription Service]
McpRuntime[MCP Runtime]
OpcClient[OPC UA Client]
API --> Routers
Routers --> Deps
Deps --> Services
Deps --> Model
Services --> SubSvc
Services --> McpRuntime
Model --> OpcClient
SubSvc --> OpcClient
end
OpcClient <-->|OPC UA Binary| UaServer[(External OPC UA Server)]
sequenceDiagram
participant C as Client
participant R as /v1 Router
participant D as Dependencies
participant M as Model Cache/Builder
participant O as OPC UA Client
participant U as OPC UA Server
participant S as i3X Subscription Service
Note over C,R: Read current values
C->>R: POST /v1/objects/value
R->>D: get_or_build_model()
D->>M: cache hit? else build()
M-->>D: BuildResult
R->>O: read_values(node_ids)
O->>U: Read/Browse requests
U-->>O: values + metadata
O-->>R: values
R-->>C: bulk success/error envelope
Note over C,R: Streaming subscription
C->>R: POST /v1/subscriptions
R->>S: create_subscription()
S-->>R: subscriptionId
R-->>C: success
C->>R: POST /v1/subscriptions/register
R->>S: register_items(elementIds,maxDepth)
S->>O: native subscribe OR polling loop
O->>U: MonitoredItems or periodic reads
U-->>O: data changes
O-->>S: normalized updates
alt SSE stream (push)
C->>R: POST /v1/subscriptions/stream
R->>S: wait_for_updates(afterSequence)
S-->>R: sequence updates
R-->>C: SSE data events + keepalive
else Polling sync (pull)
C->>R: POST /v1/subscriptions/sync
R->>S: sync(acknowledgeSequence)
S-->>R: pending updates
R-->>C: batch of updates
end
alt Optional current-value write enabled (I3X_ENABLE_WRITES=1)
C->>R: PUT /v1/objects/value
R->>D: get_or_build_model()
D->>M: cache hit? else build()
M-->>D: BuildResult
R->>O: read/write checks + write_value()
O->>U: OPC UA write request
U-->>O: write status
O-->>R: per-item write outcomes
R-->>C: bulk success/error envelope
else Optional current-value write disabled
C->>R: PUT /v1/objects/value
R-->>C: 501 Not Implemented
end
Quick Start
Requirements:
- Python 3.10 to 3.12
- uv
- Optional: running OPC UA server
Install dependencies:
uv sync --extra dev
Start API:
uv run uvicorn i3x_server.main:app --reload --host 127.0.0.1 --port 8000
Start without OPC UA server (PowerShell):
$env:I3X_SKIP_OPCUA_CONNECT="1"
uv run uvicorn i3x_server.main:app --reload --host 127.0.0.1 --port 8000 --loop none
Enable MCP support explicitly when you want the /mcp endpoints and MCP tool catalog to be available:
$env:I3X_ENABLE_MCP="1"
uv run uvicorn i3x_server.main:app --reload --host 127.0.0.1 --port 8000 --loop none
If you do not set I3X_ENABLE_MCP, the app starts without MCP support and /mcp returns 404.
Use encrypted OPC UA with the included sample client certificate (development/testing):
uv run python scripts/generate_opcua_client_cert.py
$env:I3X_OPCUA_SECURITY_MODE="SignAndEncrypt"
$env:I3X_OPCUA_SECURITY_POLICY="Basic256Sha256"
$env:I3X_OPCUA_CLIENT_CERT_PATH="./certs/opcua-client-sample/client-cert.pem"
$env:I3X_OPCUA_CLIENT_KEY_PATH="./certs/opcua-client-sample/client-key.pem"
uv run uvicorn i3x_server.main:app --reload --host 127.0.0.1 --port 8000 --loop none
The OPC UA server may require manually trusting the sample client certificate before the secure session can be established.
If you create your own OPC UA client certificates, ensure they include OPC UA-compatible SAN and key usage fields (application URI, DNS names, clientAuth EKU, and signing/encipherment key usages) and match the server's exposed security mode/policy.
OpenAPI/Swagger:
When running the nginx reverse proxy from docker compose up -d, use http://localhost:8080/ for the HTTP entrypoint or https://localhost:8443/ for the HTTPS entrypoint. 8443 is mapped to nginx's TLS port, so http://localhost:8443/ will return a 400 error by design.
API Surface
Active endpoints are exposed under /v1 for:
- info and metadata (
/info,/namespaces,/objecttypes,/relationshiptypes) - filtered type queries (
/objecttypes/query,/relationshiptypes/query) - object queries and values (
/objects,/objects/list,/objects/related,/objects/value,/objects/history) - subscriptions (
/subscriptions,/subscriptions/register,/subscriptions/unregister,/subscriptions/sync,/subscriptions/list,/subscriptions/delete,/subscriptions/stream)
Current scope emphasis: this implementation prioritizes read/query/subscribe operations and includes optional current-value write support.
Optional OPC UA diagnostic endpoints are exposed under /ua:
- client state and connectivity (
/ua/state,/ua/connection) - session limits and metrics (
/ua/limits,/ua/metrics)
Optional MCP endpoints are exposed only when I3X_ENABLE_MCP=1:
- SSE discovery and JSON-RPC 2.0 entry point (
GET /mcp,POST /mcp) - tool catalog and REST tool call (
/mcp/tools,/mcp/call) - prompt listing, definition, and execution (
/mcp/prompts,/mcp/prompts/{name},/mcp/prompts/execute) - resource listing and reading (
/mcp/resources,/mcp/resources/read) - root listing (
/mcp/roots)
MCP scope emphasis: the MCP bridge supports initialize, tools/list, tools/call, prompts/list, prompts/get, resources/list, resources/read, roots/list, and JSON-RPC 2.0 batch requests.
MCP write policy: PUT routes are intentionally excluded from MCP tool generation. Object writes are available via REST only.
Current Limitations
- historical update APIs are not implemented (
PUT /v1/objects/history,PUT /v1/objects/{element_id}/historyreturn501 Not Implemented) GET /v1/objects/{element_id}/historycurrently returns501 Not Implemented- current-value write support is optional and controlled by
I3X_ENABLE_WRITES:I3X_ENABLE_WRITES=1:capabilities.update.current=true,PUT /v1/objects/valueenabled- default (
I3X_ENABLE_WRITESunset/0): write endpoints return501 Not Implemented
Docker
Quickstart (Docker image):
docker run -d --name i3x2ua-master -p 8080:8000 -e I3X_ENABLE_MCP=1 -e I3X_OPCUA_ENDPOINT=opc.tcp://opcua.umati.app:4843 ghcr.io/andreasheine/i3x2ua:master
Multiline (bash):
docker run -d \
--name i3x2ua-master \
-p 8080:8000 \
-e I3X_ENABLE_MCP=1 \
-e I3X_OPCUA_ENDPOINT=opc.tcp://opcua.umati.app:4843 \
ghcr.io/andreasheine/i3x2ua:master
Run with compose:
docker compose up -d
Build your own image with an explicit server version for /v1/info:
docker build --build-arg BUILD_VERSION=1.1.0 -t i3x2ua:1.1.0 .
If BUILD_VERSION is not set, the API falls back to master.
The stack now starts the API behind an nginx reverse proxy. The app container stays internal, while nginx exposes HTTP and optional HTTPS.
The default compose setup also enables container hardening (read_only, tmpfs, dropped Linux capabilities, no-new-privileges).
Optional environment variables:
I3X_ENABLE_MCP=1to enable MCP support; it is disabled by defaultI3X_ENABLE_WRITES=1to enable current-value writes (PUT /v1/objects/valueandPUT /v1/objects/{element_id}/value)I3X_OPCUA_CERTS_DIR=./certsto mount OPC UA client/server certificate files into the app container (/app/certs)NGINX_HTTPS_ENABLED=1to enable TLS terminationNGINX_SSL_CERTS_DIR=./certswithfullchain.pemandprivkey.pemNGINX_BASIC_AUTH_ENABLED=1withNGINX_BASIC_AUTH_USERandNGINX_BASIC_AUTH_PASSWORDNGINX_SERVER_NAMEfor the public host name
If you enable HTTPS, mount or place the certificate files in the configured cert directory before starting Compose.
If your OPC UA server runs on the Docker host machine, set I3X_OPCUA_ENDPOINT to opc.tcp://host.docker.internal:<port> instead of 127.0.0.1.
For local HTTPS testing with the nginx reverse proxy, generate sample certificates:
uv run python scripts/generate_https_dev_cert.py
Then use these environment values:
NGINX_HTTPS_ENABLED=1NGINX_SSL_CERTS_DIR=./certsNGINX_SSL_CERTIFICATE=/etc/nginx/certs/https-sample/fullchain.pemNGINX_SSL_CERTIFICATE_KEY=/etc/nginx/certs/https-sample/privkey.pem
Development
uv run ruff check .
uv run ruff format .
uv run mypy .
uv run pytest -q
uv run pytest -q --cov=i3x_server --cov-report=term-missing
Production Deployment and i3X Strict Compliance
This application implements the i3X API specification and is designed to run behind a reverse proxy / api gateway that is responsible for:
- TLS termination — the app itself serves plain HTTP; all HTTPS is handled by nginx.
- Authentication and authorization — the app has no built-in auth layer; token validation, basic auth, or mTLS are enforced at the proxy level.
Required reverse proxy responsibilities for strict i3X compliance
| Requirement | Implementation |
|---|---|
| TLS (HTTPS) for all client-facing traffic | nginx ssl_certificate + ssl_certificate_key |
| Client authentication (API key / OAuth / mTLS) | nginx auth_request or satisfy any directives |
| Rate limiting | nginx limit_req_zone |
| Access logging | nginx access_log |
See the NGINX configuration reference and HTTPS guide for details.
License
Open Source License (AGPL-3.0-or-later)
This project is licensed under the GNU Affero General Public License v3.0 or later. See the LICENSE file for the full legal text.
Commercial Licensing
Sponsors at USD 1/month or higher are granted a commercial license while sponsorship remains active, as defined in the sponsor terms. This enables commercial use without AGPL copyleft obligations during active sponsorship.
Sponsor @AndreasHeine via GitHub Sponsors
See COMMERCIAL_LICENSE.md for full grant scope, limits, grace period, and verification details.
Third-Party and Upstream Components
This repository includes third-party and upstream materials with their own licenses, including the bundled i3X/ subtree.
See THIRD_PARTY_NOTICES.md for details.
Contributions
By contributing to this repository, you agree that your contributions are provided under AGPL-3.0-or-later for the open-source distribution and may be included in commercially licensed distributions of this project. See CONTRIBUTING.md for sign-off requirements and contribution workflow.
No comments yet
Be the first to share your take.