Metadata-Version: 2.4
Name: paraview-mcp-python
Version: 0.1.5
Summary: MCP server for controlling ParaView via AI assistants
Project-URL: Homepage, https://github.com/djeada/paraview-mcp-server
Project-URL: Repository, https://github.com/djeada/paraview-mcp-server
Project-URL: Issues, https://github.com/djeada/paraview-mcp-server/issues
Author-email: Adam Djellouli <adam@djellouli.com>
License-Expression: MIT
License-File: LICENSE
Keywords: ai,mcp,model-context-protocol,paraview,scientific-computing,visualization
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Visualization
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.10
Requires-Dist: mcp>=1.0.0
Requires-Dist: pydantic>=2.0
Provides-Extra: dev
Requires-Dist: mypy>=1.15; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
Requires-Dist: pytest-cov>=6.0; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Requires-Dist: ruff>=0.11; extra == 'dev'
Description-Content-Type: text/markdown

# ParaView MCP Server

**Control ParaView with AI assistants through the Model Context Protocol.**

`paraview-mcp-python` provides an MCP server plus a ParaView-side bridge so
AI assistants such as Codex CLI and Claude Desktop can inspect a ParaView
session, open datasets, apply filters, color data, run ParaView Python, and
export screenshots.

The command installed for MCP clients is still:

```bash
paraview-mcp-server
```

---

## What Parts Are There?

There are four moving pieces in the GUI workflow:

| Part | Runs where | Purpose |
|---|---|---|
| **MCP client** | Codex CLI, Claude Desktop, or another MCP host | Starts the MCP server and calls tools. |
| **MCP server** | Normal Python environment | Speaks MCP over stdio and forwards tool calls to ParaView over TCP. |
| **ParaView bridge** | `pvpython` process | Receives TCP JSON commands and executes `paraview.simple` operations. |
| **ParaView runtime** | `pvserver` plus a ParaView GUI client | Owns the shared ParaView session that the GUI and bridge both use. |

ParaView is not Blender: Python plugins and VTK timer callbacks are pipeline
extension mechanisms, not a safe general-purpose remote-control hook for a live
GUI process. The supported GUI workflow here uses ParaView's client/server
model. `paraview-mcp-launch` starts a local `pvserver`, connects the ParaView
GUI as the first client, then connects a `pvpython` bridge client to the same
session.

```
┌──────────────────────────────┐      stdio      ┌────────────────────────┐
│ MCP Client                   │ ◄──────────────► │ MCP Server             │
│ Codex / Claude / other host  │                  │ paraview-mcp-server    │
└──────────────────────────────┘                  └──────────┬─────────────┘
                                                              │ JSON/TCP
                                                              │ 127.0.0.1:9876
                                                   ┌──────────▼─────────────┐
                                                   │ ParaView Bridge        │
                                                   │ pvpython client        │
                                                   └──────────┬─────────────┘
                                                              │ ParaView client/server
                                                   ┌──────────▼─────────────┐
                                                   │ pvserver + GUI client  │
                                                   │ shared ParaView state  │
                                                   └────────────────────────┘
```

Why a ParaView-side bridge? ParaView's useful automation API is
`paraview.simple`, and it must execute inside a ParaView Python runtime. The
MCP server itself is only a protocol adapter; it cannot modify a ParaView
session unless a ParaView-side bridge is running.

For live GUI modification, use:

```text
Codex/Claude -> MCP server -> pvpython bridge -> pvserver <- ParaView GUI
```

For headless automation, use:

```text
Codex/Claude -> MCP server -> bridge inside pvpython -> headless ParaView runtime
```

See [`docs/architecture.md`](docs/architecture.md) for a full diagram, protocol reference,
and tool namespace table.

---

## Install

### Option A: Install the MCP server from PyPI

Use this when you only need the MCP server executable in your normal Python
environment:

```bash
pip install paraview-mcp-python
```

This installs:

```bash
paraview-mcp-server
paraview-mcp-launch
```

### Option B: Clone this repository for the ParaView bridge

The bridge code must be available to ParaView's Python runtime. For live GUI
control and local development, clone the repository:

```bash
git clone https://github.com/djeada/paraview-mcp-server.git
cd paraview-mcp-server
python -m venv .venv
source .venv/bin/activate          # Windows: .venv\Scripts\activate
pip install -e .
```

This creates:

```bash
.venv/bin/paraview-mcp-server
.venv/bin/paraview-mcp-launch
```

---

## Start Everything

Start the ParaView side with one command:

```bash
cd /path/to/paraview-mcp-server
paraview-mcp-launch
```

For a local editable checkout:

```bash
.venv/bin/paraview-mcp-launch
```

Expected output:

```text
ParaView MCP bridge ready on 127.0.0.1:9876
Launching ParaView GUI connected to cs://127.0.0.1:11111
```

Keep that terminal running. Closing it stops the GUI, bridge, and local
`pvserver` session.

### Optional: Start a Headless `pvpython` Bridge

Use this only when you do not need to modify an already-open ParaView GUI:

```bash
cd /path/to/paraview-mcp-server
pvpython scripts/start_paraview_bridge.py
```

Expected output:

```text
ParaView bridge ready on 127.0.0.1:9876
```

Keep that terminal running. This controls the `pvpython` session, not a GUI
window opened separately.

### GUI/Qt Limitations

The default live bridge runs in a separate `pvpython` process connected to the
same `pvserver` session as the ParaView GUI. It is not running inside the GUI's
Qt event loop. In that mode, the bridge can modify the ParaView pipeline and
server-side views, but it cannot reliably inspect or manage GUI top-level
widgets because Qt bindings such as `PySide6`, `PyQt5`, or `qtpy` may not be
available in the bridge runtime.

Practical consequences:

- `GetActiveViewOrCreate("RenderView")`, `Show()`, or `Render()` can create a
  detached VTK render window instead of reusing the visible GUI layout.
- The bridge may see a `RenderView` even though the user is looking at a
  different GUI panel.
- If a detached VTK window is created, the bridge usually cannot close it via
  Qt APIs. Use the window manager or delete the server-side view/layout.

For GUI-window automation, start the experimental in-GUI bridge from ParaView's
Python Shell:

```bash
scripts/start_paraview_gui_bridge.py
```

For pipeline automation from the default bridge, prefer creating/updating
sources and filters first. Only call `Show()`/`Render()` after confirming a
usable render view exists, and prefer `Outline`, `Slice`, `Glyph`, and
`StreamTracer` filters over displaying large simulation volumes as opaque
surfaces.

### Verify the Bridge Directly

Before involving an MCP client, send one raw bridge command:

```bash
python scripts/paraview_bridge_request.py scene.get_info
```

Expected response shape:

```json
{
  "success": true,
  "result": {
    "source_count": 0,
    "active_view_type": "RenderView"
  }
}
```

If this fails, fix the bridge before configuring Codex or Claude.

### Register the MCP Server with Codex CLI

If you installed from PyPI:

```bash
codex mcp add paraview -- paraview-mcp-server
codex mcp list
```

If you are using the local repository:

```bash
codex mcp add paraview -- /absolute/path/to/paraview-mcp-server/.venv/bin/paraview-mcp-server
codex mcp list
```

Codex starts the MCP server automatically when needed. The ParaView bridge
must already be running separately.

### Register with Claude Desktop

**Claude Desktop** — add to `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "paraview": {
      "command": "/absolute/path/to/.venv/bin/paraview-mcp-server"
    }
  }
}
```

For a PyPI install, use the absolute path returned by:

```bash
which paraview-mcp-server
```

Restart Claude Desktop after editing the config.

### Verify Through Your MCP Client

With the bridge still running, ask your MCP client:

```text
List all sources in the current ParaView session.
```

The client should call `paraview_scene_list_sources` and return the current
ParaView pipeline sources from the server-backed GUI session started by
`paraview-mcp-launch`.

---

## What Can It Control?

There are two levels of control:

1. **Fixed MCP tools** for common workflows: scene inspection, loading data,
   filters, display/coloring, camera, screenshots, data export, and animation
   export.
2. **Python execution** through `paraview_python_exec`, which can run trusted
   local Python inside the ParaView bridge session. Use this for anything not
   covered by a fixed tool, including arbitrary `paraview.simple` scripts.

So the fixed tool list is intentionally finite, but the Python execution tool
is the general escape hatch for the broader ParaView API.

---

## Example prompts

Once both processes are running and your MCP client is configured:

- *"List all sources in the current ParaView session."*
- *"Open `/data/disk_out_ref.ex2`."*
- *"Create a slice through X = 0 of the disk dataset."*
- *"Color the dataset by Pressure."*
- *"Save a screenshot to `/tmp/view.png`."*
- *"Apply a contour filter on Pressure with isovalues 0.5 and 1.0."*
- *"Set the camera to position [10, 5, 5] looking at the origin."*
- *"Set the background to a gradient from white to dark blue."*
- *"Export an animation to `/tmp/anim.avi`."*

---

## Tool reference (31 tools)

### Scene / session
| Tool | Description |
|---|---|
| `paraview_scene_get_info` | Session info: source count, active view type |
| `paraview_scene_list_sources` | List all pipeline sources |
| `paraview_scene_list_views` | List open render views |
| `paraview_source_get_properties` | Properties of a named source |

### Data loading
| Tool | Description |
|---|---|
| `paraview_source_open_file` | Open a dataset (VTK, VTU, ExodusII, CSV, …) |
| `paraview_source_delete` | Remove a source from the pipeline |
| `paraview_source_rename` | Rename a source |

### Filters — basic
| Tool | Description |
|---|---|
| `paraview_filter_slice` | Slice filter with origin + normal |
| `paraview_filter_clip` | Clip filter with origin + normal |
| `paraview_filter_contour` | Contour / isosurface by scalar array and values |
| `paraview_filter_threshold` | Threshold filter by scalar range |

### Filters — advanced
| Tool | Description |
|---|---|
| `paraview_filter_calculator` | Calculator filter with expression |
| `paraview_filter_stream_tracer` | Stream Tracer for vector field streamlines |
| `paraview_filter_glyph` | Glyph filter for vector visualization |

### Display / coloring
| Tool | Description |
|---|---|
| `paraview_display_show` | Make a source visible |
| `paraview_display_hide` | Hide a source |
| `paraview_display_color_by` | Color by a data array |
| `paraview_display_set_representation` | Surface / Wireframe / Points / Volume |
| `paraview_display_set_opacity` | Set opacity (0.0 – 1.0) |
| `paraview_display_rescale_transfer_function` | Rescale color map to data range |

### Camera / view
| Tool | Description |
|---|---|
| `paraview_view_reset_camera` | Fit all visible sources in the view |
| `paraview_view_set_camera` | Set camera position, focal point, view-up |
| `paraview_view_set_background` | Set solid or gradient background color |

### Export
| Tool | Description |
|---|---|
| `paraview_export_screenshot` | Save a PNG or JPEG screenshot |
| `paraview_export_data` | Export source data to VTK/CSV/… |
| `paraview_export_animation` | Export animation to video/frames |

### Python execution
| Tool | Description |
|---|---|
| `paraview_python_exec` | Run Python in bridge or headless pvpython |
| `paraview_python_exec_async` | Start a long-running Python job (headless) |

### Job management
| Tool | Description |
|---|---|
| `paraview_job_status` | Get status of an async job |
| `paraview_job_cancel` | Cancel a running async job |
| `paraview_job_list` | List all known async jobs |

---

## Python execution

`paraview_python_exec` is the escape hatch for workflows that need more than the fixed
tool set. The script runs in the bridge process where `paraview.simple` is already imported.

**Parameters:**

| Parameter | Type | Description |
|---|---|---|
| `code` | `str` | Inline Python source (mutually exclusive with `script_path`) |
| `script_path` | `str` | Path to a `.py` file (mutually exclusive with `code`) |
| `args` | `dict` | Arguments exposed as `args` inside the script |
| `timeout_seconds` | `int` | Cooperative timeout (default: 30s) |
| `transport` | `str` | `"bridge"` (default) or `"headless"` (separate pvpython process) |

**Execution namespace:**

| Variable | Type | Description |
|---|---|---|
| `pvs` | module | `paraview.simple` |
| `args` | dict | Arguments from the `args` parameter |
| `__result__` | Any | Set this to return a JSON-serialisable value |

**Example:**

```python
# Open a file and create a slice
src = pvs.OpenDataFile(args["filepath"])
view = pvs.GetActiveViewOrCreate("RenderView")
pvs.Show(src, view)
filt = pvs.Slice(Input=src)
filt.SliceType.Origin = [0, 0, 0]
filt.SliceType.Normal = [1, 0, 0]
pvs.Show(filt, view)
pvs.ResetCamera(view)
__result__ = {"done": True}
```

See [`docs/python-execute-design.md`](docs/python-execute-design.md) for the full design,
schema reference, and more examples.

---

## Async job execution

For long-running pipelines, use `paraview_python_exec_async`:

1. Start a job → returns `job_id` immediately
2. Poll with `paraview_job_status` → check `status` field
3. Cancel with `paraview_job_cancel` if needed

Async jobs run in a separate headless `pvpython` process via `HeadlessPvpythonExecutor`.

---

## Troubleshooting

### ParaView is connected, but MCP cannot close or inspect a window

If the Pipeline Browser shows `cs://127.0.0.1:11111`, the ParaView GUI is
connected to `pvserver`. That does not mean the MCP TCP bridge is running or
that MCP has access to GUI Qt widgets. Verify the bridge separately:

```bash
python scripts/paraview_bridge_request.py scene.get_info
```

If a second VTK render window appears and refuses to close, it was likely
created by the `pvpython` bridge through `GetActiveViewOrCreate("RenderView")`
or a display/render command. The bridge normally cannot close this via Qt
because the default runtime is not the GUI process and often has no Qt Python
bindings. Delete the server-side view/layout, use the window manager, or restart
the ParaView-side bridge. For workflows that must control GUI windows directly,
use the in-GUI bridge from ParaView's Python Shell.

---

## Python execution trust model

- **Trusted local execution** — `paraview_python_exec` can run arbitrary Python available to the active
  ParaView Python process, including imports and full `paraview.simple` workflows.
- **Output bounding** — stdout/stderr capped at **50 KB**.
- **Cooperative timeout** — default 30 seconds per script execution.
- **Script path validation** — optionally restrict execution to approved root directories.
- The bridge runs inside ParaView's Python runtime with the same trust level as that local session.
- This is a local desktop automation tool — not a public API sandbox.

---

## Development

### Install dev dependencies

```bash
pip install -e ".[dev]"
```

### Run tests

```bash
pytest tests/
```

Tests do **not** require ParaView to be installed. The command handler tests patch
`_import_pv` with a `MagicMock` that mimics the `paraview.simple` API.

### Send a raw bridge command (for debugging)

```bash
python scripts/paraview_bridge_request.py scene.get_info
python scripts/paraview_bridge_request.py source.open_file --params '{"filepath":"/data/disk.vtu"}'
python scripts/paraview_bridge_request.py export.screenshot --params '{"filepath":"/tmp/shot.png"}'
```

---

## Project structure

```
paraview-mcp-server/
├── pyproject.toml
├── src/
│   └── paraview_mcp_server/
│       ├── __init__.py          # Re-exports main()
│       ├── server.py            # FastMCP stdio server (31 tools)
│       ├── launcher.py          # Starts pvserver, GUI, and bridge together
│       └── headless.py          # Headless pvpython executor + job manager
├── bridge/
│   ├── __init__.py
│   ├── server.py                # TCP socket bridge server
│   ├── gui_bridge.py            # Experimental in-GUI bridge helpers
│   ├── command_handler.py       # Command registry + paraview.simple handlers (27 commands)
│   └── execution.py             # trusted local python.execute helper
├── scripts/
│   ├── start_paraview_bridge.py
│   ├── start_paraview_gui_bridge.py
│   ├── paraview_bridge_request.py
│   └── library/                 # Reusable pvpython snippets
│       ├── open_dataset.py
│       ├── create_slice.py
│       ├── create_contour.py
│       ├── color_by.py
│       ├── reset_camera.py
│       └── save_screenshot.py
├── docs/
│   ├── architecture.md
│   └── python-execute-design.md
└── tests/
    ├── test_server.py           # 31 tools, connection, headless, async jobs
    ├── test_protocol.py         # Wire encoding, fake bridge integration
    └── test_command_handler.py  # All 27 handlers + execution controls
```

---

## License

MIT — see [LICENSE](LICENSE).
