The code_execution tool enables LLM agents to orchestrate multiple upstream MCP tools in a single request using JavaScript or TypeScript. Instead of making multiple round-trips to the model, you can execute complex multi-step workflows with conditional logic, loops, and data transformations—all within a single execution context.
TypeScript support: Set language: "typescript" to write code with type annotations, interfaces, enums, and generics. Types are automatically stripped before execution with near-zero overhead (<5ms).
✅ Use code_execution when:
- You need to call 2+ tools and combine their results
- You need conditional logic based on tool responses
- You need to transform or aggregate data from multiple sources
- You need to iterate over data and call tools for each item
- You need to handle errors gracefully with fallbacks
❌ Don't use code_execution when:
- You're calling a single tool (use
call_tooldirectly) - The workflow is simple and linear (use sequential tool calls)
- You need long-running operations (>2 minutes)
- You need access to filesystem, network, or Node.js modules
Execute multiple tool calls in a single request, eliminating the network round-trips between agent and model.
Before (3 round-trips):
Agent → Model: "Get user data"
Model → Agent: call_tool(github, get_user, {username: "octocat"})
Agent → Model: "Here's the user data"
Model → Agent: call_tool(github, list_repos, {user: "octocat"})
Agent → Model: "Here are the repos"
Model → Agent: call_tool(github, get_repo, {repo: "Hello-World"})
After (1 round-trip):
Agent → Model: "Get user and their repos"
Model → Agent: code_execution({code: "...", input: {...}})
Implement conditional branching, loops, and error handling that would require multiple model invocations.
// Conditional logic
const user = call_tool('github', 'get_user', {username: input.username});
if (!user.ok) {
return {error: 'User not found'};
}
// Loop with accumulation
const results = [];
for (const repoName of input.repos) {
const repo = call_tool('github', 'get_repo', {name: repoName});
if (repo.ok) {
results.push(repo.result);
}
}
return {repos: results, count: results.length};Transform, filter, and aggregate data from multiple tool calls before returning results.
const repos = call_tool('github', 'list_repos', {user: input.username});
if (!repos.ok) return repos;
// Filter and transform
const activeRepos = repos.result
.filter(r => !r.archived && r.pushed_at > input.since)
.map(r => ({name: r.name, stars: r.stargazers_count, language: r.language}));
return {repos: activeRepos, total: activeRepos.length};┌─────────────────────────────────────────────┐
│ LLM Agent │
│ - Receives code_execution tool description │
│ - Writes JavaScript to orchestrate tools │
└────────────┬────────────────────────────────┘
│
│ code_execution request
│ {code, input, options}
▼
┌─────────────────────────────────────────────┐
│ MCPProxy Server │
│ - Validates request and options │
│ - Checks if feature is enabled │
└────────────┬────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ JavaScript Runtime Pool │
│ - Acquires VM from pool (blocks if full) │
│ - Creates isolated sandbox │
│ - Binds input global and call_tool() │
└────────────┬────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ JavaScript Execution │
│ - Runs code with timeout watchdog │
│ - Enforces max_tool_calls limit │
│ - Restricts to allowed_servers │
│ - Returns JSON-serializable result │
└────────────┬────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ Upstream Tool Calls │
│ - Forwards call_tool() to upstream servers │
│ - Respects quarantine and security rules │
│ - Returns {ok, result} or {ok, error} │
└─────────────────────────────────────────────┘
- Request Parsing: Extract
code,input, andoptionsfrom the request - Validation: Verify timeout (1-600000ms) and max_tool_calls (>= 0)
- Pool Acquisition: Acquire a JavaScript VM from the pool (blocks if all VMs are in use)
- Sandbox Setup: Create isolated environment with
inputglobal andcall_tool()function - Execution: Run JavaScript with timeout enforcement and tool call tracking
- Result Extraction: Validate result is JSON-serializable and return structured response
- Pool Release: Return VM to pool for reuse
- Response: Return
{ok: true, value: <result>}or{ok: false, error: {...}}
The JavaScript execution environment is heavily sandboxed to prevent security issues:
❌ Not Available:
require()- No module loadingsetTimeout()/setInterval()- No timers- Filesystem access - No
fsmodule - Network access - No
httporfetch - Environment variables - No
process.env - Node.js built-ins - JavaScript standard library only
✅ Available:
input- Global variable with request input datacall_tool(serverName, toolName, args)- Function to call upstream MCP tools- Modern JavaScript (ES2020+) standard library including Array, Object, String, Math, Date, JSON, Map, Set, Symbol, Promise, Proxy, Reflect
{
"enable_code_execution": false, // Must be explicitly enabled (default: false)
"code_execution_timeout_ms": 120000, // Default: 2 minutes, max: 10 minutes
"code_execution_max_tool_calls": 0, // Default: unlimited
"code_execution_pool_size": 10 // Default: 10 concurrent VMs
}Per-Request Overrides:
{
"code": "...",
"input": {...},
"options": {
"timeout_ms": 60000, // Override timeout for this request
"max_tool_calls": 20, // Limit tool calls for this request
"allowed_servers": ["github"] // Restrict to specific servers
}
}Code execution respects existing MCPProxy security features:
- Quarantined servers cannot be called via
call_tool() - Server enable/disable settings are enforced
- Authentication requirements are preserved
Edit your configuration file (~/.mcpproxy/mcp_config.json):
{
"enable_code_execution": true,
"mcpServers": [
{
"name": "github",
"url": "https://api.github.com/mcp",
"protocol": "http",
"enabled": true
}
]
}pkill mcpproxy
mcpproxy serve# Simple JavaScript test
mcpproxy code exec --code="({ result: input.value * 2 })" --input='{"value": 21}'
# TypeScript test
mcpproxy code exec --language typescript --code="const x: number = 42; ({ result: x })"
# Call upstream tool
mcpproxy code exec --code="call_tool('github', 'get_user', {username: input.user})" --input='{"user":"octocat"}'The code_execution tool will appear in the tools list when an LLM agent connects to MCPProxy:
{
"name": "code_execution",
"description": "Execute JavaScript code that orchestrates multiple upstream MCP tools...",
"inputSchema": {
"type": "object",
"properties": {
"code": {"type": "string", "description": "JavaScript source code..."},
"input": {"type": "object", "description": "Input data accessible as global input variable..."},
"options": {"type": "object", "description": "Execution options..."}
},
"required": ["code"]
}
}// Fetch user, then fetch their repos
const userRes = call_tool('github', 'get_user', {username: input.username});
if (!userRes.ok) {
return {error: userRes.error.message};
}
const reposRes = call_tool('github', 'list_repos', {user: input.username});
if (!reposRes.ok) {
return {error: reposRes.error.message};
}
return {
user: userRes.result,
repos: reposRes.result,
repo_count: reposRes.result.length
};// Try primary server, fallback to secondary
let result = call_tool('primary-db', 'query', {sql: input.query});
if (!result.ok) {
// Primary failed, try backup
result = call_tool('backup-db', 'query', {sql: input.query});
}
return result.ok ? result.result : {error: 'Both databases unavailable'};// Fetch details for multiple items
const results = [];
const errors = [];
for (const id of input.ids) {
const res = call_tool('api-server', 'get_item', {id});
if (res.ok) {
results.push(res.result);
} else {
errors.push({id, error: res.error});
}
}
return {
success: results,
failed: errors,
success_count: results.length,
error_count: errors.length
};// Get repos and compute statistics
const reposRes = call_tool('github', 'list_repos', {user: input.username});
if (!reposRes.ok) return reposRes;
const repos = reposRes.result;
const totalStars = repos.reduce((sum, r) => sum + (r.stargazers_count ?? 0), 0);
const languages = {};
for (const repo of repos) {
const lang = repo.language ?? 'Unknown';
languages[lang] = (languages[lang] ?? 0) + 1;
}
return {
total_repos: repos.length,
total_stars: totalStars,
avg_stars: Math.round(totalStars / repos.length),
languages
};// Syntax error - caught before execution
code_execution({code: "invalid javascript {"})
// Returns: {ok: false, error: {code: "SYNTAX_ERROR", message: "...", stack: "..."}}
// Runtime error - caught during execution
code_execution({code: "throw new Error('Something went wrong')"})
// Returns: {ok: false, error: {code: "RUNTIME_ERROR", message: "Something went wrong", stack: "..."}}// Tool returns error - handled in JavaScript
var res = call_tool('github', 'get_user', {username: 'nonexistent-user-12345'});
if (!res.ok) {
return {error: 'User not found: ' + res.error.message};
}
return res.result;// Execution exceeds timeout (default: 2 minutes)
code_execution({
code: "while(true) {}", // Infinite loop
options: {timeout_ms: 1000}
})
// Returns: {ok: false, error: {code: "TIMEOUT", message: "JavaScript execution timed out"}}// Exceeds max_tool_calls limit
code_execution({
code: "for (var i = 0; i < 100; i++) { call_tool('api', 'ping', {}); }",
options: {max_tool_calls: 10}
})
// Returns: {ok: false, error: {code: "MAX_TOOL_CALLS_EXCEEDED", message: "..."}}You can write code execution scripts in TypeScript by setting the language parameter to "typescript". TypeScript types are automatically stripped before execution using esbuild, with near-zero transpilation overhead.
- Type annotations:
const x: number = 42 - Interfaces:
interface User { name: string; age: number; } - Type aliases:
type StringOrNumber = string | number - Generics:
function identity<T>(arg: T): T { return arg; } - Enums:
enum Direction { Up = "UP", Down = "DOWN" } - Namespaces:
namespace MyLib { export const value = 42; } - Type assertions:
const x = value as string
{
"name": "code_execution",
"arguments": {
"code": "interface User { name: string; }\nconst user: User = { name: input.username };\n({ greeting: 'Hello ' + user.name })",
"language": "typescript",
"input": {"username": "Alice"}
}
}curl -X POST http://127.0.0.1:8080/api/v1/code/exec \
-H "Content-Type: application/json" \
-H "X-API-Key: your-key" \
-d '{
"code": "const x: number = 42; ({ result: x })",
"language": "typescript"
}'mcpproxy code exec --language typescript \
--code="const x: number = 42; ({ result: x })"- TypeScript support uses type-stripping only (no type checking or semantic validation)
- Valid JavaScript is also valid TypeScript, so you can always use
language: "typescript"even for plain JS - The transpiled output runs in the same ES2020+ goja sandbox with all existing capabilities
- Transpilation errors return the
TRANSPILE_ERRORerror code with line/column information
- Use modern JavaScript syntax (arrow functions, const/let, template literals, destructuring are all supported)
- Avoid deeply nested logic
- Prefer explicit error handling over implicit failures
// Bad: Assumes success
var user = call_tool('github', 'get_user', {username: input.username});
return user.result.name; // Crashes if user.ok is false
// Good: Checks response
var user = call_tool('github', 'get_user', {username: input.username});
if (!user.ok) {
return {error: user.error.message};
}
return {name: user.result.name};// Quick operations: 30 seconds
{options: {timeout_ms: 30000}}
// Multiple tool calls: 2 minutes (default)
{options: {timeout_ms: 120000}}
// Heavy processing: 5 minutes
{options: {timeout_ms: 300000}}// Protect against runaway loops
{
code: "for (var i = 0; i < input.items.length; i++) { ... }",
options: {max_tool_calls: 100}
}// Restrict to specific servers for sensitive operations
{
code: "call_tool('production-db', 'delete', {id: input.id})",
options: {allowed_servers: ["production-db"]}
}- Examples: See examples.md for 10+ working code samples
- API Reference: See api-reference.md for complete schema documentation
- Troubleshooting: See troubleshooting.md for common issues and solutions
- CLI Usage: Run
mcpproxy code exec --helpfor command-line testing