Skip to main content
Glama
mikesplore
by mikesplore
README.md
# Vela MCP Server

MCP (Model Context Protocol) server that exposes [Vela RemotePC](https://github.com/mikesplore/vela) endpoints as tools, so AI clients (Claude Desktop, Cline, Cursor, Gemini, etc.) can control remote systems.

## Architecture

The server supports **two transport modes** that share the same 150+ tool definitions (`tools.py`):

### 1. STDIO (single-tenant, per-process)

Each process is bound to **one agent** via environment variables. Run multiple agents by defining multiple entries in `mcp_settings.json`, each with its own env block.

**MCP client config** (`cline_mcp_settings.json` or `claude_desktop_config.json`):
```json
{
  "mcpServers": {
    "vela-home": {
      "command": "/path/to/.venv/bin/mcp",
      "args": ["run", "vela_mcp/tools.py:mcp"],
      "cwd": "/path/to/vela-mcp",
      "env": {
        "VELA_API_URL": "https://vela.mikesplore.tech",
        "VELA_TOKEN": "<relay_secret>",
        "AGENT_ID": "agt_123"
      }
    },
    "vela-work": {
      "command": "/path/to/.venv/bin/mcp",
      "args": ["run", "vela_mcp/tools.py:mcp"],
      "cwd": "/path/to/vela-mcp",
      "env": {
        "VELA_API_URL": "https://vela.mikesplore.tech",
        "VELA_TOKEN": "<relay_secret>",
        "AGENT_ID": "agt_456"
      }
    }
  }
}
```

### 2. HTTP (multi-tenant, shared server)

A single server instance handles **any number of agents**. Create a scoped MCP connection with `vela --mcp`; it returns an opaque URL and a dedicated bearer credential:
```
MCP URL:        https://mcp.mikesplore.tech/mcp/c_8f4a...
Authentication: Bearer <mcp-credential>
```

The MCP credential is stored as a hash and can expire or be revoked independently of the Vela relay secret. The gateway resolves the connection to its agent and uses the relay secret internally. Neither `agent_id` nor `relay_secret` is accepted in the MCP URL.

Claude connectors select OAuth during setup. The gateway provides OAuth discovery, dynamic client registration, PKCE authorization, short-lived bearer access tokens, and rotating refresh tokens. During authorization, enter the MCP credential printed once by `vela --mcp`; supported clients can then renew access without asking for that credential again.

### MCP tool safety

The gateway applies the same risk categories used by Vela’s assistant. Read-only tools execute immediately. Medium- and high-risk tools return a short-lived, single-use approval link and `request_id` instead of executing. Open the returned `approval_url`, approve once, then let the MCP client continue the exact pending request. The server claims and completes that request atomically, so duplicate continuations cannot execute the action twice. High-risk approvals also require the Vela `ASSISTANT_ACTION_PIN` when one was configured during connection creation.

Each MCP connection has a user-selected tool policy: `strict` (default) requires approval for medium/high-risk tools, `read_only` exposes only read-only tools, and `open` executes all exposed tools without MCP approval. Set `MCP_TOOL_POLICY` in the agent `.env`, or override one connection with `vela --mcp --mcp-policy read_only`.

Stdio mode also supports `strict` approvals. The first gated call starts a
loopback-only approval page on `127.0.0.1`, returns a one-time local link, and
waits for the client to continue the request. The server never opens the
browser automatically and the page is not reachable from the network. Set
`ASSISTANT_ACTION_PIN` in the MCP environment if high-risk local approvals
should require a PIN.

### How multi-tenancy works

- **vela-mcp** stores scoped MCP connection records. It extracts only an opaque connection ID and a dedicated MCP credential from each request, then resolves the agent server-side.
- **velavps** (the relay server) enforces isolation: `_verify_agent_access(agent_id, secret)` rejects requests where the secret doesn't match the agent's registered secret, or where the agent belongs to another user.
- **150+ tools** are untouched — they call `vela_client.get/post/…` which resolves to either the per-request or per-process client automatically.

## Quick Start

```bash
python -m vela_mcp.server
```

Server starts on `http://0.0.0.0:8002` with the MCP endpoint at `/mcp/{connection_id}`.

## Setup

1. Copy `.env.example` to `.env`
2. Set `VELA_API_URL` — your Vela relay VPS host (e.g. `https://vela.mikesplore.tech`)
3. For **stdio mode**: also set `VELA_TOKEN` (relay secret) and `AGENT_ID`
4. For **HTTP mode**: you only need `VELA_API_URL` and `PORT`; credentials come from the client

## Run Script

```bash
./run.sh            # start the MCP server (HTTP/streamable transport)
./run.sh dev        # start the MCP Inspector (dev/testing UI)
```

## Project Layout

```
vela_mcp/
  __init__.py
  config.py      # Settings (VELA_API_URL, VELA_TOKEN, AGENT_ID, PORT)
  client.py      # RelayConnection, VelaClient, per-request contextvar proxy
  server.py      # FastAPI app with scoped middleware at /mcp/{connection_id}
  tools.py       # 150+ MCP tool definitions (unchanged)
```

## Development

```bash
.venv/bin/mcp dev vela_mcp/tools.py:mcp
```

## Requirements

- Python >= 3.10
- `mcp >= 1.0.0`
- `fastapi >= 0.110.0`
- `httpx >= 0.27.0`
- `pydantic-settings >= 2.0.0`
- `uvicorn >= 0.30.0`