Skip to content

MCP Server for Claude Desktop

The Model Context Protocol (MCP) server allows Claude Desktop to execute code directly through ERA Agent. Claude can write, test, and debug code in multiple languages using natural language.

Terminal window
cd era-agent
go build -o agent
/Users/yourname/era-agent
pwd

Your agent path: /Users/yourname/era-agent/agent

Important: You need the absolute path (starting with /), not a relative path.

Open the config file:

Terminal window
code ~/Library/Application\ Support/Claude/claude_desktop_config.json

Add this configuration (update the path!):

{
"mcpServers": {
"era-agent": {
"command": "/Users/yourname/era-agent/agent",
"args": ["mcp"],
"env": {
"AGENT_LOG_LEVEL": "info"
}
}
}
}

Completely quit Claude Desktop (not just close):

  • macOS: Cmd+Q or Claude → Quit
  • Linux: File → Quit
  • Windows: File → Exit

Then restart Claude Desktop.

Try these prompts in Claude Desktop:

Execute this Python code:
print("Hello from ERA Agent!")
print(f"2 + 2 = {2 + 2}")

Claude should use the era_execute_code tool and show you the output.

Once configured, Claude has access to 14 ERA Agent tools:

These tools provide the easiest way to run code in a specific language without managing sessions:

Execute Python code with a single command.

Claude Usage:

Run this Python FizzBuzz:
for i in range(1, 16):
if i % 15 == 0:
print(f'{i}: FizzBuzz')
elif i % 3 == 0:
print(f'{i}: Fizz')
elif i % 5 == 0:
print(f'{i}: Buzz')
else:
print(f'{i}: {i}')

Features:

  • Automatic Python 3 execution
  • Handles complex code (f-strings, loops, classes)
  • Clean output capture
  • No manual language specification needed

Execute Node.js/JavaScript code.

Claude Usage:

Run this in Node.js:
const data = [1, 2, 3, 4, 5];
console.log('Sum:', data.reduce((a, b) => a + b));
console.log('Average:', data.reduce((a, b) => a + b) / data.length);

Execute TypeScript with ts-node.

Claude Usage:

Run this TypeScript code:
interface User {
name: string;
age: number;
}
const user: User = { name: 'Alice', age: 30 };
console.log(`${user.name} is ${user.age} years old`);

Note: Requires ts-node in the VM environment.

Execute code with Deno runtime.

Claude Usage:

Run this with Deno:
const text = await Deno.readTextFile('/etc/hosts');
console.log('First 5 lines:', text.split('\\n').slice(0, 5));

Note: Requires Deno in the VM environment.

Execute shell commands for system operations.

Claude Usage:

Run these shell commands:
- Check Python version: python3 --version
- Install packages: pip install pandas numpy
- List directory: ls -la

Common Uses:

  • Package installation: pip install requests, npm install lodash
  • Environment setup: apt-get update && apt-get install -y git
  • File operations: mkdir data && cd data && wget [url]
  • System info: uname -a, df -h, free -m

Security Note: Shell commands run with the same isolation as code execution.

Execute code in an ephemeral sandbox.

Claude Usage:

Execute this Python code:
import math
print(f"π ≈ {math.pi:.5f}")

What It Does:

  • Creates temporary VM
  • Executes code
  • Returns output
  • Cleans up automatically

Create a persistent execution environment.

Claude Usage:

Create a Python session with 2 CPUs and 512MB memory

What It Does:

  • Creates named session
  • Configures resources
  • Returns session ID
  • State persists between runs

Execute code in an existing session.

Claude Usage:

In session abc-123, run:
counter = 0
print(f"Counter: {counter}")
Then run:
counter += 1
print(f"Counter: {counter}")

What It Does:

  • Executes in existing VM
  • Maintains state/variables
  • Faster than new VM each time

List all active sessions.

Claude Usage:

Show me all ERA Agent sessions

Get session details.

Claude Usage:

What's the status of session abc-123?

Delete a session.

Claude Usage:

Delete session abc-123

Upload a file to session workspace.

Claude Usage:

Upload a file called data.csv to session abc-123 with this content:
name,age
Alice,30
Bob,25

Read a file from session workspace.

Claude Usage:

Read the output.txt file from session abc-123

List files in session workspace.

Claude Usage:

What files are in session abc-123?

You: “Run this Python code: print([x**2 for x in range(10)])

Claude: Uses era_python (automatically)

Result:

[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Why it works: Language-specific tool makes execution straightforward.

You: “Write a FizzBuzz in Python and log each step”

Claude: Uses era_python with:

print("Starting FizzBuzz for numbers 1 to 15")
print()
for i in range(1, 16):
if i % 15 == 0:
print(f'{i}: FizzBuzz')
elif i % 3 == 0:
print(f'{i}: Fizz')
elif i % 5 == 0:
print(f'{i}: Buzz')
else:
print(f'{i}: {i}')
print()
print('FizzBuzz complete!')

Result: Full output with logging at each step.

You: “Create a Python session, install pandas, and analyze some data”

Claude:

  1. Uses era_create_session → Gets session ID (e.g., abc-123)
  2. Uses era_shell with session: pip install pandas
  3. Uses era_run_in_session:
    import pandas as pd
    data = {'name': ['Alice', 'Bob'], 'age': [30, 25]}
    df = pd.DataFrame(data)
    print(df.describe())

Result: Statistics on the data.

You: “Create a Python session, upload this CSV, and analyze it”

Claude:

  1. Uses era_create_session
  2. Uses era_upload_file with CSV data
  3. Uses era_shell: pip install pandas
  4. Uses era_run_in_session:
    import pandas as pd
    df = pd.read_csv('data.csv')
    print(df.describe())
    df['processed'] = df['value'] * 2
    df.to_csv('output.csv', index=False)
  5. Uses era_read_file to read output.csv

Result: Processed data returned.

You: “Test this algorithm in Python and Node.js”

Claude:

  1. Uses era_python:

    def quicksort(arr):
    if len(arr) <= 1:
    return arr
    pivot = arr[len(arr) // 2]
    left = [x for x in arr if x < pivot]
    middle = [x for x in arr if x == pivot]
    right = [x for x in arr if x > pivot]
    return quicksort(left) + middle + quicksort(right)
    print(quicksort([3,6,8,10,1,2,1]))
  2. Uses era_node:

    function quicksort(arr) {
    if (arr.length <= 1) return arr;
    const pivot = arr[Math.floor(arr.length / 2)];
    const left = arr.filter(x => x < pivot);
    const middle = arr.filter(x => x === pivot);
    const right = arr.filter(x => x > pivot);
    return [...quicksort(left), ...middle, ...quicksort(right)];
    }
    console.log(quicksort([3,6,8,10,1,2,1]));
  3. Compares results

Result: Both implementations produce [1, 1, 2, 3, 6, 8, 10]

You: “Test this TypeScript interface”

Claude: Uses era_typescript:

interface User {
name: string;
age: number;
email?: string;
}
const user: User = {
name: 'Alice',
age: 30
};
console.log(`User: ${user.name}, Age: ${user.age}`);
// Type error would be caught:
// const badUser: User = { name: 'Bob' }; // Missing 'age'

Result: Type-safe execution with TypeScript compiler.

You: “Scrape the top stories from Hacker News”

Claude:

  1. Uses era_create_session with network access
  2. Uses era_shell: pip install requests beautifulsoup4
  3. Uses era_run_in_session:
    import requests
    from bs4 import BeautifulSoup
    response = requests.get('https://news.ycombinator.com')
    soup = BeautifulSoup(response.text, 'html.parser')
    stories = soup.find_all('span', class_='titleline')
    for i, story in enumerate(stories[:5], 1):
    print(f"{i}. {story.get_text()}")

Result: Top 5 Hacker News stories.

Minimal setup:

{
"mcpServers": {
"era-agent": {
"command": "/path/to/agent",
"args": ["mcp"]
}
}
}

Enable debug logs:

{
"mcpServers": {
"era-agent": {
"command": "/path/to/agent",
"args": ["mcp"],
"env": {
"AGENT_LOG_LEVEL": "debug"
}
}
}
}

Store sessions in custom location:

{
"mcpServers": {
"era-agent": {
"command": "/path/to/agent",
"args": ["mcp"],
"env": {
"AGENT_STATE_DIR": "/custom/path/to/state",
"AGENT_LOG_LEVEL": "info"
}
}
}
}

Run multiple ERA Agent configurations:

{
"mcpServers": {
"era-agent-default": {
"command": "/path/to/agent",
"args": ["mcp"]
},
"era-agent-debug": {
"command": "/path/to/agent",
"args": ["mcp"],
"env": {
"AGENT_LOG_LEVEL": "debug"
}
}
}
}

Problem: Configuration not loaded or server failed to start.

Solutions:

  1. Check config file syntax:

    Terminal window
    # Validate JSON
    cat ~/Library/Application\ Support/Claude/claude_desktop_config.json | jq '.'
  2. Verify agent path:

    Terminal window
    # Test the path
    /your/path/to/agent --help
  3. Check permissions:

    Terminal window
    chmod +x /path/to/agent
  4. Restart Claude Desktop completely (Cmd+Q, not just close window)

  5. Check logs:

    Terminal window
    # macOS
    tail -f ~/Library/Logs/Claude/mcp*.log
    # Linux
    tail -f ~/.config/Claude/logs/mcp*.log

Problem: VM service error or Docker/Firecracker not running.

Solutions:

  1. Open Docker Desktop
  2. Verify Docker is running:
    Terminal window
    docker ps
  3. Restart Claude Desktop

Problem: Code takes too long to execute.

Solution: Ask Claude to increase timeout:

Execute this code with a 60 second timeout:
[long-running code]

Or create session with higher default:

Create a Python session with a 120 second default timeout

Problem: Incorrect agent path in config.

Solutions:

  1. Use absolute path (starting with /):

    "command": "/Users/name/era-agent/agent" // ✅ Good
    "command": "~/era-agent/agent" // ❌ Bad
    "command": "./agent" // ❌ Bad
  2. Verify path exists:

    Terminal window
    ls -l /your/path/to/agent

Problem: Environment variables or PATH not set.

Solution: Add environment variables to config:

{
"mcpServers": {
"era-agent": {
"command": "/path/to/agent",
"args": ["mcp"],
"env": {
"AGENT_LOG_LEVEL": "info",
"PATH": "/usr/local/bin:/usr/bin:/bin",
"HOME": "/Users/yourname"
}
}
}
}

“Shell Escaping Errors” (Fixed in Latest Version)

Section titled ““Shell Escaping Errors” (Fixed in Latest Version)”

Previous Problem: Python code with f-strings or complex syntax failed with errors like:

sh: -c: line 0: syntax error near unexpected token '('
nprint("Starting...") # Literal 'n' instead of newline

Cause: Double-escaping bug—JSON newlines were being escaped twice by shell commands.

Fix: Code is now written directly to files using os.WriteFile() in Go, completely bypassing shell escaping. Update to the latest version if you’re experiencing this.

How to Check: Run FizzBuzz with f-strings. If it works, you have the fix:

for i in range(1, 16):
print(f'{i}: FizzBuzz' if i % 15 == 0 else f'{i}: Fizz' if i % 3 == 0 else i)

The system handles complex Python features correctly:

# F-strings, loops, and formatting all work perfectly
for i in range(1, 16):
if i % 15 == 0:
print(f'{i}: FizzBuzz')
elif i % 3 == 0:
print(f'{i}: Fizz')

Why it works: Code is written directly to files by the Go process, bypassing all shell escaping issues.

Newlines and indentation are preserved exactly:

def factorial(n):
"""Calculate factorial recursively"""
if n <= 1:
return 1
return n * factorial(n - 1)
print(f"10! = {factorial(10)}")

Single quotes, double quotes, backticks, and other special characters work:

const message = `He said, "It's working!"`;
console.log(message);

Pitfall 1: Using era_execute_code Without Language

Section titled “Pitfall 1: Using era_execute_code Without Language”

Problem:

Execute this code: print("Hello")

Claude might not know which language to use.

Solution: Use language-specific tools:

Use era_python to run: print("Hello")

Or be explicit:

Execute this Python code: print("Hello")

Pitfall 2: Installing Packages Mid-Session

Section titled “Pitfall 2: Installing Packages Mid-Session”

Problem:

# This will fail if pandas isn't installed
import pandas as pd
df = pd.read_csv('data.csv')

Solution: Install packages first with era_shell:

1. Create a Python session
2. Use era_shell: pip install pandas
3. Then run your pandas code

Or create session with packages parameter:

Create a Python session and install pandas and numpy

Pitfall 3: Assuming Persistent State in Ephemeral Execution

Section titled “Pitfall 3: Assuming Persistent State in Ephemeral Execution”

Problem:

Run: x = 42
Then run: print(x) # This will fail!

Solution: Use sessions for stateful execution:

1. Create a Python session (remember the ID)
2. In that session, run: x = 42
3. In that same session, run: print(x) # ✅ Works!

Problem:

# This might fail
with open('/home/user/myfile.txt') as f:
data = f.read()

Solution: Use session workspace:

1. Upload file to session: era_upload_file
2. Use relative path: with open('myfile.txt') as f:

🔧 Technical Details: How Code Execution Works

Section titled “🔧 Technical Details: How Code Execution Works”

Understanding the internals helps debug issues:

  1. Code Submission: Claude sends code to ERA Agent via MCP
  2. File Writing: ERA Agent writes code directly to a file (e.g., era_script_123456789.py)
  3. VM Setup: Creates or uses existing VM with proper runtime
  4. Command Execution: Runs simple command (e.g., python3 era_script_123456789.py)
  5. Output Capture: Stdout and stderr captured to files
  6. Result Return: Output returned to Claude via MCP

Key Insight: No shell escaping is involved—files are written directly from Go using os.WriteFile(). This is why complex code with special characters works reliably.

Previous Approach (Had Issues):

Terminal window
# Used heredoc with shell escaping - caused double-escaping
cat > script.py << 'EOF'
print("Hello")
EOF
python3 script.py

Problem: JSON newlines (\n) → Go unescapes → Shell %q re-escapes → Syntax errors

Current Approach (Fixed):

// Write file directly in Go
os.WriteFile("/vm/workdir/era_script_123.py", []byte(code), 0644)
// Execute simple command
exec.Command("python3", "era_script_123.py")

Result: No escaping issues, all special characters preserved.

Good Use Cases:

Terminal window
# Package management
pip install requests beautifulsoup4
npm install lodash axios
# Environment setup
apt-get update && apt-get install -y postgresql-client
# File operations
mkdir -p data/processed
wget https://example.com/data.csv -O data/input.csv
# System information
python3 --version
node --version
df -h

What NOT to Use era_shell For:

  • Running Python/JavaScript code (use era_python, era_node instead)
  • Complex multi-line scripts (create a file and execute it)

Good:

Terminal window
# Use && for dependent commands
pip install pandas && python -c "import pandas; print(pandas.__version__)"

Bad:

Terminal window
# Don't rely on shell state between calls
cd /some/dir
ls # Won't be in /some/dir anymore!

Set them in the command:

Terminal window
PYTHONPATH=/custom/path python3 script.py

Or use session environment:

Create a Python session with environment variable PYTHONPATH=/custom/path
ScenarioToolReason
Single Python scriptera_pythonFast, no session overhead
Multiple related runsSession + era_run_in_sessionVM reuse, state persistence
Package installationera_shellSystem-level operations
Quick calculationera_python / era_nodeMinimal setup
Long-running taskSession with higher timeoutConfigurable limits

Use Ephemeral (era_python, etc.) when:

  • Single, independent execution
  • No package installation needed
  • Quick tests or calculations

Use Sessions when:

  • Multiple related operations
  • Installing packages
  • Building on previous state
  • Large data loading (load once, use multiple times)

By default, network is disabled:

import requests
requests.get('https://example.com') # Will fail!

Enable when needed:

Create a Python session with network access

Only enable network for:

  • API calls
  • Package downloads
  • Web scraping
  • External data fetching

Each session has isolated filesystem:

  • Session A cannot access Session B’s files
  • Ephemeral VMs cleaned up after execution
  • No access to host filesystem

Default limits prevent resource exhaustion:

  • CPU: 1 core (prevents runaway processes)
  • Memory: 256 MiB (prevents OOM)
  • Timeout: 30 seconds (prevents infinite loops)

Request higher limits when needed:

Create a Python session with 4 CPUs, 2GB memory, and 120 second timeout

Good Prompts:

"Execute this Python code: print('Hello')"
"Create a Node.js session with network access"
"In session abc-123, run: x = 42"
"List all active sessions"

Less Effective:

"Can you run code?" (too vague)
"Execute: print(x)" (no context about language or session)

Create once, use multiple times:

1. "Create a Python session"
2. "In that session, import pandas and load data.csv"
3. "Now calculate the mean of column 'age'"
4. "Create a visualization and save it"
5. "Read the output file"

Upload, process, read:

1. "Create a Python session"
2. "Upload requirements.txt with content: pandas numpy"
3. "Install the requirements"
4. "Upload data.csv with [data]"
5. "Process the data and save results"
6. "Read the results file"
  • All code runs in isolated Firecracker VMs
  • Network access disabled by default
  • Filesystem isolation per session
  • Resource limits enforced

Network is disabled by default. Enable only when needed:

Create a Python session with network access enabled

Claude will use network_mode: "allow_all".

Sessions stored locally:

  • macOS: ~/Library/Application Support/era-agent
  • Linux: ~/.local/share/era-agent
  • Custom: Set AGENT_STATE_DIR

Data never leaves your machine.

First execution in a session is slower (VM startup):

  • First run: ~2-3 seconds
  • Subsequent runs: ~0.5-1 second

Tip: Use persistent sessions for multiple operations.

Default limits:

  • CPU: 1 core
  • Memory: 256 MiB
  • Timeout: 30 seconds

Request higher resources when needed:

Create a Python session with 4 CPUs and 2GB memory

With ERA Agent, Claude can:

"Analyze this CSV data and create summary statistics"
"Create a Python session with network access, fetch data from this API, and process it"
"Create a Node.js project with multiple files, install dependencies, and run tests"
"Implement quicksort in Python, test it with random data, and measure performance"
"Create a Python session, install BeautifulSoup, scrape this URL, and extract data"
"Test this function with various inputs and edge cases"