@interopio/code-mode
v0.0.2
Published
io.Intelligence Code Mode Library
Downloads
374
Readme
io.Intelligence Code Mode
Table of Contents
- Introduction
- Installation
- Core Concepts
- API Reference
- Configuration
- Usage
- Integration Options
- Security and Runtime Behavior
- Development
Introduction
@interopio/code-mode enables Large Language Models (LLMs) to orchestrate multiple tools by generating and executing JavaScript inside an isolated QuickJS WebAssembly sandbox. Instead of exposing every tool directly to the model, Code Mode exposes a small set of meta-tools for discovering tools, reading their schemas, and running a program that coordinates them.
Key Features
- Sandboxed execution: Runs model-generated JavaScript in an isolated QuickJS WASM runtime.
- Reduced tool surface: Replaces a large tool list with three stable meta-tools.
- Multi-tool orchestration: Allows one program to call, combine, and transform results from several tools.
- Browser and Node.js support: Provides dedicated entry points for browser and Node.js hosts.
- Tool filtering: Excludes tools from the model-visible catalog using configuration or each tool's
enabledflag. - Policy interception: Applies approval, authorization, validation, or rate-limiting logic before each real tool call.
- Independent sessions: Supports concurrent runs with separate tool registries and cancellation signals.
- Resource limits: Configures sandbox timeout and memory limits.
Target Audience
Developers building AI assistants, MCP clients, or MCP servers that need to expose a large tool catalog while keeping the model-facing interface compact and allowing controlled, multi-step execution.
Installation
npm install @interopio/code-modeThe package provides separate browser and Node.js entry points:
import { IoCodeModeFactory } from "@interopio/code-mode/browser";
import { IoCodeModeFactory } from "@interopio/code-mode/node";The root import, @interopio/code-mode, resolves to the browser build.
Requirements
- A QuickJS WebAssembly file accessible to the host.
- A browser-served URL for
wasmLocationwhen using the browser entry point. - A filesystem-accessible path for
wasmLocationwhen using the Node.js entry point. - Tool implementations that accept a single object argument and return a
ToolResult.
Core Concepts
Sandboxed Tool Orchestration
Code Mode binds registered tools as asynchronous functions inside a QuickJS sandbox. The model writes plain JavaScript that calls these functions, processes their results, and returns a final value.
Tool names are converted to safe JavaScript identifiers under the tool_ namespace. For example, weather.get-current may be exposed as tool_weather_get_current. Always use the exact name returned by getTools.
Tool calls cross the sandbox boundary into the host application. The host remains responsible for the actual tool implementation and any external side effects.
Meta-Tools
getLlmTools() returns three tools for the LLM:
| Tool | Description |
| ------------------- | ------------------------------------------------------------------------------- |
| getTools | Lists available tools and their descriptions. |
| getToolDefinition | Returns the input and output schemas for a selected tool. |
| executeCode | Runs plain JavaScript with the available tools bound as asynchronous functions. |
The executeCode input includes:
{
code: string;
explanation: string;
}The explanation is required by the model-facing schema to encourage a clear execution plan. It is passed through but is not interpreted by the runtime.
Sessions
The factory returns a root API that owns the shared sandbox. Call createSession() for each independent or concurrent run:
const codeMode = await IoCodeModeFactory(config);
const session = codeMode.createSession();Each session has its own:
- Tool registry.
- Tool filter application.
- Runtime state.
setTools() replaces a session's complete registry for future runs. getLlmTools() snapshots the current registry and optional signal, so later setTools() calls do not change already-prepared runs. This allows concurrent runs to share one session while keeping their tools and cancellation signals isolated.
Tool Policies
Code Mode provides two policy layers:
toolFiltercontrols whether a tool is visible and callable.interceptors.onToolCallruns immediately before a tool is invoked.
An interceptor may allow a call, deny it with a reason, or replace its input:
interceptors: {
onToolCall: async ({ tool, input }) => {
if (tool.name === "payments.transfer") {
return { allowed: false, reason: "Transfers require manual approval." };
}
return { allowed: true, input };
},
}Because the interceptor is asynchronous, it can wait for user approval before allowing or denying execution.
API Reference
IoCodeModeFactory
IoCodeModeFactory(
config: IoCodeMode.Config
): Promise<IoCodeMode.API>Creates the Code Mode API and configures the shared QuickJS sandbox.
Parameters:
config: Runtime, policy, and WASM configuration.
Returns: A promise resolving to an IoCodeMode.API instance.
Throws: A validation error when required configuration is missing or invalid.
API Methods
getSystemInstruction
getSystemInstruction(): stringReturns the instructions that teach the model how to use the meta-tools and sandbox-bound functions. Custom instructions are appended to the built-in protocol.
setTools
setTools(update: RuntimeTool[] | ((current: RuntimeTool[]) => RuntimeTool[])): voidReplaces or updates the current session's tool registry. Filtering and safe-name generation are applied when the registry is updated.
getLlmTools
getLlmTools(options?: { signal?: AbortSignal }): LlmTool[]Returns the getTools, getToolDefinition, and executeCode meta-tools to expose to the LLM. The optional signal is scoped to executions through the returned tool wrappers.
createSession
createSession(): IoCodeMode.APICreates an independent session that shares the configured sandbox but has its own registry and cancellation state.
Runtime Tools
Tools passed to setTools() use this shape:
interface RuntimeTool {
name: string;
description?: string;
inputSchema?: Record<string, unknown>;
outputSchema?: Record<string, unknown>;
source?: { mcpName?: string };
_meta?: Record<string, unknown>;
enabled?: boolean;
execute(
args: Record<string, unknown>,
options?: { signal?: AbortSignal }
): Promise<{
content: Array<{ type: "text"; text: string }>;
structuredContent?: Record<string, unknown>;
isError?: boolean;
}>;
}enabled: falseexcludes the tool from the catalog.sourceand_metaare passed to filters and interceptors when supplied by the host.- When
outputSchemais present, Code Mode attempts to parse successful JSON output before returning it to sandbox code. - A result marked
isError: true(or a rejectedexecute) makes the sandbox-side call throw with the tool's error message, so failures are distinguishable from data and an unhandled one fails the whole script.
Configuration
interface Config {
wasmLocation: string;
overwriteSystemInstruction?: (current: string) => string;
interceptors?: {
onToolCall?: (context: ToolCallContext) => Promise<InterceptorDecision>;
};
timeout?: number;
memoryLimit?: number;
toolFilter?: (tool: ToolDefinition) => boolean;
}| Property | Type | Required | Default | Description |
| ---------------------------- | ---------- | -------- | ----------- | --------------------------------------------------------------------------- |
| wasmLocation | string | Yes | - | QuickJS WASM URL in a browser or filesystem path in Node.js. |
| overwriteSystemInstruction | function | No | Identity | Receives the default system instruction and returns the instruction to use. |
| interceptors.onToolCall | function | No | Allow | Asynchronous policy hook invoked before every sandbox-originated tool call. |
| timeout | number | No | 30000 | Maximum sandbox execution time in milliseconds. |
| memoryLimit | number | No | 128 | Sandbox memory limit in megabytes. |
| toolFilter | function | No | Include all | Returns true to include a tool or false to hide it. |
Usage
Browser
Place the QuickJS WASM file in a publicly served location, such as public/quickjs/emscripten-module.wasm in a Vite or Angular application:
import { IoCodeModeFactory, type IoCodeMode } from "@interopio/code-mode/browser";
const hostTools = [
{
name: "search",
description: "Searches the host application's data.",
inputSchema: {
type: "object",
properties: { query: { type: "string" } },
required: ["query"],
},
execute: async ({ query }) => ({
content: [{ type: "text", text: JSON.stringify({ query, matches: [] }) }],
structuredContent: { query, matches: [] },
}),
},
] satisfies IoCodeMode.RuntimeTool[];
const codeMode = await IoCodeModeFactory({
wasmLocation: "/quickjs/emscripten-module.wasm",
timeout: 30_000,
memoryLimit: 128,
toolFilter: (tool) => !tool.name.startsWith("admin."),
});
const session = codeMode.createSession();
session.setTools([
{
name: "weather.get",
description: "Returns the current weather for a city.",
inputSchema: {
type: "object",
properties: { city: { type: "string" } },
required: ["city"],
},
outputSchema: {
type: "object",
properties: { temperatureC: { type: "number" } },
required: ["temperatureC"],
},
execute: async ({ city }) => ({
content: [{ type: "text", text: JSON.stringify({ city, temperatureC: 21 }) }],
structuredContent: { city, temperatureC: 21 },
}),
},
]);
const systemInstruction = codeMode.getSystemInstruction();
const toolsForModel = session.getLlmTools();Pass systemInstruction and toolsForModel to the model integration used by the host application.
Node.js
Use the Node.js entry point and provide a filesystem-accessible WASM path:
import path from "node:path";
import { fileURLToPath } from "node:url";
import { IoCodeModeFactory } from "@interopio/code-mode/node";
const currentDirectory = path.dirname(fileURLToPath(import.meta.url));
const codeMode = await IoCodeModeFactory({
wasmLocation: path.join(currentDirectory, "quickjs/emscripten-module.wasm"),
});The host build or deployment must copy the WASM file to the configured location.
Cancellation
Associate an AbortSignal with one run's LLM tools:
const abortController = new AbortController();
const session = codeMode.createSession();
session.setTools(tools);
const toolsForModel = session.getLlmTools({ signal: abortController.signal });
abortController.abort();After cancellation, Code Mode prevents additional tool calls and settles an in-flight execution with an error result. Cancellation is cooperative: a CPU-bound script inside QuickJS may continue until the configured timeout, but its result is discarded and it cannot dispatch additional host tool calls.
Integration Options
Standalone Integration
Use Code Mode directly when the host application owns the LLM connection and tool-call loop. In this setup, Code Mode acts as a plug-in layer: the host registers its tools with a session, adds the Code Mode system instruction to the model request, and exposes only the returned meta-tools to the model.
import { IoCodeModeFactory } from "@interopio/code-mode/browser";
const codeMode = await IoCodeModeFactory({
wasmLocation: "/quickjs/emscripten-module.wasm",
timeout: 30_000,
memoryLimit: 128,
overwriteSystemInstruction: (current) => `${current}\nFollow the host application's authorization rules.`,
toolFilter: (tool) => !tool.name.startsWith("internal."),
interceptors: {
onToolCall: async ({ input }) => ({ allowed: true, input }),
},
});
const session = codeMode.createSession();
const abortController = new AbortController();
session.setTools(hostTools);
const systemInstruction = codeMode.getSystemInstruction();
const metaTools = session.getLlmTools({ signal: abortController.signal });The host then adapts each returned meta-tool to its model SDK:
- Use
name,description,inputSchema, andoutputSchemaas the model-visible tool definition. - Route model tool calls to the matching meta-tool's
execute(args)function. - Return the resulting
content,structuredContent, andisErrorthrough the host's normal tool-result channel. - Keep the session alive for the duration of the model run so all three meta-tools use the same tool registry and cancellation signal.
For Node.js hosts, import IoCodeModeFactory from @interopio/code-mode/node and provide a filesystem path in wasmLocation. Standalone integration does not require @interopio/ai-web, @interopio/mcp-core, or an io.Connect environment.
With @interopio/ai-web
Configure Code Mode at the AI Web client layer to virtualize the combined tools received from connected MCP servers:
import { IoAiWebFactory } from "@interopio/ai-web";
import { IoCodeModeFactory } from "@interopio/code-mode/browser";
const aiWeb = await IoAiWebFactory(io, {
agentServer: {
baseUrl: "https://agent.example.com",
},
codeMode: {
factory: IoCodeModeFactory,
config: {
wasmLocation: "/quickjs/emscripten-module.wasm",
},
},
});AI Web creates a session for each agent stream and declares the experimental codeMode capability to connected MCP servers.
With @interopio/mcp-core
Configure Code Mode on the MCP server to virtualize non-UI tools for clients that do not provide their own Code Mode implementation:
import { IoCodeModeFactory } from "@interopio/code-mode/node";
import { IoIntelMCPCoreFactory } from "@interopio/mcp-core";
const mcpCore = await IoIntelMCPCoreFactory(io, {
licenseKey,
transport: { type: "http" },
server: {
name: "io-intelligence-mcp",
codeMode: {
factory: IoCodeModeFactory,
config: {
wasmLocation: quickJsWasmPath,
},
},
},
});MCP Apps tools with UI resources remain directly registered because a sandboxed script cannot render their UI. If an MCP client declares capabilities.experimental.codeMode, MCP Core serves the plain tool list for that client so the layer closest to the LLM performs virtualization.
Transport packages such as @interopio/mcp-http and @interopio/mcp-web expose this configuration through their mcpCoreServer setting.
Security and Runtime Behavior
- Model-generated code executes in QuickJS rather than the host JavaScript context.
- Sandbox code can access only the functions explicitly registered by the host.
- All tool arguments and results cross the boundary as JSON-compatible values.
- Tool interceptors are the central place for authorization, approval, argument rewriting, and rate limits.
toolFilterandenabled: falseremove tools from discovery and execution.- Timeout and memory limits constrain each sandbox execution.
- Code Mode does not make external tools safe automatically; the host must still validate inputs and protect side-effecting operations.
- A session should not be reused across concurrent requests.
Development
# Build browser and Node.js bundles
npm run build
# Run browser smoke tests
npm run smoke:browser
# Run Node.js smoke tests
npm run smoke
# Run the repeated Node.js stress scenario
npm run stress
# Check lint rules
npm run lint
# Apply automatic lint fixes
npm run lint:fixThe package's public TypeScript contract is defined in code-mode.d.ts.
