Skip to content

OpenCode integration guide: MCP, permissions, and API routing gotchas #27450

@lpcox

Description

@lpcox

Summary

While integrating OpenCode into an AWF smoke test workflow, we hit several non-obvious configuration issues. This documents the problems and solutions as a reference for anyone configuring OpenCode in an agentic workflow.


Problem 1: OpenCode doesn't auto-discover MCP servers

Symptom: The agent runs and completes successfully but never calls any MCP tools. The `safe-output-items.jsonl` artifact is empty even though the MCP gateway is running.

Root cause: OpenCode has its own built-in tool registry (bash, read, glob, grep, write, etc.) and does not auto-discover MCP servers. Without explicit config, the agent has no MCP tools.

Fix: Add an explicit `mcp` section to `opencode.jsonc`:

```json
{
"mcp": {
"safeoutputs": {
"type": "remote",
"url": "http://host.docker.internal:${MCP_GATEWAY_PORT}/mcp/safeoutputs",
"headers": { "Authorization": "${MCP_GATEWAY_API_KEY}" },
"timeout": 30000
}
}
}
```

Note: The key is `mcp` (not `mcpServers` — that's the VS Code / Cline convention).


Problem 2: MCP gateway URL must include the routed path

Symptom: MCP connection fails with `transport connection failed` / 404.

Root cause: The MCP gateway runs in routed mode and exposes servers at `/mcp/`, not at the root `/`. Pointing to `http://host.docker.internal:80\` returns 404.

Fix: Use the full routed path: `http://host.docker.internal:${PORT}/mcp/safeoutputs\`

The gateway startup log confirms this:
```
Routes: /mcp/ where is one of: [safeoutputs]
```


Problem 3: Agent permissions use `permission` (singular), not `permissions`

Symptom: In non-interactive `opencode run` mode, tool calls fail silently or are auto-rejected. MCP connects (tools appear in registry) but the model's file/directory operations are blocked.

Root cause: OpenCode's `ConfigAgent.Info` schema field is `permission` (singular). Using `permissions` (plural) causes the block to be silently moved to `options` by the `normalize()` function — no error is thrown and the config is ignored.

```json
// ❌ Wrong — silently ignored
"agent": {
"build": {
"permissions": { "external_directory": "allow" }
}
}

// ✅ Correct
"agent": {
"build": {
"permission": { "external_directory": "allow" }
}
}
```


Problem 4: `external_directory` must be explicitly allowed in non-interactive mode

Symptom: File read/write tool calls are auto-rejected even though MCP tools are available.

Root cause: OpenCode's default ruleset includes:
```json
{ "permission": "external_directory", "pattern": "*", "action": "ask" }
```

In interactive mode, this prompts the user. In non-interactive `opencode run` mode, `ask` becomes an automatic deny.

Fix: Override this in `agent.build.permission`:

```json
{
"agent": {
"build": {
"permission": {
"external_directory": "allow",
"bash": "allow",
"edit": "allow",
"read": "allow"
}
}
}
}
```


Problem 5: Copilot API has no `/v1` prefix

Symptom: API calls fail with 404 when using `api.githubcopilot.com` as the provider base URL.

Root cause: `api.githubcopilot.com` uses `/chat/completions` directly (no `/v1` prefix). The `@ai-sdk/openai-compatible` provider appends `/chat/completions` to the `baseURL` — if the baseURL already includes `/v1`, you get a doubled path.

Fix: Use `https://models.inference.ai.azure.com\` (Azure AI endpoint) instead of a Copilot-specific URL, or configure the provider baseURL to not include `/v1`.


Problem 6: `COPILOT_GITHUB_TOKEN` must be in the AWF Execute step's `env:`

Symptom: The api-proxy port 10004 (OpenCode) fails to start or returns auth errors.

Root cause: The AWF api-proxy's `resolveOpenCodeRoute()` reads `COPILOT_GITHUB_TOKEN` from the container environment. If the token is only set in an earlier shell step's `env:`, it's not forwarded into the AWF container.

Fix: Pass the token explicitly in the AWF execute step:

```yaml

  • name: Execute
    env:
    COPILOT_GITHUB_TOKEN: ${{ steps.copilot-token.outputs.token }}
    run: |
    awf --enable-api-proxy ... opencode run ...
    ```

Reference: Working OpenCode config structure

```json
{
"provider": {
"copilot-proxy": {
"name": "Copilot Proxy",
"type": "openai-compatible",
"baseURL": "http://172.30.0.30:10004",
"models": ["gpt-4.1", "claude-sonnet-4-5"]
}
},
"model": "copilot-proxy/claude-sonnet-4-6",
"mcp": {
"safeoutputs": {
"type": "remote",
"url": "http://host.docker.internal:${MCP_GATEWAY_PORT}/mcp/safeoutputs",
"headers": { "Authorization": "${MCP_GATEWAY_API_KEY}" },
"timeout": 30000
}
},
"agent": {
"build": {
"permission": {
"bash": "allow",
"edit": "allow",
"read": "allow",
"glob": "allow",
"grep": "allow",
"write": "allow",
"external_directory": "allow"
}
}
}
}
```

Metadata

Metadata

Assignees

Labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions