README.md

# Os Supervisor Mcp

TCP MCP server for supervising child BEAM OS instances. Each child runs a Mix project from a given path, spawned via `Port.open` and connected through Erlang distribution for remote evaluation.

## Installation

Add `os_sup_mcp` to your list of dependencies in `mix.exs`:

```elixir
def deps do
  [
    {:os_sup_mcp, "~> 0.1.0"}
  ]
end
```

## Architecture

```
OsSupMcp.Application
  └── Supervisor (rest_for_one)
        ├── OsSupMcp.Node.Manager        (GenServer, ETS registry)
        ├── OsSupMcp.Node.WorkerSupervisor (DynamicSupervisor)
        │     ├── Worker (child1) -- wraps Port
        │     ├── Worker (child2)
        │     └── ...
        └── OsSupMcp.MCP.Server          (TCP listener, port 9100)
```

Workers use `restart: :transient` — if a child BEAM crashes, the DynamicSupervisor automatically restarts it with the same config. Clean stops don't trigger restart.

## MCP Tools

| Tool | Params | Description |
|------|--------|-------------|
| `status` | _(none)_ | List all managed nodes: name, connected, OS PID, uptime |
| `start` | `name`, `path`, `args?` | Start a child node from a Mix project directory |
| `stop` | `name` | Stop a running child node |
| `restart` | `name` | Stop + start with same config |
| `eval` | `name`, `code` | Evaluate Elixir expression on a child node via `:erpc` |

## Usage

```bash
# Start the server
cd os_sup_mcp
mix deps.get
mix run --no-halt
```

The MCP server listens on TCP port 9100 (configurable in `config/config.exs`).

### Manual testing with socat

```bash
# Initialize
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' | socat - TCP:localhost:9100

# List tools
echo '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' | socat - TCP:localhost:9100

# Start a child node
echo '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"start","arguments":{"name":"myapp","path":"/path/to/mix/project"}}}' | socat - TCP:localhost:9100

# Eval on child
echo '{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"eval","arguments":{"name":"myapp","code":"1 + 1"}}}' | socat - TCP:localhost:9100

# Stop child
echo '{"jsonrpc":"2.0","id":5,"method":"tools/call","params":{"name":"stop","arguments":{"name":"myapp"}}}' | socat - TCP:localhost:9100
```

### Claude Code MCP integration

Add to your MCP config:

```json
{
  "mcpServers": {
    "os_sup_mcp": {
      "type": "stdio",
      "command": "socat",
      "args": ["STDIO", "TCP:localhost:9100"]
    }
  }
}
```

## Configuration

```elixir
# config/config.exs
config :os_sup_mcp,
  mcp_port: 9100
```

## Programmatic API

```elixir
OsSupMcp.start_node("myapp", "/path/to/project")
OsSupMcp.eval_on_node("myapp", "Enum.sum(1..10)")
OsSupMcp.list_nodes()
OsSupMcp.restart_node("myapp")
OsSupMcp.stop_node("myapp")
```

## License

MIT — see [LICENSE](LICENSE) for details.