mcpXL30 0.1.0
FastMCP server exposing Philips XL30 ESEM controls via pyxl30 (unofficial)
License: BSD
Requires Python: >=3.11

# mcpXL30

__WORK IN PROGRESS__

`mcpXL30` is an MCP (Model Context Protocol) server that exposes a curated
subset of the [pyxl30](https://github.com/tspspi/pyxl30) control library to LLM
agents (i.e. AI contorl of the XL30 ESEM).

Note that this project was partially generated with LLM support.

## Features

- Async MCP tools for common Philips XL30 operations (identify, read/set high
  tension, change scan modes, trigger pump/vent cycles, capture TIFF images).
- Live MCP resources that describe the connected instrument and active config.
- Safety envelope that limits accelerating voltage and sensitive operations.
- Fine grained settings that control allowed operations for the LLM agent.
- Optional remote HTTP/UDS transport with the authentication workflow explained
  in my [blog post](https://www.tspi.at/2026/01/10/chatgptremotemcp.html).

## Installation

```bash
pip install .
# or
pip install ".[remote]"  # adds FastAPI/uvicorn/argon2 for remote mode
```

## Configuration

The server loads JSON configuration from `~/.config/mcpxl30/config.json` by
default (override via `--config`). A minimal example:

```json
{
  "instrument": {
    "port": "/dev/ttyUSB0",
    "log_level": "INFO",
    "retry_count": 3,
    "reconnect_count": 3
  },
  "image_capture": {
    "remote_directory": "C:\\\\TEMP",
    "filename_prefix": "MCPIMG_"
  },
  "safety": {
    "max_high_tension_kv": 15.0,
    "allow_venting": false,
    "allow_pumping": true,
    "allow_scan_mode_changes": true,
    "allow_stage_motion": false,
    "allow_detector_switching": true,
    "allow_beam_shift": true,
    "allow_scan_rotation_changes": true,
    "allow_image_filter_changes": true,
    "allow_specimen_current_mode_changes": false,
    "allow_beam_blank_control": true,
    "allow_oplock_control": false
  },
  "logging": {
    "level": "INFO",
    "logfile": null
  },
  "remote_server": {
    "uds": "/var/run/mcpxl30.sock",
    "api_key_kdf": {
      "algorithm": "argon2id",
      "salt": "<base64>",
      "time_cost": 3,
      "memory_cost": 65536,
      "parallelism": 1,
      "hash_len": 32,
      "hash": "<base64>"
    }
  }
}
```

Use `mcpxl30-genkey --config /path/to/config.json` (or `mcpxl30 --genkey`) to
generate a new API key and populate the Argon2 hash inside the `remote_server`
block. The plain token prints once to stdout.

The `safety` block gates riskier capabilities. Keep `allow_stage_motion` and
`allow_oplock_control` disabled unless you trust the calling agent. Imaging and
detector-related fields default to safe-but-capable settings, but you can toggle
them per deployment.

## Running the server

### stdio transport (default)

```bash
mcpxl30 --config ~/.config/mcpxl30/config.json
```

The process utilizes the `stdio` transport.

### Remote FastAPI/uvicorn transport

```bash
pip install "mcpXL30[remote]"
mcpxl30 --transport remotehttp --config ~/.config/mcpxl30/config.json
```

- The FastAPI app exposes `/mcp` (MCP streaming API) and `/status` (unauthenticated health check).
- Authentication expects the API key in `Authorization: Bearer`, `X-API-Key`,
  or the `?api_key=` query parameter.
- Binding uses a [Unix domain socket](https://www.tspi.at/2026/01/04/UDS.html) (`remote_server.uds`)
  unless you specify a TCP `port`, in which case `host` (default `0.0.0.0`) applies.

## MCP functionality

### Tools

**Instrument Basics**

| Tool | Purpose |
| --- | --- |
| `instrument_identify` | Return the microscopes type/serial, scan mode, high tension. |
| `read_high_tension` / `set_high_tension` | Inspect or change accelerating voltage (safety-capped). |
| `get_scan_mode` / `set_scan_mode` | Read or change scan mode (`allow_scan_mode_changes`). |
| `capture_image` / `trigger_photo_capture` | Store images (TIFF or console photo). |
| `control_vent`, `pump_chamber` | Chamber vent/pump control (safety gated). |

**Beam & Detector Controls**

| Tool | Purpose |
| --- | --- |
| `get_spot_size` / `set_spot_size` | Read or set probe current (1–10). |
| `get_magnification` / `set_magnification` | Read or set magnification (20–400 000). |
| `get_stigmator` / `set_stigmator` | Inspect/update stigmator X/Y. |
| `get_detector` / `set_detector` | Inspect or switch active detector (`allow_detector_switching`). |
| `read_high_tension` | Included above but relevant for beam tuning. |

**Scan Timing & Geometry**

| Tool | Purpose |
| --- | --- |
| `get_line_time` / `set_line_time` | Read or set line time (ms or `TV`). |
| `get_lines_per_frame` / `set_lines_per_frame` | Inspect or adjust lines per frame. |
| `get_scan_rotation` / `set_scan_rotation` | Read or set scan rotation (`allow_scan_rotation_changes`). |
| `get_area_dot_shift` / `set_area_dot_shift` | Manage area/dot shift percentages (`allow_beam_shift`). |
| `get_selected_area_size` / `set_selected_area_size` | Control selected area dimensions. |

**Imaging Utilities**

| Tool | Purpose |
| --- | --- |
| `get_contrast` / `set_contrast` | Read or set contrast (0–100). |
| `get_brightness` / `set_brightness` | Read or set brightness (0–100). |
| `auto_contrast_brightness`, `auto_focus` | Run built-in adjustment routines. |
| `get_databar_text` / `set_databar_text` | Inspect or update the image databar text. |

**Stage & Alignment**

| Tool | Purpose |
| --- | --- |
| `stage_home` | Home the stage (`allow_stage_motion`). |
| `get_stage_position` / `set_stage_position` | Read or move X/Y/Z/tilt/rotation (`allow_stage_motion`). |
| `get_beam_shift` / `set_beam_shift` | Inspect or adjust beam shift (`allow_beam_shift`). |

**Image Filtering & Specimen Current**

| Tool | Purpose |
| --- | --- |
| `get_image_filter_mode` / `set_image_filter_mode` | Manage FastMCP image filter + frame count (`allow_image_filter_changes`). |
| `get_specimen_current_detector_mode` / `set_specimen_current_detector_mode` | Inspect or change detector mode (`allow_specimen_current_mode_changes`). |
| `get_specimen_current` | Read specimen current (requires measure mode). |

**Beam Safety & Locks**

| Tool | Purpose |
| --- | --- |
| `is_beam_blanked`, `blank_beam`, `unblank_beam` | Inspect or control beam blank state (`allow_beam_blank_control`). |
| `get_oplock_state`, `set_oplock_state` | Inspect or control the operator lock (`allow_oplock_control`). |

Every setter uses blocking pyxl30 calls inside `asyncio.to_thread`, preserving the FastMCP event loop responsiveness. Review the safety settings to enable only the tools you trust agents with.

All setters perform blocking pyxl30 calls inside `asyncio.to_thread` so the MCP
event loop stays responsive.

### Resources

- `mcpxl30://instrument/capabilities` – supported scan modes, image filter
  names, and the configured safety envelope.
- `mcpxl30://instrument/config` – sanitized live configuration (excludes API
  secrets).

## Examples

An `examples/example_config.json` file is included to bootstrap deployments.
The repository mirrors `mcpMQTT`'s project layout so existing FastMCP
infrastructure (supervisors, packaging, docs) can be reused with minimal
changes.