Skip to content

Support OpenCode 2 alongside OpenCode 1 - #5

Open
kylemclaren wants to merge 3 commits into
mainfrom
opencode-2-support
Open

Support OpenCode 2 alongside OpenCode 1#5
kylemclaren wants to merge 3 commits into
mainfrom
opencode-2-support

Conversation

@kylemclaren

Copy link
Copy Markdown

OpenCode 2 replaces the plugin API. A plugin no longer returns hooks from a server function: it exports a setup function, registers servers and commands with transforms, and intercepts live operations with hooks.

One package can serve both releases, because each release resolves a different package export. OpenCode 1 resolves ./server, and OpenCode 2 resolves ., so neither release loads the other's file.

  • src/v1.js keeps the OpenCode 1 plugin. Its configuration, permission, and hook behavior does not change.
  • src/v2.js adds the OpenCode 2 plugin.
  • src/shared.js holds what does not differ: the server defaults, the naming rules, the guidance text, the command templates, the trigger pattern, and the destructive tools.

Behavior differences that the two plugin APIs force:

  • OpenCode 2 gives plugins no permission draft, so the plugin changes a decision from allow to ask in the permission.evaluate hook. A configured deny never reaches the hook.
  • OpenCode 2 has no compaction hook, so the compaction instruction is part of the guidance text there. OpenCode 1 keeps its hook.
  • OpenCode 2 separates the MCP startup, catalog, and execution timeouts, and adds the oauth and codemode options.

The guidance text changes for both releases. It now names the sprites_file_* tools, which the Sprites MCP server offers, in place of the previous base64 transfer advice. It also names the mcp auth command of the release that is running.

Tests cover both releases: the export map through Node resolution, each entry point directly, the real OpenCode 1 CLI, and a real OpenCode 2 service. test/context.js replays transforms and disposes registrations the way the runtime does, and test/type-contract.ts asserts that it stays assignable to the real plugin contract.

OpenCode 2 replaces the plugin API. A plugin no longer returns hooks from
a `server` function: it exports a `setup` function, registers servers and
commands with transforms, and intercepts live operations with hooks.

One package can serve both releases, because each release resolves a
different package export. OpenCode 1 resolves `./server`, and OpenCode 2
resolves `.`, so neither release loads the other's file.

- `src/v1.js` keeps the OpenCode 1 plugin. Its configuration, permission,
  and hook behavior does not change.
- `src/v2.js` adds the OpenCode 2 plugin.
- `src/shared.js` holds what does not differ: the server defaults, the
  naming rules, the guidance text, the command templates, the trigger
  pattern, and the destructive tools.

Behavior differences that the two plugin APIs force:

- OpenCode 2 gives plugins no permission draft, so the plugin changes a
  decision from `allow` to `ask` in the `permission.evaluate` hook. A
  configured `deny` never reaches the hook.
- OpenCode 2 has no compaction hook, so the compaction instruction is
  part of the guidance text there. OpenCode 1 keeps its hook.
- OpenCode 2 separates the MCP `startup`, `catalog`, and `execution`
  timeouts, and adds the `oauth` and `codemode` options.

The guidance text changes for both releases. It now names the
`sprites_file_*` tools, which the Sprites MCP server offers, in place of
the previous base64 transfer advice. It also names the `mcp auth` command
of the release that is running.

Tests cover both releases: the export map through Node resolution, each
entry point directly, the real OpenCode 1 CLI, and a real OpenCode 2
service. `test/context.js` replays transforms and disposes registrations
the way the runtime does, and `test/type-contract.ts` asserts that it
stays assignable to the real plugin contract.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TiiJqxPLpgfJXG5fvTUa73
@kylemclaren
kylemclaren requested a review from aezell August 27, 2026 19:10
`/sprites-status` failed with "Expected array at [\"files\"]". The command
executor passed `files`, `agents`, and `skills` to `session.prompt` even
when the caller sent no attachments. Prompt validation accepts an absent
key, but it rejects a key whose value is undefined.

Build the attachment keys only when they carry entries.

The fake plugin context records the prompt input without validating it,
so a unit test alone cannot catch this. The new test asserts the absence
of the keys, and the fix is verified against a real OpenCode 2 service:
the command fails with the reported error before it, and succeeds after.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TiiJqxPLpgfJXG5fvTUa73
@kylemclaren
kylemclaren marked this pull request as ready for review August 27, 2026 19:35

@aezell aezell left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a thing called codemode that I think we want to default to false because if true it changes how it calls the MCP tools in a way that our server doesn't yet support. We can add that support but the fastest way is to set it to false for now.

OpenCode 2 groups MCP tools in Code Mode by default. The model then
reaches them through a dispatcher, as `tools.sprites.<tool>(input)`. The
Sprites MCP server does not support that call shape yet, so the plugin
sets `codemode` to `false` on the server it registers.

The `sprites_*` tools are then on the model's own tool list. A real model
request carries 24 Sprites tools with Code Mode off, and none with it on.

The `codemode` option still accepts `true`, for use after the server
supports the dispatcher.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TiiJqxPLpgfJXG5fvTUa73
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants