Skip to main content
Glama
bigsbypuglise

Fusion Studio MCP

README.md
# Fusion Studio MCP

Local MCP server for inspecting and controlling standalone Blackmagic Fusion Studio through Fusion's scripting API.

This project starts conservatively: prove the Fusion connection first, then expose read-only inspection tools, then add guarded edit tools with dry-run and undo behavior.

## Current Status

- Project scaffold created: 2026-07-13
- Target app found: Blackmagic Fusion 21.0.2
- Python import path confirmed for Fusion's bundled `fusionscript.so`
- Live composition inspection verified against `Composition1`
- Local `.venv` created with pytest, ruff, and the Python MCP SDK
- Read-only MCP tools implemented for status, node listing, single-node lookup, selected-node reads, and compact comp summaries
- Guarded editing MCP tools implemented for selection, input reads/writes, renaming, and FlowView positioning
- Controlled node creation, image connection editing, input disconnect, connection inspection, and confirmed single-node deletion implemented
- Configurable node creation plus native keyframe and expression tools implemented
- Composition controls plus transactional graph and batch tools implemented
- Render validation, Saver settings, diagnostics, tool catalog/schema, and graph export tools implemented
- Live editing validation passed against disposable `Composition1`

## Requirements

- macOS
- Blackmagic Fusion Studio 21.0.2 or compatible
- Python 3.11+ required (`requires-python = ">=3.11"`); macOS's built-in Python 3.9 is too old
- Fusion's bundled scripting library path available:
  `/Applications/Blackmagic Fusion 21/Fusion.app/Contents/Libraries`
- Python MCP SDK for server mode

## Install with Claude Desktop on macOS

Recommended for artists and non-developers: [Install Fusion Studio MCP - Claude Desktop
(macOS)](docs/INSTALL_CLAUDE_DESKTOP_MACOS.md).

This is the public, two-copy-paste setup path for standalone Fusion Studio and Claude Desktop on
macOS. It installs from the public GitHub repository:
<https://github.com/bigsbypuglise/fusion-studio-mcp>

Use this path if you want Claude Desktop to inspect a Fusion comp, list nodes, and build guarded
node trees without doing a developer setup.

Quick version:

1. Open Fusion Studio 21 and open or create a comp.
2. Run the install paste from the Claude Desktop macOS guide.
3. Run the Claude config paste from the guide.
4. Fully quit Claude Desktop with Command-Q, reopen it, and ask Claude to check the Fusion
   connection.

Notes:

- Fusion Studio 21 is recommended; Fusion Studio 20 may work.
- This currently targets standalone Fusion Studio, not DaVinci Resolve.
- Fusion Studio must be running with an active composition open.
- The first Claude tool call may ask for permission.
- The default novice installer tracks the latest public `main` branch so re-running paste #1 updates
  the local install. Users who prefer a pinned stable release can use the tagged-release option in
  the guide.

## Advanced Installation

The macOS workflow below has been live-tested with Fusion Studio 21.0.2. The Windows workflow is
documented for equivalent Fusion Studio installs, but still needs clean-machine validation on Windows.

### macOS Developer Setup

Supported live-tested environment:

- Blackmagic Fusion Studio 21.0.2
- Python 3.11+
- Confirmed app path: `/Applications/Blackmagic Fusion 21/Fusion.app`
- Confirmed scripting library path: `/Applications/Blackmagic Fusion 21/Fusion.app/Contents/Libraries`

Install Python with Homebrew if needed:

```bash
brew install python@3.11
```

Create and install the local environment:

```bash
cd /path/to/fusion-studio-mcp
PYTHON_BIN=/opt/homebrew/bin/python3.11 scripts/setup_local.sh
```

Or do it manually:

```bash
cd /path/to/fusion-studio-mcp
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -e '.[dev]'
```

Start Fusion Studio, create or open a disposable composition, then run:

```bash
export PYTHONPATH="/Applications/Blackmagic Fusion 21/Fusion.app/Contents/Libraries:src"
python scripts/test_fusion_connection.py
python -m fusion_mcp.server
```

`python -m fusion_mcp.server` is a stdio MCP server entrypoint. It is meant to be launched by an MCP
client; when run directly in a plain terminal without a client attached, it may exit cleanly after
stdin closes. Use `scripts/test_fusion_connection.py` for a standalone connection smoke test.

macOS MCP client example:

```json
{
  "mcpServers": {
    "fusion-studio": {
      "command": "/path/to/fusion-studio-mcp/.venv/bin/python",
      "args": ["-m", "fusion_mcp.server"],
      "env": {
        "PYTHONPATH": "/Applications/Blackmagic Fusion 21/Fusion.app/Contents/Libraries:/path/to/fusion-studio-mcp/src"
      }
    }
  }
}
```

macOS troubleshooting:

- If Fusion is closed or no comp is active, connection tools return `FUSION_NOT_RUNNING` or `NO_ACTIVE_COMP`.
- If `fusionscript` cannot import, confirm `PYTHONPATH` includes Fusion's `Contents/Libraries` path.
- macOS may show security prompts the first time tools communicate with another app. Allow Fusion and the terminal/MCP client to communicate locally.
- Use unique absolute output folders for Saver renders to avoid overwrite protection and Fusion filename edge cases.

### Windows Developer Setup

Documented target environment:

- Blackmagic Fusion Studio 21.0.2 or compatible
- Python 3.11+
- Common app path: `C:\Program Files\Blackmagic Design\Fusion 21\Fusion.exe`
- Common scripting library path: `C:\Program Files\Blackmagic Design\Fusion 21`

Install Python from <https://www.python.org/downloads/windows/> and enable "Add python.exe to PATH",
or install with `winget`:

```powershell
winget install Python.Python.3.11
python --version
py -3.11 --version
```

Create and install the local environment from PowerShell:

```powershell
cd C:\path\to\fusion-studio-mcp
.\scripts\setup_local.ps1 -Python py -PythonArgs "-3.11"
.\.venv\Scripts\Activate.ps1
```

If PowerShell blocks the script, run it for the current process only:

```powershell
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
.\scripts\setup_local.ps1 -Python py -PythonArgs "-3.11"
```

Manual Command Prompt setup:

```bat
cd C:\path\to\fusion-studio-mcp
py -3.11 -m venv .venv
.venv\Scripts\activate.bat
python -m pip install --upgrade pip
python -m pip install -e .[dev]
```

Start Fusion Studio, create or open a disposable composition, then run from PowerShell:

```powershell
.\.venv\Scripts\Activate.ps1
$env:PYTHONPATH = "C:\Program Files\Blackmagic Design\Fusion 21;C:\path\to\fusion-studio-mcp\src"
.\.venv\Scripts\python.exe .\scripts\test_fusion_connection.py
.\.venv\Scripts\python.exe -m fusion_mcp.server
```

As on macOS, the server command is intended to be launched by an MCP client over stdio. If it is run
manually in a shell without a client attached, it may exit when stdin closes.

Windows MCP client example:

```json
{
  "mcpServers": {
    "fusion-studio": {
      "command": "C:\\path\\to\\fusion-studio-mcp\\.venv\\Scripts\\python.exe",
      "args": ["-m", "fusion_mcp.server"],
      "env": {
        "PYTHONPATH": "C:\\Program Files\\Blackmagic Design\\Fusion 21;C:\\path\\to\\fusion-studio-mcp\\src"
      }
    }
  }
}
```

Windows troubleshooting:

- These Windows steps are documented but not yet live-validated on a clean Windows machine.
- If `python` is not found, use `py -3.11` or reinstall Python with PATH integration enabled.
- If `fusionscript` cannot import, verify the Fusion install path and add its scripting directory to `PYTHONPATH`.
- Quote paths with spaces in PowerShell and JSON; use doubled backslashes in JSON strings.
- If connection fails, confirm Fusion Studio is open, not blocked by Windows security prompts, and has an active composition.
- Use absolute Saver output paths in writable folders, and prefer unique output folders per render.

## Quick Connection Test

1. Open standalone Fusion Studio.
2. Open or create a disposable composition.
3. Run:

```bash
cd /path/to/fusion-studio-mcp
PYTHONPATH="/Applications/Blackmagic Fusion 21/Fusion.app/Contents/Libraries:src" .venv/bin/python scripts/test_fusion_connection.py
```

The script prints structured JSON. If Fusion is closed, it should return `success: false` with `FUSION_NOT_RUNNING`.

## Read-Only Inspection Probe

Run all read-only tool functions without starting an MCP client:

```bash
cd /path/to/fusion-studio-mcp
PYTHONPATH="/Applications/Blackmagic Fusion 21/Fusion.app/Contents/Libraries:src" .venv/bin/python scripts/inspect_read_only.py
```

Current read-only tools:

- `fusion_get_status_tool`
- `fusion_list_nodes_tool`
- `fusion_get_node_tool(node_name)`
- `fusion_get_selected_nodes_tool`
- `fusion_summarize_comp_tool`
- `fusion_get_node_input_tool(node_name, input_name)`

Node list entries include:

- Node name and Fusion tool type
- Flow position from `comp.CurrentFrame.FlowView.GetPosTable(tool)` when Fusion exposes it
- Inputs with ID, name, data type, current non-image value, connected output, and input attrs
- Outputs with ID, name, data type, connected inputs, and output attrs
- Key settings summary for non-image controls

## Run MCP Server

After setup, run:

```bash
cd /path/to/fusion-studio-mcp
PYTHONPATH="/Applications/Blackmagic Fusion 21/Fusion.app/Contents/Libraries:src" .venv/bin/python -m fusion_mcp.server
```

Example MCP-style operations:

```text
fusion_get_status_tool()
fusion_list_nodes_tool()
fusion_get_node_tool(node_name="Blur1")
fusion_get_selected_nodes_tool()
fusion_summarize_comp_tool()
fusion_select_nodes_tool(node_names=["Background1", "Transform1"])
fusion_get_node_input_tool(node_name="Transform1", input_name="Blend")
fusion_set_node_input_tool(node_name="Transform1", input_name="Blend", value=0.5)
fusion_set_node_name_tool(node_name="Transform1", new_name="Transform_Main")
fusion_set_node_position_tool(node_name="Transform_Main", x=0, y=0)
fusion_create_node_tool(tool_id="Blur", name="Blur_Main", x=1, y=0)
fusion_create_node_tool(
  tool_id="Saver",
  name="Saver_Main",
  x=2,
  y=0,
  settings={"Clip": "/tmp/example.exr"}
)
fusion_connect_nodes_tool(
  source_node="Transform_Main",
  source_output="Output",
  target_node="Blur_Main",
  target_input="Input",
  replace=false
)
fusion_get_connections_tool(node_name="Blur_Main")
fusion_disconnect_input_tool(node_name="Blur_Main", input_name="Input")
fusion_delete_node_tool(node_name="Blur_Main", confirm=true)
fusion_set_keyframe_tool(node_name="Transform_Main", input_name="Size", frame=0, value=1.0, replace=false)
fusion_get_keyframes_tool(node_name="Transform_Main", input_name="Size")
fusion_delete_keyframe_tool(node_name="Transform_Main", input_name="Size", frame=10, confirm=true)
fusion_clear_keyframes_tool(node_name="Transform_Main", input_name="Center", confirm=true)
fusion_set_expression_tool(node_name="Transform_Main", input_name="Angle", expression="time/10", replace=false)
fusion_get_expression_tool(node_name="Transform_Main", input_name="Angle")
fusion_remove_expression_tool(node_name="Transform_Main", input_name="Angle", confirm=true)
fusion_get_comp_settings_tool()
fusion_set_comp_range_tool(start_frame=1, end_frame=48)
fusion_set_current_frame_tool(frame=24)
fusion_get_current_frame_tool()
fusion_save_comp_tool(path="/tmp/example.comp")
fusion_get_comp_path_tool()
fusion_create_graph_tool(
  nodes=[
    {"id": "bg", "tool_id": "Background", "name": "BG", "x": -4, "y": 0,
     "settings": {"TopLeftRed": 0.2, "TopLeftGreen": 0.4, "TopLeftBlue": 0.8}},
    {"id": "tx", "tool_id": "Transform", "name": "TX", "x": -2, "y": 0},
    {"id": "blur", "tool_id": "Blur", "name": "Blur", "x": 0, "y": 0,
     "settings": {"XBlurSize": 3.0}},
    {"id": "saver", "tool_id": "Saver", "name": "Saver", "x": 2, "y": 0,
     "settings": {"Clip": "/tmp/example.exr"}}
  ],
  connections=[
    {"source": "bg", "target": "tx", "source_output": "Output", "target_input": "Input"},
    {"source": "tx", "target": "blur", "source_output": "Output", "target_input": "Input"},
    {"source": "blur", "target": "saver", "source_output": "Output", "target_input": "Input"}
  ],
  comp_settings={"start_frame": 1, "end_frame": 48}
)
fusion_batch_set_inputs_tool(changes=[{"node_name": "BG", "input_name": "TopLeftRed", "value": 0.5}])
fusion_batch_set_keyframes_tool(keyframes=[{"node_name": "TX", "input_name": "Size", "frame": 1, "value": 1.0}])
fusion_batch_delete_nodes_tool(node_names=["Blur"], confirm=true)
fusion_get_saver_settings_tool(node_name="Saver")
fusion_set_saver_settings_tool(node_name="Saver", settings={"Clip": "/tmp/render.exr"})
fusion_validate_render_tool(saver_node="Saver", allow_overwrite=false)
fusion_render_tool(start_frame=1, end_frame=1, saver_node="Saver", confirm=true, allow_overwrite=false)
fusion_get_render_status_tool()
fusion_abort_render_tool(confirm=true)
fusion_validate_comp_tool()
fusion_find_broken_connections_tool()
fusion_find_missing_media_tool()
fusion_get_tool_catalog_tool(search="blur")
fusion_get_tool_schema_tool(tool_id="Blur")
fusion_export_graph_tool(node_names=["BG", "TX", "Saver"])
fusion_validate_graph_request_tool(nodes=[...], connections=[...], comp_settings={...})
```

Example compact summary response from live empty `Composition1`:

```json
{
  "success": true,
  "operation": "fusion_summarize_comp",
  "agent_summary": {
    "composition_name": "Composition1",
    "node_count": 0,
    "selected_nodes": [],
    "tool_type_counts": {},
    "plain_english": "Composition Composition1 has 0 node(s); 0 selected."
  }
}
```

## Safe Editing Tools

Editing operations validate the active composition, node, and input before modifying anything. Writes
use Fusion `Lock()`, `StartUndo(...)`, `EndUndo(True)`, and `Unlock()` via a defensive cleanup
wrapper so failed operations do not leave an undo group open.

Implemented editing tools:

- `fusion_select_nodes_tool(node_names)`
- `fusion_get_node_input_tool(node_name, input_name)`
- `fusion_set_node_input_tool(node_name, input_name, value)`
- `fusion_set_node_name_tool(node_name, new_name)`
- `fusion_set_node_position_tool(node_name, x, y)`
- `fusion_create_node_tool(tool_id, name=None, x=None, y=None)`
- `fusion_connect_nodes_tool(source_node, source_output, target_node, target_input, replace=False)`
- `fusion_disconnect_input_tool(node_name, input_name)`
- `fusion_get_connections_tool(node_name=None)`
- `fusion_delete_node_tool(node_name, confirm=False)`
- `fusion_set_keyframe_tool(node_name, input_name, frame, value, replace=False)`
- `fusion_get_keyframes_tool(node_name, input_name)`
- `fusion_delete_keyframe_tool(node_name, input_name, frame, confirm=False)`
- `fusion_clear_keyframes_tool(node_name, input_name, confirm=False)`
- `fusion_set_expression_tool(node_name, input_name, expression, replace=False)`
- `fusion_get_expression_tool(node_name, input_name)`
- `fusion_remove_expression_tool(node_name, input_name, confirm=False)`
- `fusion_get_comp_settings_tool()`
- `fusion_set_comp_range_tool(start_frame, end_frame)`
- `fusion_set_current_frame_tool(frame)`
- `fusion_get_current_frame_tool()`
- `fusion_save_comp_tool(path=None)`
- `fusion_get_comp_path_tool()`
- `fusion_create_graph_tool(nodes, connections, comp_settings=None)`
- `fusion_batch_set_inputs_tool(changes)`
- `fusion_batch_set_keyframes_tool(keyframes)`
- `fusion_batch_delete_nodes_tool(node_names, confirm=False)`
- `fusion_get_saver_settings_tool(node_name)`
- `fusion_set_saver_settings_tool(node_name, settings)`
- `fusion_validate_render_tool(saver_node=None, allow_overwrite=False)`
- `fusion_render_tool(start_frame=None, end_frame=None, saver_node=None, confirm=False, allow_overwrite=False)`
- `fusion_get_render_status_tool()`
- `fusion_abort_render_tool(confirm=False)`
- `fusion_validate_comp_tool()`
- `fusion_find_broken_connections_tool()`
- `fusion_find_missing_media_tool()`
- `fusion_get_tool_catalog_tool(search=None)`
- `fusion_get_tool_schema_tool(tool_id)`
- `fusion_export_graph_tool(node_names=None)`
- `fusion_validate_graph_request_tool(nodes, connections, comp_settings=None)`

`fusion_set_node_input_tool` supports common Fusion value types:

- finite scalar numbers
- booleans, converted to `1` or `0` for numeric Fusion inputs
- strings for `Text` inputs
- strings for `FuID` inputs, validated against per-input choices when Fusion exposes them
- points as `[x, y]` or `{"x": x, "y": y}`
- colors as `[r, g, b]`, `[r, g, b, a]`, or matching dict keys when Fusion exposes a true `Color` input

Unsupported or ambiguous values return a structured failure and do not attempt a write. Image inputs
are rejected by value-setting tools because they must be edited as connections, not scalar values.

Built-in Background color controls in Fusion Studio 21.0.2 are exposed as separate numeric channel
inputs such as `TopLeftRed`, `TopLeftGreen`, `TopLeftBlue`, and `TopLeftAlpha`, each using
`INPID_InputControl == "ColorControl"` and `INPS_DataType == "Number"`. Live validation confirmed
setting these channels with scalar floats, for example `TopLeftRed = 0.25`.

Example success response:

```json
{
  "success": true,
  "operation": "fusion_set_node_input",
  "node_name": "FMC_Test_Transform",
  "input_id": "Size",
  "previous_value": 1.0,
  "requested_value": 0.8,
  "confirmed_value": 0.8,
  "summary": "Set FMC_Test_Transform.Size."
}
```

Example validation failure:

```json
{
  "success": false,
  "operation": "fusion_set_node_input",
  "error_code": "UNSUPPORTED_INPUT_VALUE",
  "error_message": "Image inputs are connections, not scalar values.",
  "node_name": "FMC_Test_Transform",
  "input_id": "Input",
  "requested_value": 1
}
```

## Connection And Node Tools

`fusion_create_node_tool` accepts optional `settings`, applied after the node exists and is
validated. Setting failures produce a partial result: the created node remains in the comp, successful
settings are reported, and failed settings include per-input error codes. `Saver` can receive its
Clip path through `settings={"Clip": "/path/to/output.exr"}` and no longer needs the placeholder when
that setting is supplied.

Connection editing is currently scoped to Image flow links. The tool layer rejects modifier,
expression, mask, and scalar links with explicit socket metadata instead of pretending they are
ordinary image pipes.

Safety behavior:

- tool IDs are checked against Fusion's local registry before creation
- `MediaOut` is rejected in standalone Fusion because its registry class is not creatable there
- `Saver` is creatable with an explicit placeholder Clip path to avoid Fusion opening a file dialog
- target inputs are not replaced unless `replace=true`
- self-connections and obvious image-flow cycles are rejected
- deletion requires `confirm=true` and only accepts one node name per call
- all create/connect/disconnect/delete operations use Fusion undo groups

Example connection success:

```json
{
  "success": true,
  "operation": "fusion_connect_nodes",
  "previous_state": null,
  "requested_state": {
    "source_node": "Background1",
    "source_output": "Output",
    "target_node": "Transform1",
    "target_input": "Input",
    "replace": false
  },
  "confirmed_state": {
    "node": "Background1",
    "output": "Output",
    "data_type": "Image"
  }
}
```

Example delete guard:

```json
{
  "success": false,
  "operation": "fusion_delete_node",
  "error_code": "DELETE_CONFIRMATION_REQUIRED",
  "error_message": "Deletion requires confirm=true."
}
```

## Composition And Batch Tools

Composition controls expose frame range, current frame, save path, and explicit `.comp` saves. Live
validation confirmed `fusion_set_comp_range_tool(1, 48)`, `fusion_set_current_frame_tool(24)`, and
`fusion_save_comp_tool("/tmp/fusionmcp_batch_*.comp")` in standalone Fusion Studio 21.0.2.

`fusion_create_graph_tool` accepts:

- `nodes`: objects with `id`, `tool_id` or `tool_type`, optional `name`, optional `x`/`y`, and optional `settings`
- `connections`: objects with graph node ids in `source`/`target`, plus `source_output` and `target_input`
- `comp_settings`: currently supports `start_frame`/`end_frame` or `global_start`/`global_end`

Graph creation validates duplicate names, missing graph refs, invalid tool ids, obvious cycles, and
unsupported values before or during the transaction. It creates nodes first, applies settings, then
connects Image sockets. If a later step fails, it attempts to disconnect links created by that request
and delete only nodes created by that request, returning `status: "rolled_back"` with per-item
rollback results.

Batch input/keyframe/delete tools use one Fusion undo group per batch. Batch deletion requires
`confirm=true` and only deletes explicitly listed nodes.

## Render, Diagnostics, And Export

Render execution requires `confirm=true`. `fusion_validate_render_tool` checks active comp state,
Saver existence/type, absolute output path, supported file extension, missing output directory,
overwrite risk, valid frame range, and connected Saver image input without intentionally rendering.
Existing outputs return `OUTPUT_ALREADY_EXISTS` unless `allow_overwrite=true` is explicitly supplied.

`fusion_render_tool` with a specific `saver_node` uses the canonical live-confirmed Saver transaction:
it keeps the original Saver object, does not rewrite `Clip`, does not poll render status before
rendering, calls `comp.Render` once with `Start`, `End`, `Wait`, and `Tool`, then confirms output files
from the Saver Clip path on disk. If Fusion reports completion but no expected output file appears,
the tool returns `RENDER_OUTPUT_MISSING` with render trace details.

Diagnostics include comp range checks, disconnected required image inputs, missing Loader paths,
invalid Saver paths, expressions that evaluate to `None`, unresolved tool IDs, and duplicate names
where detectable.

`fusion_export_graph_tool` emits `fusionmcp.graph.v1` with exact tool IDs, input IDs, safe settings,
FlowView positions, image-flow connections, and native keyframes where safely readable.

## Animation And Expressions

Native keyframes use Fusion's own modifiers:

- scalar inputs use `BezierSpline`
- Point inputs use `Path`
- expressions use `input.SetExpression(...)`

The tools distinguish `static`, `keyframed`, `expression`, and other `modifier` states. They reject
silent overwrites: setting a keyframe on an expression input, setting an expression on a keyframed
input, or setting a duplicate keyframe returns a structured error. Removing animation/expression data
requires `confirm=true`.

Live-confirmed examples:

```json
{
  "success": true,
  "operation": "fusion_get_keyframes",
  "keyframes": [
    { "frame": 0.0, "value": 1.0 },
    { "frame": 20.0, "value": 3.0 }
  ],
  "state": {
    "state_type": "keyframed",
    "modifier_type": "BezierSpline"
  }
}
```

Bad expressions are rejected when Fusion accepts the expression string but it evaluates to `None` at
the current frame.

## Testing

Run unit tests and lint:

```bash
cd /path/to/fusion-studio-mcp
.venv/bin/python -m pytest
.venv/bin/python -m ruff check src tests scripts
```

Run the opt-in live tests only against a disposable active Fusion composition:

```bash
RUN_FUSION_LIVE_TESTS=1 PYTHONPATH="/Applications/Blackmagic Fusion 21/Fusion.app/Contents/Libraries:src" .venv/bin/python -m pytest tests/test_live_editing.py tests/test_live_connections.py tests/test_live_animation.py -q
```

## Safety

Write tools intentionally modify the active composition. Use them only on disposable or explicitly
approved comps until broader production safeguards are added.

Read-only inspection intentionally does not call `Output.GetValue()` because that can trigger upstream rendering. Output inspection only reads output attrs and connection metadata.

Production compositions should not be used for integration testing.

Standalone Fusion Studio 21.0.2 treats `MediaOut` as Resolve-specific for this project. Standalone
Fusion does not require a terminal node. `Saver` is supported as a normal standalone tool when created
without `AddTool` defsettings and then assigned `Clip` after creation; no render is triggered by
creation or inspection.

Render release note: live isolation on 2026-07-14 found that Saver `Clip` writes can read back as a
valid string while the saved `.comp` contains a zero-span internal `Clip` object (`Length = 0`,
`GlobalStart/GlobalEnd = -2000000000`). In that state `comp.Render(...)` returns success and reports
the frame rendered, but no file is written. `fusion_render_tool` no longer rewrites Saver `Clip`
immediately before render; validation remains side-effect-free.

Use `scripts/diagnose_saver_initialization.py` for live Saver initialization probes. The current raw
working sequence is to create an unlocked fresh comp, lock while adding `Background`/`Saver`, set
Saver `Clip` after creation, connect while locked, unlock, and render with `Tool=saver`. The harness
and public `fusion_render_tool` now share that transaction; five consecutive public graph/validate/render
cycles wrote EXR files successfully.

## Troubleshooting

- Fusion connection: make sure standalone Fusion Studio is open and has an active composition.
- Python paths: include Fusion's bundled library path in `PYTHONPATH`, followed by this repo's `src`.
- Saver paths: use absolute paths, supported extensions, existing output folders, and preferably unique folders per render.
- Permissions: render output directories must be writable by the current macOS user.
- Blocked renders: validate first with `fusion_validate_render_tool`; common errors are `OUTPUT_DIRECTORY_MISSING`, `OUTPUT_ALREADY_EXISTS`, `SAVER_INPUT_DISCONNECTED`, and `INVALID_RENDER_RANGE`.
- MediaOut: standalone Fusion Studio treats `MediaOut` as Resolve-specific in this project; use `Saver` for standalone renders.

## Versioning

The project uses semantic versioning. The current patch release is `0.1.1`: local Fusion Studio MCP
control with guarded editing, graph building, diagnostics, graph export, confirmed Saver-targeted
rendering, and macOS clean-machine acceptance notes. Reserve `1.0.0` for broader production
confidence across more Fusion/Resolve versions and larger real-world comps.

TDQS

B3.2/5.0

Scored across 45 tools

Disambiguation5/5

Every tool has a clearly distinct purpose and targeting a specific operation or resource. The descriptions are precise and avoid overlap, even for similar operations like single vs batch keyframe setting.

Naming Consistency5/5

All tools follow a consistent `fusion_<verb>_<noun>_tool` pattern using snake_case, with imperative verbs and clear nouns. No mixing of conventions.

Tool Count3/5

45 tools is high, exceeding the typical well-scoped range of 3-15. While the complexity of Fusion Studio may warrant many operations, the tool count feels bloated with many batch and single-operation variants that could be consolidated.

Completeness4/5

The tool surface covers a comprehensive set of CRUD-like operations for nodes, connections, inputs, keyframes, expressions, rendering, validation, and comp management. Minor gaps exist, such as the lack of a general 'set comp settings' tool beyond frame range.

Maintenance

ActivityMaintained
ResponsivenessNo issues