Skip to main content
Glama
README.md
# vred-mcp

A local MCP server that lets MCP-compatible AI clients (Claude Code, OpenAI
Codex) inspect and control the currently open scene in a running Autodesk
VRED Professional 2027 instance, through a small authenticated bridge that
runs inside VRED itself.

**Status: Phase 1, 2, and 3 complete, live-verified against a running
VRED 2027 instance.** 22 tools exist: 13 read-only (connectivity,
scene/camera/material/variant inspection, node lookup, bounded scene tree)
and 9 mutations (select/clear selection, show/hide, move/rotate/scale,
switch camera, capture screenshot), all gated behind `VRED_ALLOW_MUTATIONS`
with a `dry_run` option that works even when disabled. Every mutation was
applied for real against a live scene and confirmed restored to its
original values afterward - see `IMPLEMENTATION_PLAN.md` sections 10-11 for
what live verification caught along the way (several doc-reading mistakes,
one real networking bug, and one bad assumption about how to reload code
changes). Save and basic scene-lifecycle tools are now implemented. The server is now registered
with both Claude Code (`✔ Connected` via `claude mcp list`) and Codex -
Codex was verified with two real non-interactive agent runs that actually
called `vred_ping` and `get_scene_summary` through MCP and got back correct
live VRED data (see `IMPLEMENTATION_PLAN.md` section 12).

## 1. Purpose

Give an AI coding/automation agent a safe, auditable way to ask "what's in
the currently open VRED scene" and (in later phases) make small, explicit,
reversible changes to it - without ever giving the agent the ability to run
arbitrary code inside VRED.

## 2. Supported VRED version

Autodesk VRED Professional **2027** (internal version 19.0), confirmed
against the actual local installation at
`C:\Program Files\Autodesk\VREDPro-19.0` (`autodesk_VRED_2027.swidtag`).
Not tested against any other VRED version or edition (VREDDesign,
VREDPresenter).

## 3. Architecture

```
Claude Code / OpenAI Codex   (MCP client, stdio transport)
        |
vred-mcp server               external Python process (this repo's venv)
        | HTTP over 127.0.0.1 only, Bearer token auth, JSON-only envelope
VRED bridge                   runs INSIDE VRED's embedded Python 3.11,
        |                     loaded via VRED's Script Editor
        | vrTimer-marshaled calls, VRED main/render thread only
Autodesk VRED Professional 2027
```

Why it's shaped this way (full reasoning in `IMPLEMENTATION_PLAN.md`):

- VRED's own built-in web interface (`http://localhost:8888/pythoneval?...`)
  evaluates arbitrary Python passed in the URL. It is real and documented,
  but using it directly would make this server a disguised
  `execute_python` tool, which the project's own security requirements
  forbid. So a separate, purpose-built bridge was written instead, with a
  fixed operation whitelist.
- VRED's embedded Python interpreter (3.11) is a different process/runtime
  than whatever runs this MCP server - they must talk over a local
  transport regardless. HTTP over loopback was chosen since it needs
  nothing beyond the Python standard library on the VRED side.
- VRED's `vrTimer` fires synchronously with VRED's render loop
  (documented behavior). The bridge's HTTP handling happens on a background
  thread; actual VRED API calls are marshaled onto the main thread through
  a `vrTimer` callback, so nothing calls VRED's scene API off-thread and
  nothing blocks VRED's UI.

## 4. Security model

- The bridge binds only to `127.0.0.1`. It refuses to start on any other
  host, and refuses to start if its auth token is still the placeholder
  value.
- Every bridge request needs `Authorization: Bearer <token>`, checked with
  a constant-time comparison (`hmac.compare_digest`).
- The bridge accepts only `{"op": "<name>", "args": {...}}`. `<name>` must
  be a key in `vred_bridge/command_registry.COMMAND_REGISTRY` - a fixed
  Python dict of handler functions. There is no code path anywhere in this
  project that takes a string of Python (or a VRED method name) from the
  network and executes/calls it.
- Request bodies over `VRED_MAX_REQUEST_BYTES` are rejected without being
  read into memory.
- Mutation and save operations (Phase 3/4, not yet implemented) are gated
  behind `VRED_ALLOW_MUTATIONS` / `VRED_ALLOW_SAVE`, both `false` by
  default.
- The MCP server never logs the auth token (only its length) and never logs
  full scene dumps or binary image data.
- `execute_python`, `execute_shell`, `run_command`, `delete_node`, and
  similar tools are intentionally absent and are not planned - see the main
  task spec this project was built from.

## 5. Prerequisites

- Windows, with Autodesk VRED Professional 2027 installed and able to run.
- Python 3.10+ available on PATH (this project was built and tested against
  the system's Python 3.14.3; 3.11/3.12 also present on this machine work
  too - `uv`/`pipx` are not installed here, so setup below uses a plain
  `venv` + `pip`).
- Claude Code and/or OpenAI Codex CLI installed (`claude --version`,
  `codex --version`).

## 6. Supported Python version

`>=3.10` for the MCP server (see `pyproject.toml`). The VRED bridge runs
under whatever Python VRED 2027 embeds (3.11) and only uses the standard
library - it is not installed via pip and has no version constraint of its
own beyond "whatever VRED 2027 ships."

## 7. Installation

```powershell
cd C:\Users\<you>\autodesk-vred-mcp
.\scripts\install.ps1
```

This creates `.venv`, installs the project in editable mode with dev
dependencies, and copies `.env.example` to `.env` if `.env` doesn't exist
yet. If your PowerShell execution policy blocks running local scripts, either
run `powershell -ExecutionPolicy Bypass -File .\scripts\install.ps1`, or set
an execution policy scoped to your user
(`Set-ExecutionPolicy -Scope CurrentUser RemoteSigned`) - the project does
not require or configure this itself.

## 8. Dependency management

Plain `pip` + `venv` (`uv` and `pipx` were checked and are not installed on
this machine, so the scripts don't depend on either). Runtime dependencies:
`mcp>=1.27.0` (official Python MCP SDK, includes `FastMCP`), `pydantic`,
`pydantic-settings`, `httpx`. Dev-only: `pytest`, `pytest-asyncio`, `ruff`.
The VRED-side bridge has **zero** dependencies beyond VRED's own embedded
Python standard library.

## 9. Token generation

```powershell
.\scripts\generate_token.ps1
```

Prints a 64-character hex token. Put the **same value** in two places:

- `.env` -> `VRED_BRIDGE_TOKEN=<token>`
- `vred_bridge\bridge_config.py` -> `TOKEN = "<token>"` (or set the
  `VRED_BRIDGE_TOKEN` environment variable before launching VRED, which
  bridge_config.py reads first).

## 10. `.env` configuration

Copy `.env.example` to `.env` (done automatically by `install.ps1`) and
fill in `VRED_BRIDGE_TOKEN`. All settings are validated eagerly at server
startup (`src/vred_mcp/config.py`) - a missing/placeholder token, a
non-localhost `VRED_HOST`, an out-of-range port, or an invalid `LOG_LEVEL`
all produce a clear error message on stderr instead of a confusing failure
later. See `.env.example` for the full list of variables and their
defaults.

## 11. How to load the bridge in VRED Professional 2027

Confirmed live against a real running VRED 2027 instance - not just doc
text. Full detail (including VRED's sandbox behavior) in
`vred_bridge/README.md`.

1. **Recommended: launch VRED with its Python sandbox disabled**, so the
   next steps don't hit a wall of "Allow for this project" popups (VRED
   2027 intercepts stdlib networking calls the bridge needs, one function
   at a time, and clicking through them all is impractical):
   ```powershell
   $env:VRED_INSECURE_PYTHON = "1"
   & "C:\Program Files\Autodesk\VREDPro-19.0\bin\WIN64\VREDPro.exe"
   ```
   This only affects the one launched process - it does not change any
   persistent setting. If you'd rather keep the sandbox on, you can click
   "Allow for this project" through each popup instead; expect several,
   since different socket operations trigger separate prompts.
2. In VRED, open **View > Terminal** - this is VRED's Python console (a
   persistent REPL, prompt `root:`). This is what the docs call the
   "Script Editor"; there is no separately-named panel.
3. Type these two lines and press Enter after each:
   ```python
   __file__ = "C:/Users/<you>/autodesk-vred-mcp/vred_bridge/vred_mcp_bridge.py"
   exec(open(__file__).read())
   ```
   (Use forward slashes or escaped backslashes - Python string, not a raw
   Windows path.)
4. Watch for a line starting with `[vred-mcp-bridge]`.
   `listening on http://127.0.0.1:8765/rpc ...` means it's up;
   `REFUSING TO START: ...` explains what's misconfigured (usually the
   token).

**If you close the Terminal panel**, its Python session resets - `__file__`
and `stop_bridge()` become undefined again next time you open it. This does
**not** stop an already-running bridge (it keeps running as long as VRED
does); it only means step 3 needs to be re-run to load a *new* bridge after
a restart.

**To reload a running bridge after editing `bridge_config.py` or
`command_registry.py` (without closing the panel)**, `stop_bridge()` +
step 3 alone is not enough - confirmed live: Python's module cache means
the code changes silently don't take effect and new operations keep
returning `UNSUPPORTED_OPERATION`. Use this instead:
```python
stop_bridge()
import sys
for mod_name in ("bridge_config", "command_registry"):
    sys.modules.pop(mod_name, None)
exec(open(__file__).read())
```
See `vred_bridge/README.md` for the full explanation.

A documented fallback that doesn't use the console at all (relaunches VRED
with the bridge script base64-encoded on the command line via
`-postpython`) is in `vred_bridge/README.md`.

## 12. How to start the MCP server

You normally won't do this by hand - Claude Code/Codex launch it for you
(see sections 14/15). To run it manually (e.g. to watch its log output):

```powershell
.\scripts\start_mcp.ps1
```

This runs `python -m vred_mcp` with stdio transport. All logging goes to
stderr (and optionally `logs/vred_mcp.log`) - stdout is reserved for MCP
protocol frames.

## 13. How to test connectivity

Two independent checks, so you can tell a bridge problem from an MCP-client
problem:

```powershell
# 1. Bridge only, bypasses MCP entirely:
.\scripts\test_connection.ps1

# 2. Full path, via the MCP tool itself, once connected to a client:
#    ask Claude Code / Codex to call the `vred_ping` tool.
```

`test_connection.ps1` POSTs an authenticated `ping` op directly to
`http://127.0.0.1:8765/rpc` and prints the structured JSON response (or a
clear error if VRED/the bridge isn't reachable).

## 14. How to connect Claude Code

Verified against Claude Code 2.1.179 (`claude mcp --help`).

```powershell
claude mcp add vred-mcp -s user -- "C:\Users\<you>\autodesk-vred-mcp\.venv\Scripts\python.exe" -m vred_mcp
```

- `-s user` registers it for your user account (available in every
  project). Use `-s project` instead to write a `.mcp.json` in the current
  project directory, or `-s local` (the default) for this project only,
  stored outside version control.
- There is no `--cwd` flag - `config.py` resolves `.env` relative to the
  project's own install location, not the launch directory, so this works
  regardless of scope.
- See `client-configs/claude-code.example.json` for the equivalent raw
  `.mcp.json` shape if you want to hand-edit instead.

**Verify it's connected:**

```powershell
claude mcp list
claude mcp get vred-mcp
```

Inside a Claude Code session, ask it to list available tools, or just ask
it to call `vred_ping`.

**Remove/disable:**

```powershell
claude mcp remove vred-mcp
```

## 15. How to connect OpenAI Codex

Verified against codex-cli 0.144.5 (`codex mcp --help`, and empirically by
adding/inspecting/removing a throwaway entry - see `IMPLEMENTATION_PLAN.md`
section 8).

```powershell
codex mcp add vred-mcp -- "C:\Users\<you>\autodesk-vred-mcp\.venv\Scripts\python.exe" -m vred_mcp
```

This writes a `[mcp_servers.vred-mcp]` table into
`%USERPROFILE%\.codex\config.toml` (Codex MCP servers are global, not
project-scoped). See `client-configs/codex.example.toml` for exactly what
that block looks like.

**Verify it's recognized:**

```powershell
codex mcp list
codex mcp get vred-mcp
```

**Remove/disable:**

```powershell
codex mcp remove vred-mcp
```

## 16. How to use read-only mode

The default. `VRED_ALLOW_MUTATIONS=false` in `.env` (the default) blocks
all 9 Phase 3 mutation tools before any network call is made - they return
a structured `MUTATIONS_DISABLED` error. `dry_run=true` still works on any
of them even in this mode (it validates/resolves everything and reports
what *would* happen, without calling it) - useful for letting an AI agent
preview a change before you decide to enable mutations.

## 17. How to enable mutations

Set `VRED_ALLOW_MUTATIONS=true` in `.env`, **and** make sure the bridge
running inside VRED agrees - either launch VRED with the
`VRED_ALLOW_MUTATIONS` environment variable set beforehand, or set it live
in VRED's Terminal console for the current session:
```python
bridge_config.ALLOW_MUTATIONS = True
```
Both sides enforce this independently (defense in depth) - if either says
disabled, the call is blocked. The 9 mutation tools:
`select_nodes`, `clear_selection`, `show_nodes`, `hide_nodes`,
`set_node_translation`, `set_node_rotation`, `set_node_scale`,
`activate_camera`, `capture_screenshot`. All were live-tested against a
real scene with real before/after values and confirmed fully restored
afterward - see `IMPLEMENTATION_PLAN.md` section 11.

## 18. How to enable save operations

Set `VRED_ALLOW_SAVE=true` in `.env` to enable save and basic
scene-lifecycle tools. These operations are restricted to
`VRED_ALLOWED_SAVE_DIR` on both sides.

## 19. Tool list (current)

All 22 tools below have been exercised through the real MCP server code
path against a live running VRED 2027 instance (see `IMPLEMENTATION_PLAN.md`
sections 10-11) - not just unit-tested with mocks. Mutation tools were
applied for real and confirmed restored to original values afterward.

**Read-only:**

| Tool | What it does |
|---|---|
| `vred_ping` | Confirms the bridge is reachable, authenticated, and VRED responded. Call this first. |
| `get_vred_version` | Returns VRED product version info and the current scene's filename (empty string for an unsaved scene). |
| `vred_diagnose` | Bundles `vred_ping`, `get_vred_version`, and `get_scene_summary` into one read-only health check. |
| `get_current_scene_path` / `get_current_scene_name` | Convenience wrappers around the scene filename. |
| `get_scene_summary` | Quick overview: file, node/camera/material/variant-set counts. |
| `save_scene` / `save_scene_as` / `new_scene` | Save the current scene, save to a new file, or create a new empty scene. |
| `get_scene_tree` | Bounded, nested scene hierarchy (depth/node-count capped server-side; optional root, name/type filter, hidden-node inclusion). |
| `find_nodes` | Find nodes by name (contains/exact/wildcard), result-capped. |
| `get_selected_nodes` | Nodes currently selected in VRED. |
| `get_node_info` | Full detail for one node, resolved by `unique_path`. |
| `get_cameras` / `get_active_camera` | List cameras / get the active one. |
| `get_variant_sets` | List variant set names. |
| `get_variant_set_details` | List the node/material variants, viewpoints, and animations inside one set. |
| `activate_variant` | Activate a node/material variant or reset a variant set to defaults. |
| `get_materials` | List materials (id/name/type), result-capped. |
| `get_selected_materials` | List the materials currently selected in the material editor. |
| `get_used_materials` | List the materials actually used in the scene, or under a subtree if you pass a `unique_path`. |
| `get_scene_health_report` | One-call audit for hidden nodes, unassigned materials, and variant coverage. |
| `apply_material_to_nodes` | Apply one material to one or more scene nodes by `unique_path`. |
| `select_nodes_by_materials` | Select all scene nodes that reference the given materials. |
| `compare_scene_snapshots` | Compare two captured scene snapshots and return object/material/selection/camera diffs. |

**Mutations** (blocked unless `VRED_ALLOW_MUTATIONS=true` on both sides - section 17; `dry_run=true` always works):

| Tool | What it does |
|---|---|
| `select_nodes` | Select one or more nodes by `unique_path`, replacing current selection. Fails the whole call if any path doesn't resolve. |
| `clear_selection` | Deselect everything. |
| `show_nodes` / `hide_nodes` | Toggle visibility for one or more nodes. |
| `set_node_translation` / `set_node_rotation` / `set_node_scale` | Move/rotate/scale a node - `[x, y, z]` values. |
| `activate_camera` | Switch the active/viewport camera. |
| `capture_screenshot` | Write a PNG/JPG/BMP of the current render view to a path inside `VRED_ALLOWED_OUTPUT_DIR`. |
| `capture_scene_snapshot` | Capture a screenshot and bundle it with a compact scene-state snapshot for visual reasoning. |

Planned tools and their VRED API verification status:
`VRED_API_COMPATIBILITY.md`.

## 20. Example AI prompts

- "Ping the VRED bridge and tell me if it's connected."
- "What version of VRED is running, and what scene file is open?"
- "Run the VRED diagnostic and tell me whether the bridge, version, and scene summary all look healthy."
- "Save the current scene."
- "Save this scene to a new file inside the approved save directory."
- "Create a new empty scene."
- "Give me a summary of the current scene."
- "Show the scene tree, two levels deep."
- "Find every node whose name contains wheel."
- "What's currently selected?"
- "Show info for the node with unique_path 2:Front."
- "List the cameras in the scene, and tell me which one is active."
- "Capture a scene snapshot to C:\\VRED_MCP_OUTPUT\\snapshot.png and summarize what you see."
- "Run the scene health report and tell me what looks off."
- "Compare these two scene snapshots and summarize the object and material changes."
- "What variant sets does this scene have?"
- "List the materials in the scene."
- "List the materials actually used in this scene."
- "Which materials are selected right now?"
- "Apply material 10 to 1:Front and 2:Body."
- "Select every node that uses material 10."
- "Select the node with unique_path 2:Front."
- "Hide the node with unique_path 3:Wheel_FL." (needs mutations enabled)
- "Do a dry run of hiding 3:Wheel_FL and show me what would change."
- "Activate the Front camera."
- "Move the node at 3:Wheel_FL to [100, 0, 0]."
- "Take a screenshot and save it to C:\VRED_MCP_OUTPUT\shot.png."

If you want a reusable vision prompt, see `client-configs/vision-prompt-template.md`.

Variant activation, scene loading, and save operations are available now,
but save/load/new are gated by `VRED_ALLOW_SAVE` and the save path
restrictions.

## 21. Known limitations

- Variant control is implemented for set contents and activation:
  `get_variant_set_details` and `activate_variant`.
- Save and scene-lifecycle tools are implemented (`save_scene`,
  `save_scene_as`, `load_scene`, `new_scene`), gated by `VRED_ALLOW_SAVE`
  and restricted to `VRED_ALLOWED_SAVE_DIR` for writes.
- `get_vred_version`'s version fields come from static install metadata,
  not a live VRED API call - see `VRED_API_COMPATIBILITY.md` for why.
- `capture_screenshot`'s allowed-extension whitelist (`.png`/`.jpg`/`.jpeg`/`.bmp`)
  is a reasonable assumption, not confirmed against VRED's actual writer
  support; it also doesn't call `updateRender()` first, so it captures
  whatever's currently rendered, not necessarily a fresh frame.
- Translation/rotation/scale units: VRED's UI was observed set to
  millimeters, but whether the Python API's raw values always match that
  display unit wasn't independently confirmed.
- The bridge's Python console session (VRED's Terminal panel) resets its
  globals whenever that panel is closed and reopened - see section 11 for
  what that does and doesn't affect, and for the separate gotcha about
  reloading *code* changes without closing the panel.
(No known limitations remain around client integration - both Claude Code
and Codex were verified end to end with real tool calls, not just
connection checks. See `IMPLEMENTATION_PLAN.md` section 12.)

## 22. Security limitations

- The bridge has no rate limiting beyond the request-body size cap - a
  local process that has your token can call it as fast as it wants. This
  is considered acceptable for a single-user localhost tool but is not a
  DoS-hardened service.
- `VRED_MAX_JOBS_PER_TICK` (hardcoded to 50 in `vred_mcp_bridge.py`) bounds
  per-tick work but a sustained flood of requests could still keep VRED's
  main thread busier than normal.
- The token lives in plaintext in `.env` and in `bridge_config.py`. Treat
  both like any other local secret (they're gitignored; don't commit them).
- `VRED_INSECURE_PYTHON=1` (section 11) disables VRED's own Python sandbox
  for the whole launched VRED process, not just this bridge script. It's
  scoped to that one process (not persisted), but be aware it also removes
  sandbox protection from anything else you run in that VRED session's
  console while it's active.

## 23. Troubleshooting

| Symptom | Likely cause |
|---|---|
| `vred-mcp: configuration error: ...` on server startup | `.env` missing/invalid - message says exactly which field. |
| `vred_ping` returns `BRIDGE_UNREACHABLE` | VRED isn't running, or the bridge script hasn't been loaded/run in the Terminal console yet. Run `.\scripts\test_connection.ps1` to confirm. |
| `vred_ping` returns `AUTHENTICATION_FAILED` | Token mismatch between `.env` and `bridge_config.py`/`VRED_BRIDGE_TOKEN` - regenerate with `generate_token.ps1` and update both. |
| Bridge prints `REFUSING TO START: 'vrTimer' is not defined` | The script was pasted without first setting `__file__`, or it's not actually running inside VRED's Terminal console. |
| A popup says "A Python function is currently blocked by VRED" | VRED's Python sandbox is on. Click "Allow for this project" to continue, or relaunch VRED with `VRED_INSECURE_PYTHON=1` (section 11) to avoid repeated popups as different functions get exercised. |
| `NameError: name 'stop_bridge' is not defined` after previously loading the bridge | The Terminal panel was closed and reopened, which resets its Python session. If a bridge was already running before that, it's likely still up - check with `.\scripts\test_connection.ps1` before reloading (reloading while one is already running just needs the two-line load again). |
| `REQUEST_TIMEOUT` | VRED may be busy (long-running operation blocking the render loop) or the vrTimer wasn't activated - check the Terminal output for `[vred-mcp-bridge]` startup lines. |
| A mutation tool returns `MUTATIONS_DISABLED` | Expected default behavior. Either pass `dry_run=true` to preview, or enable mutations on both sides (section 17). |
| A mutation tool worked before but now returns `UNSUPPORTED_OPERATION` after you edited `command_registry.py` | You need the `sys.modules` eviction reload, not the plain two-line one - see section 11 / `vred_bridge/README.md`. |
| `NODE_NOT_FOUND` from a bulk tool (`select_nodes`, `hide_nodes`, ...) | Check `details.missing` in the response - it lists exactly which `unique_path`(s) didn't resolve. The whole call is rejected rather than partially applied. |

## 24. How to stop the bridge

From VRED's Terminal console: `stop_bridge()`. Or just close VRED - the
bridge is inside VRED's process, so nothing is left running afterward.

## 25. How to uninstall / remove the MCP configuration

```powershell
claude mcp remove vred-mcp     # if added to Claude Code
codex mcp remove vred-mcp      # if added to Codex
```

Then delete the project directory and (if you set one) any persistent
`VRED_BRIDGE_TOKEN` environment variable you configured outside `.env`.