Skip to content

feat(config): enable the code_execution tool by default - #1264

Merged
Dumbris merged 3 commits into
mainfrom
chore/enable-code-execution-default
Sep 12, 2026
Merged

feat(config): enable the code_execution tool by default#1264
Dumbris merged 3 commits into
mainfrom
chore/enable-code-execution-default

Conversation

@Dumbris

@Dumbris Dumbris commented Sep 12, 2026

Copy link
Copy Markdown
Member

Summary

code_execution ships on from v0.66.0. Alongside the flip, the cross-model review (opencode astra, 3 rounds → CLEAN) surfaced two gaps that had to close before the tool is on for everyone, plus the release notice.

Default flip

  • DefaultConfig().EnableCodeExecution = true (internal/config/config.go). Load is default-then-merge, so an explicit false in the file still wins; a file that omits the key inherits true. Config files written by earlier releases carry the key explicitly, so existing installs keep their setting — documented in the release notice.
  • Surface goldens: the frozen pre-feature capture (pre099/) is untouched. The merge-base goldens are regenerated with the live tool present, and the two enumerated-delta gates now allow code_execution as an addition on default_server and a stub → live modification on the two routing-mode surfaces, pinned field by field by assertCodeExecutionLive (stub code param survives, script present, code not schema-required, title/annotations/description are the live tool's).
  • Docs (configuration.md, config-file.md, features/code-execution.md, routing-modes.md, code_execution/*), CLAUDE.md, Settings help text (Web UI + macOS tray catalog), and the Settings security-posture pill no longer flags an enabled sandbox as "review this".
  • .github/RELEASE_NOTICE.md for v0.66.0: the default flip (explicit vs omitted key), the Spec-105 scope-hardening fixes, Windows child-process cleanup, the stale-OAuth health fix.

Hardening found by the review

  1. A timed-out script was never stopped. Execute returned TIMEOUT and abandoned the goroutine — a for(;;){} kept a core for the life of the process, and the pool slot was released the moment the handler returned. Execute now calls vm.Interrupt on the deadline; an interrupt that lands during value.Export() (a getter on the returned object) surfaces as a goja panic rather than a RunString error, so executeWithVM recovers it into a TIMEOUT result — without that the process died. The lone call_tool() now dispatches under the execution's timeout context like call_tools() batches already did.
  2. output_sanitisation did not reach scripts. The bridge returned client.CallTool's result raw, so an operator's redact/block policy — enforced on every call_tool_* dispatch — was bypassed inside code_execution, where a script can read the secret, forward it to another upstream, or return it. Sub-call results now go through applyOutputSanitisation before they are recorded, stored in history, or handed to JavaScript; a block surfaces as a failed call. contentTrust is derived exactly as on the direct path (unannotated ⇒ open-world ⇒ untrusted, so strip applies), and the sub-call mints one correlation id shared by the policy decision and the activity record (previously two ids, which could never be joined).

Testing

TDD throughout — each new test observed failing first (TestDefaultConfig_CodeExecutionEnabled, TestExecuteTimeoutInterruptsTheVM (2s goroutine still running without the interrupt), TestExecuteTimeoutDuringExportDoesNotPanic (process panic without the recover), TestSubCallSanitisation_*, TestSingletonCallToolDispatchesUnderTheExecutionContext).

  • go test -race on internal/config internal/runtime internal/httpapi internal/telemetry internal/jsruntime cmd/... and internal/server (CI skip regex) — green
  • frontend: vitest 1264/1264, vue-tsc clean
  • golangci-lint v2 with .github/.golangci.yml — 0 issues
  • not run: ./scripts/test-api-e2e.sh (CI runs it)

Not addressed here (pre-existing, filed separately): sub-call activity records truncate the detection text to 8 KiB, and the parent code_execution record never invokes sensitive-data detection on script input/output.

🤖 Generated with Claude Code

Dumbris and others added 3 commits September 12, 2026 12:34
The sandboxed JavaScript/TypeScript tool ships on from v0.66.0. New
installs get "enable_code_execution": true; a config file that omits
the key inherits it, and an explicit false in the file still wins
(default-then-merge), so existing installs keep whatever they have.

Surface tests measured against the frozen flag-off capture now treat
code_execution as an enumerated addition on default_server and a
stub-to-live modification on the two routing-mode surfaces, pinned
field by field. Docs, Settings help text and the security-posture pill
(an enabled sandbox is no longer flagged "review this") follow.

Adds .github/RELEASE_NOTICE.md for v0.66.0: the default flip, the
scope-hardening fixes, Windows process cleanup and the stale-OAuth
health fix.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…l output

Two gaps the cross-model review found on the way to shipping the tool
on by default.

A script that overran its timeout was reported as TIMEOUT to the
caller and then abandoned: nothing interrupted the goja VM, so a busy
loop kept a core for the life of the process, and the pool slot it
held was released the moment the handler returned. Execute now calls
vm.Interrupt on the deadline, and the lone call_tool() dispatches under
the execution's timeout context like call_tools() batches already did,
so an upstream call in flight when the script is cut off is cancelled
with it.

The script bridge returned client.CallTool's result raw, so an
operator's output_sanitisation redact/block policy — enforced on every
call_tool_* dispatch — did not reach JavaScript, where a script can read
the secret, forward it to another upstream, or return it. Sub-call
results now go through the same applyOutputSanitisation seam before
they are recorded, stored or handed to the script; a blocked response
surfaces as a failed call.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…elation id per sub-call

Cross-model review round 2/3 on the timeout and sanitisation seams:

- An interrupt that lands while value.Export() is still running script
  code (a getter on the returned object) is raised by goja as a panic,
  not a RunString error; unrecovered in the script goroutine it took
  the process down. executeWithVM now recovers *goja.InterruptedError
  into a TIMEOUT result.
- The sub-call bridge minted its correlation id twice (sanitisation and
  activity), and mintCorrelationIDAt bumps a sequence per call, so the
  policy decision could never be joined to its activity record. One id
  is minted at the top of CallTool and threaded through.
- contentTrust for a sub-call is derived through ContentTrustForTool
  unconditionally, as handleCallToolVariant does: a tool with no
  annotations is open-world, hence untrusted, so strip mode reaches it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying mcpproxy-docs with  Cloudflare Pages  Cloudflare Pages

Latest commit: ce521a6
Status: ✅  Deploy successful!
Preview URL: https://e5428686.mcpproxy-docs.pages.dev
Branch Preview URL: https://chore-enable-code-execution.mcpproxy-docs.pages.dev

View logs

@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 78.78788% with 7 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/server/mcp_code_execution.go 72.72% 5 Missing and 1 partial ⚠️
internal/jsruntime/runtime.go 90.90% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@github-actions

Copy link
Copy Markdown
Contributor

📦 Build Artifacts

Workflow Run: View Run
Branch: chore/enable-code-execution-default

Available Artifacts

  • archive-darwin-amd64 (29 MB)
  • archive-darwin-arm64 (27 MB)
  • archive-linux-amd64 (17 MB)
  • archive-linux-arm64 (16 MB)
  • archive-windows-amd64 (29 MB)
  • archive-windows-arm64 (26 MB)
  • frontend-dist-pr (0 MB)
  • installer-dmg-darwin-amd64 (24 MB)
  • installer-dmg-darwin-arm64 (21 MB)

How to Download

Option 1: GitHub Web UI (easiest)

  1. Go to the workflow run page linked above
  2. Scroll to the bottom "Artifacts" section
  3. Click on the artifact you want to download

Option 2: GitHub CLI

gh run download 34687375997 --repo smart-mcp-proxy/mcpproxy-go

Note: Artifacts expire in 14 days.

@Dumbris
Dumbris merged commit 9f7fe2e into main Sep 12, 2026
43 checks passed
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