Using Polaris from OpenCode¶
OpenCode integrates with Polaris natively — no translation gateway required (unlike Claude Code). There are two ways to bring the Advisor in:
- As an MCP tool — the Advisor MCP endpoint. OpenCode keeps using its normal coding model and calls the Advisor for grounded, cited answers. Recommended — you keep OpenCode's full agentic/coding ability and add the brain as a tool.
- As the model backend — the OpenAI-compatible completions endpoint configured as a custom provider. Every OpenCode turn is answered by the Advisor.
The Advisor is a knowledge provider, not a coding model
The Advisor serves grounded, cite-then-verified answers from a published brain — it has no tool-use/function-calling and only answers from the brain's corpus. Using it as OpenCode's model turns OpenCode into a Q&A harness over your brain, not a coding agent. For real work, prefer option 1 (MCP).
Both paths authenticate with a Polaris API key (sk-polaris-…, advisor:read scope). Mint one
from the SPA (Settings → API keys) or the CLI (see Managing keys).
OpenCode config lives in ~/.config/opencode/opencode.json (global) or opencode.json in the project
root. Secrets stay out of the config file via interpolation, and OpenCode offers
two forms:
| Syntax | Reads from | Use when |
|---|---|---|
{file:~/path/to/key} |
contents of a file | Recommended. Persists across shells and reboots — nothing to re-export. |
{env:POLARIS_API_KEY} |
the shell environment | You already manage the key via your shell profile, direnv, or a secrets agent. |
OpenCode does not read .env files
This is the single most common way this setup fails. OpenCode only interpolates {env:VAR} from
the environment of the shell that launched it — putting POLARIS_API_KEY=… in a project
.env does nothing. Worse, an unset variable is silently replaced with an empty string, so the
request goes out as a bare Authorization: Bearer and Polaris rejects it with an opaque 401:
If you see that 401, check echo $POLARIS_API_KEY in the shell you started OpenCode from before
suspecting the key itself. Prefer {file:…} to sidestep the problem entirely.
Recommended setup — write the raw key to a file and reference it with {file:…}:
mkdir -p ~/.secrets && chmod 700 ~/.secrets
printf '%s' 'sk-polaris-xxxx' > ~/.secrets/polaris-key # printf, not echo — see below
chmod 600 ~/.secrets/polaris-key
No trailing newline
{file:…} substitutes the file's contents verbatim, newline included. echo appends one,
which lands inside the Authorization header value and breaks auth. Use printf '%s', or
truncate -s -1 a file you already wrote.
Alternative — if you'd rather use {env:…}, put the export somewhere your shell actually loads
(~/.bashrc, ~/.zshrc), or use direnv with a .envrc containing dotenv to pull in an existing
.env. A one-off export in a terminal you later close will not survive:
OpenCode config lives in ~/.config/opencode/opencode.json (global) or opencode.json in the project
root; {env:VAR} interpolates environment variables so secrets stay out of the file.
<host> below is wherever the gateway is published — localhost:8088 locally, or the public origin
of a deployed host (the reference deployment serves /v1/ through nginx behind a Tailscale Funnel;
see Edge & Tailscale).
1. Advisor as an MCP tool (recommended)¶
OpenCode supports remote MCP servers directly. Point
it at the streamable-http Advisor endpoint and pass the key as a bearer header (oauth: false tells
OpenCode to use the header instead of attempting an OAuth flow):
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"polaris-advisor": {
"type": "remote",
"url": "https://<host>/v1/mcp",
"enabled": true,
"oauth": false,
"headers": {
"Authorization": "Bearer {file:~/.secrets/polaris-key}"
}
}
}
}
Swap in "Bearer {env:POLARIS_API_KEY}" if you're managing the key through your shell instead — but
re-read the warning above about .env first.
Verify the connection before doing anything else:
The brain is selected by the key, so there's nothing else to configure. The Advisor's tools
(answer, retrieve, read_wiki) then appear to the model; reference them by the server name in a
prompt:
Local dev host
Against a local dev.sh stack the URL is http://localhost:8088/v1/mcp (plain http, port
8088). If you keep both a local and a deployed Polaris wired up, mint a separate key per
host and store them in distinct files (e.g. ~/.secrets/polaris-local-key vs
~/.secrets/polaris-key) — a key minted on one gateway is not valid on the other, and a local DB
reset invalidates local keys. Verify the right one with opencode mcp list.
Make citations survive¶
This is the one behavior that surprises people. The Advisor's answer/retrieve tools return the
answer and a rendered ## Sources block — one entry per citation with the verbatim quote,
[source](origin_url), and [wiki: entity](ref) deep-links (labels cleaned, duplicates collapsed) —
plus the full structured citation model alongside it. But OpenCode hands
the tool result to your coding model, and a summarizing model — especially a small local one —
often rewrites the answer and drops the citations. That's a model behavior, not a Polaris
omission: the sources are in the tool result. (Confirm any time with opencode mcp list + a raw
call, or see consuming citations.)
Two fixes, most effective first:
1. Add a standing rule so the agent always keeps sources. OpenCode auto-loads a global
~/.config/opencode/AGENTS.md for every project — put the rule there:
## Polaris Advisor (polaris-advisor MCP)
When you call any `polaris-advisor` tool (`answer`, `retrieve`, `read_wiki`):
- Always preserve its citations. If the tool's text response contains a `## Sources` section,
reproduce it **verbatim** at the end of your reply — never summarize it away or drop it.
- Keep the `[source](…)` URLs and `[wiki: …](…)` deep-links intact.
- If the Advisor returns no sources, say so rather than implying the answer is grounded.
AGENTS.md is read at session start — restart OpenCode after creating it. (A project-local
AGENTS.md, or the instructions array in opencode.json, works too; the global file applies
everywhere. Note instructions paths are resolved relative to the project, so a global config should
prefer the auto-loaded AGENTS.md.)
2. Use a stronger model for advisory turns. A larger model (Claude, or a bigger local model) preserves tool output far more reliably than a small local model. Keep your coding model for code and switch when citations matter, or add a per-prompt nudge: "…use polaris-advisor and include its Sources section."
Integration Prompt¶
You will need to create an API key for the advisor to use the MCP server. Have your coding agent do the wiring:
I have opencode installed. Look at https://polaris-themis-lime.tail8cee6e.ts.net/docs/OPENCODE/ for
instructions on how to configure opencode for polaris. Assume polaris-themis-lime.tail8cee6e.ts.net
is the hostname. Let's configure the MCP server first. My API key is in .env — note that opencode
does NOT read .env, so follow the doc's recommended {file:...} setup rather than pointing the config
at {env:...}. Verify with `opencode mcp list` in a shell where POLARIS_API_KEY is unset.
2. Advisor as a model provider (completions)¶
OpenCode speaks OpenAI Chat Completions via the
@ai-sdk/openai-compatible provider, so it can call the Polaris
completions endpoint directly. Set baseURL to the brain-scoped path (the SDK appends
/chat/completions) and apiKey to your sk-polaris-… key:
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"polaris": {
"npm": "@ai-sdk/openai-compatible",
"name": "Polaris Advisor",
"options": {
"baseURL": "https://<host>/v1/brains/<brain_id>",
"apiKey": "{file:~/.secrets/polaris-key}"
},
"models": {
"polaris-advisor": {
"name": "Polaris Advisor",
"limit": { "context": 200000, "output": 65536 }
}
}
}
}
}
Then pick it with the model switcher (/models) or pin a default:
apiKeyis sent asAuthorization: Bearer …— exactly what the endpoint expects.- The endpoint ignores OpenCode's extra request fields (tools, sampling params) and always serves the
grounded
answer; it streams, so OpenCode renders progressively. - The Advisor's structured citations ride along in the response's
polarisobject / viaresponse_format: json_object— see Structured metadata — but whether OpenCode surfaces that extra field depends on the AI SDK.
Caveats¶
- No tool use. As a model backend the Advisor can't call tools, so OpenCode's editing/agentic loop degrades to Q&A. Keep OpenCode on a real coding model and use option 1 (MCP) to add the brain.
- One brain per key. The MCP endpoint derives the brain from the key; the provider
baseURLpins it in the path. Use separate keys/providers for separate brains. - Published brain required. Grounded answers need a published version of the brain.
Troubleshooting¶
polaris-advisor failed — SSE error: Non-200 status code (401)¶
The key never reached the server. In order of likelihood:
- Config uses
{env:POLARIS_API_KEY}but the variable isn't set in the launching shell. The key living in a project.envdoes not count — OpenCode doesn't read.env. Interpolation of an unset variable yields an empty string, so the header goes out as a bareBearer. Check withecho $POLARIS_API_KEY; switch to{file:…}for a durable fix. - The key file has a trailing newline.
printf '%s'writes it clean;echodoes not.wc -c < ~/.secrets/polaris-keyshould match the key length exactly. - Wrong scope or a revoked key. The key needs
advisor:read. Confirm it independently:
curl -sS -o /dev/null -w '%{http_code}\n' -L -X POST https://<host>/v1/mcp \
-H "Authorization: Bearer $(cat ~/.secrets/polaris-key)" \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"curl","version":"1"}}}'
200 means the key and endpoint are fine and the problem is in how OpenCode is resolving it.
curl needs -L, OpenCode does not
/v1/mcp may answer with a 308 redirect depending on how the host is fronted. OpenCode's MCP
client follows it transparently, so both /v1/mcp and /v1/mcp/ work in config. Hand-rolled
curl checks need -L or they'll report a confusing 308 that looks like an endpoint error.
References¶
- OpenCode — MCP servers · Providers · Config
- Polaris — Advisor MCP endpoint · Completions endpoint