A2A wrapper service for opencode
Project description
opencode-a2a-server
Turn OpenCode into a stateful A2A service with a clear runtime boundary.
opencode-a2a-server exposes OpenCode through standard A2A interfaces and adds
the runtime pieces that raw agent runtimes usually do not provide by default:
authentication, session continuity, streaming contracts, interrupt handling,
and explicit security guidance.
Why This Project Exists
OpenCode is useful as an interactive runtime, but applications and gateways need a stable service layer around it. This repository provides that layer by:
- bridging A2A transport contracts to OpenCode session/message/event APIs
- making session and interrupt behavior explicit and auditable
- keeping the server/runtime contract explicit while leaving deployment supervision to the operator
What It Already Provides
- A2A HTTP+JSON endpoints (
/v1/message:send,/v1/message:stream,GET /v1/tasks/{task_id}:subscribe) - A2A JSON-RPC endpoint (
POST /) for standard methods and OpenCode-oriented extensions - SSE streaming with normalized
text,reasoning, andtool_callblocks - session continuation via
metadata.shared.session.id - request-scoped model selection via
metadata.shared.model - OpenCode session query/control extensions and provider/model discovery
- released CLI install/upgrade flow and a foreground runtime entrypoint
Extension Capability Overview
The Agent Card declares six extension URIs. Shared contracts are intended for any compatible consumer; OpenCode-specific contracts stay provider-scoped even though they are exposed through A2A JSON-RPC.
| Extension URI | Scope | Primary use |
|---|---|---|
urn:a2a:session-binding/v1 |
Shared | Bind a main chat request to an existing upstream session via metadata.shared.session.id |
urn:a2a:model-selection/v1 |
Shared | Override the default upstream model for one main chat request |
urn:a2a:stream-hints/v1 |
Shared | Advertise canonical stream metadata for blocks, usage, interrupts, and session hints |
urn:opencode-a2a:session-query/v1 |
OpenCode-specific | Query external sessions and invoke OpenCode session control methods |
urn:opencode-a2a:provider-discovery/v1 |
OpenCode-specific | Discover normalized OpenCode provider/model summaries |
urn:a2a:interactive-interrupt/v1 |
Shared | Reply to interrupt callbacks observed from stream metadata |
Detailed consumption guidance:
- Shared session binding:
docs/guide.md#shared-session-binding-contract - Shared model selection:
docs/guide.md#shared-model-selection-contract - Shared stream hints:
docs/guide.md#shared-stream-hints-contract - OpenCode session query and provider discovery:
docs/guide.md#opencode-session-query--provider-discovery-a2a-extensions - Shared interrupt callback:
docs/guide.md#shared-interrupt-callback-a2a-extension - Compatibility profile and retention guidance:
docs/guide.md#compatibility-profile
Design Principle
One OpenCode + opencode-a2a-server instance pair is treated as a
single-tenant trust boundary.
This repository's intended scaling model is parameterized self-deployment: consumers should launch their own isolated instance pairs instead of sharing one runtime across mutually untrusted tenants.
- OpenCode may manage multiple projects/directories, but one deployed instance is not a secure multi-tenant runtime.
- Shared-instance identity/session checks are best-effort coordination, not hard tenant isolation.
- For mutually untrusted tenants, deploy separate instance pairs with isolated Linux users or containers, isolated workspace roots, isolated credentials, and distinct runtime ports.
Logical Components
flowchart TD
Hub["A2A client / a2a-client-hub / app"] --> Api["opencode-a2a-server transport"]
Api --> Mapping["Task / session / interrupt mapping"]
Mapping --> Runtime["OpenCode HTTP runtime"]
Api --> Auth["Bearer auth + request logging controls"]
Runtime --> Workspace["Shared workspace / environment boundary"]
This repository wraps OpenCode in a service layer. It does not change OpenCode into a hard multi-tenant isolation platform.
Recommended Client Side
If you need a client-side integration layer to consume this service, prefer a2a-client-hub.
It is a better place for client concerns such as A2A consumption, upstream
adapter normalization, and application-facing integration, while
opencode-a2a-server stays focused on the server/runtime boundary around
OpenCode.
Security Model
This project improves the service boundary around OpenCode, but it is not a hard multi-tenant isolation layer.
A2A_BEARER_TOKENprotects the A2A surface, but it is not a tenant isolation boundary inside one deployed instance.- LLM provider keys are consumed by the OpenCode process. Prompt injection or indirect exfiltration attempts may still expose sensitive values.
- Deployment supervision is intentionally BYO. If you wrap this runtime with
systemd, Docker, Kubernetes, or another supervisor, you own the service user, secret storage, restart policy, and hardening choices.
Read before deployment:
User Paths
Released versions are published to PyPI and mapped to Git tags / GitHub Releases. This is the recommended entry point for users.
Path 1: Run a Released CLI in an Existing User Environment
Install the latest release:
uv tool install opencode-a2a-server
Upgrade an existing installation:
uv tool upgrade opencode-a2a-server
Install an exact release:
uv tool install "opencode-a2a-server==<version>"
Run it against an existing project/workspace:
GOOGLE_GENERATIVE_AI_API_KEY=<your-key> \
OPENCODE_PROVIDER_ID=google \
OPENCODE_MODEL_ID=gemini-3.1-pro-preview \
opencode serve
A2A_BEARER_TOKEN=prod-token \
A2A_HOST=127.0.0.1 \
A2A_PORT=8000 \
A2A_PUBLIC_URL=http://127.0.0.1:8000 \
OPENCODE_MANAGED_SERVER=true \
OPENCODE_WORKSPACE_ROOT=/abs/path/to/workspace \
opencode-a2a-server serve
OPENCODE_WORKSPACE_ROOT is the default workspace root that this runtime
exposes to OpenCode.
Default address: http://127.0.0.1:8000
OpenCode upstream modes:
- Managed upstream: set
OPENCODE_MANAGED_SERVER=trueandopencode-a2a-serverwill start a localopencode serve, capture its actual listening URL, and stop it on shutdown. - External upstream: you start and manage
opencode serveyourself, then pointOPENCODE_BASE_URLat that HTTP endpoint.
Common runtime variables:
| Variable | Required | Default | Purpose |
|---|---|---|---|
A2A_BEARER_TOKEN |
Yes | None | Bearer token required for authenticated runtime requests. |
OPENCODE_BASE_URL |
No | http://127.0.0.1:4096 |
Upstream OpenCode HTTP endpoint for externally managed upstream mode. |
OPENCODE_MANAGED_SERVER |
No | false |
Start and manage a local opencode serve child process. |
OPENCODE_MANAGED_SERVER_HOST |
No | 127.0.0.1 |
Bind host used when managed upstream mode is enabled. |
OPENCODE_MANAGED_SERVER_PORT |
No | auto-pick | Bind port used when managed upstream mode is enabled. |
OPENCODE_COMMAND |
No | opencode |
OpenCode CLI executable used for managed upstream mode. |
OPENCODE_STARTUP_TIMEOUT |
No | 20 |
Seconds to wait for managed upstream startup. |
OPENCODE_WORKSPACE_ROOT |
No | None | Default workspace root exposed to OpenCode. |
OPENCODE_PROVIDER_ID |
No | None | Default provider for the upstream runtime. |
OPENCODE_MODEL_ID |
No | None | Default model for the upstream runtime. Set together with OPENCODE_PROVIDER_ID. |
A2A_HOST |
No | 127.0.0.1 |
Bind host for the A2A server. |
A2A_PORT |
No | 8000 |
Bind port for the A2A server. |
A2A_PUBLIC_URL |
No | http://127.0.0.1:8000 |
Public base URL advertised by the Agent Card. |
A2A_LOG_LEVEL |
No | WARNING |
Server log level. |
A2A_LOG_PAYLOADS |
No | false |
Enable request/response payload logging. |
A2A_LOG_BODY_LIMIT |
No | 0 |
Payload preview size used when payload logging is enabled. |
A2A_MAX_REQUEST_BODY_BYTES |
No | 1048576 |
Maximum accepted request size. |
A2A_ALLOW_DIRECTORY_OVERRIDE |
No | true |
Allow request-level metadata.opencode.directory overrides. |
A2A_ENABLE_SESSION_SHELL |
No | false |
Enable high-risk opencode.sessions.shell. |
OPENCODE_TIMEOUT |
No | 120 |
Upstream OpenCode request timeout in seconds. |
OPENCODE_TIMEOUT_STREAM |
No | None | Upstream OpenCode stream timeout override in seconds. |
If you omit OPENCODE_PROVIDER_ID / OPENCODE_MODEL_ID, opencode serve
uses your local OpenCode defaults (for example ~/.config/opencode/opencode.json).
When OPENCODE_MANAGED_SERVER=true, OPENCODE_BASE_URL is ignored and the
runtime binds itself to the managed child process instead.
For provider-specific auth, model IDs, and config details, use the OpenCode official docs and CLI:
- Providers: https://opencode.ai/docs/providers/
- Models: https://opencode.ai/docs/models/
- Local checks:
opencode auth list,opencode models,opencode models <provider>
This path is for users who already manage their own shell, workspace, and process lifecycle.
Use any supervisor you prefer for long-running operation:
systemd- Docker / container runtimes
- Kubernetes
supervisord,pm2, or similar process managers
The project no longer ships built-in host bootstrap or process-manager wrappers. The official product surface is the runtime entrypoint itself.
Minimal self-managed systemd example:
- Create an env file such as
/etc/opencode-a2a/alpha.env:
A2A_BEARER_TOKEN=replace-me
A2A_HOST=127.0.0.1
A2A_PORT=8000
A2A_PUBLIC_URL=https://a2a.example.com
OPENCODE_MANAGED_SERVER=true
OPENCODE_WORKSPACE_ROOT=/srv/my-workspace
- Create a unit file such as
/etc/systemd/system/opencode-a2a-server.service:
[Unit]
Description=OpenCode A2A Server
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
WorkingDirectory=/srv/my-workspace
EnvironmentFile=/etc/opencode-a2a/alpha.env
ExecStart=/home/dev/.local/bin/opencode-a2a-server serve
Restart=on-failure
RestartSec=2
[Install]
WantedBy=multi-user.target
Replace ExecStart with the absolute path returned by command -v opencode-a2a-server.
Minimal managed-upstream foreground example:
A2A_BEARER_TOKEN=dev-token \
A2A_HOST=127.0.0.1 \
A2A_PORT=8000 \
A2A_PUBLIC_URL=http://127.0.0.1:8000 \
OPENCODE_MANAGED_SERVER=true \
OPENCODE_WORKSPACE_ROOT=/abs/path/to/workspace \
opencode-a2a-server serve
Advanced: externally managed upstream
Use this mode when you intentionally want opencode serve and
opencode-a2a-server to be supervised independently.
OPENCODE_BASE_URL=http://127.0.0.1:4096 \
A2A_BEARER_TOKEN=dev-token \
A2A_HOST=127.0.0.1 \
A2A_PORT=8000 \
A2A_PUBLIC_URL=http://127.0.0.1:8000 \
OPENCODE_WORKSPACE_ROOT=/abs/path/to/workspace \
opencode-a2a-server serve
Migration notes:
OPENCODE_DIRECTORYhas been removed. UseOPENCODE_WORKSPACE_ROOT.- Built-in
init-release-system,deploy-release, anduninstall-instancehave been removed. - Secret storage, service users, restart policy, and supervisor configuration are now operator-managed.
Contributor Paths
Use the repository checkout directly only for development, local debugging, or validation against unreleased changes.
Quick source run:
uv sync --all-extras
GOOGLE_GENERATIVE_AI_API_KEY=<your-key> \
OPENCODE_PROVIDER_ID=google \
OPENCODE_MODEL_ID=gemini-3.1-pro-preview \
opencode serve
A2A_BEARER_TOKEN=dev-token \
OPENCODE_BASE_URL=http://127.0.0.1:4096 \
A2A_HOST=127.0.0.1 \
A2A_PORT=8000 \
OPENCODE_WORKSPACE_ROOT=/abs/path/to/workspace \
uv run opencode-a2a-server serve
Baseline validation:
uv run pre-commit run --all-files
uv run pytest
Documentation Map
User Docs
- docs/guide.md Product behavior, API contracts, and detailed streaming/session/interrupt consumption guidance.
- SECURITY.md Threat model, deployment caveats, and vulnerability disclosure guidance.
- CONTRIBUTING.md Contributor workflow, validation baseline, and documentation expectations.
- scripts/README.md Contributor helper script index.
License
Apache-2.0. See LICENSE.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file opencode_a2a_server-0.2.3.tar.gz.
File metadata
- Download URL: opencode_a2a_server-0.2.3.tar.gz
- Upload date:
- Size: 183.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4a878531718f96d03a1dd4d642a9e28f59752bb797c6f57ee925432f8e8cd1cc
|
|
| MD5 |
87b050b6414180ed1ef1996f36aaf864
|
|
| BLAKE2b-256 |
6007e697ac6c1e15138e1d7b4946c11c8cbe06d6864c1bf3b8371e27e6f83f89
|
Provenance
The following attestation bundles were made for opencode_a2a_server-0.2.3.tar.gz:
Publisher:
publish.yml on Intelligent-Internet/opencode-a2a-server
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
opencode_a2a_server-0.2.3.tar.gz -
Subject digest:
4a878531718f96d03a1dd4d642a9e28f59752bb797c6f57ee925432f8e8cd1cc - Sigstore transparency entry: 1131135505
- Sigstore integration time:
-
Permalink:
Intelligent-Internet/opencode-a2a-server@c30d2edbff622fd4670253584e534699ac4b3276 -
Branch / Tag:
refs/tags/v0.2.3 - Owner: https://github.com/Intelligent-Internet
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@c30d2edbff622fd4670253584e534699ac4b3276 -
Trigger Event:
push
-
Statement type:
File details
Details for the file opencode_a2a_server-0.2.3-py3-none-any.whl.
File metadata
- Download URL: opencode_a2a_server-0.2.3-py3-none-any.whl
- Upload date:
- Size: 61.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
78639e9efb68b868ba8c892aac44d0fc04c377de130fe25d7cafd6112245766e
|
|
| MD5 |
1130b3daefa57c4eace048e321e01a9a
|
|
| BLAKE2b-256 |
f584f66e3acfa9ccdc7f71588370f60166f8ee6ef2cc0a5ed16134ee833750b1
|
Provenance
The following attestation bundles were made for opencode_a2a_server-0.2.3-py3-none-any.whl:
Publisher:
publish.yml on Intelligent-Internet/opencode-a2a-server
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
opencode_a2a_server-0.2.3-py3-none-any.whl -
Subject digest:
78639e9efb68b868ba8c892aac44d0fc04c377de130fe25d7cafd6112245766e - Sigstore transparency entry: 1131135568
- Sigstore integration time:
-
Permalink:
Intelligent-Internet/opencode-a2a-server@c30d2edbff622fd4670253584e534699ac4b3276 -
Branch / Tag:
refs/tags/v0.2.3 - Owner: https://github.com/Intelligent-Internet
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@c30d2edbff622fd4670253584e534699ac4b3276 -
Trigger Event:
push
-
Statement type: