Conversation
…ed names OpenCode 2's editor.add() defaults tools to codemode: true, which keeps them out of the model's direct tool list -- they are only reachable by writing code against the execute tool's catalog. Routing enforcement, meanwhile, tells the model to call context-mode_ctx_execute / ctx_fetch_and_index / ctx_search directly, and the V2 registration used the bare ctx_* names, so the redirect pointed at tools that did not exist. Register the V2 tools with codemode: false and under createToolNamer(platform) names, the same names the routing block and execute.before redirects use. Observed on OpenCode 2.0.7: before, the model's direct tool list had no ctx_* tools while curl was redirected to context-mode_ctx_execute; after, all ctx_* tools are direct and the redirect names match. Adds a test that runs the real V2 setup() against a fake OpenCode 2 context and asserts codemode: false plus routed names (red without the fix).
…option
OpenCode 2 composes the model-visible tool name as <namespace>_<name>. Instead
of baking the routed prefix into the name, split toolNamer(name) into
{ name, namespace } (v2ToolIdentity) so the tools use the host's native
namespace field while the visible names stay exactly what the router tells the
model to call. Any namer shape other than a clean <prefix>_<tool> (bare names,
mcp__x__tool) is registered verbatim with no namespace.
Verified on OpenCode 2.0.7: the model sees context-mode_ctx_execute etc., and
context-mode_ctx_execute runs.
Tests: v2ToolIdentity unit cases; the setup() test now asserts, per tool, that
the host-composed visible name equals namer(name) (no prefix-startsWith
tautology), awaits the transform callback, and removes its abort listener.
hooks/core/routing.mjs only redirects curl/wget and large-output commands when isMCPReady() finds a live readiness sentinel, which the stdio MCP server writes from its main(). With native V2 tools there is no MCP server, so the redirects silently depended on an unrelated context-mode MCP process (e.g. another client's) being alive: on a host with none, curl ran straight through. The V2 setup() now writes the same sentinel for its own process (PID contents, 30s refresh against the reader's 90s freshness window, unref'd timer) and removes it on dispose. Observed on OpenCode 2.0.7: curl was redirected on a machine where a Claude Code context-mode MCP happened to be running and not on one without; with this change it is redirected on both. The V1 native-tool path looks to share this dependency; left untouched here as it was not tested against a live OpenCode 1.x host.
5 tasks
Owner
|
Thanks for this — all three commits are now on
Verified: Notes for the record:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What / Why / How
Follow-up to mksglu#1171 (targets its branch, so it can be folded into that PR rather than compete with it). I ran mksglu#1171 against a live OpenCode 2.0.7 server and it loads and hooks correctly. But three things kept context-mode from being automatic there. Each is one commit with a test that fails without the fix:
The
ctx_*tools were not directly callable. In OpenCode 2,ToolEditor.add()defaults tocodemode: true, which leaves a tool out of the model's direct tool list: it can only be reached by writing code against theexecutetool's catalog. The routing layer, meanwhile, tells the model to callcontext-mode_ctx_execute,context-mode_ctx_fetch_and_indexandcontext-mode_ctx_searchdirectly, while the V2 registration used the barectx_*names. So the redirect named tools that did not exist. The tools are now registered withcodemode: false. (4b91426)Names now follow the router, through OpenCode 2's own
namespace. OpenCode 2 builds the name the model sees as<namespace>_<name>.v2ToolIdentity()splitscreateToolNamer(platform)(tool)into{ name, namespace }, so the visible name is exactly what the routing block and theexecute.beforeredirects use, without hardcoding a prefix. If the OpenCode naming later changes (e.g. fix(tool-naming): use native ctx_* names for OpenCode/KiloCode plugin tools mksglu/context-mode#1161's barectx_*), registration follows automatically. (468b7eb)Routing redirects depended on an unrelated process.
routing.mjsonly redirects curl/wget and large-output commands whenisMCPReady()finds a live readiness sentinel, and only the stdio MCP server'smain()writes one. With native V2 tools there is no MCP server. So on a machine where some other client happened to have a context-mode MCP server running,curlwas redirected; on a machine without one, it ran straight through. The V2setup()now writes the same sentinel for its own process: PID contents, refreshed every 30s against the reader's 90s window, on an unref'd timer, removed on dispose. (86c15ef)Verified on OpenCode 2.0.7, as a local plugin on a live server with no context-mode MCP running:
context-mode_ctx_*tools appear as direct tools, andcontext-mode_ctx_executeruns;curl -s https://example.com | head -c 80through the shell tool comes back ascontext-mode: curl/wget redirected. Call context-mode_ctx_execute(...), and the tools it names exist;contexthook (4,239 chars, alongside another plugin'scontexthook);ctx_executestill honourspermissions.deny(sudo -n true→Command blocked by security policy).Two notes, not changed here:
<dir>/index.js. It ignorespackage.jsonmain/exportsfor local paths, and rejects a file path withconfigured plugin path must be a directory. That doesn't affect npm installs, but "Option B — manual install" from a clone would silently load nothing on V2 unless the package root has anindex.js.Affected platforms
Test plan
tests/opencode-plugin.test.ts: runs the real V2setup()against a minimal fake OpenCode 2 context and asserts:codemode: false;<namespace>_<name>) equalscreateToolNamer("opencode")(name);setup(),isMCPReady()is true, and the sentinel is removed on dispose.v2ToolIdentity: the prefixed form maps to a namespace; bare names andmcp__…shapes are used as-is.Checklist
npm testpasses: 4,707 pass. The one failure,tests/scripts/asymmetric-drift-assert.test.ts(npm pack dry-run), fails identically on fix(opencode): support OpenCode 2 plugin API (V1/V2 dual export) mksglu/context-mode#1171's head and on this machine without these changes, so it looks environmental.npm run typecheckpassesnode:pathjoins, plus the existingsentinelDir()nextbranch: this targets fix(opencode): support OpenCode 2 plugin API (V1/V2 dual export) mksglu/context-mode#1171's branch (which targetsmain)