velxio/docs/MCP.md

6.8 KiB
Raw Blame History

Velxio MCP Server

Velxio exposes a Model Context Protocol (MCP) server that allows AI agents (e.g. Claude, Cursor) to:

  • Create and update circuits using a structured JSON format
  • Import and export circuits in the Wokwi diagram.json format
  • Generate Arduino code from circuit definitions
  • Compile projects and receive structured results (hex/binary, logs)

Tools

Tool Description
compile_project Compile Arduino sketch files → Intel HEX / binary
run_project Compile and mark artifact as simulation-ready
import_wokwi_json Parse a Wokwi diagram.json → Velxio circuit
export_wokwi_json Serialise a Velxio circuit → Wokwi diagram.json
create_circuit Create a new circuit definition
update_circuit Merge changes into an existing circuit
generate_code_files Generate starter .ino code from a circuit

Transport Options

1. stdio (Claude Desktop / CLI agents)

Run the MCP server as a child process:

cd backend
python mcp_server.py

Claude Desktop config (~/.claude/claude_desktop_config.json):

{
  "mcpServers": {
    "velxio": {
      "command": "python",
      "args": ["/absolute/path/to/velxio/backend/mcp_server.py"]
    }
  }
}

2. SSE / HTTP (web agents, Cursor IDE)

Run the MCP SSE server on a separate port (default: 8002):

cd backend
python mcp_sse_server.py --port 8002

MCP client config (SSE transport):

{
  "mcpServers": {
    "velxio": {
      "url": "http://localhost:8002/sse"
    }
  }
}

Note: The SSE server runs separately from the main FastAPI backend (port 8001) to avoid Starlette version conflicts. Both can run simultaneously.


Setup

  1. Install dependencies:

    cd backend
    pip install -r requirements.txt
    
  2. Ensure arduino-cli is installed (required for compile_project / run_project):

    arduino-cli version
    arduino-cli core update-index
    arduino-cli core install arduino:avr
    

Example Walkthroughs

Step 1 — Create a circuit:

{
  "tool": "create_circuit",
  "arguments": {
    "board_fqbn": "arduino:avr:uno",
    "components": [
      { "id": "led1", "type": "wokwi-led", "left": 150, "top": 100, "attrs": { "color": "red" } },
      { "id": "r1", "type": "wokwi-resistor", "left": 150, "top": 180, "attrs": { "value": "220" } }
    ],
    "connections": [
      { "from_part": "uno", "from_pin": "13", "to_part": "led1", "to_pin": "A", "color": "green" },
      { "from_part": "led1", "from_pin": "C", "to_part": "r1", "to_pin": "1", "color": "black" },
      { "from_part": "r1", "from_pin": "2", "to_part": "uno", "to_pin": "GND.1", "color": "black" }
    ]
  }
}

Step 2 — Generate code:

{
  "tool": "generate_code_files",
  "arguments": {
    "circuit": "<result from Step 1>",
    "sketch_name": "blink",
    "extra_instructions": "Blink the red LED every 500ms"
  }
}

Step 3 — Compile the generated code (edit the sketch content as needed):

{
  "tool": "compile_project",
  "arguments": {
    "files": [
      {
        "name": "blink.ino",
        "content": "void setup() { pinMode(13, OUTPUT); }\nvoid loop() { digitalWrite(13, HIGH); delay(500); digitalWrite(13, LOW); delay(500); }"
      }
    ],
    "board": "arduino:avr:uno"
  }
}

Response:

{
  "success": true,
  "hex_content": ":100000000C9434000C943E000C943E000C943E...",
  "binary_content": null,
  "binary_type": null,
  "stdout": "",
  "stderr": ""
}

Example 2 — Import a Wokwi Circuit

Import:

{
  "tool": "import_wokwi_json",
  "arguments": {
    "diagram_json": "{\"version\":1,\"author\":\"example\",\"editor\":\"wokwi\",\"parts\":[{\"type\":\"wokwi-arduino-uno\",\"id\":\"uno\",\"top\":0,\"left\":0,\"attrs\":{}},{\"type\":\"wokwi-led\",\"id\":\"led1\",\"top\":100,\"left\":200,\"attrs\":{\"color\":\"green\"}}],\"connections\":[[\"uno:13\",\"led1:A\",\"green\",[]]]}"
  }
}

Response:

{
  "board_fqbn": "arduino:avr:uno",
  "components": [
    { "id": "uno", "type": "wokwi-arduino-uno", "left": 0, "top": 0, "rotate": 0, "attrs": {} },
    { "id": "led1", "type": "wokwi-led", "left": 200, "top": 100, "rotate": 0, "attrs": { "color": "green" } }
  ],
  "connections": [
    { "from_part": "uno", "from_pin": "13", "to_part": "led1", "to_pin": "A", "color": "#00ff00" }
  ],
  "version": 1
}

Example 3 — Export to Wokwi Format

{
  "tool": "export_wokwi_json",
  "arguments": {
    "circuit": {
      "board_fqbn": "arduino:avr:uno",
      "components": [
        { "id": "led1", "type": "wokwi-led", "left": 200, "top": 100, "rotate": 0, "attrs": {} }
      ],
      "connections": [
        { "from_part": "uno", "from_pin": "13", "to_part": "led1", "to_pin": "A", "color": "green" }
      ],
      "version": 1
    },
    "author": "my-agent"
  }
}

Response (Wokwi diagram.json format):

{
  "version": 1,
  "author": "my-agent",
  "editor": "velxio",
  "parts": [
    { "type": "wokwi-arduino-uno", "id": "uno", "top": 0, "left": 0, "attrs": {} },
    { "type": "wokwi-led", "id": "led1", "top": 100, "left": 200, "rotate": 0, "attrs": {} }
  ],
  "connections": [
    ["uno:13", "led1:A", "green", []]
  ]
}

Circuit Data Format

Velxio circuits are plain JSON objects:

{
  "board_fqbn": "arduino:avr:uno",
  "version": 1,
  "components": [
    {
      "id": "led1",
      "type": "wokwi-led",
      "left": 200,
      "top": 100,
      "rotate": 0,
      "attrs": { "color": "red" }
    }
  ],
  "connections": [
    {
      "from_part": "uno",
      "from_pin": "13",
      "to_part": "led1",
      "to_pin": "A",
      "color": "green"
    }
  ]
}

Supported Board FQBNs

Board FQBN
Arduino Uno arduino:avr:uno
Arduino Mega arduino:avr:mega
Arduino Nano arduino:avr:nano
Raspberry Pi Pico rp2040:rp2040:rpipico
ESP32 DevKit esp32:esp32:esp32

Common Component Types (Wokwi element names)

  • wokwi-led — LED (attrs: color)
  • wokwi-resistor — Resistor (attrs: value in Ω)
  • wokwi-pushbutton — Push button
  • wokwi-buzzer — Passive buzzer
  • wokwi-servo — Servo motor
  • wokwi-lcd1602 — 16×2 LCD display
  • wokwi-neopixel — NeoPixel RGB LED

Sandboxing & Limits

  • Compilation runs in a temporary directory cleaned up after each call.
  • arduino-cli is invoked as a subprocess with no elevated privileges.
  • There is no explicit CPU/memory timeout in the default configuration. For production deployments, set COMPILATION_TIMEOUT_SECONDS in the environment and enforce it at the process level.

Running Tests

cd backend
pip install pytest pytest-asyncio
python -m pytest tests/test_mcp_tools.py -v