docs
// Guides

OpenCode Integration

Register Erebine as a custom provider in opencode.json, point it at your endpoint, and drive your own models from the terminal. Streaming, tool calls, and reasoning content travel through the standard OpenAI-compatible adapter.

Overview

OpenCode is a terminal-based AI coding assistant in the same lane as Claude Code and Aider. It loads custom providers through the @ai-sdk/openai-compatible npm adapter, which talks to any OpenAI-compatible API.

Erebine exposes OpenAI-compatible Chat Completions and Responses API endpoints, so OpenCode loads as a provider entry, not a fork. Point it at your endpoint, supply an API key, and drive your own models from the terminal.

Zero adapter code. The integration is a JSON entry under provider.erebine; OpenCode pulls @ai-sdk/openai-compatible from npm on first run and uses it for streaming, tool calling, and reasoning content.

Prerequisites

  • An OpenCode installation (npm, brew, or binary)
  • A Erebine account with an active endpoint
  • An API key with inference scope

Installing OpenCode

npm
npm install -g opencode
Homebrew
brew install opencode

Creating an API Key

In your Erebine dashboard, navigate to Settings > API Keys and create a new key with the inference scope. Copy the key, you will need it for the configuration file.

Finding Your Endpoint URL

Your endpoint URL follows the format:

URL Format
https://api.erebine.ai/proj_ABC123/WORKSPACE_ID/ENDPOINT_SLUG/v1

Replace WORKSPACE_ID with your ws_-prefixed workspace ID and ENDPOINT_SLUG with the slug shown on your endpoint's detail page. The project ID (proj_ABC123) is visible in your dashboard URL and project settings. The workspace prefix scopes the request to that workspace; this is the form the downloaded config uses.

To route across every generative endpoint in a workspace instead of naming one slug, use the semantic-routed base URL. The router picks the endpoint that best matches each request:

https://api.erebine.ai/proj_ABC123/WORKSPACE_ID/_semantic/router/v1

Replace WORKSPACE_ID with your ws_-prefixed workspace ID. See Semantic Router for the full request shape.

The router picks the endpoint per request, so the serving model can change between turns of one session. The X-Erebine-Routed-Model response header names what served each turn. A session that replays its history verbatim stays on one model without any extra configuration; see Pin a Conversation for what changes that. Do not pin a fixed X-Conversation-Id in options.headers -- a config header is one constant value, and every session would share one affinity key.

Configuration

Create or edit the OpenCode configuration file at ~/.config/opencode/opencode.json. The following example configures Erebine as a custom provider:

~/.config/opencode/opencode.json
{ "$schema": "https://opencode.ai/config.json", "compaction": { "auto": true, "prune": true }, "instructions": "You are working inside a Erebine workspace... (see Instructions section)", "lsp": true, "provider": { "erebine": { "npm": "@ai-sdk/openai-compatible", "name": "Erebine - proj_ABC123", "options": { "baseURL": "https://api.erebine.ai/proj_ABC123/WORKSPACE_ID/my-endpoint/v1", "apiKey": "ere_my-project_abc123", "headers": { "X-Erebine-Augment-Corrective-Retries": "on" } }, "models": { "my-endpoint-slug": { "name": "deepseek-r1-distill-llama-70b", "limit": { "context": 131072, "output": 8192 }, "modalities": { "input": ["text", "image"], "output": ["text"] }, "tool_call": true, "reasoning": true, "temperature": true, "options": { "reasoningEffort": "medium", "reasoningSummary": "auto", "textVerbosity": "medium" } } } } } }

Replace placeholder values. Substitute WORKSPACE_ID with your ws_-prefixed workspace ID, my-endpoint with your endpoint slug, ere_my-project_abc123 with your actual API key, and deepseek-r1-distill-llama-70b with the model name deployed on your endpoint. The per-model dictionary key (my-endpoint-slug) becomes the second half of the OpenCode model id, the full id has the shape erebine/<endpoint-slug>.

Auth lives in options.apiKey. The @ai-sdk/openai-compatible adapter reads the bearer token from options.apiKey and synthesises the Authorization: Bearer ... header itself. Putting the key in options.headers.Authorization is silently ignored by the adapter and the endpoint returns 401.

Capabilities are data-driven. Every model carries tool_call, reasoning, and temperature capability booleans resolved from the deployed model's family. The options block (reasoningEffort / reasoningSummary / textVerbosity) appears only for models that expose a reasoning parser; non-reasoning models report reasoning: false and omit options. The bundle does not emit supportedReasoningEfforts or a variants map, neither is part of the OpenCode model schema.

Responses API Endpoint

OpenCode can also use the Responses API endpoint for multi-turn conversations with server-managed state. The base URL is the same, the OpenAI SDK automatically selects the correct endpoint based on the method called (client.responses.create() vs client.chat.completions.create()).

URL Format (Responses API)
https://api.erebine.ai/proj_ABC123/ENDPOINT_SLUG/v1/responses

No configuration changes are required in opencode.json to use the Responses API. The same baseURL serves both Chat Completions and Responses endpoints.

Per-Project Configuration

You can also place an opencode.json file in your project root. Project-level configuration overrides the global config at ~/.config/opencode/opencode.json.

MCP

OpenCode also speaks the Model Context Protocol over streamable HTTP. Add the Erebine MCP server to the same opencode.json file under the mcp.servers map and the full workspace tool catalog (memory, intelligence, artifacts, code, governed execution) becomes available to the agent on the next session.

~/.config/opencode/opencode.json (MCP block)
{ "mcp": { "erebine": { "type": "remote", "url": "https://api.erebine.ai/proj_ABC123/v1/mcp", "enabled": true, "oauth": false, "timeout": 30000, "headers": { "Authorization": "Bearer ere_my-project_abc123", "X-Erebine-Workspace": "<workspace_external_id>" } } } }

oauth: false and timeout: 30000 are load-bearing. With oauth unset, OpenCode runs an OAuth discovery probe against the MCP URL before falling back to header auth. Erebine does not speak OAuth, so the probe burns the connect budget on a dead end. The default tools/list budget of 5000 ms is tight for a cold-path workspace-scoped catalog; timeout: 30000 gives the cold path room without stalling warm-path failures.

Replace placeholders with values from your project. <workspace_external_id> is the ws_-prefixed workspace identifier, not the project id already present in the URL; list candidates with erectl workspaces list. The MCP block coexists peacefully with the provider.erebine block above; chat-completions calls continue to flow through the OpenAI-compatible adapter while tool-call traffic reaches the workspace surface over MCP.

See the general MCP integration documentation for the full tool catalog, the governed-execution model, and the security gates that apply to every MCP request.

Compaction

The downloaded bundle emits a top-level compaction block:

"compaction": { "auto": true, "prune": true }

auto tells OpenCode to summarise older turns once the session approaches the model's context ceiling; prune lets OpenCode drop redundant tool-call output from the summarised transcript. The defaults match Erebine's posture on auto-summarisation. Set either flag to false if you need verbatim transcript retention (long debugging sessions, audit replay). Expect higher token spend on long sessions in that mode.

Instructions

The downloaded bundle inlines a top-level instructions string. OpenCode prepends that string to every session's system prompt before the host model sees the first user turn. The bundled content is a short project-rules block that nudges the model to invoke the workspace MCP tools (memory, intelligence, artifacts, code, governed execution) rather than ask the user, and to persist load-bearing decisions back into the workspace.

Operators may edit or remove the instructions string in their saved opencode.json. Re-downloading the bundle restores the canonical wording. If a future session feels over-eager to call tools, trim or rewrite the block locally, the field is plain text and OpenCode treats it as a verbatim system prefix.

Field Reference

The following tables describe each field in the downloaded configuration bundle.

// providerProvider Options

Field Type Description
npm string The npm adapter package. Always @ai-sdk/openai-compatible for Erebine.
name string Human-readable provider label shown in the OpenCode picker. The downloaded bundle uses Erebine - <workspace_external_id>. This is a display name only; the provider id used in model ids is the JSON key (erebine).
options.baseURL string Erebine endpoint URL including project ID, workspace ID, and endpoint slug. Must end with /v1.
options.apiKey string Bearer API key. The adapter reads this field and synthesises the Authorization header. Do NOT put the key under options.headers.Authorization, the adapter does not read from there.
options.headers object Constant headers sent on every request. The downloaded bundle ships X-Erebine-Augment-Corrective-Retries: on here so a malformed or empty model turn is retried a bounded number of times. Add or remove augmentation headers to taste; do NOT put the API key here (see options.apiKey).

// modelModel Options

The per-model dictionary key (e.g., my-endpoint-slug) is the local alias OpenCode shows in its picker and the second half of the provider/model id (the full id has the shape erebine/<endpoint-slug>). The name field beneath it is the deployed model identifier the inference endpoint expects to see in the request body.

Field Type Description
name string Deployed model identifier sent in the request body. Must exactly match the model name shown on the endpoint detail page.
limit.context integer Maximum context-window size in tokens. Emitted when the endpoint advertises a context ceiling.
limit.output integer Maximum output tokens per request. Emitted when the endpoint advertises an output ceiling.
modalities object Declared input and output modalities for the endpoint's model, derived from the capabilities the model advertises. OpenCode treats a custom provider's models as text-only unless this block declares otherwise, so a vision model must list image before OpenCode will accept an image attachment. Semantic-routed bundles advertise the union across the routed models; the Semantic Router dispatches each request to a model that covers it.
tool_call boolean Whether the deployed model's family emits structured tool calls. Asserting it lets OpenCode treat the model as tool-capable for this custom provider.
reasoning boolean true when the deployed model exposes a reasoning parser, false otherwise.
temperature boolean Whether the model accepts the temperature parameter. Emitted as true.
options.reasoningEffort string Default reasoning effort, emitted only for reasoning models. Always a level the model's family supports: graded families default to medium; binary (none/high) families such as Mistral default to none.
options.reasoningSummary string Reasoning summary behaviour. Renderer emits auto.
options.textVerbosity string Output verbosity hint for reasoning models. Renderer emits medium.

// rootTop-Level Fields

Field Type Description
$schema string OpenCode config JSON schema URL. Always https://opencode.ai/config.json.
compaction.auto boolean Enable automatic summarisation when the session nears the model context ceiling. Renderer emits true. See the Compaction section.
compaction.prune boolean Drop redundant tool-call output from the summarised transcript. Renderer emits true.
instructions string Project-rules block prepended to every session's system prompt. See the Instructions section.

// mcpMCP Fields

Field Type Description
mcp.<name>.type string Transport. Always remote for the Erebine MCP server (streamable HTTP).
mcp.<name>.url string Workspace MCP endpoint, e.g. https://api.erebine.ai/proj_ABC123/v1/mcp.
mcp.<name>.enabled boolean Set to true to make the server visible to the agent.
mcp.<name>.oauth boolean Set to false to disable OpenCode's OAuth discovery probe. Erebine authenticates with bearer + workspace headers; the OAuth probe fails and burns the connect budget if left enabled.
mcp.<name>.timeout integer Per-request timeout in milliseconds for tools/list and tool invocations. Renderer emits 30000 to cover the cold-path workspace-scoped catalog fetch.
mcp.<name>.headers.Authorization string Bearer API key, e.g. Bearer ere_my-project_abc123.
mcp.<name>.headers.X-Erebine-Workspace string Workspace external identifier (the ws_... form shown throughout the dashboard).

Supported Features

The following features have been validated with the Erebine API and the @ai-sdk/openai-compatible adapter.

Streaming (SSE)

OpenCode uses Server-Sent Events (SSE) streaming by default. Erebine's streaming implementation follows the OpenAI specification:

  • Each chunk includes id, object, created, model, and choices fields
  • Content is delivered via choices[].delta.content
  • finish_reason is null until the final content chunk
  • The data: [DONE] sentinel terminates the stream
  • The final chunk includes a usage object with prompt_tokens, completion_tokens, and total_tokens

Tool Calling

OpenCode drives file operations, shell commands, and code editing through tool calling. Erebine forwards tools and tool_choice to the model unchanged, and relays the model's tool-call frames back to OpenCode verbatim:

  • The tools and tool_choice request fields are sent to the model
  • Tool call responses include choices[].delta.tool_calls with id, type, function.name, and function.arguments
  • Streaming tool calls accumulate arguments across chunks
  • finish_reason: "tool_calls" is set when the model decides to call tools
  • Multi-turn tool interactions (the tool message role) are passed through correctly

Model support required. Tool calling must be supported by the model deployed on your endpoint. Not all models support function calling. Check your model's documentation for tool calling compatibility.

Reasoning Content

Some models (e.g., DeepSeek-R1, QwQ) emit reasoning or "thinking" content alongside their responses. The downloaded bundle declares this surface via the per-model reasoning: true flag plus an options block carrying the default reasoningEffort; OpenCode displays reasoning content in a collapsible section.

  • Erebine relays reasoning_content fields in the response as-is
  • Reasoning content is model-dependent, non-reasoning models report reasoning: false and omit the options block

Finish Reason Mapping

Finish Reason Description
stop Natural end of generation. The model completed its response.
length The max_tokens limit was reached.
tool_calls The model wants to invoke one or more tools.

Passthrough

OpenCode owns the conversation. Erebine serves it as sent: your system prompt stays at index 0, your tool definitions and tool_choice are honored verbatim, no tool call runs that the model did not emit, and one OpenCode request is one model turn. Nothing is inserted or rewritten behind OpenCode's back; the only exception is an augmentation you opt into through options.headers (see below).

Erebine's own agentic harness is opt-in. If you want it, add X-Erebine-Augment: on to options.headers in your provider block. Individual features are addressable one at a time; see Passthrough and Augmentation.

One augmentation is on by default in the downloaded bundle: X-Erebine-Augment-Corrective-Retries: on. It lets Erebine retry a malformed or empty model turn a bounded number of times before returning it, so a single bad generation does not surface as a failed request. Remove that entry from options.headers for strict passthrough where every model turn is returned exactly as first produced.

max_tokens

OpenCode may request a max_tokens value that exceeds what is left of your model's context window. The value is sent verbatim, so the request returns 400 context_length_exceeded rather than a quietly shortened answer. Lower max_tokens in the model config, use an endpoint with a larger context window, or send X-Erebine-Augment-Max-Output-Tokens-Clamp: on to have Erebine fit the value to the remaining context.

Tools on a Model Without Tool Support

OpenCode always sends tools. An endpoint whose model has no tool-call support returns 400 rather than answering as though no tools were sent. Route OpenCode at a tool-capable model.

Limitations

Unsupported OpenAI Extensions

  • response_format (structured outputs / JSON mode), support depends on the model deployed on your endpoint
  • seed, passed through but determinism depends on model support

Reasoning Content Availability

Reasoning content (reasoning_content in the response delta) is only available for models that produce it (e.g., DeepSeek-R1, QwQ). For non-reasoning models the downloaded bundle emits reasoning: false and omits the options block.

Troubleshooting

401 Unauthorized

The API key is missing, invalid, or does not have the inference scope.

  • Verify the API key is set on options.apiKey, not options.headers.Authorization. The @ai-sdk/openai-compatible adapter only reads options.apiKey; a key tucked under options.headers is silently ignored.
  • Re-download the bundle from the dashboard to make sure the key has not been rotated.
  • Check that the key has the inference scope in your dashboard.

404 Not Found

The endpoint URL is incorrect or the endpoint does not exist.

  • Verify the baseURL includes your project ID, workspace ID, and endpoint slug
  • Confirm the URL ends with /v1
  • Check that the endpoint is active in your dashboard

Model Not Found

The name field in your model config does not match the deployed model.

  • The name field must exactly match the model name shown on your endpoint detail page
  • Model names are case-sensitive

Connection Timeout

Requests may time out if no workers are available.

  • Check your endpoint status in the dashboard
  • Verify that at least one agent is healthy and connected
  • For EIM nodes, confirm the agent process is running and registered

Tool Calls Not Working

If OpenCode reports that tool calling is unavailable:

  • Verify your model supports function calling (not all models do).
  • Check your endpoint status in the dashboard for any errors.
  • Confirm the model's config block shows tool_call: true. Re-downloading the bundle restores the capability flags resolved from the deployed model's family.

Reasoning Content Not Displayed

If reasoning content does not appear in OpenCode:

  • Confirm the model config block shows reasoning: true and an options block. Re-downloading the bundle is the simplest way to restore them.
  • Verify you are using a model that produces reasoning tokens (e.g., DeepSeek-R1, QwQ).
  • Check that the model deployed on your endpoint supports reasoning output.

MCP: 403 scope_insufficient

The MCP route enforces workspace scoping separately from the inference key. A 403 with {"error":{"code":"scope_insufficient","type":"authentication_error"}} means the API key on the MCP Authorization header lacks one of the workspace scopes the requested tool requires.

  • Re-download the bundle so the MCP block uses a key with the workspace scopes the dashboard provisions by default.
  • Confirm X-Erebine-Workspace matches the workspace the key is bound to. A mismatch surfaces as "Workspace not bound" on prompts/get and resources/read.

MCP: 404 on Tool Calls

The MCP endpoint is only mounted when the deployment has the MCP feature gate enabled (EREBINE_MCP_ENABLED). If api.erebine.ai/proj_ABC123/v1/mcp returns 404, either the bundle URL is wrong (extra path segments, wrong workspace) or the feature is off on this deployment.

429 Rate Limited

Inference and MCP traffic share the workspace rate-limit envelope. A 429 indicates the workspace is over its per-window quota. OpenCode does not auto-retry; back off and re-issue the request once the quota resets. The Retry-After response header (when present) lists the recommended wait in seconds.

Verifying Connectivity

Use curl to test your endpoint independently of OpenCode:

curl
curl https://api.erebine.ai/proj_ABC123/WORKSPACE_ID/my-endpoint/v1/chat/completions \ -H "Authorization: Bearer ere_my-project_abc123" \ -H "Content-Type: application/json" \ -d '{ "model": "deepseek-r1-distill-llama-70b", "messages": [{"role": "user", "content": "Hello!"}], "max_tokens": 50 }'

If this returns a valid response, the issue is in your OpenCode configuration. If it returns an error, resolve the API issue first.