Skip to content

feat: Code Mode — WASM-sandboxed JS execution for MCP tool orchestration#30

Merged
christianromeni merged 9 commits intomainfrom
feat/code-mode-prototype
Mar 29, 2026
Merged

feat: Code Mode — WASM-sandboxed JS execution for MCP tool orchestration#30
christianromeni merged 9 commits intomainfrom
feat/code-mode-prototype

Conversation

@christianromeni
Copy link
Copy Markdown
Contributor

Summary

  • Code Mode: LLMs write JavaScript that orchestrates multiple MCP tool calls in one execution, reducing token usage by 30-80%
  • WASM Sandbox: QuickJS compiled to WASM via Wazero — no filesystem, no network, no host access
  • 3 MCP Tools: list_servers, search_tools, execute_code on dedicated /api/v1/mcp endpoint
  • Per-tool blocklist: Admins can block specific tools from Code Mode execution
  • Persistent tool cache: Tool schemas cached in DB, available from first request without HTTP calls
  • SSE detection: Deprecated SSE servers auto-deactivated with clear error message
  • TypeScript types: Tool schemas injected into execute_code description so LLMs see available tools at tools/list time
  • JS Proxy pattern: Dynamic tool dispatch via ES6 Proxy, any tool name characters supported
  • Console capture: console.log/warn/error output returned in execution results
  • Execution history: UUIDv7 per execution groups tool calls for tracing

Endpoints

Route Purpose
POST /api/v1/mcp Code Mode MCP server (list_servers, search_tools, execute_code)
POST /api/v1/mcp/voidllm Management MCP server (list_models, get_usage, etc.)
POST /api/v1/mcp/:alias External MCP server proxy
GET /api/v1/mcp-servers/:id/tools List cached tools with blocked status
GET/POST/DELETE /api/v1/mcp-servers/:id/blocklist Tool blocklist CRUD
POST /api/v1/mcp-servers/:id/refresh-tools Force tool cache refresh (60s cooldown)

Config

mcp:
  code_mode:
    enabled: true          # opt-in, default false
    pool_size: 8           # concurrent WASM runtimes
    memory_limit_mb: 16    # per execution
    timeout: 30s           # per execution
    max_tool_calls: 50     # per execution

Migrations

  • 0005: mcp_tool_blocklist table
  • 0006: code_mode_execution_id on mcp_tool_calls
  • 0007: mcp_server_tools persistent cache table

Test plan

  • go test ./... -race — all packages green
  • 200+ new tests (executor, pool, cache, types, SSE, hooks, blocklist)
  • Live tested: execute_code → AWS Knowledge MCP → search results returned
  • Live tested: Claude Code connected to /api/v1/mcp, tools visible and callable
  • Code review (4 rounds)
  • Security audit (4 rounds, 0 open critical/high)
  • External AI review (Codex + Gemini)

…ion (#30)

Add Code Mode: LLMs write JavaScript that orchestrates multiple MCP tool
calls in a single execution, reducing token usage by 30-80%. Scripts run
in a QuickJS/WASM sandbox (fastschema/qjs + Wazero) embedded in VoidLLM.

Three new built-in MCP tools:
- list_servers: discover available MCP servers
- search_tools: find tools by keyword across servers
- execute_code: run JS with MCP tools as async functions

Runtime: pool of QJS runtimes (default 8), fresh runtime per execution,
tool schema cache with lazy fetch, console capture in results.

Configurable via voidllm.yaml (disabled by default):
  mcp.code_mode.enabled, pool_size, memory_limit_mb, timeout, max_tool_calls

Also fixes session re-init double-check in MCP proxy.
#31)

Split built-in MCP into two servers:
- /api/v1/mcp — Code Mode (list_servers, search_tools, execute_code)
- /api/v1/mcp/voidllm — Management (list_models, get_usage, etc.)
- /api/v1/mcp/:alias — External MCP server proxy

Per-tool blocklist for Code Mode:
- Migration 0005: mcp_tool_blocklist table
- CRUD API: GET/POST/DELETE /mcp-servers/:id/blocklist
- Defense in depth: filtered before sandbox injection + checked in ToolCaller
- Blocklist also applied to search_tools and list_servers tool counts

Tool refresh endpoint:
- POST /mcp-servers/:id/refresh-tools with 60s cooldown

Admin controls:
- code_mode_enabled toggle in API response and PATCH
- UI: Code Mode toggle column, expanded row with blocklist management

Shared MCP handler helper eliminates POST/SSE handler duplication.
…ory, TypeScript types (#32)

JS Proxy pattern replaces static preamble generator:
- Single __callTool dispatch via ES6 Proxy interception
- Any tool name characters supported, preamble is O(1) in tool count

SSE upstream transport support:
- Auto-detect Streamable HTTP vs deprecated SSE protocol
- Lazy detection with sync.Once, origin validation on endpoints

Execution history:
- Migration 0006: code_mode_execution_id on mcp_tool_calls
- UUIDv7 per execute_code call groups all tool calls

Dynamic TypeScript types in execute_code description:
- GenerateToolTypeDefs converts cached tool schemas to TS declarations
- OnToolsListHook injects types at tools/list time

Bug fixes:
- MCP access control enforced in all Code Mode closures for global servers
- ToolCache fetcher resolves servers across all scopes
- Frontend blocklist DELETE matches backend query parameter API
…buttons (#33)

Persistent tool cache:
- Migration 0007: mcp_server_tools table for DB-backed tool schemas
- Startup loads from DB (zero HTTP calls, TypeScript types immediately available)
- 24h background refresh keeps schemas current
- Write-through on every fetch (RefreshServer, GetTools)
- DB entries marked stale on load so they refresh within maxAge

SSE transport detection:
- Servers using deprecated SSE protocol auto-deactivated at startup
- Clear error message: "server uses deprecated SSE transport"
- Test connection also detects and deactivates SSE servers

Tools list UI:
- GET /mcp-servers/:id/tools endpoint returns cached tools with blocked status
- Expanded row shows all tools with Block/Unblock buttons
- Block buttons work for YAML-sourced servers (blocklist is independent of source)
- Plug icon centered in sidebar and MCP servers page

Also fixes:
- ToolStore.Delete uses server ID (not alias) to avoid soft-delete lookup failure
- Corrupt JSON schemas skipped on DB load instead of serving empty schemas
Extract 3 closures (ExecuteCode, ListAccessibleMCPServers, SearchMCPTools)
from app.go into codeModeService in code_mode.go. Shared accessibleServers
helper eliminates duplicated server-listing + access-check logic.

New tests:
- code_mode_test.go: 21 tests (mock DB, real WASM executor)
- mcp_tool_blocklist_test.go: 11 tests (CRUD, conflicts, isolation)
- mcp_server_tools_test.go: 14 tests (upsert, replace, active filter)

app.go reduced by ~400 lines.
@christianromeni christianromeni merged commit bd772fd into main Mar 29, 2026
6 checks passed
@christianromeni christianromeni deleted the feat/code-mode-prototype branch March 29, 2026 00:05
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.

1 participant