MCX (Modular Code Execution)
An MCP server that enables AI agents to execute sandboxed JavaScript and TypeScript code instead of calling individual tools directly. It significantly reduces token usage by allowing agents to filter, aggregate, and transform data locally before returning results.
README
MCX - Modular Code Execution
███╗ ███╗ ██████╗██╗ ██╗
████╗ ████║██╔════╝╚██╗██╔╝
██╔████╔██║██║ ╚███╔╝
██║╚██╔╝██║██║ ██╔██╗
██║ ╚═╝ ██║╚██████╗██╔╝ ██╗
╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝
MCP server that lets AI agents execute code instead of calling tools directly.
Based on Anthropic's code execution article.
The Problem
Traditional MCP has two inefficiencies:
-
Tool definition overload - Loading all tool definitions floods context. Thousands of tools = hundreds of thousands of tokens before any work begins.
-
Intermediate result bloat - Every API response passes through the model. A list of 100 records with nested data can consume 50,000+ tokens.
The Solution
Instead of calling tools directly, the agent writes code that runs in a sandbox:
// Agent writes this code, MCX executes it
const invoices = await api.getInvoices({ limit: 100 });
return {
count: invoices.length,
total: sum(invoices, 'amount'),
byStatus: count(invoices, 'status')
};
// Returns ~50 tokens instead of 50,000
Result: 98% token reduction by filtering data inside the execution environment.
Key Benefits
| Benefit | Description |
|---|---|
| Progressive Disclosure | Adapters loaded on demand, not upfront. Agent only sees what it needs. |
| Context Efficiency | Filtering, aggregation, and transformation happen in sandbox. Model sees results, not raw data. |
| Control Flow | Loops, conditionals, retries run as native code - no back-and-forth with model. |
| Privacy | Intermediate data stays in sandbox. Model only sees what code explicitly returns. |
| Skills | Save reusable operations as skills that combine multiple adapter calls. |
Installation
bun install
bun run build
Fully Bun-native: Uses Bun for package management, building, and runtime. No Node.js required.
Quick Start
1. Setup
# Create environment file
cp .env.template .env
# Create config file
cp mcx.config.template.ts mcx.config.ts
2. Create Adapters
# Option A: Generate from OpenAPI docs (recommended)
mcx gen
# Option B: Copy from template
cp adapters/adapter.template.ts adapters/my-api.ts
3. Run
# Start MCP server (stdio mode for Claude Code)
mcx serve
# Or just run mcx (serve is the default command)
mcx
Templates
| File | Description |
|---|---|
.env.template |
Environment variables (API keys, etc.) |
mcx.config.template.ts |
MCX configuration (adapters, sandbox settings) |
adapters/adapter.template.ts |
Adapter template with CRUD examples |
skills/skill.template.ts |
Skill template with 3 patterns |
CLI Commands
mcx serve
Start the MCP server. This is the default command when running mcx without arguments.
mcx serve [options]
Options:
-t, --transport <type> Transport mode: stdio (default) or http
-p, --port <number> HTTP port (default: 3100, only for http)
-c, --cwd <path> Working directory for config and adapters
Features:
- Auto-discovers
mcx.config.tsby walking up the directory tree - Loads
.envfiles automatically - HTTP transport binds to
127.0.0.1only (localhost) - HTTP mode exposes
/healthendpoint for monitoring
mcx gen
Generate adapters from OpenAPI specs. Run without arguments for interactive TUI mode.
# Interactive TUI (recommended)
mcx gen
# CLI mode - single file
mcx gen ./api-docs/users.md -n users
# CLI mode - batch directory
mcx gen ./api-docs -n myapi
# Filter specific endpoints
mcx gen ./api-docs -n myapi --include "invoices,payments"
mcx gen ./api-docs -n myapi --exclude "reports,audit"
Options:
-n, --name <name> Adapter name (auto-detected from source)
-o, --output <path> Output file (default: adapters/<name>.ts)
-b, --base-url <url> API base URL (auto-detected from OpenAPI)
-a, --auth <type> Auth type: basic, bearer, apikey, none
--read-only Generate GET methods only
--include <patterns> Include only matching endpoints (comma-separated)
--exclude <patterns> Exclude matching endpoints (comma-separated)
Filtering: Patterns match against category (folder name) or method name. Case-insensitive partial matching.
mcx init
Initialize a new MCX project in the current directory.
mcx init
Creates:
mcx.config.ts- Configuration fileadapters/example.ts- Example adapterskills/hello.ts- Example skill
mcx list
List all available adapters and skills. Alias: mcx ls
mcx list
mcx ls
mcx run
Run a skill or script directly.
# Run a skill by name
mcx run daily-summary date=2024-01-15
# Run a script file
mcx run ./scripts/migrate.ts
MCP Tools
MCX exposes three tools to the AI agent:
| Tool | Description |
|---|---|
mcx_execute |
Execute JavaScript/TypeScript code in sandbox with adapter access |
mcx_run_skill |
Run a named skill with optional inputs |
mcx_list |
List available adapters and skills (read-only) |
Built-in Helpers
Functions available in the sandbox for efficient data handling:
| Helper | Usage | Description |
|---|---|---|
pick(arr, fields) |
pick(data, ['id', 'name']) |
Extract specific fields (supports dot-notation: 'address.city') |
first(arr, n) |
first(data, 5) |
First N items (default: 5) |
sum(arr, field) |
sum(data, 'amount') |
Sum numeric field |
count(arr, field) |
count(data, 'status') |
Count by field value |
table(arr, maxRows) |
table(data, 20) |
Format as markdown table (default: 10 rows) |
Console Methods
All console methods are captured and returned in the response:
console.log('Debug info'); // [LOG] Debug info
console.warn('Warning'); // [WARN] Warning
console.error('Error'); // [ERROR] Error
console.info('Info'); // [INFO] Info
Adapter Access
Adapters are available both via the adapters object and as top-level globals:
// Both work identically
await adapters.crm.getLeads({ limit: 10 });
await crm.getLeads({ limit: 10 });
Usage Patterns
Bad: Raw API response floods context
return await api.getRecords({ limit: 100 });
// 100 objects × 500 tokens each = 50,000 tokens
Good: Filter before returning
const data = await api.getRecords({ limit: 100 });
return pick(data, ['id', 'name', 'status']);
// 100 objects × 3 fields = ~500 tokens
Good: Return summary only
const data = await api.getRecords({ limit: 100 });
return {
count: data.length,
total: sum(data, 'amount'),
byStatus: count(data, 'status')
};
// ~50 tokens
Good: Debug with logs, return minimal
const data = await api.getRecords({ limit: 10 });
console.log(table(pick(data, ['id', 'name', 'amount'])));
return { count: data.length };
// Logs show table, return is tiny
Polling Loop (native control flow)
let found = false;
while (!found) {
const messages = await slack.getChannelHistory({ channel: 'C123' });
found = messages.some(m => m.text.includes('deployment complete'));
if (!found) await new Promise(r => setTimeout(r, 5000));
}
return { status: 'deployment complete' };
// Runs entirely in sandbox, no model round-trips
Generating Adapters
Auto-generate adapters from OpenAPI specs in markdown files.
Interactive TUI
mcx gen
The TUI wizard guides you through:
- Source selection - Single file or batch directory with file browser
- Analysis summary - Shows endpoints, categories, detected auth
- Adapter name - Auto-suggested from API name
- Output path - File browser for destination
- Auth/Base URL - Only asked if not auto-detected
- Config import - Option to add adapter to
mcx.config.ts
Auto-Detection
MCX automatically detects:
- Base URL from OpenAPI
serversfield - Authentication from OpenAPI
securitySchemes:http/basic→ Basic Authhttp/bearer→ Bearer TokenapiKey→ API Key (header or query)
- SDK-based APIs from code examples in markdown (TypeScript and Python)
Batch Processing
Process entire directories of API docs:
mcx gen ./alegra-endpoints -n alegra
# Scans all .md files, extracts OpenAPI specs, generates single adapter
SDK-Based Adapters
When MCX detects SDK usage in docs, it generates SDK wrappers instead of fetch-based code:
// Generated from SDK-based API docs
import { ZepClient } from '@getzep/zep-cloud';
const client = new ZepClient({ apiKey: process.env.ZEP_API_KEY });
export const zep = defineAdapter({
tools: {
addMemory: {
execute: async (params) => client.memory.add(params.sessionId, params),
},
},
});
Creating Adapters
import { defineAdapter } from '@mcx/adapters';
export const myApi = defineAdapter({
name: 'myapi',
description: 'My API adapter',
tools: {
getRecords: {
description: 'Fetch records',
parameters: {
limit: { type: 'number', description: 'Max results' },
},
execute: async (params) => {
return fetch(`${BASE_URL}/records?limit=${params.limit}`).then(r => r.json());
},
},
},
});
Built-in Adapters
Fetch Adapter
Generic HTTP client with all standard methods.
import { createFetchAdapter } from '@mcx/adapters';
const api = createFetchAdapter({
baseUrl: 'https://api.example.com',
headers: { 'X-API-Key': process.env.API_KEY },
timeout: 30000,
});
Tools: get, post, put, patch, delete, head, request
Creating Skills
Skills are reusable operations that combine multiple adapter calls.
Using defineSkill
import { defineSkill } from '@mcx/core';
export const dailySummary = defineSkill({
name: 'daily-summary',
description: 'Summarize daily activity across systems',
adapters: ['crm', 'analytics'],
code: `
const leads = await crm.getLeads({ date: inputs.date });
const visits = await analytics.getPageViews({ date: inputs.date });
return {
date: inputs.date,
leads: leads.length,
visits: sum(visits, 'count'),
conversion: (leads.length / sum(visits, 'count') * 100).toFixed(2) + '%'
};
`,
sandbox: {
timeout: 10000,
memoryLimit: 128,
},
});
Using skillBuilder (Fluent API)
import { skillBuilder } from '@mcx/core';
export const processData = skillBuilder('process-data')
.description('Fetch, transform, and store data')
.requires('api', 'db')
.timeout(15000)
.memoryLimit(256)
.code(`
const raw = await api.fetchRecords({ limit: 1000 });
const filtered = pick(raw, ['id', 'name', 'amount']);
const result = await db.bulkInsert(filtered);
return { inserted: result.count };
`)
.build();
Native Function Skills
For complex logic, use a native TypeScript function instead of code string:
export const complexSkill = defineSkill({
name: 'complex-operation',
description: 'Skill with native TypeScript logic',
adapters: ['api'],
run: async ({ adapters, inputs }) => {
const data = await adapters.api.getData(inputs);
// Complex processing with full TypeScript support
const processed = data
.filter(item => item.active)
.map(item => ({
...item,
score: calculateScore(item),
}))
.sort((a, b) => b.score - a.score);
return { top10: processed.slice(0, 10) };
},
});
Skill Directory Structure
Skills can be single files or directories:
skills/
├── daily-summary.ts # Single file skill
├── complex-workflow/ # Directory skill
│ ├── index.ts # Entry point (required)
│ └── helpers.ts # Supporting modules
Configuration
mcx.config.ts
import { defineConfig } from '@mcx/core';
import { myAdapter } from './adapters/my-adapter';
export default defineConfig({
// Adapters to load
adapters: [myAdapter],
// Skills to load (optional, auto-discovered from skills/)
skills: [],
// Sandbox configuration
sandbox: {
timeout: 5000, // Execution timeout (ms)
memoryLimit: 128, // Memory limit (MB)
allowAsync: true, // Allow async/await
globals: {}, // Custom globals
},
// Environment variables to inject (available in adapters)
env: {
API_KEY: process.env.API_KEY,
},
});
Fluent Config Builder
import { configBuilder } from '@mcx/core';
export default configBuilder()
.adapter(myAdapter)
.adapters(otherAdapter1, otherAdapter2)
.sandbox({ timeout: 10000 })
.build();
Programmatic API
Use MCX programmatically in your own applications:
import { createExecutor } from '@mcx/core';
const executor = createExecutor();
await executor.loadConfig('./mcx.config.ts');
// Execute code
const result = await executor.execute(`
const data = await api.getRecords({ limit: 10 });
return pick(data, ['id', 'name']);
`);
// Run a skill
const skillResult = await executor.runSkill('daily-summary', {
inputs: { date: '2024-01-15' },
});
MCXExecutor Methods
| Method | Description |
|---|---|
loadConfig(path?) |
Load configuration from file |
registerAdapter(adapter) |
Register an adapter |
unregisterAdapter(name) |
Remove an adapter |
getAdapter(name) |
Get adapter by name |
getAdapterNames() |
List all adapter names |
registerSkill(skill) |
Register a skill |
unregisterSkill(name) |
Remove a skill |
getSkill(name) |
Get skill by name |
getSkillNames() |
List all skill names |
execute(code, options?) |
Execute code in sandbox |
runSkill(name, options?) |
Run a skill |
configureSandbox(config) |
Update sandbox defaults |
Claude Code Integration
Add to your project's .mcp.json:
{
"mcpServers": {
"mcx": {
"command": "bun",
"args": ["run", "mcx", "serve"],
"cwd": "/path/to/project"
}
}
}
Or with environment variables:
{
"mcpServers": {
"mcx": {
"command": "bun",
"args": ["run", "mcx", "serve"],
"cwd": "/path/to/project",
"env": {
"API_KEY": "your-api-key"
}
}
}
}
Server Options
# Default: stdio transport (for Claude Code)
mcx serve
# HTTP transport (for testing, other MCP clients, custom integrations)
mcx serve -t http -p 3100
# Specify working directory
mcx serve -c /path/to/project
| Option | Description |
|---|---|
-t, --transport |
stdio (default) or http |
-p, --port |
HTTP port (default: 3100, only for http transport) |
-c, --cwd |
Working directory for config and adapters |
HTTP Transport
When using HTTP transport:
- Server binds to
127.0.0.1only (localhost, for security) - MCP endpoint:
POST /mcp - Health check:
GET /healthreturns{ status, server, version }
Result Summarization
Large results are automatically summarized to prevent context overflow:
- Arrays truncated to 5 items with
"... and N more"indicator - Nested arrays limited to 3 items
- Objects with >5 keys are summarized
Architecture
┌──────────┐ ┌───────────────────────────────────┐
│ Claude │ ───▶ │ MCX Server │
│ /LLM │ code │ ┌─────────┐ ┌─────────────────┐ │
└──────────┘ │ │ Sandbox │ │ Adapters │ │
│ │ (Bun │ │ api.getRecords()│ │
◀───────────│ │ Worker) │ │ api.createItem()│ │
result │ └─────────┘ └─────────────────┘ │
(filtered) │ │ │
│ ▼ │
│ ┌─────────────────────────────┐ │
│ │ Helpers: pick/sum/count/... │ │
│ └─────────────────────────────┘ │
└───────────────────────────────────┘
Runtime
MCX is 100% Bun-native:
- Sandbox: Bun Workers (native JavaScript isolation)
- HTTP: Bun.serve (no Express)
- Files: Bun.file/Bun.Glob (no node:fs, no glob)
- Env: Automatic .env loading (no dotenv)
Benefits:
- Faster startup (~100ms)
- Smaller bundle (~0.5MB vs 1.5MB)
- No native module compilation issues
- Single runtime (no Node.js required)
License
MIT
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。