---
title: OpenCode
description: Install native Mirage filesystem tools in OpenCode, with stale-write protection and no MCP server.
icon: /images/opencode-logo.svg
---

[OpenCode](https://opencode.ai) can use Mirage through a native plugin, without MCP. The plugin replaces its filesystem and shell tools with Mirage-backed versions.

## Install

Create a `mirage.yaml` in your project:

```yaml mirage.yaml
mounts:
  /:
    resource: ram
```

Add the plugin to `opencode.json`:

```json opencode.json
{
  "$schema": "https://opencode.ai/config.json",
  "plugin": ["@struktoai/mirage-opencode"]
}
```

Run `opencode` normally. Mirage searches the project and parent directories for `mirage.yaml`, `workspace.yaml`, `.mirage/workspace.yaml`, and their `.yml` variants.

To select another config or disable stale-write protection, pass plugin options:

```json opencode.json
{
  "plugin": [
    [
      "@struktoai/mirage-opencode",
      {
        "config": "config/agent-workspace.yaml",
        "staleWriteProtection": true
      }
    ]
  ]
}
```

You can also set `MIRAGE_OPENCODE_CONFIG` or `MIRAGE_CONFIG` to the workspace config path.

## Stale writes

Stale-write protection is enabled by default and isolated per OpenCode session. `read` records a content fingerprint; `write` and `edit` verify it immediately before changing the file. If the file changed, OpenCode must reread it before retrying.

`bash` commands are not tracked because they can read or mutate arbitrary paths. Use the structured `read`, `write`, and `edit` tools when stale-write protection is required.

## Custom plugin

Use `@struktoai/mirage-agents/opencode` directly when the workspace is constructed in code rather than YAML:

```bash
bun add @struktoai/mirage-agents @struktoai/mirage-node @opencode-ai/plugin
```

Create `.opencode/plugins/mirage.ts`:

```ts .opencode/plugins/mirage.ts
import { MountMode, OpsRegistry, RAMResource, Workspace } from '@struktoai/mirage-node'
import { miragePlugin } from '@struktoai/mirage-agents/opencode'

const ram = new RAMResource()
const ops = new OpsRegistry()
for (const op of ram.ops()) ops.register(op)
const ws = new Workspace({ '/': ram }, { mode: MountMode.WRITE, ops })

export default miragePlugin(ws)
```

Add the dependencies to `.opencode/package.json`:

```json .opencode/package.json
{
  "dependencies": {
    "@struktoai/mirage-agents": "*",
    "@struktoai/mirage-node": "*",
    "@opencode-ai/plugin": "*"
  }
}
```

## Exports

| Symbol | Purpose |
| --- | --- |
| `mirageTools(ws, options?)` | Returns OpenCode tool definitions for `read`, `write`, `edit`, `ls`, `bash`, `glob`, and `grep`. |
| `miragePlugin(ws, options?)` | Returns a native OpenCode plugin backed by `ws`. |
| `MirageOpenCodeOptions` | Supports `staleWriteProtection` (default `true`). |
| `StaleMirageFileError` | Indicates that a tracked file changed after it was read. |

### Tool reference

Tools return text except PDF and image reads, which return OpenCode attachments.

| Tool | Input | Output |
| --- | --- | --- |
| `read` | `{ filePath }` | UTF-8 text; a PDF/image attachment; or metadata for other binaries |
| `write` | `{ filePath, content }` | Confirmation; creates parent directories |
| `edit` | `{ filePath, oldString, newString, replaceAll? }` | Confirmation with replacement count |
| `ls` | `{ path }` | Entries, with `/` after directories |
| `bash` | `{ command }` | Merged stdout and stderr |
| `glob` | `{ pattern, path? }` | `find -name` results |
| `grep` | `{ pattern, path? }` | `grep -rn` results |

`bash`, `glob`, and `grep` run through the Mirage shell across all mounts.

## Examples

- [`examples/typescript/agents/opencode/ram_opencode.ts`](https://github.com/strukto-ai/mirage/blob/main/examples/typescript/agents/opencode/ram_opencode.ts), an end-to-end runner that spawns OpenCode via `@opencode-ai/sdk`, loads a RAM-backed plugin that pre-writes `/hello.txt`, and asks `gpt-5.4-mini` to `cat` it.
