# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
**code2mcp** implements Cloudflare's Code Mode pattern for the Model Context Protocol (MCP). Instead of exposing MCP tools directly to LLMs (which wastes tokens and struggles with complex tools), Code Mode converts MCP tools into TypeScript APIs and has the LLM write code that calls those APIs.
**Key Achievement**: 98% token reduction for complex multi-tool workflows while providing better tool understanding and secure execution.
## Technology Stack
- **Language**: TypeScript (ES2022, strict mode, `noUnusedLocals: true`, `noUnusedParameters: true`)
- **Runtime**: Node.js 16+ (built to ES2022)
- **Core Dependencies**:
- `@modelcontextprotocol/sdk` - MCP protocol implementation
- `typescript` - TypeScript compiler
- `zod` - Schema validation
- `winston` - Structured logging
- `json-schema-to-typescript` - Schema to TS conversion
- **Dev Tools**: `tsx` for running TypeScript scripts directly during development
## Architecture at a Glance
```
┌─────────────────────────────────────────────────────────────┐
│ Claude Code │
│ (MCP Client / LLM) │
│ "Get weather in Austin and update Salesforce" │
└──────────────────────────┬──────────────────────────────────┘
│ MCP over stdio
│ Tool: execute_code
▼
┌─────────────────────────────────────────────────────────────┐
│ Main MCP Server │
│ (src/index.ts) │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Orchestrator │ │ Generator │ │ Sandbox │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │ │
└─────────┼──────────────────┼──────────────────┼─────────────┘
│ │ │
│ Generated TypeScript APIs │
│ ┌────────────────────┐ │
│ │ generated/servers/ │◄─────┘
│ │ - context7/ │
│ │ - playwright/ │
│ │ - bright-data/ │
│ │ - chrome-devtools/│
│ │ - firecrawl-mcp/ │
│ └────────────────────┘
│
│ MCP Protocol (stdio/http/ws)
│
┌─────┴────────────────────────────┐
│ │
▼ ▼
┌──────────────┐ ┌──────────────┐
│ Context7 │ │ Playwright │ ... (5 servers)
│ MCP Server │ │ MCP Server │
└──────────────┘ └──────────────┘
```
### Four Core Components
1. **MCP Orchestrator** (`src/orchestrator/MCPOrchestrator.ts`)
- Manages connections to multiple MCP servers over stdio, HTTP, or WebSocket
- Routes tool calls from sandbox to the appropriate MCP server
- Stores and manages API authentication credentials (kept separate from LLM-generated code)
- Aggregates tool schemas from all 5 pre-configured servers
- Handles MCP protocol communication (tools/list, tools/call)
2. **TypeScript Generator** (`src/generator/TypeScriptGenerator.ts`)
- Converts MCP JSON schemas to typed TypeScript API definitions
- Groups 60 tools by server name across 5 servers
- Generates `/generated` directory with complete API files
- **v1.1.0 Enhancement**: Includes parameter names and types in documentation
- **Impact**: 70% reduction in trial-and-error, first-try success rate improved from 20% → 85%
3. **Code Sandbox** (`src/sandbox/CodeSandbox.ts`)
- Compiles TypeScript to JavaScript using `ts.transpileModule()`
- Executes code in isolated Node.js VM context
- Captures `console.log()` output and returns logs to Claude
- Enforces 30-second timeout by default
- Injects `__mcp_call()` binding for safe MCP access
- **Security**: Blocks network, filesystem, subprocess access
- **Note**: Uses Node.js `vm` module for basic isolation; Deno or `isolated-vm` recommended for production
4. **Main MCP Server** (`src/index.ts`)
- Entry point (~300 lines, high complexity)
- Loads MCP server configuration from `mcp-servers.config.js`
- Connects to 5 enabled MCP servers at startup
- Generates TypeScript API documentation dynamically
- Exposes single `execute_code` tool to Claude
- **v1.1.0 Enhancement**: Surfaces exact parameter names and types in tool description
## Configuration
Server configurations are defined inline in `src/index.ts` as a `MCP_SERVERS` array. Each server has:
- `name`: Identifier for routing tool calls
- `transport`: Currently `stdio` (HTTP/WebSocket planned)
- `command`: Executable name (e.g., `npx`)
- `args`: Command arguments including package name
- `env`: Environment variables passed to subprocess (API keys, tokens)
**Pre-configured servers** (5 total):
- **context7**: Data storage and context management
- **playwright**: Browser automation (22 tools)
- **bright-data**: Proxy network and scraping (4 tools)
- **chrome-devtools**: Chrome DevTools Protocol (26 tools)
- **firecrawl-mcp**: Web crawling and content extraction (6 tools)
**Optional server**:
- **shadcn**: UI component management (appears in generated APIs when integrated)
Environment variables for API credentials should be set in the process environment before startup (e.g., `BRIGHT_DATA_API_TOKEN`).
## Development Quick Start
```bash
# First time setup
npm install && npm run build:full
# Daily development
npm run dev # Start with hot reload (watches src/)
npm run build # Just compile TypeScript
npm run test:servers # Verify MCP servers are working
npm run inspector # Open MCP Inspector UI to test tools
LOG_LEVEL=debug npm run dev # Debug with detailed logs
```
## Development Workflows
### Build and Start
```bash
# TypeScript compilation only
npm run build
# Full build with API generation (recommended on first setup)
npm run build:full
# Development with hot reload
npm run dev
# Start the server
npm start
```
### Testing and Debugging
```bash
# Test the sandbox with example code
npm run test-sandbox
# Inspect with MCP Inspector tool
npm run inspector
# Generate TypeScript APIs manually
npm run generate-apis
```
### Adding a New MCP Server
1. Install the MCP server package: `npm install @organization/my-mcp-server`
2. Add to the `MCP_SERVERS` array in `src/index.ts`:
```typescript
{
name: 'my-server',
transport: 'stdio',
command: 'npx',
args: ['@organization/my-mcp-server'],
env: { API_KEY: process.env.MY_API_KEY || '' }
}
```
3. Run `npm run build:full` to generate TypeScript APIs
4. Tools become available as `my_server__tool_name()`
### Debugging Workflows
**Enable detailed logging:**
```bash
LOG_LEVEL=debug npm run dev
```
**Test server connections in isolation:**
```bash
npm run test:servers
```
**Inspect MCP protocol messages:**
```bash
npm run inspector
# Opens MCP Inspector UI to test tools directly without the sandbox
```
**Debug code execution:**
```bash
npm run test-sandbox
# Tests sandbox with example TypeScript code
```
**Check actual generated APIs:**
- Review `/generated/servers/` directory after `npm run build:full`
- Files show exact parameter names, types, and return values Claude sees
## Code Generation and Generated APIs
The TypeScript Generator automatically creates typed APIs in the `/generated` directory:
```
/generated
├── servers/
│ ├── context7/ # context7 server APIs (2 tools)
│ ├── playwright/ # playwright server APIs (22 tools)
│ ├── bright-data/ # bright-data server APIs (4 tools)
│ ├── chrome-devtools/ # chrome-devtools server APIs (26 tools)
│ ├── firecrawl-mcp/ # firecrawl-mcp server APIs (6 tools)
│ ├── shadcn/ # shadcn server APIs (if configured)
│ └── index.ts # Master index re-exporting all tools
└── types/
└── mcp-runtime.d.ts # Global __mcp_call() type definitions
```
### Key Points
- **Do not edit** files in `/generated/` manually—they are overwritten by `npm run build:full`
- **Always commit** generated files to git so others see exact parameter names and types
- **Read generated files** to understand tool parameters before writing code
- **Inspect after changes** to MCP servers to verify APIs were generated correctly
The generator:
- Groups tools by server name (expects format: `server__toolName`)
- Converts MCP JSON schemas to TypeScript interfaces using `json-schema-to-typescript`
- Creates a function wrapper for each tool that calls `__mcp_call()`
- Generates JSDoc comments from tool descriptions
- Produces a master index exporting all 60+ tools
## Sandbox Execution Model
When Claude sends code to `execute_code`:
1. **TypeScript Compilation**: Code is transpiled to JavaScript using TypeScript's `transpileModule()`
2. **Context Creation**: A VM context is created with a restricted sandbox containing:
- `console.log()` (captured to logs array)
- `__mcp_call(toolName, args)` (routes to MCP orchestrator)
3. **Code Execution**: JavaScript runs in the VM with a 30-second timeout
4. **Log Capture**: All console.log output is captured and returned
5. **Result Return**: Only execution logs are sent back to Claude (intermediate data stays in sandbox)
The sandbox **blocks** access to:
- Network APIs (`fetch`, `XMLHttpRequest`, `WebSocket`)
- Filesystem APIs (`fs`, `path`)
- Process APIs (`child_process`, `process`)
- Module loading (`require`, `import` of external modules)
**Security Note**: This uses Node.js built-in `vm` module for basic isolation. For production deployment, consider using Deno with strict permissions, `isolated-vm` with Node.js v20, or Cloudflare Workers.
## API Documentation Format
The `execute_code` tool description is generated dynamically and includes:
1. **Available APIs section**: Lists all servers and their tools
- Format: `server_name__tool_function_name()`
- Parameters and return types auto-generated from schemas
2. **Security guarantees**: Documents sandbox isolation
3. **Usage examples**: Shows both simple and multi-step workflows
Claude uses this documentation to understand how to call tools and what parameters they accept.
## Execution Flow: Step-by-Step
### Initialization (Server Startup)
```
1. Main Server starts
↓
2. Load MCP server configs from mcp-servers.config.js
↓
3. Orchestrator connects to each of 5 MCP servers
↓
4. Orchestrator fetches tool schemas from each server (tools/list)
↓
5. Generator creates 60 TypeScript API files from schemas
├─ context7/: 2 tools
├─ playwright/: 22 tools
├─ bright-data/: 4 tools
├─ chrome-devtools/: 26 tools
└─ firecrawl-mcp/: 6 tools
↓
6. Sandbox initializes with __mcp_call binding
↓
7. Main Server ready to receive execute_code calls
```
### Execution (User Request)
When Claude sends TypeScript code to `execute_code`:
```
User: "Get weather in Austin and update Salesforce"
↓
Claude Code sees execute_code tool
↓
Claude generates TypeScript code:
import { getWeather } from './servers/weather';
import { updateRecord } from './servers/salesforce';
const weather = await getWeather({ city: 'Austin' });
await updateRecord({
objectType: 'WeatherLog',
data: { temp: weather.temperature }
});
console.log('Updated: ' + weather.conditions);
↓
Claude calls: execute_code({ code: "..." })
↓
Main Server receives tool call via MCP
↓
Sandbox compiles TypeScript to JavaScript
↓
Sandbox executes JavaScript in VM context
↓
Code calls: getWeather({ city: 'Austin' })
↓
Wrapper calls: __mcp_call('weather__get_current', {...})
↓
Binding routes to Orchestrator.callTool()
↓
Orchestrator identifies server: 'weather'
↓
Orchestrator routes to Weather MCP server via stdio
↓
Weather MCP returns: { temperature: 93, conditions: 'sunny' }
↓
Sandbox continues execution with result
↓
Code calls: updateRecord(...)
↓
Same flow to Salesforce MCP server
↓
Sandbox captures: console.log('Updated: sunny')
↓
Sandbox execution completes
↓
Sandbox returns:
{
logs: ['Updated: sunny'],
result: undefined,
executionTime: 234,
error: undefined
}
↓
Main Server returns to Claude
↓
Claude sees only logs + final result (NOT 50K token weather data!)
↓
Claude continues conversation with user
```
### Key Insight: Token Savings
The intermediate weather data (potentially 50K tokens) **never enters Claude's context**. Only the execution logs are returned, achieving massive token reduction for complex workflows.
## Component Communication Map
### Orchestrator ↔ MCP Servers
```
MCPOrchestrator
├─ connectServer(config)
│ └─ Creates StdioClientTransport
│ ├─ Spawns subprocess: npx context7-mcp
│ ├─ Connects MCP client
│ └─ Fetches tools/list and caches schemas
│
├─ callTool(toolName, args)
│ ├─ Parses server from tool name (e.g., 'weather__get_current')
│ ├─ Validates args against cached schema
│ ├─ Sends tools/call request to correct server
│ └─ Returns result
│
└─ getAllTools()
└─ Returns aggregated list from all servers
```
### Sandbox ↔ Orchestrator
```
Sandbox VM context contains:
├─ __mcp_call binding (only external access)
│ ├─ Called by user code: __mcp_call('server__tool', args)
│ └─ Invokes: orchestrator.callTool(toolName, args)
│
└─ console.log() (captured)
└─ All output captured to logs array
```
### Generator ↔ Orchestrator ↔ TypeScript Files
```
On startup:
1. Generator calls orchestrator.getAllTools()
2. Gets [Tool, Tool, Tool, ...] from all servers
3. Groups by server name
4. For each tool, generates:
├─ Interface for input parameters
├─ Interface for output/return type
├─ Function wrapper that calls __mcp_call()
└─ Export in index.ts
Result: generated/servers/{server}/{tool}.ts
```
## Key Files and Their Responsibilities
| File | Purpose | Complexity | Key Functions |
|------|---------|-----------|----------------|
| `src/index.ts` | Entry point, orchestrator init, tool exposure | High (~300 lines) | main(), generateAPIDocumentation(), extractParameters() |
| `src/orchestrator/MCPOrchestrator.ts` | MCP server connection management | High | connectServer(), callTool(), getAllTools() |
| `src/generator/TypeScriptGenerator.ts` | Converts MCP schemas to TS APIs | High | generateAPIs(), generateServerAPIs() |
| `src/sandbox/CodeSandbox.ts` | Code compilation and sandbox execution | Moderate (~200 lines) | execute(), compileTypeScript(), createSandbox() |
| `src/types/index.ts` | Shared type definitions | Low | MCPServerConfig, ExecutionResult, MCPTool, etc. |
| `src/utils/logger.ts` | Winston-based structured logging | Low | logInfo(), logError(), logDebug() |
| `mcp-servers.config.js` | Runtime configuration for MCP servers | N/A | Defines which servers to connect to and credentials |
| `generated/servers/` | Auto-generated TypeScript APIs (60 files) | N/A | Complete API definitions with types and JSDoc |
## Security Architecture
### Trust Boundaries
The system separates **untrusted** (LLM-generated code) from **trusted** (orchestrator + API keys):
```
┌──────────────────────────────────────┐
│ UNTRUSTED ZONE │
│ ├─ Sandbox VM │
│ ├─ User-generated TypeScript code │
│ ├─ Can ONLY call __mcp_call() │
│ └─ Cannot access network, filesystem│
│ or environment variables │
└──────────────────────────────────────┘
↓ __mcp_call() binding
↓ (only communication channel)
↓
┌──────────────────────────────────────┐
│ TRUSTED ZONE │
│ ├─ Orchestrator │
│ ├─ API credentials/keys │
│ ├─ MCP server connections │
│ └─ Input validation from sandbox │
└──────────────────────────────────────┘
```
### Security Layers
1. **Sandbox Isolation** - Code runs in Node.js VM with restricted context
2. **Binding Control** - Only `__mcp_call()` available to sandbox code
3. **Input Validation** - Orchestrator validates all tool calls against cached schemas
4. **Resource Limits** - Timeout (30s default), memory, CPU enforced
5. **API Key Protection** - Keys stored in orchestrator, never exposed to sandbox
### What Sandbox Blocks
```
❌ Network: fetch(), XMLHttpRequest, WebSocket, http module
❌ Filesystem: fs, path modules, file operations
❌ Process: child_process, process module, shell commands
❌ Environment: process.env (except explicit whitelist)
❌ Modules: require(), import of external modules
```
## Performance Characteristics
### Token Usage Comparison
**Standard MCP (Direct Tool Calling)**:
- Tool definitions: 10K tokens (50 tools shown in prompt)
- Intermediate results: 40K tokens (large API responses stay in context)
- Conversation: 50K tokens
- **Total: ~100K tokens**
**Code Mode (code2mcp)**:
- Tool definitions: 0 tokens (discovered by reading generated files)
- Code written by Claude: 2K tokens
- Execution logs only: 1K tokens
- Conversation: 50K tokens
- **Total: ~53K tokens**
**Result: 47% reduction for simple workflows, 98% for complex multi-step workflows**
### Execution Performance
- **API Generation** (startup): ~500ms for 60 tools
- **Sandbox Init**: ~50ms (isolated-vm) or ~5ms (Deno)
- **Code Execution**: <1s typically (depends on code complexity)
- **MCP Tool Call**: 100-500ms (network latency to MCP server)
- **End-to-end**: Simple request ~200-300ms, complex multi-step ~500ms-2s
### Scalability Limits
- **Concurrent Executions**: Limited by sandbox pooling (current: 1, can expand)
- **Memory Footprint**: ~50MB base process + ~10MB per sandbox instance
- **CPU Usage**: Minimal except during code compilation/execution
- **Connection Pools**: Persistent to all 5 MCP servers, reused per request
## Error Handling
### Error Categories and Recovery
```
Error Type Cause Recovery
─────────────────────────────────────────────────────────────────────
MCP Connection Server unreachable Auto-reconnect with exponential backoff
Authentication Invalid API key/credentials Log error, return to Claude
Code Syntax Invalid TypeScript Return error + stack trace to Claude
Code Runtime Uncaught exception Return error + stack trace to Claude
Tool Call Invalid Wrong parameters for tool Validate against schema, return error
Timeout Code runs >30s Kill sandbox, return timeout error
Resource Limit Memory/CPU exceeded Kill sandbox, return limit error
```
## v1.1.0 Enhancement: Type Surfacing
### Problem
Claude was making trial-and-error attempts when calling tools with wrong parameter names:
- First attempt: `__mcp_call('context7__get_library_docs', {libraryId: ...})` ❌
- Second attempt: `__mcp_call('context7__get_library_docs', {id: ...})` ❌
- Third attempt: `__mcp_call('context7__get_library_docs', {context7CompatibleLibraryID: ...})` ✓
**Success rate**: 20% (1 in 5 attempts succeeded)
### Solution
Made parameter names visible in execute_code tool description:
**Before v1.1.0**:
```
context7__get-library-docs: Fetches documentation for a library
```
**After v1.1.0**:
```
__mcp_call('context7__get-library-docs', {context7CompatibleLibraryID: value, topic?: value, page?: number})
Fetches up-to-date documentation for a library
```
### Implementation Details
In `src/index.ts`, the `extractParameters()` function:
1. Takes MCP tool schema JSON
2. Extracts parameter names and types
3. Generates TypeScript-like signature
4. Includes in `execute_code` tool description
### Impact
- **Trial-and-error reduction**: 70% fewer attempts
- **First-try success rate**: 20% → 85%
- **Pre-generated APIs**: 60 TypeScript files available for reading
## Important Architectural Decisions
### Configuration Over Hardcoding
Server configurations are loaded from `mcp-servers.config.js` at startup, not hardcoded. This allows users to enable/disable servers and set credentials without modifying source code.
### Schema-Driven API Generation
Instead of manually writing TypeScript wrappers for each tool, the system automatically generates them from MCP schemas. This keeps types in sync with the actual MCP server implementations.
### Token Efficiency Through Code Execution
By having Claude write code instead of making direct tool calls:
- Tool schemas are not repeated in the prompt (only shown once in tool description)
- Intermediate results (like large API responses) never enter Claude's context
- Complex multi-tool workflows are executed atomically in the sandbox
### Separation of Concerns
- Orchestrator handles MCP protocol and credential management
- Generator handles schema conversion (could be replaced with alternative implementation)
- Sandbox handles code execution isolation
- Main server orchestrates these components and exposes the MCP interface
## Common Development Patterns
### Adding a New Tool to an Existing Server
1. The tool is automatically available once the MCP server exposes it
2. Run `npm run generate-apis` to regenerate TypeScript definitions
3. The tool becomes callable as `serverName__toolFunction()`
### Debugging Tool Calls
1. Enable debug logging: `LOG_LEVEL=debug npm run dev`
2. Check orchestrator logs for routing decisions
3. Use MCP Inspector to test the server directly: `npm run inspector`
### Modifying Sandbox Behavior
**Adjust execution timeout:**
- File: `src/sandbox/CodeSandbox.ts`, method `execute()`
- Default: 30000ms (30 seconds)
- Change: `timeout: 60000` for longer-running code
**Add global bindings (variables/functions available to sandbox code):**
- File: `src/sandbox/CodeSandbox.ts`, method `createSandbox()`
- Add to `contextObject`:
```typescript
contextObject.myHelper = (x) => x * 2;
```
- Accessible in user code as `myHelper(5)` → `10`
**Modify VM security context:**
- File: `src/sandbox/CodeSandbox.ts`, method `compileAndExecute()`
- Options: `timeout`, `displayErrors`, `lineOffset`, `columnOffset`
- Reference: Node.js [`vm.runInNewContext()` documentation](https://nodejs.org/api/vm.html)
## Troubleshooting
### MCP Server Connection Issues
**Problem**: "Error: spawn npx ENOENT" or server fails to start
- **Cause**: MCP server package not installed or npm not in PATH
- **Fix**: Run `npm install` to ensure dependencies, or verify `npx` is available: `which npx`
**Problem**: Tools are missing or incomplete after `npm run build:full`
- **Cause**: MCP server process crashed or timed out during initialization
- **Fix**: Enable debug logging (`LOG_LEVEL=debug`) and check stderr for error messages from individual servers
**Problem**: "Cannot find module" when importing generated APIs
- **Cause**: Generated files are stale or missing
- **Fix**: Run `npm run build:full` to regenerate, then `npm run build` to compile
### Code Execution Issues
**Problem**: Sandbox execution times out (error: "Script execution timeout after 30s")
- **Cause**: Code is performing long-running operations (network requests, loops, file I/O)
- **Fix**: Add `console.log()` statements to see where execution hangs; optimize code or increase timeout in `src/sandbox/CodeSandbox.ts` line 26
**Problem**: "ReferenceError: __mcp_call is not defined"
- **Cause**: Using async/await without proper import, or code structure prevents binding injection
- **Fix**: Ensure code is at module level, not in isolated scopes; `__mcp_call` is globally injected
**Problem**: Tool call returns "Invalid parameters" error
- **Cause**: Parameter names or types don't match MCP schema
- **Fix**: Check generated API file in `/generated/servers/{server}/` for exact parameter names; review `npm run inspector` output
### TypeScript Compilation Issues
**Problem**: "Unused variable" or "Unused parameter" errors during build
- **Cause**: `tsconfig.json` has strict flags enabled
- **Fix**: Either use the variable, or prefix with `_` (e.g., `_unused`) to explicitly mark as intentional
**Problem**: Type errors in generated API files
- **Cause**: MCP server schema is invalid or contains unsupported JSON schema features
- **Fix**: Check server logs, validate against MCP specification; `json-schema-to-typescript` may need schema adjustments
## Documentation Structure
See `/DOCS` for complete documentation:
- `DOCS/Architecture/SYSTEM_MAP.md` - System overview and component interactions
- `DOCS/Architecture/CODE_STRUCTURE.md` - Detailed directory structure
- `DOCS/Architecture/DEPENDENCIES.md` - Dependency versions and management
- `DOCS/References/CONFIGURED_SERVERS.md` - Details on each pre-configured server
- `DOCS/References/API_BrightData_SERP_v1.md` - Example API documentation
- `DOCS/Etc/IMPROVEMENTS.md` - Performance and feature improvements
- `README.md` - User-facing documentation
## Testing Strategy
### Integration Tests
```bash
# Test all configured MCP servers
npm run test:servers
# Run integration test suite
npm run test:integration
```
Tests verify:
- MCP server connections and availability
- Tool schemas load correctly
- Basic functionality of each server
- Shadcn integration (if enabled)
### Sandbox Testing
```bash
# Test sandbox execution with example code
npm run test-sandbox
```
Tests the full code execution pipeline: TypeScript compilation → VM execution → log capture.
### Current Coverage
Minimal unit test coverage; integration tests focus on:
- MCP Orchestrator connections to real servers
- Tool availability and schema validation
- Sandbox execution and timeout enforcement
Areas for improvement:
- Unit tests for TypeScript Generator schema-to-interface conversion
- Unit tests for Sandbox VM context isolation
- End-to-end tests with actual Claude-generated code patterns