paraview-mcp-python 0.1.1__tar.gz → 0.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. {paraview_mcp_python-0.1.1 → paraview_mcp_python-0.1.2}/PKG-INFO +208 -33
  2. {paraview_mcp_python-0.1.1 → paraview_mcp_python-0.1.2}/README.md +207 -32
  3. paraview_mcp_python-0.1.2/bridge/gui_bridge.py +55 -0
  4. {paraview_mcp_python-0.1.1 → paraview_mcp_python-0.1.2}/bridge/models.py +2 -2
  5. {paraview_mcp_python-0.1.1 → paraview_mcp_python-0.1.2}/bridge/server.py +13 -0
  6. {paraview_mcp_python-0.1.1 → paraview_mcp_python-0.1.2}/docs/architecture.md +27 -7
  7. {paraview_mcp_python-0.1.1 → paraview_mcp_python-0.1.2}/docs/python-execute-design.md +8 -4
  8. {paraview_mcp_python-0.1.1 → paraview_mcp_python-0.1.2}/pyproject.toml +2 -2
  9. paraview_mcp_python-0.1.2/scripts/start_paraview_gui_bridge.py +42 -0
  10. paraview_mcp_python-0.1.2/tests/test_gui_bridge.py +55 -0
  11. {paraview_mcp_python-0.1.1 → paraview_mcp_python-0.1.2}/.gitignore +0 -0
  12. {paraview_mcp_python-0.1.1 → paraview_mcp_python-0.1.2}/LICENSE +0 -0
  13. {paraview_mcp_python-0.1.1 → paraview_mcp_python-0.1.2}/bridge/__init__.py +0 -0
  14. {paraview_mcp_python-0.1.1 → paraview_mcp_python-0.1.2}/bridge/command_handler.py +0 -0
  15. {paraview_mcp_python-0.1.1 → paraview_mcp_python-0.1.2}/bridge/execution.py +0 -0
  16. {paraview_mcp_python-0.1.1 → paraview_mcp_python-0.1.2}/scripts/library/color_by.py +0 -0
  17. {paraview_mcp_python-0.1.1 → paraview_mcp_python-0.1.2}/scripts/library/create_contour.py +0 -0
  18. {paraview_mcp_python-0.1.1 → paraview_mcp_python-0.1.2}/scripts/library/create_slice.py +0 -0
  19. {paraview_mcp_python-0.1.1 → paraview_mcp_python-0.1.2}/scripts/library/open_dataset.py +0 -0
  20. {paraview_mcp_python-0.1.1 → paraview_mcp_python-0.1.2}/scripts/library/reset_camera.py +0 -0
  21. {paraview_mcp_python-0.1.1 → paraview_mcp_python-0.1.2}/scripts/library/save_screenshot.py +0 -0
  22. {paraview_mcp_python-0.1.1 → paraview_mcp_python-0.1.2}/scripts/paraview_bridge_request.py +0 -0
  23. {paraview_mcp_python-0.1.1 → paraview_mcp_python-0.1.2}/scripts/start_paraview_bridge.py +0 -0
  24. {paraview_mcp_python-0.1.1 → paraview_mcp_python-0.1.2}/src/paraview_mcp_server/__init__.py +0 -0
  25. {paraview_mcp_python-0.1.1 → paraview_mcp_python-0.1.2}/src/paraview_mcp_server/headless.py +0 -0
  26. {paraview_mcp_python-0.1.1 → paraview_mcp_python-0.1.2}/src/paraview_mcp_server/server.py +0 -0
  27. {paraview_mcp_python-0.1.1 → paraview_mcp_python-0.1.2}/tests/__init__.py +0 -0
  28. {paraview_mcp_python-0.1.1 → paraview_mcp_python-0.1.2}/tests/test_bridge_server.py +0 -0
  29. {paraview_mcp_python-0.1.1 → paraview_mcp_python-0.1.2}/tests/test_command_handler.py +0 -0
  30. {paraview_mcp_python-0.1.1 → paraview_mcp_python-0.1.2}/tests/test_protocol.py +0 -0
  31. {paraview_mcp_python-0.1.1 → paraview_mcp_python-0.1.2}/tests/test_server.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: paraview-mcp-python
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: MCP server for controlling ParaView via AI assistants
5
5
  Project-URL: Homepage, https://github.com/djeada/paraview-mcp-server
6
6
  Project-URL: Repository, https://github.com/djeada/paraview-mcp-server
@@ -31,50 +31,99 @@ Requires-Dist: pytest>=7.0; extra == 'dev'
31
31
  Requires-Dist: ruff>=0.11; extra == 'dev'
32
32
  Description-Content-Type: text/markdown
33
33
 
34
- # paraview-mcp-server
34
+ # ParaView MCP Server
35
35
 
36
36
  **Control ParaView with AI assistants through the Model Context Protocol.**
37
37
 
38
- `paraview-mcp-server` is a two-process bridge that lets AI assistants such as Claude Desktop
39
- and Codex CLI open datasets, apply filters, color data, and export screenshots in ParaView
40
- using natural language.
38
+ `paraview-mcp-python` provides an MCP server plus a ParaView-side bridge so
39
+ AI assistants such as Codex CLI and Claude Desktop can inspect a ParaView
40
+ session, open datasets, apply filters, color data, run ParaView Python, and
41
+ export screenshots.
42
+
43
+ The command installed for MCP clients is still:
44
+
45
+ ```bash
46
+ paraview-mcp-server
47
+ ```
41
48
 
42
49
  ---
43
50
 
44
- ## How it works
51
+ ## What Parts Are There?
52
+
53
+ There are three moving pieces:
54
+
55
+ | Part | Runs where | Purpose |
56
+ |---|---|---|
57
+ | **MCP client** | Codex CLI, Claude Desktop, or another MCP host | Starts the MCP server and calls tools. |
58
+ | **MCP server** | Normal Python environment | Speaks MCP over stdio and forwards tool calls to ParaView over TCP. |
59
+ | **ParaView GUI bridge** | Already-open ParaView GUI process | Receives TCP JSON commands and executes `paraview.simple` operations in that live GUI session. |
60
+
61
+ The ParaView bridge is the ParaView-side component. It plays the same role as
62
+ the Blender add-on in `blender-mcp-server`: it must run inside the application
63
+ you want to control. For live GUI control, start the bridge from ParaView's
64
+ Python Shell using `scripts/start_paraview_gui_bridge.py`.
45
65
 
46
66
  ```
47
- MCP Client (Claude Desktop, Codex CLI, …)
48
- ⇅ stdio
49
- paraview-mcp-server ← thin MCP server, defines 31 tools
50
- ⇅ JSON / TCP localhost:9876
51
- ParaView bridge (pvpython) ← dispatches commands with paraview.simple
52
-
53
- paraview.simple / servermanager
67
+ ┌──────────────────────────────┐ stdio ┌────────────────────────┐
68
+ │ MCP Client │ ◄──────────────► │ MCP Server │
69
+ Codex / Claude / other host │ │ paraview-mcp-server │
70
+ └──────────────────────────────┘ └──────────┬─────────────┘
71
+ JSON/TCP
72
+ │ 127.0.0.1:9876
73
+ ┌──────────▼─────────────┐
74
+ │ ParaView Bridge │
75
+ │ live GUI process │
76
+ └──────────┬─────────────┘
77
+
78
+ ┌──────────▼─────────────┐
79
+ │ paraview.simple │
80
+ │ ParaView runtime │
81
+ └────────────────────────┘
54
82
  ```
55
83
 
56
- - The **MCP server** is a normal Python package. It speaks MCP over stdio and forwards
57
- every tool call as a JSON request to the bridge over a local TCP socket.
58
- - The **bridge** runs inside `pvpython`. It receives JSON commands, dispatches them through
59
- a command registry, calls `paraview.simple`, and returns JSON results.
60
- - Neither process depends on the other's code at import time.
61
- - A **headless pvpython executor** lets the MCP server run scripts in a separate
62
- `pvpython` process for long-running or async workflows (no bridge needed).
84
+ Why a ParaView-side bridge? ParaView's useful automation API is
85
+ `paraview.simple`, and it must execute inside a ParaView Python runtime. The
86
+ MCP server itself is only a protocol adapter; it cannot modify a ParaView GUI
87
+ unless the bridge is running inside that GUI process.
88
+
89
+ For live GUI modification, use:
90
+
91
+ ```text
92
+ Codex/Claude -> MCP server -> bridge inside open ParaView GUI -> live GUI session
93
+ ```
94
+
95
+ For headless automation, use:
96
+
97
+ ```text
98
+ Codex/Claude -> MCP server -> bridge inside pvpython -> headless ParaView runtime
99
+ ```
63
100
 
64
101
  See [`docs/architecture.md`](docs/architecture.md) for a full diagram, protocol reference,
65
102
  and tool namespace table.
66
103
 
67
104
  ---
68
105
 
69
- ## Quick start
106
+ ## Install
70
107
 
71
- ### 1. Install the MCP server
108
+ ### Option A: Install the MCP server from PyPI
109
+
110
+ Use this when you only need the MCP server executable in your normal Python
111
+ environment:
72
112
 
73
113
  ```bash
74
114
  pip install paraview-mcp-python
75
115
  ```
76
116
 
77
- For local development from this repository:
117
+ This installs:
118
+
119
+ ```bash
120
+ paraview-mcp-server
121
+ ```
122
+
123
+ ### Option B: Clone this repository for the ParaView bridge
124
+
125
+ The bridge code must be available to ParaView's Python runtime. For live GUI
126
+ control and local development, clone the repository:
78
127
 
79
128
  ```bash
80
129
  git clone https://github.com/djeada/paraview-mcp-server.git
@@ -84,18 +133,112 @@ source .venv/bin/activate # Windows: .venv\Scripts\activate
84
133
  pip install -e .
85
134
  ```
86
135
 
87
- ### 2. Start the ParaView bridge
136
+ This creates:
137
+
138
+ ```bash
139
+ .venv/bin/paraview-mcp-server
140
+ ```
141
+
142
+ ---
143
+
144
+ ## Start Everything
145
+
146
+ Start the pieces in this order.
147
+
148
+ ### 1. Start the ParaView GUI Bridge
149
+
150
+ Open ParaView, then run the GUI bridge script from the Python Shell:
151
+
152
+ 1. In ParaView, open **Tools -> Python Shell**.
153
+ 2. Click **Run Script**.
154
+ 3. Select:
155
+
156
+ ```text
157
+ /absolute/path/to/paraview-mcp-server/scripts/start_paraview_gui_bridge.py
158
+ ```
159
+
160
+ Expected output:
161
+
162
+ ```text
163
+ ParaView MCP GUI bridge started on 127.0.0.1:9876
164
+ ```
165
+
166
+ If `paraview-mcp-python` is installed into ParaView's Python environment, you
167
+ can also start the live GUI bridge directly from the Python Shell:
168
+
169
+ ```python
170
+ from bridge.gui_bridge import start_gui_bridge, stop_gui_bridge
171
+ start_gui_bridge()
172
+ ```
173
+
174
+ Leave ParaView open. MCP commands now modify this live GUI session.
175
+
176
+ To stop the bridge from the ParaView Python Shell:
177
+
178
+ ```python
179
+ stop_gui_bridge()
180
+ ```
181
+
182
+ ### 2. Optional: Start a Headless `pvpython` Bridge
88
183
 
89
- In a terminal that has `pvpython` on `PATH`:
184
+ Use this only when you do not need to modify an already-open ParaView GUI:
90
185
 
91
186
  ```bash
187
+ cd /path/to/paraview-mcp-server
92
188
  pvpython scripts/start_paraview_bridge.py
93
- # → ParaView bridge ready on 127.0.0.1:9876
94
189
  ```
95
190
 
96
- The bridge listens for JSON commands from the MCP server.
191
+ Expected output:
192
+
193
+ ```text
194
+ ParaView bridge ready on 127.0.0.1:9876
195
+ ```
196
+
197
+ Keep that terminal running. This controls the `pvpython` session, not a GUI
198
+ window opened separately.
199
+
200
+ ### 3. Verify the Bridge Directly
201
+
202
+ Before involving an MCP client, send one raw bridge command:
203
+
204
+ ```bash
205
+ python scripts/paraview_bridge_request.py scene.get_info
206
+ ```
207
+
208
+ Expected response shape:
209
+
210
+ ```json
211
+ {
212
+ "success": true,
213
+ "result": {
214
+ "source_count": 0,
215
+ "active_view_type": "RenderView"
216
+ }
217
+ }
218
+ ```
219
+
220
+ If this fails, fix the bridge before configuring Codex or Claude.
221
+
222
+ ### 4. Register the MCP Server with Codex CLI
223
+
224
+ If you installed from PyPI:
225
+
226
+ ```bash
227
+ codex mcp add paraview -- paraview-mcp-server
228
+ codex mcp list
229
+ ```
230
+
231
+ If you are using the local repository:
97
232
 
98
- ### 3. Register the MCP server with your AI client
233
+ ```bash
234
+ codex mcp add paraview -- /absolute/path/to/paraview-mcp-server/.venv/bin/paraview-mcp-server
235
+ codex mcp list
236
+ ```
237
+
238
+ Codex starts the MCP server automatically when needed. The ParaView bridge
239
+ must already be running separately.
240
+
241
+ ### 5. Register with Claude Desktop
99
242
 
100
243
  **Claude Desktop** — add to `claude_desktop_config.json`:
101
244
 
@@ -109,12 +252,42 @@ The bridge listens for JSON commands from the MCP server.
109
252
  }
110
253
  ```
111
254
 
112
- **Codex CLI:**
255
+ For a PyPI install, use the absolute path returned by:
113
256
 
114
257
  ```bash
115
- codex mcp add paraview -- /absolute/path/to/.venv/bin/paraview-mcp-server
258
+ which paraview-mcp-server
116
259
  ```
117
260
 
261
+ Restart Claude Desktop after editing the config.
262
+
263
+ ### 6. Verify Through Your MCP Client
264
+
265
+ With the bridge still running, ask your MCP client:
266
+
267
+ ```text
268
+ List all sources in the current ParaView session.
269
+ ```
270
+
271
+ The client should call `paraview_scene_list_sources` and return the current
272
+ ParaView pipeline sources from the live GUI session if you started
273
+ `start_paraview_gui_bridge.py`.
274
+
275
+ ---
276
+
277
+ ## What Can It Control?
278
+
279
+ There are two levels of control:
280
+
281
+ 1. **Fixed MCP tools** for common workflows: scene inspection, loading data,
282
+ filters, display/coloring, camera, screenshots, data export, and animation
283
+ export.
284
+ 2. **Python execution** through `paraview_python_exec`, which can run trusted
285
+ local Python inside the ParaView bridge session. Use this for anything not
286
+ covered by a fixed tool, including arbitrary `paraview.simple` scripts.
287
+
288
+ So the fixed tool list is intentionally finite, but the Python execution tool
289
+ is the general escape hatch for the broader ParaView API.
290
+
118
291
  ---
119
292
 
120
293
  ## Example prompts
@@ -261,12 +434,12 @@ Async jobs run in a separate headless `pvpython` process via `HeadlessPvpythonEx
261
434
 
262
435
  ## Python execution trust model
263
436
 
264
- - **Trusted local execution** — `paraview_python_exec` can run arbitrary Python available to `pvpython`,
265
- including imports and full `paraview.simple` workflows.
437
+ - **Trusted local execution** — `paraview_python_exec` can run arbitrary Python available to the active
438
+ ParaView Python process, including imports and full `paraview.simple` workflows.
266
439
  - **Output bounding** — stdout/stderr capped at **50 KB**.
267
440
  - **Cooperative timeout** — default 30 seconds per script execution.
268
441
  - **Script path validation** — optionally restrict execution to approved root directories.
269
- - The bridge runs inside `pvpython` with the same trust level as a local ParaView session.
442
+ - The bridge runs inside ParaView's Python runtime with the same trust level as that local session.
270
443
  - This is a local desktop automation tool — not a public API sandbox.
271
444
 
272
445
  ---
@@ -311,9 +484,11 @@ paraview-mcp-server/
311
484
  ├── bridge/
312
485
  │ ├── __init__.py
313
486
  │ ├── server.py # TCP socket bridge server
487
+ │ ├── gui_bridge.py # Non-blocking live GUI bridge lifecycle
314
488
  │ ├── command_handler.py # Command registry + paraview.simple handlers (27 commands)
315
489
  │ └── execution.py # trusted local python.execute helper
316
490
  ├── scripts/
491
+ │ ├── start_paraview_gui_bridge.py
317
492
  │ ├── start_paraview_bridge.py
318
493
  │ ├── paraview_bridge_request.py
319
494
  │ └── library/ # Reusable pvpython snippets
@@ -1,47 +1,96 @@
1
- # paraview-mcp-server
1
+ # ParaView MCP Server
2
2
 
3
3
  **Control ParaView with AI assistants through the Model Context Protocol.**
4
4
 
5
- `paraview-mcp-server` is a two-process bridge that lets AI assistants such as Claude Desktop
6
- and Codex CLI open datasets, apply filters, color data, and export screenshots in ParaView
7
- using natural language.
5
+ `paraview-mcp-python` provides an MCP server plus a ParaView-side bridge so
6
+ AI assistants such as Codex CLI and Claude Desktop can inspect a ParaView
7
+ session, open datasets, apply filters, color data, run ParaView Python, and
8
+ export screenshots.
9
+
10
+ The command installed for MCP clients is still:
11
+
12
+ ```bash
13
+ paraview-mcp-server
14
+ ```
8
15
 
9
16
  ---
10
17
 
11
- ## How it works
18
+ ## What Parts Are There?
19
+
20
+ There are three moving pieces:
21
+
22
+ | Part | Runs where | Purpose |
23
+ |---|---|---|
24
+ | **MCP client** | Codex CLI, Claude Desktop, or another MCP host | Starts the MCP server and calls tools. |
25
+ | **MCP server** | Normal Python environment | Speaks MCP over stdio and forwards tool calls to ParaView over TCP. |
26
+ | **ParaView GUI bridge** | Already-open ParaView GUI process | Receives TCP JSON commands and executes `paraview.simple` operations in that live GUI session. |
27
+
28
+ The ParaView bridge is the ParaView-side component. It plays the same role as
29
+ the Blender add-on in `blender-mcp-server`: it must run inside the application
30
+ you want to control. For live GUI control, start the bridge from ParaView's
31
+ Python Shell using `scripts/start_paraview_gui_bridge.py`.
12
32
 
13
33
  ```
14
- MCP Client (Claude Desktop, Codex CLI, …)
15
- ⇅ stdio
16
- paraview-mcp-server ← thin MCP server, defines 31 tools
17
- ⇅ JSON / TCP localhost:9876
18
- ParaView bridge (pvpython) ← dispatches commands with paraview.simple
19
-
20
- paraview.simple / servermanager
34
+ ┌──────────────────────────────┐ stdio ┌────────────────────────┐
35
+ │ MCP Client │ ◄──────────────► │ MCP Server │
36
+ Codex / Claude / other host │ │ paraview-mcp-server │
37
+ └──────────────────────────────┘ └──────────┬─────────────┘
38
+ JSON/TCP
39
+ │ 127.0.0.1:9876
40
+ ┌──────────▼─────────────┐
41
+ │ ParaView Bridge │
42
+ │ live GUI process │
43
+ └──────────┬─────────────┘
44
+
45
+ ┌──────────▼─────────────┐
46
+ │ paraview.simple │
47
+ │ ParaView runtime │
48
+ └────────────────────────┘
21
49
  ```
22
50
 
23
- - The **MCP server** is a normal Python package. It speaks MCP over stdio and forwards
24
- every tool call as a JSON request to the bridge over a local TCP socket.
25
- - The **bridge** runs inside `pvpython`. It receives JSON commands, dispatches them through
26
- a command registry, calls `paraview.simple`, and returns JSON results.
27
- - Neither process depends on the other's code at import time.
28
- - A **headless pvpython executor** lets the MCP server run scripts in a separate
29
- `pvpython` process for long-running or async workflows (no bridge needed).
51
+ Why a ParaView-side bridge? ParaView's useful automation API is
52
+ `paraview.simple`, and it must execute inside a ParaView Python runtime. The
53
+ MCP server itself is only a protocol adapter; it cannot modify a ParaView GUI
54
+ unless the bridge is running inside that GUI process.
55
+
56
+ For live GUI modification, use:
57
+
58
+ ```text
59
+ Codex/Claude -> MCP server -> bridge inside open ParaView GUI -> live GUI session
60
+ ```
61
+
62
+ For headless automation, use:
63
+
64
+ ```text
65
+ Codex/Claude -> MCP server -> bridge inside pvpython -> headless ParaView runtime
66
+ ```
30
67
 
31
68
  See [`docs/architecture.md`](docs/architecture.md) for a full diagram, protocol reference,
32
69
  and tool namespace table.
33
70
 
34
71
  ---
35
72
 
36
- ## Quick start
73
+ ## Install
37
74
 
38
- ### 1. Install the MCP server
75
+ ### Option A: Install the MCP server from PyPI
76
+
77
+ Use this when you only need the MCP server executable in your normal Python
78
+ environment:
39
79
 
40
80
  ```bash
41
81
  pip install paraview-mcp-python
42
82
  ```
43
83
 
44
- For local development from this repository:
84
+ This installs:
85
+
86
+ ```bash
87
+ paraview-mcp-server
88
+ ```
89
+
90
+ ### Option B: Clone this repository for the ParaView bridge
91
+
92
+ The bridge code must be available to ParaView's Python runtime. For live GUI
93
+ control and local development, clone the repository:
45
94
 
46
95
  ```bash
47
96
  git clone https://github.com/djeada/paraview-mcp-server.git
@@ -51,18 +100,112 @@ source .venv/bin/activate # Windows: .venv\Scripts\activate
51
100
  pip install -e .
52
101
  ```
53
102
 
54
- ### 2. Start the ParaView bridge
103
+ This creates:
104
+
105
+ ```bash
106
+ .venv/bin/paraview-mcp-server
107
+ ```
108
+
109
+ ---
110
+
111
+ ## Start Everything
112
+
113
+ Start the pieces in this order.
114
+
115
+ ### 1. Start the ParaView GUI Bridge
116
+
117
+ Open ParaView, then run the GUI bridge script from the Python Shell:
118
+
119
+ 1. In ParaView, open **Tools -> Python Shell**.
120
+ 2. Click **Run Script**.
121
+ 3. Select:
122
+
123
+ ```text
124
+ /absolute/path/to/paraview-mcp-server/scripts/start_paraview_gui_bridge.py
125
+ ```
126
+
127
+ Expected output:
128
+
129
+ ```text
130
+ ParaView MCP GUI bridge started on 127.0.0.1:9876
131
+ ```
132
+
133
+ If `paraview-mcp-python` is installed into ParaView's Python environment, you
134
+ can also start the live GUI bridge directly from the Python Shell:
135
+
136
+ ```python
137
+ from bridge.gui_bridge import start_gui_bridge, stop_gui_bridge
138
+ start_gui_bridge()
139
+ ```
140
+
141
+ Leave ParaView open. MCP commands now modify this live GUI session.
142
+
143
+ To stop the bridge from the ParaView Python Shell:
144
+
145
+ ```python
146
+ stop_gui_bridge()
147
+ ```
148
+
149
+ ### 2. Optional: Start a Headless `pvpython` Bridge
55
150
 
56
- In a terminal that has `pvpython` on `PATH`:
151
+ Use this only when you do not need to modify an already-open ParaView GUI:
57
152
 
58
153
  ```bash
154
+ cd /path/to/paraview-mcp-server
59
155
  pvpython scripts/start_paraview_bridge.py
60
- # → ParaView bridge ready on 127.0.0.1:9876
61
156
  ```
62
157
 
63
- The bridge listens for JSON commands from the MCP server.
158
+ Expected output:
159
+
160
+ ```text
161
+ ParaView bridge ready on 127.0.0.1:9876
162
+ ```
163
+
164
+ Keep that terminal running. This controls the `pvpython` session, not a GUI
165
+ window opened separately.
166
+
167
+ ### 3. Verify the Bridge Directly
168
+
169
+ Before involving an MCP client, send one raw bridge command:
170
+
171
+ ```bash
172
+ python scripts/paraview_bridge_request.py scene.get_info
173
+ ```
174
+
175
+ Expected response shape:
176
+
177
+ ```json
178
+ {
179
+ "success": true,
180
+ "result": {
181
+ "source_count": 0,
182
+ "active_view_type": "RenderView"
183
+ }
184
+ }
185
+ ```
186
+
187
+ If this fails, fix the bridge before configuring Codex or Claude.
188
+
189
+ ### 4. Register the MCP Server with Codex CLI
190
+
191
+ If you installed from PyPI:
192
+
193
+ ```bash
194
+ codex mcp add paraview -- paraview-mcp-server
195
+ codex mcp list
196
+ ```
197
+
198
+ If you are using the local repository:
64
199
 
65
- ### 3. Register the MCP server with your AI client
200
+ ```bash
201
+ codex mcp add paraview -- /absolute/path/to/paraview-mcp-server/.venv/bin/paraview-mcp-server
202
+ codex mcp list
203
+ ```
204
+
205
+ Codex starts the MCP server automatically when needed. The ParaView bridge
206
+ must already be running separately.
207
+
208
+ ### 5. Register with Claude Desktop
66
209
 
67
210
  **Claude Desktop** — add to `claude_desktop_config.json`:
68
211
 
@@ -76,12 +219,42 @@ The bridge listens for JSON commands from the MCP server.
76
219
  }
77
220
  ```
78
221
 
79
- **Codex CLI:**
222
+ For a PyPI install, use the absolute path returned by:
80
223
 
81
224
  ```bash
82
- codex mcp add paraview -- /absolute/path/to/.venv/bin/paraview-mcp-server
225
+ which paraview-mcp-server
83
226
  ```
84
227
 
228
+ Restart Claude Desktop after editing the config.
229
+
230
+ ### 6. Verify Through Your MCP Client
231
+
232
+ With the bridge still running, ask your MCP client:
233
+
234
+ ```text
235
+ List all sources in the current ParaView session.
236
+ ```
237
+
238
+ The client should call `paraview_scene_list_sources` and return the current
239
+ ParaView pipeline sources from the live GUI session if you started
240
+ `start_paraview_gui_bridge.py`.
241
+
242
+ ---
243
+
244
+ ## What Can It Control?
245
+
246
+ There are two levels of control:
247
+
248
+ 1. **Fixed MCP tools** for common workflows: scene inspection, loading data,
249
+ filters, display/coloring, camera, screenshots, data export, and animation
250
+ export.
251
+ 2. **Python execution** through `paraview_python_exec`, which can run trusted
252
+ local Python inside the ParaView bridge session. Use this for anything not
253
+ covered by a fixed tool, including arbitrary `paraview.simple` scripts.
254
+
255
+ So the fixed tool list is intentionally finite, but the Python execution tool
256
+ is the general escape hatch for the broader ParaView API.
257
+
85
258
  ---
86
259
 
87
260
  ## Example prompts
@@ -228,12 +401,12 @@ Async jobs run in a separate headless `pvpython` process via `HeadlessPvpythonEx
228
401
 
229
402
  ## Python execution trust model
230
403
 
231
- - **Trusted local execution** — `paraview_python_exec` can run arbitrary Python available to `pvpython`,
232
- including imports and full `paraview.simple` workflows.
404
+ - **Trusted local execution** — `paraview_python_exec` can run arbitrary Python available to the active
405
+ ParaView Python process, including imports and full `paraview.simple` workflows.
233
406
  - **Output bounding** — stdout/stderr capped at **50 KB**.
234
407
  - **Cooperative timeout** — default 30 seconds per script execution.
235
408
  - **Script path validation** — optionally restrict execution to approved root directories.
236
- - The bridge runs inside `pvpython` with the same trust level as a local ParaView session.
409
+ - The bridge runs inside ParaView's Python runtime with the same trust level as that local session.
237
410
  - This is a local desktop automation tool — not a public API sandbox.
238
411
 
239
412
  ---
@@ -278,9 +451,11 @@ paraview-mcp-server/
278
451
  ├── bridge/
279
452
  │ ├── __init__.py
280
453
  │ ├── server.py # TCP socket bridge server
454
+ │ ├── gui_bridge.py # Non-blocking live GUI bridge lifecycle
281
455
  │ ├── command_handler.py # Command registry + paraview.simple handlers (27 commands)
282
456
  │ └── execution.py # trusted local python.execute helper
283
457
  ├── scripts/
458
+ │ ├── start_paraview_gui_bridge.py
284
459
  │ ├── start_paraview_bridge.py
285
460
  │ ├── paraview_bridge_request.py
286
461
  │ └── library/ # Reusable pvpython snippets
@@ -0,0 +1,55 @@
1
+ """Helpers for starting the bridge from an already-open ParaView GUI session."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from bridge.server import HOST, PORT, ParaViewBridgeServer
8
+
9
+ _SERVER: ParaViewBridgeServer | None = None
10
+
11
+
12
+ def start_gui_bridge(host: str = HOST, port: int = PORT) -> dict[str, Any]:
13
+ """Start the bridge inside the current ParaView Python process.
14
+
15
+ This function is intentionally non-blocking so it can be called from the
16
+ ParaView GUI Python shell without freezing the application.
17
+ """
18
+ global _SERVER
19
+ if _SERVER is not None and _SERVER.is_running:
20
+ return {
21
+ "host": _SERVER.host,
22
+ "port": _SERVER.port,
23
+ "running": True,
24
+ "already_running": True,
25
+ }
26
+
27
+ server = ParaViewBridgeServer(host=host, port=port)
28
+ server.start()
29
+ _SERVER = server
30
+ return {
31
+ "host": server.host,
32
+ "port": server.port,
33
+ "running": True,
34
+ "already_running": False,
35
+ }
36
+
37
+
38
+ def stop_gui_bridge() -> dict[str, Any]:
39
+ """Stop the bridge started by :func:`start_gui_bridge`."""
40
+ global _SERVER
41
+ if _SERVER is None:
42
+ return {"running": False, "stopped": False}
43
+
44
+ host = _SERVER.host
45
+ port = _SERVER.port
46
+ _SERVER.stop()
47
+ _SERVER = None
48
+ return {"host": host, "port": port, "running": False, "stopped": True}
49
+
50
+
51
+ def gui_bridge_status() -> dict[str, Any]:
52
+ """Return the current GUI bridge status."""
53
+ if _SERVER is None or not _SERVER.is_running:
54
+ return {"running": False}
55
+ return {"host": _SERVER.host, "port": _SERVER.port, "running": True}
@@ -1,7 +1,7 @@
1
1
  """Dependency-free bridge command parameter validation.
2
2
 
3
- The bridge runs inside ParaView's ``pvpython``, which commonly does not have
4
- the MCP server's Python dependencies installed. These classes intentionally
3
+ The bridge runs inside ParaView's Python runtime, which commonly does not have
4
+ the MCP server's Python dependencies installed. These classes intentionally
5
5
  provide the small ``model_validate(...).model_dump(...)`` surface used by the
6
6
  command handler without importing third-party packages.
7
7
  """
@@ -33,6 +33,18 @@ class ParaViewBridgeServer:
33
33
  self._handler = CommandHandler()
34
34
  self._handler_lock = threading.Lock()
35
35
 
36
+ @property
37
+ def host(self) -> str:
38
+ return self._host
39
+
40
+ @property
41
+ def port(self) -> int:
42
+ return self._port
43
+
44
+ @property
45
+ def is_running(self) -> bool:
46
+ return self._running
47
+
36
48
  def start(self):
37
49
  if self._running:
38
50
  return
@@ -40,6 +52,7 @@ class ParaViewBridgeServer:
40
52
  self._server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
41
53
  self._server_socket.settimeout(1.0)
42
54
  self._server_socket.bind((self._host, self._port))
55
+ self._host, self._port = self._server_socket.getsockname()[:2]
43
56
  self._server_socket.listen(5)
44
57
  self._running = True
45
58
  self._thread = threading.Thread(target=self._accept_loop, daemon=True)
@@ -19,20 +19,26 @@
19
19
  │ JSON / TCP localhost:9876
20
20
  │ (newline-delimited JSON)
21
21
  ┌──────────▼─────────────┐
22
- │ ParaView bridge │ Runs inside pvpython
23
- │ bridge/ │
22
+ │ ParaView GUI bridge │ Runs inside the open ParaView GUI
23
+ │ bridge/ │ via scripts/start_paraview_gui_bridge.py
24
24
  │ · ParaViewBridgeServer│
25
25
  │ · CommandHandler │ 27 registered commands
26
26
  │ · execute_code() │
27
27
  └──────────┬─────────────┘
28
28
 
29
29
  ┌──────────▼─────────────┐
30
- │ paraview.simple │ ParaView Python API
30
+ │ paraview.simple │ Live ParaView GUI session
31
31
  │ servermanager │
32
32
  └────────────────────────┘
33
33
  ```
34
34
 
35
- Alternative headless transport (no bridge required):
35
+ Alternative headless bridge:
36
+
37
+ ```
38
+ MCP Client → paraview-mcp-server → pvpython scripts/start_paraview_bridge.py
39
+ ```
40
+
41
+ Alternative headless script transport (no long-running bridge required):
36
42
 
37
43
  ```
38
44
  ┌────────────────────────┐
@@ -67,6 +73,7 @@ Alternative headless transport (no bridge required):
67
73
  | Module | Responsibility |
68
74
  |---|---|
69
75
  | `server.py` | Threaded TCP socket server, newline-delimited JSON framing |
76
+ | `gui_bridge.py` | Non-blocking helpers for starting/stopping the bridge inside ParaView GUI |
70
77
  | `command_handler.py` | Command registry mapping 27 command names to `paraview.simple` calls |
71
78
  | `execution.py` | `execute_code()` — trusted local Python execution with timeout, output cap, and optional script path validation |
72
79
  | `__init__.py` | Package marker |
@@ -213,20 +220,33 @@ MCP client
213
220
 
214
221
  ## Lifecycle
215
222
 
216
- 1. User starts the bridge: `pvpython scripts/start_paraview_bridge.py`
223
+ 1. User starts the live GUI bridge from ParaView: **Tools → Python Shell → Run Script**,
224
+ selecting `scripts/start_paraview_gui_bridge.py`.
217
225
  2. Bridge server binds to `127.0.0.1:9876` and listens for TCP connections.
218
226
  3. User starts an MCP client (Claude Desktop, Codex CLI, etc.)
219
227
  4. MCP client spawns `paraview-mcp-server` over stdio.
220
228
  5. MCP server connects to bridge on startup (or lazy-connects on first tool call).
221
229
  6. User issues a natural language request → client calls an MCP tool → server
222
230
  forwards as JSON → bridge dispatches → returns result.
223
- 7. User stops the bridge with Ctrl+C. Server reconnects on next call if the bridge restarts.
231
+ 7. User stops the GUI bridge with `stop_gui_bridge()` in ParaView's Python Shell.
232
+ Server reconnects on next call if the bridge restarts.
224
233
 
225
234
  ---
226
235
 
227
236
  ## Configuration
228
237
 
229
- ### Bridge
238
+ ### Live GUI Bridge
239
+
240
+ Run this from ParaView's Python Shell with **Run Script**:
241
+
242
+ ```text
243
+ scripts/start_paraview_gui_bridge.py
244
+ ```
245
+
246
+ The script starts a background TCP server in the open ParaView GUI process and
247
+ returns immediately.
248
+
249
+ ### Headless Bridge
230
250
 
231
251
  ```bash
232
252
  pvpython scripts/start_paraview_bridge.py --host 127.0.0.1 --port 9876
@@ -7,7 +7,10 @@ escape hatch for workflows that require more than the fixed tool set.
7
7
 
8
8
  Two transports are supported:
9
9
 
10
- 1. **Bridge** (default) - code runs inside the running pvpython bridge process via exec().
10
+ 1. **Bridge** (default) - code runs inside the active bridge process via exec().
11
+ For live GUI control, that process is the open ParaView GUI where
12
+ `scripts/start_paraview_gui_bridge.py` was run. For headless control, it is
13
+ the `pvpython scripts/start_paraview_bridge.py` process.
11
14
  2. **Headless** - code runs in a separate pvpython subprocess via HeadlessPvpythonExecutor.
12
15
 
13
16
  ---
@@ -67,9 +70,10 @@ Headless transport adds:
67
70
 
68
71
  ### Trusted local execution
69
72
 
70
- Bridge scripts run inside the local `pvpython` process and may import normal
71
- Python modules. This is intentional: `paraview_python_exec` is the escape hatch
72
- for full ParaView automation when the fixed MCP tool set is too small.
73
+ Bridge scripts run inside the local ParaView Python process and may import
74
+ normal Python modules. In live GUI mode, that is the open ParaView GUI process.
75
+ This is intentional: `paraview_python_exec` is the escape hatch for full
76
+ ParaView automation when the fixed MCP tool set is too small.
73
77
 
74
78
  ### Output bounding
75
79
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "paraview-mcp-python"
7
- version = "0.1.1"
7
+ version = "0.1.2"
8
8
  description = "MCP server for controlling ParaView via AI assistants"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -46,7 +46,7 @@ dev = [
46
46
  ]
47
47
 
48
48
  [tool.hatch.build.targets.wheel]
49
- packages = ["src/paraview_mcp_server"]
49
+ packages = ["src/paraview_mcp_server", "bridge"]
50
50
 
51
51
  [tool.hatch.build.targets.sdist]
52
52
  include = [
@@ -0,0 +1,42 @@
1
+ """Start the MCP bridge inside an already-open ParaView GUI session.
2
+
3
+ Run from ParaView:
4
+
5
+ Tools -> Python Shell -> Run Script
6
+
7
+ Select this file. The script starts the TCP bridge in a background thread and
8
+ returns immediately, so the ParaView GUI remains usable. MCP commands will then
9
+ modify this open ParaView session.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import logging
15
+ import os
16
+ import sys
17
+
18
+ logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(name)s: %(message)s")
19
+
20
+ REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
21
+ if REPO_ROOT not in sys.path:
22
+ sys.path.insert(0, REPO_ROOT)
23
+
24
+ from bridge.gui_bridge import gui_bridge_status, start_gui_bridge, stop_gui_bridge # noqa: E402
25
+
26
+ globals()["stop_gui_bridge"] = stop_gui_bridge
27
+ globals()["gui_bridge_status"] = gui_bridge_status
28
+
29
+
30
+ def main() -> None:
31
+ host = os.environ.get("PARAVIEW_MCP_HOST", "127.0.0.1")
32
+ port = int(os.environ.get("PARAVIEW_MCP_PORT", "9876"))
33
+ status = start_gui_bridge(host=host, port=port)
34
+ state = "already running" if status["already_running"] else "started"
35
+ print(f"ParaView MCP GUI bridge {state} on {status['host']}:{status['port']}")
36
+ print("Verify from a terminal with:")
37
+ print(" python scripts/paraview_bridge_request.py scene.get_info")
38
+ print("Stop from the ParaView Python Shell with:")
39
+ print(" stop_gui_bridge()")
40
+
41
+
42
+ main()
@@ -0,0 +1,55 @@
1
+ """Tests for the ParaView GUI bridge lifecycle helpers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from unittest.mock import MagicMock, patch
6
+
7
+ from bridge import gui_bridge
8
+
9
+
10
+ def teardown_function(_function):
11
+ gui_bridge.stop_gui_bridge()
12
+
13
+
14
+ def test_start_gui_bridge_is_non_blocking_and_reports_status():
15
+ with patch("bridge.command_handler.CommandHandler", return_value=MagicMock()):
16
+ status = gui_bridge.start_gui_bridge(port=0)
17
+
18
+ assert status["running"] is True
19
+ assert status["already_running"] is False
20
+ assert status["host"] == "127.0.0.1"
21
+ assert status["port"] > 0
22
+ assert gui_bridge.gui_bridge_status() == {
23
+ "host": status["host"],
24
+ "port": status["port"],
25
+ "running": True,
26
+ }
27
+
28
+
29
+ def test_start_gui_bridge_is_idempotent():
30
+ with patch("bridge.command_handler.CommandHandler", return_value=MagicMock()):
31
+ first = gui_bridge.start_gui_bridge(port=0)
32
+ second = gui_bridge.start_gui_bridge(port=0)
33
+
34
+ assert first["running"] is True
35
+ assert second == {
36
+ "host": first["host"],
37
+ "port": first["port"],
38
+ "running": True,
39
+ "already_running": True,
40
+ }
41
+
42
+
43
+ def test_stop_gui_bridge_stops_running_server():
44
+ with patch("bridge.command_handler.CommandHandler", return_value=MagicMock()):
45
+ started = gui_bridge.start_gui_bridge(port=0)
46
+
47
+ stopped = gui_bridge.stop_gui_bridge()
48
+
49
+ assert stopped == {
50
+ "host": started["host"],
51
+ "port": started["port"],
52
+ "running": False,
53
+ "stopped": True,
54
+ }
55
+ assert gui_bridge.gui_bridge_status() == {"running": False}