# MCPBundles Desktop Proxy - Message Protocol

## Overview

The proxy is a **passive, message-driven agent** that responds to commands from the backend through a WebSocket tunnel. All intelligence lives in the web app.

## Message Types

### 1. Service Discovery

**Web → Proxy:**
```json
{
  "type": "service_discovery",
  "ports": [5432, 5433, 6379, 3306, 27017, 9200, 9223]
}
```

**Proxy → Web:**
```json
{
  "type": "discovery_result",
  "ports": {
    "5432": {
      "available": true,
      "type": "postgresql"
    },
    "5433": {
      "available": true,
      "type": "postgresql"
    },
    "6379": {
      "available": false,
      "type": null
    }
  }
}
```

**Action:** Proxy scans specified ports on localhost and reports which are listening.

---

### 2. Service Verification

**Web → Proxy:**
```json
{
  "type": "service_verify",
  "target": "localhost:5432"
}
```

**Proxy → Web:**
```json
{
  "type": "verify_result",
  "verified": true,
  "target": "localhost:5432",
  "type": "postgresql"
}
```

Or if failed:
```json
{
  "type": "verify_result",
  "verified": false,
  "target": "localhost:5432",
  "error": "Connection refused"
}
```

**Action:** Proxy attempts actual connection to verify service is reachable.

---

### 3. Start Service

**Web → Proxy:**
```json
{
  "type": "service_start",
  "service": "browser",
  "config": {
    "headless": false,
    "port": 9223
  }
}
```

**Proxy → Web:**
```json
{
  "type": "service_started",
  "started": true,
  "service": "browser",
  "port": 9223,
  "mode": "visible",
  "ws_endpoint": "ws://127.0.0.1:9223/abc123def456"
}
```

Or if failed:
```json
{
  "type": "service_started",
  "started": false,
  "error": "Playwright not installed. Install with: pip install mcpbundles[browser]"
}
```

**Important:** The `ws_endpoint` field contains the full WebSocket URL for connecting to the browser via Playwright's `connect()` method. The backend must:
1. Create a TCP tunnel to `localhost:9223`
2. Rewrite the WebSocket URL to use the tunnel endpoint (preserving the path)
3. Use `playwright.chromium.connect(ws_endpoint=rewritten_url)` instead of `connect_over_cdp()`

**Action:** Proxy starts the requested service (currently only browser supported) in server mode.

---

### 4. Stop Service

**Web → Proxy:**
```json
{
  "type": "service_stop",
  "service": "browser"
}
```

**Proxy → Web:**
```json
{
  "type": "service_stopped",
  "stopped": true,
  "service": "browser"
}
```

**Action:** Proxy stops the running service.

---

### 5. Update Service Configuration

**Web → Proxy:**
```json
{
  "type": "service_update",
  "service": "browser",
  "config": {
    "headless": true
  }
}
```

**Proxy → Web:**
```json
{
  "type": "service_updated",
  "updated": true,
  "service": "browser",
  "mode": "hidden"
}
```

**Action:** Proxy updates service configuration (may restart service if needed).

---

### 6. HTTP Request Forwarding (Existing)

**Web → Proxy:**
```json
{
  "type": "http_request",
  "request_id": "req_abc123",
  "method": "POST",
  "path": "/api/users",
  "target": "localhost:5432",
  "headers": {"Content-Type": "application/json"},
  "body": "{\"name\": \"John\"}"
}
```

**Proxy → Web:**
```json
{
  "type": "http_response",
  "request_id": "req_abc123",
  "status": 200,
  "headers": {"Content-Type": "application/json"},
  "body": "{\"id\": 123, \"name\": \"John\"}"
}
```

**Action:** Proxy forwards HTTP request to localhost service and returns response.

---

### 7. Status Request

**Web → Proxy:**
```json
{
  "type": "status_request"
}
```

**Proxy → Web:**
```json
{
  "type": "status_update",
  "services": {
    "browser": {
      "enabled": true,
      "headless": false,
      "port": 9223,
      "mode": "visible"
    }
  }
}
```

**Action:** Proxy reports current status of all services.

---

### 8. TCP Tunnel Open

**Web → Proxy:**
```json
{
  "type": "tunnel_open",
  "tunnel_id": "tun_abc123",
  "target": "localhost:5432"
}
```

**Proxy → Web (Success):**
```json
{
  "type": "tunnel_ready",
  "tunnel_id": "tun_abc123",
  "target": "localhost:5432"
}
```

**Proxy → Web (Error):**
```json
{
  "type": "tunnel_error",
  "tunnel_id": "tun_abc123",
  "error": "Browser service not running on localhost:9223. Start it first with service_start message.",
  "error_code": "CONNECTION_REFUSED",
  "target": "localhost:9223",
  "suggestion": "Send: {\"type\":\"service_start\",\"service\":\"browser\",\"config\":{\"headless\":false,\"port\":9223}}"
}
```

**Error Codes:**
- `SERVICE_NOT_RUNNING` - Service needs to be started first (proactive check)
- `CONNECTION_REFUSED` - Connection refused (port not listening)
- `CONNECTION_TIMEOUT` - Connection timeout (service not responding)
- `INVALID_TARGET` - Invalid target format
- `INVALID_HOST` - Non-localhost target (security)
- `INVALID_PORT` - Invalid port number
- `TUNNEL_OPEN_FAILED` - Other tunnel open errors

**Action:** Proxy opens a TCP connection to the target and prepares to forward raw bytes bidirectionally.

---

### 9. TCP Tunnel Data

**Web → Proxy:**
```json
{
  "type": "tunnel_data",
  "tunnel_id": "tun_abc123",
  "data": "SGVsbG8sIFdvcmxkIQ=="
}
```

**Proxy → Web:**
```json
{
  "type": "tunnel_data",
  "tunnel_id": "tun_abc123",
  "data": "UE9ORw=="
}
```

**Action:** Forwards base64-encoded raw bytes through the tunnel. Data from Web is written to TCP socket, data from TCP socket is sent to Web.

---

### 10. TCP Tunnel Close

**Web → Proxy:**
```json
{
  "type": "tunnel_close",
  "tunnel_id": "tun_abc123"
}
```

**Proxy → Web:**
```json
{
  "type": "tunnel_closed",
  "tunnel_id": "tun_abc123"
}
```

**Action:** Closes the TCP connection and cleans up tunnel resources.

---

## Service Types Supported

### Browser Service
- **Port:** 9223 (default)
- **States:** Started/Stopped
- **Modes:** Visible (headed) / Hidden (headless)
- **Connection:** Playwright browser server mode (WebSocket)
- **Requires:** `pip install mcpbundles[browser]` + `playwright install chromium`
- **Backend connects via:** `playwright.chromium.connect(ws_endpoint=...)` (NOT `connect_over_cdp`)

### SQLite Service
- **Port:** 9999 (default)
- **States:** Started/Stopped
- **Connection:** HTTP API for SQLite database file access
- **Endpoints:** `POST /query` - Execute SQL queries on local database files
- **Requires:** `pip install mcpbundles`

### Claude Code Service
- **Port:** 9300 (default)
- **States:** Started/Stopped
- **Connection:** HTTP API for Claude Code CLI execution
- **Endpoints:** 
  - `POST /run` - Execute Claude Code CLI with prompt in workFolder
  - `POST /validate` - Validate Claude CLI works and workFolder is accessible
- **Requires:** Claude CLI installed (`npm install -g @anthropic-ai/claude-cli` or `~/.claude/local/claude`)
- **Config:** `{"port": 9300}` (optional port override)

### Database Services (Discovery Only)
Proxy can discover and verify, but doesn't manage lifecycle:
- PostgreSQL (5432, 5433)
- MySQL (3306)
- Redis (6379)
- MongoDB (27017)
- Elasticsearch (9200)

### TCP Tunneling (Protocol Agnostic)
Proxy can forward raw TCP connections for ANY service:
- **Fully transparent** - Protocol-agnostic byte forwarding
- **Works with any TCP service** - PostgreSQL, Redis, MongoDB, MySQL, etc.
- **Bidirectional** - Data flows both directions through WebSocket
- **No drivers needed** - Proxy just forwards bytes
- **Security** - Only localhost targets allowed

**Use case:** Cloud MCP servers can connect to local databases as if they were remote services. For example, asyncpg running in the cloud can connect through the tunnel and communicate directly with local PostgreSQL using native wire protocol.

---

## Port Detection

Proxy automatically detects service type by port:

| Port | Service Type |
|------|-------------|
| 5432, 5433 | postgresql |
| 3306 | mysql |
| 6379 | redis |
| 27017 | mongodb |
| 9200 | elasticsearch |
| 9223 | browser |
| 9300 | claude_code |
| 9999 | sqlite |
| other | unknown |

---

## Error Handling

All operations return error information if they fail:

```json
{
  "type": "..._result",
  "error": "Human-readable error message"
}
```

Common errors:
- "Connection timeout"
- "Connection refused"
- "Service manager not available"
- "Playwright not installed..."
- "Unknown service: {name}"

---

## User Experience Flow

1. **User goes to MCPBundles.com → Local Services**
2. **Web sends:** `service_discovery` with common ports
3. **Proxy responds:** Which ports are listening
4. **Web UI shows:** "PostgreSQL found on localhost:5432"
5. **User clicks [+ Add Service]**
6. **Web sends:** `service_verify` for localhost:5432
7. **Proxy responds:** Verified ✓
8. **Web automatically creates Provider** with base_url
9. **Done - User never touched terminal**

---

## Browser Service Flow

1. **User clicks [Start Browser] with "Visible" mode**
2. **Web sends:** `service_start` with `headless: false`
3. **Proxy starts browser** in server mode - Window pops up on user's screen
4. **Proxy responds:** Started ✓, mode: visible, `ws_endpoint: ws://127.0.0.1:9223/...`
5. **Web creates TCP tunnel** to `localhost:9223`
6. **Web rewrites WebSocket URL** to use tunnel endpoint (preserving path)
7. **Web connects:** `playwright.chromium.connect(ws_endpoint=tunneled_url)`
8. **Browser automation works** through WebSocket tunnel
9. **User toggles to "Hidden"**
10. **Web sends:** `service_update` with `headless: true`
11. **Proxy restarts browser** in headless mode
12. **Browser window disappears**

---

## Implementation Notes

### Proxy Side (Local)
- Completely passive - only responds to messages
- No configuration files needed
- No UI - pure message handler
- All logic in service_manager.py and tunnel.py
- **Never needs updates for new features** - just responds to new messages

### Backend Side (Cloud)
- Active intelligence - decides what to discover/verify/start
- Maintains state of user's services
- Auto-creates Providers when services verified
- Shows UI for service management
- **All new features added here**

---

## Adding New Services

To add support for a new service (e.g., Redis management):

**Proxy:** No changes needed - already handles start/stop/update for any service name

**Backend:**
1. Add UI for Redis service
2. Send appropriate messages:
   - `service_start` with `service: "redis"`
   - `service_stop` with `service: "redis"`

**That's it.** The protocol is generic - proxy just executes commands.

---

## Security

- **Discovery:** Only scans localhost, never remote hosts
- **Verification:** Only connects to localhost
- **HTTP Forwarding:** Validates target starts with "localhost:" or "127.0.0.1:"
- **Services:** Browser is sandboxed, only accesses what user's browser can access

---

## Future Extensions

Easy to add without proxy changes:
- `service_restart` - Restart a service
- `service_logs` - Get logs from a service
- `service_health` - Health check endpoint
- `batch_discover` - Discover multiple ranges at once

Just send new message types - proxy handles them.

