diff --git a/README.md b/README.md index 990b499..52f23c9 100644 --- a/README.md +++ b/README.md @@ -91,8 +91,6 @@ pip install -r examples/requirements.txt
Use CodeRunner with Claude Code CLI for terminal-based AI assistance: -![Claude Code Demo](images/claude-code-demo.png) - **Quick Start:** ```bash @@ -102,13 +100,27 @@ cd coderunner sudo ./install.sh # 2. Install the Claude Code plugin -claude plugin marketplace add github:BandarLabs/coderunner/instavm-coderunner-plugin -claude plugin install instavm-coderunner@instavm-plugins +claude plugin marketplace add https://github.com/instavm/coderunner-plugin +claude plugin install instavm-coderunner # 3. Reconnect to MCP servers /mcp ``` +**Installation Steps:** + +1. Navigate to Plugin Marketplace: + + ![Navigate to Plugin Marketplace](images/gotoplugin.png) + +2. Add the InstaVM repository: + + ![Add InstaVM Repository](images/addrepo.png) + +3. Execute Python code with Claude Code: + + ![Execute Python Code](images/runcode.png) + That's it! Claude Code now has access to all CodeRunner tools: - **execute_python_code** - Run Python code in persistent Jupyter kernel - **navigate_and_get_all_visible_text** - Web scraping with Playwright @@ -116,7 +128,7 @@ That's it! Claude Code now has access to all CodeRunner tools: - **get_skill_info** - Get documentation for specific skills - **get_skill_file** - Read skill files and examples -**Learn more:** See [instavm-coderunner-plugin/README.md](instavm-coderunner-plugin/README.md) for detailed documentation. +**Learn more:** See the [plugin repository](https://github.com/instavm/coderunner-plugin) for detailed documentation.
diff --git a/images/addrepo.png b/images/addrepo.png new file mode 100644 index 0000000..5ce9f4e Binary files /dev/null and b/images/addrepo.png differ diff --git a/images/gotoplugin.png b/images/gotoplugin.png new file mode 100644 index 0000000..f434f92 Binary files /dev/null and b/images/gotoplugin.png differ diff --git a/images/runcode.png b/images/runcode.png new file mode 100644 index 0000000..dacb99c Binary files /dev/null and b/images/runcode.png differ diff --git a/instavm-coderunner-plugin/.claude-plugin/marketplace.json b/instavm-coderunner-plugin/.claude-plugin/marketplace.json deleted file mode 100644 index c081e37..0000000 --- a/instavm-coderunner-plugin/.claude-plugin/marketplace.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "instavm-plugins", - "owner": { - "name": "InstaVM" - }, - "plugins": [ - { - "name": "instavm-coderunner", - "source": ".", - "description": "Execute Python code in a local sandboxed Apple container with fast kernel pool performance" - } - ] -} diff --git a/instavm-coderunner-plugin/.claude-plugin/plugin.json b/instavm-coderunner-plugin/.claude-plugin/plugin.json deleted file mode 100644 index c8494ad..0000000 --- a/instavm-coderunner-plugin/.claude-plugin/plugin.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "name": "instavm-coderunner", - "version": "1.0.0", - "description": "Execute Python code in a sandboxed Apple container via local CodeRunner MCP server", - "author": { - "name": "InstaVM" - }, - "homepage": "https://github.com/instavm/coderunner", - "license": "MIT" -} diff --git a/instavm-coderunner-plugin/.mcp.json b/instavm-coderunner-plugin/.mcp.json deleted file mode 100644 index e807d64..0000000 --- a/instavm-coderunner-plugin/.mcp.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "mcpServers": { - "instavm-coderunner": { - "command": "python3", - "args": ["${CLAUDE_PLUGIN_ROOT}/scripts/mcp-proxy.py"] - } - } -} diff --git a/instavm-coderunner-plugin/README.md b/instavm-coderunner-plugin/README.md deleted file mode 100644 index 0127935..0000000 --- a/instavm-coderunner-plugin/README.md +++ /dev/null @@ -1,258 +0,0 @@ -# InstaVM CodeRunner Plugin for Claude Code - -This plugin enables Claude Code to execute Python code in a local sandboxed Apple container using [InstaVM CodeRunner](https://github.com/instavm/coderunner) via the Model Context Protocol (MCP). - -## Quick Start - -```bash -# 1. Install and start CodeRunner (one-time setup) -git clone https://github.com/instavm/coderunner.git -cd coderunner -sudo ./install.sh - -# 2. Install the Claude Code plugin -claude plugin marketplace add github:BandarLabs/coderunner/instavm-coderunner-plugin -claude plugin install instavm-coderunner@instavm-plugins - -# 3. Reconnect to MCP servers -/mcp -``` - -That's it! Claude Code now has access to all CodeRunner tools. - -## Installation - -### Prerequisites - -**IMPORTANT:** You must have CodeRunner installed and running **before** installing this plugin. - -#### Install CodeRunner - -```bash -git clone https://github.com/instavm/coderunner.git -cd coderunner -sudo ./install.sh -``` - -This will: -- Install Apple's container runtime (version 0.8.0+) -- Pull the `instavm/coderunner` container image -- Start the CodeRunner MCP service at `http://coderunner.local:8222/mcp` - -**Verify CodeRunner is running:** -```bash -curl http://coderunner.local:8222/execute -X POST -H "Content-Type: application/json" -d '{"code":"print(\"test\")"}' -``` - -### Install via GitHub URL (Recommended) - -```bash -# Add the InstaVM marketplace from GitHub -claude plugin marketplace add github:BandarLabs/coderunner/instavm-coderunner-plugin - -# Install the plugin -claude plugin install instavm-coderunner@instavm-plugins -``` - -### Option 2: Install from Local Path - -If you've cloned the repository locally: - -```bash -# Add local marketplace -claude plugin marketplace add /path/to/coderunner/instavm-coderunner-plugin - -# Install the plugin -claude plugin install instavm-coderunner@instavm-plugins -``` - -## Usage - -Once installed, Claude will have access to the `execute_python_code` tool from CodeRunner. You can ask Claude to execute Python code: - -``` -Please execute this Python code: -```python -import math -print(f"The square root of 16 is {math.sqrt(16)}") -``` -``` - -Or simply: -``` -Execute this code: print("Hello from CodeRunner!") -``` - -## Available Tools - -The plugin exposes the following MCP tools from CodeRunner: - -- **execute_python_code**: Execute Python code in a persistent Jupyter kernel with full stdout/stderr capture -- **navigate_and_get_all_visible_text**: Web scraping using Playwright - navigate to URLs and extract visible text -- **list_skills**: List all available skills (both public and user-added) in CodeRunner -- **get_skill_info**: Get documentation for a specific skill (reads SKILL.md) -- **get_skill_file**: Read any file from a skill's directory (e.g., EXAMPLES.md, API.md) - -## Features - -- **Local Execution**: Code runs on your machine in a sandboxed container -- **No Cloud Uploads**: Process files locally without uploading to cloud services -- **Fast Performance**: Pre-warmed Jupyter kernel pool (2-5 kernels) for quick execution -- **Full Output**: Returns stdout, stderr, execution time, and CPU time -- **Security**: Runs in Apple container isolation -- **MCP Integration**: Uses standard Model Context Protocol for tool communication - -## Configuration - -By default, the plugin connects to `http://coderunner.local:8222/mcp` via a stdio proxy. To customize the URL, set the `MCP_URL` environment variable in `.mcp.json`: - -```json -{ - "mcpServers": { - "instavm-coderunner": { - "command": "python3", - "args": ["${CLAUDE_PLUGIN_ROOT}/scripts/mcp-proxy.py"], - "env": { - "MCP_URL": "http://your-custom-url:8222/mcp" - } - } - } -} -``` - -## Example Output - -```python -# Code: -print("Hello from CodeRunner!") -import time -time.sleep(0.1) -for i in range(3): - print(f"Count: {i}") -``` - -**Result:** -``` -Hello from CodeRunner! -Count: 0 -Count: 1 -Count: 2 - -Execution time: 0.156s -``` - -## Requirements - -- macOS 26.0+ (recommended) for Apple Container support -- Python 3.10+ -- CodeRunner installed and running -- Claude Code with MCP plugin support - -## Troubleshooting - -### Plugin shows "failed" status - -**Most common cause:** CodeRunner container is not running. - -**Solution:** -```bash -# Check if CodeRunner is running -curl http://coderunner.local:8222/execute -X POST -H "Content-Type: application/json" -d '{"code":"print(\"test\")"}' - -# If not running, restart it: -cd /path/to/coderunner -sudo ./install.sh -``` - -### "Invalid Host header" error - -The CodeRunner MCP server is running but rejecting requests. This means the container needs to be restarted with proper hostname configuration. - -**Solution:** -```bash -# Stop existing containers -sudo pkill -f container -container system start - -# Restart CodeRunner container -container run --name coderunner --detach --rm --cpus 8 --memory 4g \ - --volume "$HOME/.coderunner/assets/skills/user:/app/uploads/skills/user" \ - --volume "$HOME/.coderunner/outputs:/app/uploads/outputs" \ - instavm/coderunner -``` - -### MCP connection errors - -1. **Check MCP logs:** - ```bash - cat ~/Library/Caches/claude-cli-nodejs/-Users-manish-coderunner-instavm-coderunner-plugin/mcp-logs-*/latest/*.jsonl - ``` - -2. **Test MCP endpoint manually:** - ```bash - curl -H "Host: coderunner.local:8222" http://coderunner.local:8222/mcp \ - -X POST -H "Content-Type: application/json" \ - -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05"}}' - ``` - -3. **Verify proxy script:** - ```bash - cd /path/to/instavm-coderunner-plugin - python3 scripts/mcp-proxy.py - # Then send: {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05"}} - ``` - -## How It Works - -This plugin uses Claude Code's MCP server integration to connect to the local CodeRunner instance: - -1. The `.mcp.json` file defines the MCP server connection -2. Python proxy script bridges stdio MCP to HTTP endpoint -3. Tools from CodeRunner are automatically discovered and made available -4. When you ask Claude to execute code, it uses the `execute_python_code` tool -5. Results are returned via the MCP protocol - -## Publishing to GitHub Marketplace - -This plugin is published on GitHub and can be installed directly via the GitHub URL. Here's how to publish updates: - -### 1. Commit and Push Changes - -```bash -cd instavm-coderunner-plugin -git add . -git commit -m "Update plugin" -git push origin main -``` - -### 2. Users Install from GitHub - -Users can install the plugin directly from GitHub: - -```bash -# Add the InstaVM marketplace -claude plugin marketplace add github:BandarLabs/coderunner/instavm-coderunner-plugin - -# Install the plugin -claude plugin install instavm-coderunner@instavm-plugins -``` - -That's it! Claude Code will automatically pull the plugin files from GitHub. - -## Repository Structure - -``` -coderunner/ -└── instavm-coderunner-plugin/ # Plugin directory - ├── .claude-plugin/ - │ ├── marketplace.json # Marketplace metadata - │ └── plugin.json # Plugin manifest - ├── scripts/ - │ └── mcp-proxy.py # stdio-to-HTTP MCP proxy - ├── .mcp.json # MCP server configuration - └── README.md # This file -``` - -## License - -MIT diff --git a/instavm-coderunner-plugin/install.sh b/instavm-coderunner-plugin/install.sh deleted file mode 100755 index 5dbb4f2..0000000 --- a/instavm-coderunner-plugin/install.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/bash -# InstaVM CodeRunner Plugin Installer for Claude Code - -PLUGIN_NAME="instavm-coderunner" -PLUGIN_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" - -echo "🚀 Installing $PLUGIN_NAME plugin for Claude Code..." -echo "" - -# Check if CodeRunner is running -echo "🔍 Checking if CodeRunner is running..." -if curl -s http://coderunner.local:8222/health > /dev/null 2>&1; then - echo "✅ CodeRunner is running at http://coderunner.local:8222" -else - echo "⚠️ Warning: CodeRunner does not appear to be running." - echo " Please install and start CodeRunner first:" - echo " cd /path/to/coderunner && sudo ./install.sh" - echo "" - read -p "Continue anyway? (y/N) " -n 1 -r - echo "" - if [[ ! $REPLY =~ ^[Yy]$ ]]; then - exit 1 - fi -fi - -# Check MCP endpoint -echo "🔍 Checking MCP endpoint..." -if curl -s http://coderunner.local:8222/mcp > /dev/null 2>&1; then - echo "✅ MCP endpoint is accessible" -else - echo "⚠️ Warning: MCP endpoint not accessible" -fi - -echo "" -echo "📦 Plugin location: $PLUGIN_DIR" -echo "" -echo "To use this plugin with Claude Code, run:" -echo "" -echo " cd $PLUGIN_DIR" -echo " claude --plugin-dir ." -echo "" -echo "Or add it permanently:" -echo "" -echo " claude add plugin $PLUGIN_DIR" -echo "" -echo "✅ Setup complete!" diff --git a/instavm-coderunner-plugin/scripts/mcp-proxy.py b/instavm-coderunner-plugin/scripts/mcp-proxy.py deleted file mode 100755 index 3823d24..0000000 --- a/instavm-coderunner-plugin/scripts/mcp-proxy.py +++ /dev/null @@ -1,133 +0,0 @@ -#!/usr/bin/env python3 -""" -InstaVM CodeRunner MCP Proxy -Bridges stdio MCP protocol to HTTP endpoint with FastMCP session management -""" - -import sys -import os -import json -import urllib.request -import urllib.error -from urllib.parse import urlparse, urlencode - -# Get MCP URL from environment or use default -MCP_URL = os.environ.get("MCP_URL", "http://coderunner.local:8222/mcp") - -# Session management -session_id = None - -def send_request(request): - """Forward request to HTTP MCP server with session ID""" - global session_id - - try: - data = json.dumps(request).encode('utf-8') - parsed_url = urlparse(MCP_URL) - host_header = f"{parsed_url.hostname}:{parsed_url.port}" if parsed_url.port else parsed_url.hostname - - headers = { - 'Content-Type': 'application/json', - 'Accept': 'application/json, text/event-stream', - 'Host': host_header - } - - # Add session ID header if we have one (but not for initialize) - if session_id and request.get("method") != "initialize": - headers['mcp-session-id'] = session_id - - url = MCP_URL - - req = urllib.request.Request( - url, - data=data, - headers=headers, - method='POST' - ) - - with urllib.request.urlopen(req, timeout=120) as response: - # Extract session ID from initialize response - if request.get("method") == "initialize": - session_id = response.headers.get('mcp-session-id') - if not session_id: - # Fallback: try Set-Cookie header - set_cookie = response.headers.get('Set-Cookie', '') - if 'mcp_session_id=' in set_cookie: - cookie_parts = set_cookie.split(';') - for part in cookie_parts: - if 'mcp_session_id=' in part: - session_id = part.split('=')[1].strip() - break - - # Handle SSE response format - body = response.read().decode('utf-8') - for line in body.split('\n'): - line = line.strip() - if line.startswith('data: '): - data_str = line[6:] # Remove 'data: ' prefix - if data_str and data_str != '[DONE]': - try: - response_obj = json.loads(data_str) - print(json.dumps(response_obj), flush=True) - except json.JSONDecodeError: - pass - - except urllib.error.HTTPError as e: - error_body = "" - try: - error_body = e.read().decode('utf-8') - except: - pass - - print(json.dumps({ - "jsonrpc": "2.0", - "id": request.get("id"), - "error": { - "code": e.code, - "message": f"HTTP Error: {e.reason}" - } - }), flush=True) - - except urllib.error.URLError as e: - print(json.dumps({ - "jsonrpc": "2.0", - "id": request.get("id"), - "error": { - "code": -32603, - "message": f"Connection Error: {e.reason}. Is CodeRunner running?" - } - }), flush=True) - - except Exception as e: - print(json.dumps({ - "jsonrpc": "2.0", - "id": request.get("id"), - "error": { - "code": -32603, - "message": f"Internal Error: {str(e)}" - } - }), flush=True) - -def main(): - """Main proxy loop""" - # Don't log on startup to avoid confusing MCP clients - for line in sys.stdin: - line = line.strip() - if not line: - continue - - try: - request = json.loads(line) - send_request(request) - except json.JSONDecodeError: - print(json.dumps({ - "jsonrpc": "2.0", - "id": None, - "error": { - "code": -32700, - "message": "Parse error" - } - }), flush=True) - -if __name__ == "__main__": - main()