Code mode capability matrix
created: 2026-09-30T08:00:00-05:00
last-updated: 2026-09-30T08:00:00-05:00
increment: 1
status: standard
tags: area:compare
description: Side-by-side capabilities of lootbox, mcpx, opencode code mode (v1, v2) and Cloudflare code mode, with a source per cell.
One row per capability, one column per product. opencode gets two columns because v1 (1.18.31) and v2 (2.0.3) differ
often enough to matter. lootbox means the deployed build (upstream 587a5a1 plus four patches); cells citing the
.lootbox/ fork say so where the fork differs (register CODE-01).
“—” means absent, with the evidence that it is absent. Rows 1–47 are the capability survey; rows 48–52 add v1/v2
differences from the register; group B lists where each product does something better than mcpx, including the
places where that conflicts with mcpx’s goal.
The reasoning and nuance for every row are in register/code-mode.md. Legend and citation
conventions: README.md.
Cloudflare cells cite a page and its section heading: how = https://developers.cloudflare.com/agents/tools/codemode/how-it-works/, api = https://developers.cloudflare.com/agents/tools/codemode/api-reference/, mcp = https://developers.cloudflare.com/agents/tools/codemode/mcp/, mcpsrv = https://developers.cloudflare.com/agents/model-context-protocol/codemode/, client = https://developers.cloudflare.com/agents/model-context-protocol/apis/client-api/, limits = https://developers.cloudflare.com/dynamic-workers/usage/limits/, dwstart = https://developers.cloudflare.com/dynamic-workers/getting-started/, chg = https://developers.cloudflare.com/changelog/post/2026-02-20-codemode-sdk-rewrite/, dwex = https://developers.cloudflare.com/dynamic-workers/examples/codemode/, blog = https://blog.cloudflare.com/code-mode/, blog2 = https://blog.cloudflare.com/code-mode-mcp/.
| # | capability | lootbox | mcpx @ 05c78b2 | opencode v1 | opencode v2 | Cloudflare |
|---|---|---|---|---|---|---|
| 1 | what executes | deno run subprocess spawned by the daemon, one per exec (.lootbox/src/lib/execute_llm_script.ts:38) | deno→bun→node subprocess spawned by the CLI, or by the daemon for /v1/exec (internal/runner/runner.go:72-106) | acorn plus a tree-walking interpreter inside the opencode process (v1:packages/codemode/src/interpreter/runtime.ts:136) | same, plus a workerd profile (v2:packages/codemode/package.json:23-25) | V8 isolate via Worker Loader, one per pass (how “Executor”) |
| 2 | language | TS | TS/JS | TS stripped to a JS subset: no classes or generators (v1:packages/codemode/README.md:254) | TS stripped to a JS subset: generators yes, classes no (v2:packages/codemode/interpreter-support.md:141, :152) | JS; TS must be compiled first (dwstart) |
| 3 | type-check | on by default (--no-check=remote) (.lootbox/src/lib/execute_llm_script.ts:34) | off by default; on/strict available (internal/settings/registry.go:244) | none; transpileModule only (v1:packages/codemode/src/interpreter/runtime.ts:116) | none (v2:packages/codemode/src/interpreter/transpile.node.ts:10) | none (api “normalizeCode”) |
| 4 | sandbox: filesystem | none by default (.lootbox/src/lib/constants.ts:78) | full (internal/runner/runner.go:53-54) | none (v1:packages/codemode/README.md:5) | none (v2:packages/codemode/README.md:8) | none (how “Connector SDKs”) |
| 5 | sandbox: network | any (--allow-net) (.lootbox/src/lib/constants.ts:78) | any; strict = 127.0.0.1, Deno only (internal/runner/runner.go:61-63) | none (v1:packages/codemode/README.md:5) | none; “Do not use fetch” (v2:packages/core/src/codemode/tool.ts:63) | blocked, or routed through a host Fetcher (api “DynamicWorkerExecutor”) |
| 6 | sandbox: subprocess | none: no --allow-run (.lootbox/src/lib/constants.ts:78) | full (internal/runner/runner.go:53-54; AGENTS.md:28-29) | none (v1:packages/codemode/README.md:5) | none (v2:packages/codemode/README.md:8) | none: “does not expose Node.js APIs … process” (how “Connector SDKs”) |
| 7 | capability bindings | the tools proxy only; network still open | none (full authority) | the host tools tree (v1:packages/codemode/README.md:319-337) | the tools tree plus opt-in extensions, none registered by core (v2:packages/codemode/interpreter-support.md:455-456) | RPC bindings per connector (how “Connectors”) |
| 8 | wall-clock limit | 10 s script, 30 s per call (.lootbox/src/lib/constants.ts:17, :20) | 120 s script (internal/settings/exec.go:16), 120 s per call (internal/defaults/defaults.json:6) | none set by the host (v1:packages/opencode/src/tool/code-mode.ts:239-260) | none set by the host (v2:packages/core/src/codemode/tool.ts:222); 12 h per MCP call (v2:packages/core/src/mcp/client.ts:34) | 60 s per the API reference, 30 s per the changelog (api “DynamicWorkerExecutor”; chg) |
| 9 | CPU / memory limit | — | — | cooperative yield; timeout only (v1:packages/codemode/README.md:286) | same | cpuMs, subRequests (limits “Set custom limits”) |
| 10 | output size limit | — (whole stdout) (.lootbox/src/lib/rpc/managers/message_router.ts:112) | — (whole stdout) (internal/execsvc/execsvc.go:413-417) | maxOutputBytes knob, unset (v1:packages/codemode/README.md:266) | same (v2:packages/codemode/README.md:180) | truncateResult 6,000 tokens; 1,000,000-character durable values (api “Code and output utilities”; how “Durable value and result limits”) |
| 11 | tool-call budget | — | — | maxToolCalls knob, unset (v1:packages/codemode/README.md:265) | same; search counts (v2:packages/codemode/README.md:171-172) | — |
| 12 | type generation from inputSchema | yes, shallow: nested objects → Record (.lootbox/src/lib/external-mcps/parse_mcp_schemas.ts:218-220) | yes: $ref/allOf/enum/const/prefixItems (internal/codegen/ts.go:41-61) | yes, JSDoc plus @default/@format tags (v1:packages/codemode/README.md:214) | yes (v2:packages/codemode/src/tool-schema.ts:216) | yes (api “JSON Schema utilities”) |
| 13 | types from outputSchema | — (McpToolResult) (.lootbox/src/lib/external-mcps/parse_mcp_schemas.ts:93) | — (ToolResult = any) (internal/codegen/emit.go:458) | yes (v1:packages/opencode/src/tool/code-mode.ts:127) | yes (v2:packages/core/src/tool/mcp.ts:48) | descriptor field exists (api “JSON Schema utilities”) |
| 14 | descriptions in types | — (verified live against /types/mcp_codedb) | JSDoc for tools and properties (internal/codegen/emit.go:81-90; internal/codegen/ts.go:229) | JSDoc (v1:packages/codemode/README.md:214) | JSDoc | doc comments (blog “Converting MCP to TypeScript”) |
| 15 | namespaces | tools.mcp_<server>.<fn> (.lootbox/src/lib/type_system/client_generator.ts:654) | tools.<ns>.<fn> plus a bare <ns> (internal/execsvc/execsvc.go:616-617) | tools.<server>.<tool> (v1:packages/opencode/src/tool/code-mode.ts:47) | tools.<ns>.<tool>, nested by dots (v2:packages/codemode/README.md:77) | <connector>.<method> as a global (how “Connector SDKs”) |
| 16 | name sanitising | [^A-Za-z0-9_]→_, no digit or reserved-word handling (.lootbox/src/lib/external-mcps/mcp_schema_fetcher.ts:152-154) | same plus a leading-digit _ prefix; no reserved words, no collision check (internal/codegen/ts.go:397-418) | keeps -; bracket notation (v1:packages/opencode/src/mcp/catalog.ts:117) | same; tool part ≤ 64 characters (v2:packages/core/src/tool/runtime.ts:274; v2:packages/core/src/tool.ts:297) | separators, digit prefix, reserved words; collisions throw (mcp, step 2) |
| 17 | result unwrapping | raw envelope (.lootbox/src/lib/rpc/execute_mcp.ts:68-72) | isError, structured, text (JSON-parsed), blocks; .raw kept (internal/codegen/emit.go:493-519) | structured, text, attachment placeholder (v1:packages/opencode/src/tool/code-mode.ts:109-115) | structured, text JSON-parsed only without a schema (v2:packages/core/src/tool/mcp.ts:96-106) | toolResult, error, structured, all-text, mixed (api “codeMcpServer”) |
| 18 | isError handling | returned as a value (.lootbox/src/lib/rpc/execute_mcp.ts:68-72) | throws ToolError (internal/codegen/emit.go:494-499) | throws, becomes a ToolFailure diagnostic (v1:packages/opencode/src/tool/code-mode.ts:161-167) | ToolFailure (v2:packages/core/src/tool/mcp.ts:78-85) | thrown connector error (api “McpConnector”) |
| 19 | binary/image content | base64 in the envelope (.lootbox/src/lib/external-mcps/parse_mcp_schemas.ts:285) | .raw, artifact(), interception at the boundary (docs/exec.md:259-275) | attached to the model as files (v1:packages/opencode/src/tool/code-mode.ts:89-100) | same (v2:packages/core/src/codemode/tool.ts:139-147) | mixed-content result (api “codeMcpServer”) |
| 20 | discovery: namespace list | lootbox tools → /namespaces (.lootbox/src/lib/lootbox-cli/tools.ts:10) | mcpx ls (AGENTS.md:8) | in the execute tool description (v1:packages/opencode/src/tool/registry.ts:283-289) | in session instructions (v2:packages/core/src/codemode/instructions.ts:21-32) | connector names in the tool description (how “What the model sees”) |
| 21 | per-namespace types | /types/<ns,ns> (.lootbox/src/lib/rpc/managers/openapi_route_handler.ts:231) | mcpx types ns or ns.tool (AGENTS.md:10-11) | — (search returns signatures) | — (same) | codemode.describe(target) (api “Sandbox codemode API”) |
| 22 | budgeted catalogue | — | 2,000 estimated tokens, round-robin (internal/codegen/catalog.go:53-68) | 2,000 estimated tokens, headers not counted (v1:packages/codemode/src/tool-runtime.ts:86; v1:packages/codemode/README.md:184) | 2,000 estimated tokens (characters / 4), plus pinned and deltas (v2:packages/core/src/codemode/catalog.ts:49-50) | full types (createCodeTool) or none (durable runtime) (how “What the model sees”) |
| 23 | search | — | mcpx search, synchronous search() in scripts, mcpx_search (internal/codegen/emit.go:1121) | await tools.$codemode.search(...) with paging (v1:packages/codemode/README.md:198) | synchronous search(), paged (v2:packages/codemode/README.md:170) | codemode.search(), top 50, includes snippets (api “Sandbox codemode API”) |
| 24 | named scripts | .lootbox/scripts, one directory (.lootbox/src/lib/lootbox-cli/exec.ts:103-120) | .mcpx/scripts upward, then user dir; recipes (AGENTS.md:21-22) | — | — | snippets saved by the host (how “Snippets”) |
| 25 | script args | stdin only (.lootbox/src/lib/lootbox-cli/exec.ts:155-168) | argv, default export, --export, @param (AGENTS.md:25, :69-71) | — | — | snippet input (api “Sandbox codemode API”) |
| 26 | streaming output / emit | — (buffered) (.lootbox/src/lib/execute_llm_script.ts:38-43) | emit(), SSE or NDJSON frames (docs/exec.md:137-148) | — (only tool-call status) (v1:packages/opencode/src/tool/code-mode.ts:216-217) | — (same) (v2:packages/core/src/codemode/tool.ts:85-88) | — (result at end) (api “Runtime tool input and output”) |
| 27 | structured logs | — (plain stderr, dropped on success) (.lootbox/src/lib/rpc/managers/message_router.ts:111-115) | log.* records with attributes (internal/codegen/emit.go:556) | logs: string[] (v1:packages/codemode/README.md:133) | logs, plus warnings (v2:packages/codemode/README.md:127-128) | logs: string[] (api “Runtime tool input and output”) |
| 28 | JSON envelope | — | --json run, structured /v1 (internal/execsvc/execsvc.go:201-213) | CodeMode.Result (internal) (v1:packages/codemode/README.md:128-145) | same (v2:packages/codemode/README.md:122-139) | completed, paused or error (api “Runtime tool input and output”) |
| 29 | MCP version as a client | SDK 1.22.0 → 2025-06-18 (@modelcontextprotocol/sdk@1.22.0/dist/esm/types.js:2) | 2025-11-25 plus 2026-07-28 (internal/mcpclient/client.go:22, :26) | SDK 1.29.0 → 2025-11-25 (v1:packages/opencode/package.json:83; https://unpkg.com/@modelcontextprotocol/sdk@1.29.0/dist/esm/types.js line 2) | client 2.0.0; legacy 2025-11-25 by default, protocol: auto or 2026-07-28 per server (v2:packages/core/package.json:113; v2:packages/schema/src/mcp.ts:20, :23) | legacy plus stateless MRTR (client “Elicitation”) |
| 30 | elicitation during a script | — (capabilities: {}) (.lootbox/src/lib/external-mcps/mcp_client_manager.ts:152) | daemon broker and policy; no script hook (internal/daemon/server.go:422-424) | — (commented out) (v1:packages/opencode/src/mcp/index.ts:43-44) | UI form, form and URL modes (v2:packages/core/src/mcp/client.ts:143, :155-158) | agent handlers (client “Elicitation”) |
| 31 | sampling | — | declared only if a handler is installed (internal/mcpclient/modern.go:46-58) | — (v1:packages/opencode/src/mcp/index.ts:41-42) | — (not in capabilities, v2:packages/core/src/mcp/client.ts:142-147) | not documented for code mode (unverified) |
| 32 | roots | — | served (internal/mcpclient/modern.go:116-120) | the instance directory (v1:packages/opencode/src/mcp/index.ts:77-78) | legacy era only (v2:packages/core/src/mcp/client.ts:144-146, :155) | not documented (unverified) |
| 33 | progress | — | parsed, never requested (internal/mcpclient/client.go:438, :487) | token requested, timeout reset (v1:packages/opencode/src/tool/code-mode.ts:154-158) | token requested, timeout not reset (v2:packages/core/src/mcp/client.ts:280-281) | unverified |
| 34 | cancellation / timeouts | script killed; upstream not cancelled (.lootbox/src/lib/rpc/execute_mcp.ts:199-208) | process group killed; notifications/cancelled sent (internal/runner/runner.go:424-438; internal/mcpclient/client.go:709-716) | abort signal passed (v1:packages/opencode/src/tool/code-mode.ts:155) | Effect interruption (v2:packages/codemode/README.md:185-186) | executor timeout (api “DynamicWorkerExecutor”) |
| 35 | pooling | one client per server (.lootbox/src/lib/external-mcps/mcp_client_manager.ts:49) | pools: shared, pooled or session, max 4 (internal/defaults/defaults.json:2-10) | one per server per instance (v1:packages/opencode/src/mcp/index.ts:145, :492) | one per server per Location (v2:packages/core/src/mcp/index.ts:152) | per Agent connection (mcp, step 3) |
| 36 | per-session scoping | — (per-session is a label only, fork) (.lootbox/src/lib/rpc/managers/mcp_integration_manager.ts:200-216) | scope keys (internal/config/scope.go:46-64) | — (one client map per instance, v1:packages/opencode/src/mcp/index.ts:145) | _meta hint only (v2:packages/core/src/mcp/client.ts:278) | execution-id resource keys (how “Resource lifetimes”) |
| 37 | stateful sharing (Chrome) | one browser for everyone (ASSESSMENT.md:84-88) | one browser per session (internal/pool/pool.go:14-16) | shared per instance (v1:packages/opencode/src/mcp/index.ts:145, :492) | shared per Location (v2:packages/core/src/mcp/index.ts:152) | per Agent connection; a connector can key a browser by execution id (how “Resource lifetimes”) |
| 38 | remote execution | always daemon-side (.lootbox/src/lib/lootbox-cli/exec.ts:40; .lootbox/src/lib/execute_llm_script.ts:20) | auto, local or remote (docs/exec.md:54) | — in-process (v1:packages/opencode/src/tool/code-mode.ts:274) | — in-process (v2:packages/core/src/codemode/tool.ts:89-127) | always on the platform (how “Executor”) |
| 39 | HTTP API | REST GETs plus WS /ws (.lootbox/src/lib/rpc/managers/openapi_route_handler.ts:132-271) | /v1/* REST over unix socket and TCP (internal/daemon/routes_exec.go:36) | none: code mode is only a registry tool (v1:packages/opencode/src/tool/registry.ts:118-119) | none (no code-mode route; rg over v2:packages/server/src, v2:packages/cli/src) | the Worker’s own fetch (dwstart) |
| 40 | MCP server surface | — (never; git log --all -S'McpServer(' is empty) | /mcp with mcpx_exec and others (internal/mcpserver/server.go:327) | — (v1:packages/opencode/src/tool/registry.ts:118-119) | — (rg over v2:packages/server/src) | codeMcpServer, openApiMcpServer (api “codeMcpServer”) |
| 41 | plugin / harness integration | an instruction file plus lootbox-link (nix:instructions/lootbox.md:1-4) | opencode plugin tools plus shell.env (plugin/opencode/mcpx-session.ts:606, :664) | built into opencode behind a flag (v1:packages/opencode/src/effect/runtime-flags.ts:48) | built in, on by default (v2:packages/schema/src/mcp.ts:34-36) | AI SDK, TanStack, browser, MCP (api entry points) |
| 42 | UI | status dashboard: namespaces and connection (.lootbox/ui/src/pages/Dashboard.tsx:128-176); cannot connect, hard-coded port 3000 (.lootbox/ui/src/lib/websocket-client.ts:34) | TUI (plugin/opencode/mcpx-tui.tsx) | TUI shows toolCalls (v1:packages/opencode/src/tool/code-mode.ts:216-217) | same (v2:packages/core/src/codemode/tool.ts:85-88) | none shipped; the log “supports developer audit views” (how “Durable execution log”) |
| 43 | persistence / history | SQLite script_runs, full output (.lootbox/src/lib/script_history.ts:29-63) | durable log plus artifact index (internal/mcpserver/server.go:339) | — (non-goal: “Durable pause/resume, replay, or storage adapters”, v1:packages/codemode/README.md:352) | — (no store in v2:packages/core/src/codemode/tool.ts) | DO-facet SQLite log, 50 kept, plus snippets (how “Durable runtime”) |
| 44 | auth to call it | none; localhost; no Origin check (.lootbox/src/lib/rpc/websocket_server.ts:190) | none; socket mode plus loopback TCP; no Origin check (internal/daemon/server.go:206-208) | the model in an opencode session (v1:packages/opencode/src/tool/registry.ts:118-119) | same (v2:packages/core/src/tool.ts:263-264) | the Worker’s auth; OAuth 2.1 for the Cloudflare MCP server (blog2 “The Cloudflare MCP server”) |
| 45 | secrets reachable by the script | no env access by default; tool workers --allow-all (.lootbox/src/lib/rpc/worker_manager.ts:301) | full os.Environ() (internal/runner/runner.go:359) | none (v1:packages/codemode/README.md:5) | none (v2:packages/codemode/README.md:8) | none: bindings hold them (how “Connectors”) |
| 46 | network egress control | on/off only (.lootbox/src/lib/get_config.ts:194-201) | Deno --allow-net=127.0.0.1 only (internal/runner/runner.go:61-63) | no network (v1:packages/codemode/README.md:5) | no network (v2:packages/core/src/codemode/tool.ts:63) | globalOutbound null or Fetcher (api “DynamicWorkerExecutor”) |
| 47 | approvals | — | elicit.confirmDestructive (internal/settings/consumer.go:47) | per-call ctx.ask (v1:packages/opencode/src/tool/code-mode.ts:147) | permission.assert (v2:packages/core/src/tool/mcp.ts:51) | requiresApproval, durable pause (how “Approvals through abort and replay”) |
| 48 | result without return | — (“Do not return from scripts”, .lootbox/LLM_QUICK_START.md:93) | stdout, emit(), or a default export (AGENTS.md:53, :69-71) | null (v1:packages/codemode/README.md:65) | last top-level expression (v2:packages/codemode/README.md:20) | last expression auto-returned (dwex “createCodeTool”) |
| 49 | un-awaited calls at program end | run on (event loop) | run on (event loop; internal/runner/runner.go:424-438 bounds them) | awaited; failures reported (v1:packages/codemode/README.md:249) | interrupted; rejections become warnings (v2:packages/codemode/README.md:18-19) | unverified |
| 50 | concurrent tool calls per program | no cap | no per-run cap; pool max 4 per server (internal/defaults/defaults.json:3) | 8 (v1:packages/codemode/src/stdlib/promise.ts:6) | unrestricted (v2:packages/codemode/README.md:187) | sequential advised when a run may pause (how “Deterministic replay”) |
| 51 | which tools code mode wraps | all MCP tools and local TS namespaces | all configured servers, in scripts only | MCP tools only, when OPENCODE_EXPERIMENTAL is set (v1:packages/opencode/src/tool/code-mode.ts:210; v1:packages/opencode/src/session/tools.ts:388) | every tool without codemode: false, on by default (v2:packages/core/src/tool.ts:228-229) | the connectors the host binds (how “Connectors”) |
| 52 | caller identity sent upstream | — (CLI sends no session, .lootbox/src/lib/lootbox-cli/exec.ts:40) | — (session stops at the daemon, internal/codegen/emit.go:528-533) | — | _meta["ai.opencode/sessionID"] on every call, child calls included (v2:packages/core/src/mcp/client.ts:278; v2:packages/core/src/tool/mcp.ts:69) | execution id passed to connector methods (how “Resource lifetimes”) |
| B | Where each is better than mcpx | |||||
| B1 | confinement | no filesystem, env or subprocess by default (.lootbox/src/lib/constants.ts:78); network stays open | none, by the user’s choice (ASSESSMENT.md:290-292) | only implemented features; no ambient APIs (v1:packages/codemode/README.md:5) | same (v2:packages/codemode/README.md:7-8) | isolate with network blocked or proxied (api “DynamicWorkerExecutor”); the guide’s model (docs/docs/2026-07-28/develop/clients/client-best-practices.mdx:292). Conflicts with mcpx’s goal |
| B2 | secrets out of reach | environment unreadable without --allow-env (.lootbox/src/lib/constants.ts:78) | script inherits the environment; the daemon’s for /v1/exec (internal/runner/runner.go:359) | no environment access (v1:packages/codemode/README.md:5) | same | credentials only in bindings (how “Connectors”) |
| B3 | checked before running | type check on by default (.lootbox/src/lib/execute_llm_script.ts:34) | off by default (internal/settings/registry.go:244-246) | — | — | — |
| B4 | typed results | — | ToolResult = any (internal/codegen/emit.go:458) | return types from outputSchema (v1:packages/opencode/src/tool/code-mode.ts:127) | same (v2:packages/core/src/tool/mcp.ts:48) | outputSchema on descriptors (api “JSON Schema utilities”) |
| B5 | bounded output and calls | — | unbounded stdout; no call budget (internal/execsvc/execsvc.go:413-417) | maxOutputBytes, maxToolCalls (v1:packages/codemode/README.md:265-266), unset by the host | same (v2:packages/codemode/README.md:179-180) | 6,000-token truncation; cpuMs, subRequests (api “Code and output utilities”; limits “Set custom limits”) |
| B6 | failures as data | — | error string, exitCode, timedOut (internal/execsvc/execsvc.go:201-213) | typed kind diagnostics (v1:packages/codemode/README.md:292-305) | same, plus warnings (v2:packages/codemode/README.md:145-159) | status: "error" variant (api “Runtime tool input and output”) |
| B7 | answer without console.log | — | stdout only for snippets (internal/execsvc/execsvc.go:607-625) | — | last expression is the result (v2:packages/codemode/README.md:20) | auto-returns the last expression (dwex “createCodeTool”) |
| B8 | discovery ergonomics | — | catalogue on request; search without paging (internal/codegen/emit.go:1121) | catalogue in the tool description automatically; paged search (v1:packages/opencode/src/tool/registry.ts:283-289; v1:packages/codemode/README.md:206) | catalogue in instructions, deltas, pinned tools (v2:packages/core/src/codemode/catalog.ts:79-84) | search also finds vetted snippets (how “Platform SDK”) |
| B9 | tool names | mcp_ prefix keeps namespaces clear of reserved words (.lootbox/src/lib/type_system/client_generator.ts:654) | no reserved-word, prelude or collision checks (internal/codegen/ts.go:397-418; internal/config/config.go:407-416) | real names, bracket notation, no collisions (v1:packages/opencode/src/mcp/catalog.ts:117) | same (v2:packages/codemode/README.md:77-78) | reserved words suffixed, collisions throw, namespaces validated (mcp, step 2; api “DynamicWorkerExecutor”) |
| B10 | harness integration | — | plugin mcpx_exec: no abort, session, cwd or live status; reads a missing field (plugin/opencode/mcpx/daemon.ts:452-455; plugin/opencode/mcpx-session.ts:621-623) | permission check and hooks per child call, live status, cancel (v1:packages/opencode/src/tool/code-mode.ts:141-147, :216-217, :262-274) | same, plus the session id on every child call (v2:packages/core/src/tool/mcp.ts:51-69) | approval per connector method (how “Approvals through abort and replay”) |
| B11 | long upstream calls | — | fixed 120 s, no progress token (internal/defaults/defaults.json:6; internal/mcpclient/client.go:438) | progress token resets the timeout (v1:packages/opencode/src/tool/code-mode.ts:154-158) | 12 h call timeout (v2:packages/core/src/mcp/client.ts:34) | — |
| B12 | questions from upstream servers | — | broker and policy; no script hook (internal/daemon/server.go:422-424; docs/protocol.md:379) | — | UI form, form and URL modes (v2:packages/core/src/mcp/client.ts:143) | agent handlers on both paths (client “Elicitation”) |
| B13 | images for a vision model | — | artifacts by reference; model needs resources/read (docs/exec.md:259-275) | attached to the model as files (v1:packages/opencode/src/tool/code-mode.ts:89-100) | same (v2:packages/core/src/codemode/tool.ts:139-147) | — |
| B14 | history and reuse | every run’s script text and output kept (.lootbox/src/lib/script_history.ts:29-63); local TS files as namespaces (.lootbox/README.md:86-99) | log records without script source (internal/mcpserver/server.go:339-341) | — | — | durable log, host-reviewed snippets, replay and rollback (how “Durable runtime”, “Snippets”, “Rollback”). Replay conflicts with mcpx’s goal |
2.2 What each assumes an MCP server should be
lootbox. lootbox treats a server as a long-lived process or endpoint. It connects to every one at daemon start,
keeps one client per server for all callers, and (fork only) pings it to decide whether it is alive
(.lootbox/src/lib/rpc/managers/mcp_integration_manager.ts:221-222; .lootbox/README.md:385-388). It assumes the
server needs nothing from the client: it declares capabilities: {}, so a server that elicits, samples or asks for
roots is refused (.lootbox/src/lib/external-mcps/mcp_client_manager.ts:150-153). It assumes a server’s catalogue fits
on one tools/list page and one resources/list page, and that tool names reduce harmlessly to [A-Za-z0-9_]
(.lootbox/src/lib/external-mcps/mcp_schema_fetcher.ts:69, :101, :152-154). It assumes a server is stateless enough
to share; the only concession to stateful servers is rewriting a port found in their command-line arguments, in the
fork (.lootbox/src/lib/rpc/managers/mcp_integration_manager.ts:82-133). Results are passed through as raw envelopes,
so the script, not lootbox, is expected to understand content[] and isError
(.lootbox/src/lib/rpc/execute_mcp.ts:68-72).
Cloudflare code mode. Cloudflare’s premise is that “Almost every MCP server is just a wrapper around an existing
traditional API”. MCP’s value is that it “provides a uniform way to connect to and learn about an API”, with
“authorization handled out-of-band” (blog, “But MCP is still useful, because it is uniform” and “What’s MCP?”). A server
is therefore a remote, OAuth-authorised HTTP endpoint that the Agent connects to once. The Agents MCP client offers
only auto, streamable-http and sse, no stdio (client, “Transport options”). The server enforces its own
authorisation: “Enforce permissions and any required approval inside upstream tool handlers” (mcpsrv, “Sandbox and
authorization boundary”). The server should describe its tools well enough to generate typed methods, and a large API
should itself be a code-mode server: two tools, search and execute, at a fixed footprint of about 1,000 tokens
(blog2, “Server‑side Code Mode”; mcpsrv, “Search and execute”). A server’s tool names must be unique after
sanitisation, or the connector throws (mcp, step 2). Side-effecting tools should be safe to pause, replay in the same
order and, ideally, revert (how, “Deterministic replay” and “Rollback”).
What opencode and mcpx assume of a server is covered in the opencode and process-model registers.