██████╗ ██████╗ ███████╗███╗ ██╗ ██████╗ ██████╗ ██████╗ ███████╗ █████╗ ██████╗ █████╗
██╔═══██╗██╔══██╗██╔════╝████╗ ██║██╔════╝██╔═══██╗██╔══██╗██╔════╝ ██╔══██╗╚════██╗██╔══██╗
██║ ██║██████╔╝█████╗ ██╔██╗ ██║██║ ██║ ██║██║ ██║█████╗█████╗███████║ █████╔╝███████║
██║ ██║██╔═══╝ ██╔══╝ ██║╚██╗██║██║ ██║ ██║██║ ██║██╔══╝╚════╝██╔══██║██╔═══╝ ██╔══██║
╚██████╔╝██║ ███████╗██║ ╚████║╚██████╗╚██████╔╝██████╔╝███████╗ ██║ ██║███████╗██║ ██║
╚═════╝ ╚═╝ ╚══════╝╚═╝ ╚═══╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝
Expose OpenCode through A2A.
opencode-a2a adds an A2A runtime layer to opencode serve, with auth, streaming, session continuity, interrupt handling, and a clear deployment boundary.
- An A2A adapter service built on
opencode serve, with inbound runtime exposure plus outbound peer calling. - It supports both roles in one process: serving as an A2A Server and hosting an embedded A2A Client for
a2a_call.
flowchart TD
External["A2A Clients / a2a-client-hub / Gateways"]
subgraph Adapter["opencode-a2a Runtime"]
Ingress["Inbound A2A Surface\nREST + JSON-RPC"]
OpenCode["OpenCode Runtime"]
Outbound["Embedded A2A Client\na2a_call"]
end
subgraph Peers["Peer A2A services"]
PeerA2A["Peer A2A Agent"]
PeerRuntime["Peer OpenCode Runtime"]
PeerA2A --> PeerRuntime
end
External -->|SendMessage,\nSendStreamingMessage| Ingress
Ingress -->|tool call| OpenCode
OpenCode -->|model/tool result events| Ingress
Ingress -->|a2a_call| Outbound
Outbound -->|SendMessage,\nSendStreamingMessage| PeerA2A
PeerA2A -->|tool result| Outbound
PeerRuntime -->|task session\nexecution| PeerA2A
From a checkout, run make help for the available commands:
make install # Install the locked development environment
make run # Start only the gateway in the background
make status
make stop # Stop only that managed gateway
make test
make lint
make check # Full doctor gate
make build
make conformance
make clean # Build/test caches only; preserves runtime state and databasesmake run requires your existing gateway configuration in the environment or a
protected, untracked .env, including A2A_STATIC_AUTH_CREDENTIALS. It does not
start OpenCode, generate credentials, or enable V2 execution. The V2 private
deployment requirements below still apply; run it inside that deployment when
using OPENCODE_V2_EXECUTION=exclusive.
The run, stop, and status convenience targets require Linux with PID file
descriptor support. They keep private process metadata and logs under
run/gateway/ (override with RUN_DIR=/path/to/private/state). Duplicate starts
reuse the tracked process; stop validates its identity and never kills by name
or port. Treat this as a local convenience, not a production supervisor.
Breaking default change: V2 is now the default upstream API, but A2A
execution remains disabled until OPENCODE_V2_EXECUTION=exclusive is explicitly
set under the private deployment contract.
This does not enable unrestricted shared sessions. Existing V1 deployments must
set OPENCODE_API_VERSION=v1 before upgrading; there is no automatic fallback.
The quick start below uses that explicit V1 compatibility mode, not V2 deployment.
Install the released CLI with uv tool:
uv tool install opencode-a2aUpgrade later with:
uv tool upgrade opencode-a2aMake sure provider credentials and a default model are configured on the OpenCode side, then start OpenCode:
opencode auth login
opencode models
opencode serve --hostname 127.0.0.1 --port 4096Treat the deployed OpenCode user's HOME/XDG config directories as part of the runtime state. If a packaged or service-managed deployment appears to ignore fresh provider env vars, inspect that user's persisted OpenCode auth/config files before assuming the A2A adapter layer is overriding credentials.
Then start opencode-a2a against that upstream:
DEMO_BEARER_TOKEN="$(python3 -c 'import secrets; print(secrets.token_hex(24))')"
A2A_STATIC_AUTH_CREDENTIALS='[{"scheme":"bearer","token":"'"${DEMO_BEARER_TOKEN}"'","principal":"automation"}]' \
OPENCODE_API_VERSION=v1 \
OPENCODE_BASE_URL=http://127.0.0.1:4096 \
A2A_TASK_STORE_DATABASE_URL=sqlite+aiosqlite:///./opencode-a2a.db \
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 serveVerify that the service is up:
curl http://127.0.0.1:8000/.well-known/agent-card.jsonThe following capability list describes explicit OpenCode API V1 compatibility
mode (OPENCODE_API_VERSION=v1). With the default V2 API, only internal
experimental session transport is available: A2A message execution fails before upstream requests, streaming and
all extensions are disabled. A separate opt-in
OPENCODE_V2_EXECUTION=exclusive profile
adds restricted text execution and streaming read/question interventions in a
dedicated private instance pair. It does not support shared upstream sessions,
arbitrary tools, model overrides, or automatic recovery after event loss. Read
the deployment preconditions and compatibility matrix
before enabling it.
- A2A HTTP+JSON endpoints such as
/message:sendand/message:stream - A2A JSON-RPC support on
POST / - SDK-owned A2A task surfaces such as
GET /tasks, task push notification config routes, JSON-RPCGetExtendedAgentCard, and HTTPGET /extendedAgentCard - Peering capabilities: can act as a client via
opencode-a2a call - Autonomous tool execution: supports
a2a_calltool for outbound agent-to-agent communication - SSE streaming with normalized
text,reasoning, andtool_callblocks - Session continuity through
metadata.shared.session.id - Request-scoped model selection through
metadata.shared.model - OpenCode-oriented JSON-RPC extensions for session and model/provider queries
- Authenticated Prometheus-compatible process metrics at
GET /metrics
- Supported A2A protocol line:
1.0 - The runtime is now v1-only across HTTP+JSON, JSON-RPC, Agent Card discovery, and protocol-aware error contracts.
- Legacy
0.3method aliases and payload shapes are rejected instead of being normalized at runtime. - The detailed runtime contract and machine-readable support boundary are documented in
docs/guide.mdanddocs/compatibility.md.
opencode-a2a supports a "Peering Node" architecture where a single process handles both inbound (Server) and outbound (Client) A2A traffic.
Interact with other A2A agents directly from the command line:
# Using the target peer agent's Bearer token via environment injection
A2A_CLIENT_BEARER_TOKEN=your-outbound-token \
opencode-a2a call http://other-agent:8000/.well-known/agent-card.json "How are you?"
# Using the target peer agent's Basic auth via environment injection
# Accepts raw user:pass or its base64-encoded value
A2A_CLIENT_BASIC_AUTH="user:pass" \
opencode-a2a call http://other-agent:8000/.well-known/agent-card.json "How are you?"The server can autonomously execute a2a_call(url, message) tool calls emitted by the OpenCode runtime. Results are fetched via A2A and returned to the model as tool results, enabling multi-agent orchestration.
When the target peer agent requires bearer auth, configure A2A_CLIENT_BEARER_TOKEN for server-side outbound calls. When the target peer agent requires Basic auth, use A2A_CLIENT_BASIC_AUTH. These outbound credentials apply to the peer specified by opencode-a2a call or a2a_call(url, message), not to this service's inbound A2A_STATIC_AUTH_CREDENTIALS. The CLI intentionally reads outbound credentials from environment variables only, so secrets do not appear in shell history or process arguments.
Server-side outbound client settings are fully wired through runtime config: A2A_CLIENT_TIMEOUT_SECONDS, A2A_CLIENT_CARD_FETCH_TIMEOUT_SECONDS, A2A_CLIENT_USE_CLIENT_PREFERENCE, A2A_CLIENT_BEARER_TOKEN, A2A_CLIENT_BASIC_AUTH, and A2A_CLIENT_SUPPORTED_TRANSPORTS.
Detailed protocol contracts, examples, and extension docs live in docs/guide.md.
Use this project when:
- you want to keep OpenCode as the runtime
- you need A2A transports and Agent Card discovery
- you want a thin service boundary instead of building your own adapter
Look elsewhere if:
- you need hard multi-tenant isolation inside one shared runtime
- you want this project to manage your process supervisor or host bootstrap
- you want a general client integration layer rather than a server wrapper
For client-side integration, prefer a2a-client-hub.
This repository improves the service boundary around OpenCode, but it does not turn OpenCode into a hardened multi-tenant platform.
A2A_STATIC_AUTH_CREDENTIALSprotects the A2A surface.- Provider auth and default model configuration remain on the OpenCode side; deployment-time precedence details and HOME/XDG state impact are documented in docs/guide.md.
- Use
A2A_CLIENT_BEARER_TOKENfor server-side outbound peer calls initiated bya2a_call. - Deployment supervision is intentionally BYO. Use
systemd, Docker, Kubernetes, or another supervisor if you need long-running operation. - The supported persistence profile runs one
opencode-a2aapplication process against its own local SQLite database. Do not run multiple Uvicorn workers or replicas against the same database file. - PostgreSQL and other SQLAlchemy dialects are not part of the supported deployment matrix.
- For mutually untrusted tenants, run separate instance pairs with isolated users, containers, workspaces, credentials, and ports.
Read before deployment:
- docs/architecture.md Service responsibility boundaries and request flow.
- docs/maintainer-architecture.md Internal module boundaries and maintainer call chains.
- docs/compatibility.md Compatibility-sensitive surface and contract-honesty guidance.
- docs/guide.md Usage guide, transport details, streaming behavior, extensions, and examples.
- docs/security-architecture.md Security surface, boundaries, and residual-risk register.
- docs/conformance.md Repository-owned black-box compatibility probes and artifact handling.
- SECURITY.md Threat model, deployment caveats, and vulnerability disclosure guidance.
For contributor workflow, local validation, and helper scripts, see CONTRIBUTING.md and scripts/README.md.
Apache-2.0. See LICENSE.