The detachment
The amicode_* tool surface (the interview rail: pick_system, set_model, formulate, solve, profile, problem, recommend, verify, to_hardware, calibrate, veloce, …) lives today as an opencode plugin (packages/extension/opencode-plugin/amicode_tools.ts, ~1800 lines, self-contained logic over the problem workspaces — not a service proxy). Under any other harness the tools are absent. The harness-contract ADR names the fix: the tool surface is product-owned; its portable carrier is an MCP server owned by the extension host.
The design (one implementation, two transports)
- Extract the implementations from the plugin file into
packages/extension/src/amicode_tools_core.ts — harness-neutral (filesystem + arguments; the session/meta plumbing the plugin's opencode API provides becomes explicit parameters). The plugin becomes a thin adapter over the core: opencode behavior byte-identical, pinned by the existing plugin tests. - An MCP stdio server exposing the same tools: a TS entry built by the repo's esbuild config (4th entry beside amico.js/amico-run.js/pasqal) →
packages/extension/bin/dist/mcp-amico.mjs. Prefer the official @modelcontextprotocol/sdk (the standard's whole point is standard clients; the supply-chain add rides this PR's normal review). If the SDK proves heavy, hand-rolled JSON-RPC over stdio (initialize/tools-list/tools-call) is the recorded fallback — decide in-slice, say which and why. - Parity test (the drift guard): the MCP server's
tools/list names + schemas ≡ the plugin's registrations, from ONE source of truth (the core's tool table). - The opencode wiring:
opencode_config's injected config gains mcp: { amicode: { type: "local", command: [node, <dist/mcp-amico.mjs>], enabled: true } } (the fork v1.18 supports local MCP in config — verified in the SDK types); the plugin pack retires its tool registrations (the file's other exports, if any, move with the core; amicode_context.ts — the prompt-time context splice — is harness-coupled by nature and STAYS a plugin). - Telaio consumption is NOT this slice: a Julia MCP client is the telaio-side follow-up (the shelf this builds toward — Telaio.jl holds entities natively and may also implement tools natively; the product never requires it).
Acceptance criteria
Pointers
- The plugin:
packages/extension/opencode-plugin/amicode_tools.ts (1796 lines, one export AmicodeTools); its tests: test/amicode_tools.test.ts. - The config injection:
src/opencode_config.ts (the plugin path + config content; the fork's McpLocalConfig shape is in the SDK types). - The esbuild config:
esbuild.config.mjs (3 entries today — amico.js, amico-run.js, pasqal). - The problem-dir contract: the tools resolve slugs against the workspace root — the MCP server gets the same roots via env (injected in the mcp.local config's environment).
The detachment
The
amicode_*tool surface (the interview rail: pick_system, set_model, formulate, solve, profile, problem, recommend, verify, to_hardware, calibrate, veloce, …) lives today as an opencode plugin (packages/extension/opencode-plugin/amicode_tools.ts, ~1800 lines, self-contained logic over the problem workspaces — not a service proxy). Under any other harness the tools are absent. The harness-contract ADR names the fix: the tool surface is product-owned; its portable carrier is an MCP server owned by the extension host.The design (one implementation, two transports)
packages/extension/src/amicode_tools_core.ts— harness-neutral (filesystem + arguments; the session/meta plumbing the plugin's opencode API provides becomes explicit parameters). The plugin becomes a thin adapter over the core: opencode behavior byte-identical, pinned by the existing plugin tests.packages/extension/bin/dist/mcp-amico.mjs. Prefer the official@modelcontextprotocol/sdk(the standard's whole point is standard clients; the supply-chain add rides this PR's normal review). If the SDK proves heavy, hand-rolled JSON-RPC over stdio (initialize/tools-list/tools-call) is the recorded fallback — decide in-slice, say which and why.tools/listnames + schemas ≡ the plugin's registrations, from ONE source of truth (the core's tool table).opencode_config's injected config gainsmcp: { amicode: { type: "local", command: [node, <dist/mcp-amico.mjs>], enabled: true } }(the fork v1.18 supports local MCP in config — verified in the SDK types); the plugin pack retires its tool registrations (the file's other exports, if any, move with the core;amicode_context.ts— the prompt-time context splice — is harness-coupled by nature and STAYS a plugin).Acceptance criteria
pnpm -r typecheck+ the extension suite green (the known environmental set excluded per the loop-21 baseline).Pointers
packages/extension/opencode-plugin/amicode_tools.ts(1796 lines, one exportAmicodeTools); its tests:test/amicode_tools.test.ts.src/opencode_config.ts(the plugin path + config content; the fork's McpLocalConfig shape is in the SDK types).esbuild.config.mjs(3 entries today — amico.js, amico-run.js, pasqal).