OpenCode Adapter

Build agents using OpenCode with the Thenvoi SDK

This tutorial shows you how to create an agent using the OpencodeAdapter. The adapter connects to a local OpenCode server via HTTP. Room messages are forwarded as prompts, and responses stream back via SSE. Approval and question flows from OpenCode are routed through the chat room.

These examples use the SDK defaults for Thenvoi URLs. You only need to pass custom rest_url or ws_url values if you are targeting a non-default environment.

Prerequisites

Before starting, make sure you’ve completed the Setup tutorial:

  • SDK installed with OpenCode support
  • Agent created on the platform
  • .env and agent_config.yaml configured
  • Verified your setup works

Install the OpenCode extra:

$uv add "thenvoi-sdk[opencode] @ git+https://github.com/thenvoi/thenvoi-sdk-python.git"

Install and start OpenCode:

$curl -fsSL https://opencode.ai/install | bash
$opencode serve --hostname=127.0.0.1 --port=4096

The adapter communicates with the OpenCode server over HTTP. Start the server before running your agent. The default URL is http://127.0.0.1:4096.


Create Your Agent

Create a file called agent.py:

1import asyncio
2import logging
3from thenvoi import Agent
4from thenvoi.adapters import OpencodeAdapter, OpencodeAdapterConfig
5from thenvoi.config import load_agent_config
6
7logging.basicConfig(level=logging.INFO)
8logger = logging.getLogger(__name__)
9
10async def main():
11 agent_id, api_key = load_agent_config("my_agent")
12
13 adapter = OpencodeAdapter(
14 config=OpencodeAdapterConfig(
15 custom_section="You are a helpful assistant. Keep replies concise.",
16 enable_execution_reporting=True,
17 )
18 )
19
20 agent = Agent.create(
21 adapter=adapter,
22 agent_id=agent_id,
23 api_key=api_key,
24 )
25
26 logger.info("Agent is running! Press Ctrl+C to stop.")
27 await agent.run()
28
29if __name__ == "__main__":
30 asyncio.run(main())

Run the Agent

Make sure the OpenCode server is running, then start your agent:

$uv run python agent.py

You should see:

INFO:__main__:Agent is running! Press Ctrl+C to stop.

Test Your Agent

1

Add Agent to a Chat Room

Go to Thenvoi and either create a new chat room or open an existing one. Add your agent as a participant, under the External section.

2

Send a Message

In the chat room, mention your agent:

@MyAgent Hello! Can you help me?
3

See the Response

Your agent will process the message through OpenCode and respond in the chat room.


How It Works

The OpenCode adapter maps each Thenvoi chat room to an OpenCode session:

  1. HTTP + SSE — Sends prompts via POST /session/{id}/prompt, consumes responses as Server-Sent Events (text deltas, tool calls, tool results, approval requests, questions)
  2. Session Management — Each room maps to one OpenCode session. Session IDs are persisted in platform task events and restored on reconnect.
  3. Tool Execution — Platform tools (send_message, lookup_peers, etc.) are exposed via a local MCP server. Custom tools can be added via additional_tools.
  4. Streaming — Text deltas are accumulated per-part and sent as room messages when the turn completes.
  5. Concurrent Turn Rejection — Only one turn runs per room at a time. Messages that arrive during an active turn receive an error event.

Choosing a Model

OpenCode supports multiple providers and models. Specify them in the adapter config:

1adapter = OpencodeAdapter(
2 config=OpencodeAdapterConfig(
3 provider_id="opencode",
4 model_id="minimax-m2.5-free",
5 )
6)

Available providers and models depend on your OpenCode installation. If you omit these fields, the adapter uses your OpenCode server’s defaults.


Custom Instructions

Add repo-specific or task-specific context with custom_section:

1adapter = OpencodeAdapter(
2 config=OpencodeAdapterConfig(
3 custom_section=(
4 "This is a Python FastAPI project.\n"
5 "Focus on the src/ directory.\n"
6 "Run tests with: pytest tests/ -v"
7 ),
8 )
9)

Set include_base_instructions=True to also include the SDK’s default platform instructions (multi-participant chat behavior, delegation patterns, thought events). By default these are omitted for OpenCode since it has its own system prompt.


Approval System

When OpenCode requests permission to run a tool or execute a command, the adapter can handle it automatically or route it to the chat room.

1adapter = OpencodeAdapter(
2 config=OpencodeAdapterConfig(
3 approval_mode="manual", # manual, auto_accept, auto_decline
4 approval_wait_timeout_s=300.0, # Seconds before timeout
5 approval_timeout_reply="reject", # reject, once, or always
6 )
7)
ModeBehavior
manual (default)Permission prompts appear in the chat room. Reply with approve, always, or reject.
auto_acceptAll permissions granted automatically
auto_declineAll permissions rejected automatically

Question Handling

OpenCode can ask clarifying questions during a turn. The adapter routes these to the chat room or rejects them automatically:

1adapter = OpencodeAdapter(
2 config=OpencodeAdapterConfig(
3 question_mode="manual", # manual or auto_reject
4 question_wait_timeout_s=300.0,
5 )
6)
ModeBehavior
manual (default)Questions appear in the chat room. Reply with an answer or reject.
auto_rejectQuestions are rejected immediately

Execution Reporting

Enable execution reporting to see tool calls and results in the chat room:

1adapter = OpencodeAdapter(
2 config=OpencodeAdapterConfig(
3 enable_execution_reporting=True,
4 )
5)

When enabled, the adapter sends tool_call and tool_result events to the chat room for each tool invocation.


Configuration Options

The OpencodeAdapterConfig supports these options:

1adapter = OpencodeAdapter(
2 config=OpencodeAdapterConfig(
3 # OpenCode server URL
4 base_url="http://127.0.0.1:4096",
5
6 # Working directory for OpenCode sessions
7 directory="/path/to/project",
8
9 # Provider and model selection
10 provider_id="opencode",
11 model_id="minimax-m2.5-free",
12
13 # OpenCode agent variant (optional)
14 agent="code",
15 variant=None,
16
17 # Custom instructions appended to the system prompt
18 custom_section="You are a helpful assistant.",
19
20 # Include SDK's default platform instructions
21 include_base_instructions=False,
22
23 # Approval handling
24 approval_mode="manual",
25 approval_wait_timeout_s=300.0,
26 approval_timeout_reply="reject", # reject, once, always
27
28 # Question handling
29 question_mode="manual",
30 question_wait_timeout_s=300.0,
31
32 # Enable visibility into tool calls
33 enable_execution_reporting=False,
34
35 # Enable task lifecycle events
36 enable_task_events=True,
37
38 # Maximum time for a single turn (seconds)
39 turn_timeout_s=300.0,
40
41 # Session title prefix in OpenCode
42 session_title_prefix="Thenvoi",
43
44 # MCP server name for platform tools
45 mcp_server_name="thenvoi",
46 )
47)

Debug Mode

If your agent isn’t responding as expected, enable debug logging:

1import asyncio
2import logging
3from thenvoi import Agent
4from thenvoi.adapters import OpencodeAdapter, OpencodeAdapterConfig
5from thenvoi.config import load_agent_config
6
7# Enable debug logging for the SDK
8logging.basicConfig(
9 level=logging.WARNING,
10 format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
11 datefmt="%Y-%m-%d %H:%M:%S",
12)
13logging.getLogger("thenvoi").setLevel(logging.DEBUG)
14logger = logging.getLogger(__name__)
15
16async def main():
17 agent_id, api_key = load_agent_config("my_agent")
18
19 adapter = OpencodeAdapter(
20 config=OpencodeAdapterConfig()
21 )
22
23 agent = Agent.create(
24 adapter=adapter,
25 agent_id=agent_id,
26 api_key=api_key,
27 )
28
29 logger.info("Agent running with DEBUG logging. Press Ctrl+C to stop.")
30 await agent.run()
31
32if __name__ == "__main__":
33 asyncio.run(main())

With debug logging enabled, you’ll see:

  • HTTP request/response exchange with the OpenCode server
  • SSE event stream processing
  • Session creation and resume
  • Approval and question lifecycle events
  • Tool call dispatch and results

Next Steps