A Model Context Protocol (MCP) server providing complete coverage of the DaVinci Resolve Scripting API. Connect AI assistants (Claude, Cursor, Windsurf) to DaVinci Resolve and control every aspect of your post-production workflow through natural language. - 26-tool compound server — all 324 API methods grouped into 26 context-efficient tools (default) - Universal installer — single python install.
Add this skill
npx mdskills install samuelgursky/davinci-resolve-mcpComprehensive DaVinci Resolve MCP server with 100% API coverage, exceptional testing, and thoughtful dual-mode design
1# DaVinci Resolve MCP Server23[](https://github.com/samuelgursky/davinci-resolve-mcp/releases)4[](#api-coverage)5[-blue.svg)](#server-modes)6[](#test-results)7[](https://www.blackmagicdesign.com/products/davinciresolve)8[](https://www.python.org/downloads/)9[](https://opensource.org/licenses/MIT)1011A Model Context Protocol (MCP) server providing **complete coverage** of the DaVinci Resolve Scripting API. Connect AI assistants (Claude, Cursor, Windsurf) to DaVinci Resolve and control every aspect of your post-production workflow through natural language.1213### What's New in v2.0.11415- **26-tool compound server** — all 324 API methods grouped into 26 context-efficient tools (default)16- **Universal installer** — single `python install.py` for macOS/Windows/Linux, 9 MCP clients17- **Dedicated timeline_item actions** — retime/speed, transform, crop, composite, audio, keyframes with validation18- **Lazy Resolve connection** — server starts instantly, connects when first tool is called19- **Bug fixes** — CreateMagicMask param type, GetCurrentClipThumbnailImage args, Python 3.13+ warning2021## Key Stats2223| Metric | Value |24|--------|-------|25| MCP Tools | **26** compound (default) / **342** granular |26| API Methods Covered | **324/324** (100%) |27| Methods Live Tested | **319/324** (98.5%) |28| Live Test Pass Rate | **319/319** (100%) |29| API Object Classes | 13 |30| Tested Against | DaVinci Resolve 19.1.3 Studio |3132## API Coverage3334Every non-deprecated method in the DaVinci Resolve Scripting API is covered. The default compound server exposes **26 tools** that group related operations by action parameter, keeping LLM context windows lean. The full granular server provides **342 individual tools** for power users. Both modes cover all 13 API object classes:3536| Class | Methods | Tools | Description |37|-------|---------|-------|-------------|38| Resolve | 21 | 21 | App control, pages, layout presets, render/burn-in presets, keyframe mode |39| ProjectManager | 25 | 25 | Project CRUD, folders, databases, cloud projects, archive/restore |40| Project | 42 | 42 | Timelines, render pipeline, settings, LUTs, color groups |41| MediaStorage | 9 | 9 | Volumes, file browsing, media import, mattes |42| MediaPool | 27 | 27 | Folders, clips, timelines, metadata, stereo, sync |43| Folder | 8 | 8 | Clip listing, export, transcription |44| MediaPoolItem | 32 | 32 | Metadata, markers, flags, properties, proxy, transcription |45| Timeline | 56 | 56 | Tracks, markers, items, export, generators, titles, stills, stereo |46| TimelineItem | 76 | 76 | Properties, markers, Fusion comps, versions, takes, CDL, AI tools |47| Gallery | 8 | 8 | Albums, stills, power grades |48| GalleryStillAlbum | 6 | 6 | Stills management, import/export, labels |49| Graph | 11 | 22 | Node operations, LUTs, cache, grades (timeline + clip graph variants) |50| ColorGroup | 5 | 10 | Group management, pre/post clip node graphs |5152## Requirements5354- **DaVinci Resolve Studio** 18.5+ (macOS, Windows, or Linux) — the free edition does not support external scripting55- **Python 3.10–3.12** recommended (3.13+ may have ABI incompatibilities with Resolve's scripting library)56- DaVinci Resolve running with **Preferences > General > "External scripting using"** set to **Local**5758## Quick Start5960```bash61# Clone the repository62git clone https://github.com/samuelgursky/davinci-resolve-mcp.git63cd davinci-resolve-mcp6465# Make sure DaVinci Resolve is running, then:66python install.py67```6869The universal installer auto-detects your platform, finds your DaVinci Resolve installation, creates a virtual environment, and configures your MCP client — all in one step.7071### Supported MCP Clients7273The installer can automatically configure any of these clients:7475| Client | Config Written To |76|--------|-------------------|77| Claude Desktop | `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) |78| Claude Code | `.mcp.json` (project root) |79| Cursor | `~/.cursor/mcp.json` |80| VS Code (Copilot) | `.vscode/mcp.json` (workspace) |81| Windsurf | `~/.codeium/windsurf/mcp_config.json` |82| Cline | VS Code global storage |83| Roo Code | VS Code global storage |84| Zed | `~/.config/zed/settings.json` |85| Continue | `~/.continue/config.json` |86| JetBrains IDEs | Manual (Settings > Tools > AI Assistant > MCP) |8788You can configure multiple clients at once, or use `--clients manual` to get copy-paste config snippets.8990### Installer Options9192```bash93python install.py # Interactive mode94python install.py --clients all # Configure all clients95python install.py --clients cursor,claude-desktop # Specific clients96python install.py --clients manual # Just print the config97python install.py --dry-run --clients all # Preview without writing98python install.py --no-venv --clients cursor # Skip venv creation99```100101### Server Modes102103The MCP server comes in two modes:104105| Mode | File | Tools | Best For |106|------|------|-------|----------|107| **Compound** (default) | `src/server.py` | 26 | Most users — fast, clean, low context usage |108| **Full** | `src/resolve_mcp_server.py` | 342 | Power users who want one tool per API method |109110The compound server's `timeline_item` tool includes dedicated actions for common workflows:111112| Category | Actions | Parameters |113|----------|---------|------------|114| **Retime** | `get_retime`, `set_retime` | process (nearest, frame_blend, optical_flow), motion_estimation (0-6) |115| **Transform** | `get_transform`, `set_transform` | Pan, Tilt, ZoomX/Y, RotationAngle, AnchorPointX/Y, Pitch, Yaw, FlipX/Y |116| **Crop** | `get_crop`, `set_crop` | CropLeft, CropRight, CropTop, CropBottom, CropSoftness, CropRetain |117| **Composite** | `get_composite`, `set_composite` | Opacity, CompositeMode |118| **Audio** | `get_audio`, `set_audio` | Volume, Pan, AudioSyncOffset |119| **Keyframes** | `get_keyframes`, `add_keyframe`, `modify_keyframe`, `delete_keyframe`, `set_keyframe_interpolation` | property, frame, value, interpolation (Linear, Bezier, EaseIn, EaseOut, EaseInOut) |120121The installer uses the compound server by default. To use the full server:122```bash123python src/server.py --full # Launch full 342-tool server124# Or point your MCP config directly at src/resolve_mcp_server.py125```126127### Manual Configuration128129If you prefer to set things up yourself, add to your MCP client config:130131```json132{133 "mcpServers": {134 "davinci-resolve": {135 "command": "/path/to/venv/bin/python",136 "args": ["/path/to/davinci-resolve-mcp/src/server.py"]137 }138 }139}140```141142Platform-specific paths:143144| Platform | API Path | Library Path |145|----------|----------|-------------|146| macOS | `/Library/Application Support/Blackmagic Design/DaVinci Resolve/Developer/Scripting` | `fusionscript.so` in DaVinci Resolve.app |147| Windows | `C:\ProgramData\Blackmagic Design\DaVinci Resolve\Support\Developer\Scripting` | `fusionscript.dll` in Resolve install dir |148| Linux | `/opt/resolve/Developer/Scripting` | `/opt/resolve/libs/Fusion/fusionscript.so` |149150## Usage Examples151152Once connected, you can control DaVinci Resolve through natural language:153154```155"What version of DaVinci Resolve is running?"156"List all projects and open the one called 'My Film'"157"Create a new timeline called 'Assembly Cut' and add all clips from the media pool"158"Add a blue marker at the current playhead position with note 'Review this'"159"Set up a ProRes 422 HQ render for the current timeline"160"Export the timeline as an EDL"161"Switch to the Color page and grab a still"162"Create a Fusion composition on the selected clip"163```164165## Test Results166167All testing performed against **DaVinci Resolve 19.1.3 Studio** on macOS with live API calls (no mocks).168169| Phase | Tests | Pass Rate | Scope |170|-------|-------|-----------|-------|171| Phase 1 | 204/204 | 100% | Safe read-only operations across all classes |172| Phase 2 | 79/79 | 100% | Destructive operations with create-test-cleanup patterns |173| Phase 3 | 20/20 | 100% | Real media import, sync, transcription, database switching, Resolve.Quit |174| Phase 4 | 10/10 | 100% | AI/ML methods, Fusion clips, stereo, gallery stills |175| Phase 5 | 6/6 | 100% | Scene cuts, subtitles from audio, graph node cache/tools/enable |176| **Total** | **319/319** | **100%** | **98.5% of all API methods tested live** |177178### Untested Methods (5 of 324)179180| Method | Reason | Help Wanted |181|--------|--------|-------------|182| `PM.CreateCloudProject` | Requires DaVinci Resolve cloud infrastructure | Yes |183| `PM.LoadCloudProject` | Requires DaVinci Resolve cloud infrastructure | Yes |184| `PM.ImportCloudProject` | Requires DaVinci Resolve cloud infrastructure | Yes |185| `PM.RestoreCloudProject` | Requires DaVinci Resolve cloud infrastructure | Yes |186| `TL.AnalyzeDolbyVision` | Requires HDR/Dolby Vision content | Yes |187188---189190## Complete API Reference191192Every method in the DaVinci Resolve Scripting API and its test status. Methods are listed by object class.193194**Status Key:**195- ✅ = Tested live, returned expected result196- ⚠️ = Tested live, API accepted call (returned `False` — needs specific context to fully execute)197- ☁️ = Requires cloud infrastructure (untested)198- 🔬 = Requires specific content/hardware (untested — PRs welcome)199200### Resolve201202| # | Method | Status | Test Result / Notes |203|---|--------|--------|---------------------|204| 1 | `Fusion()` | ✅ | Returns Fusion object |205| 2 | `GetMediaStorage()` | ✅ | Returns MediaStorage object |206| 3 | `GetProjectManager()` | ✅ | Returns ProjectManager object |207| 4 | `OpenPage(pageName)` | ✅ | Switches Resolve page |208| 5 | `GetCurrentPage()` | ✅ | Returns current page name (e.g. `"edit"`) |209| 6 | `GetProductName()` | ✅ | Returns `"DaVinci Resolve Studio"` |210| 7 | `GetVersion()` | ✅ | Returns `[19, 1, 3, 7, '']` |211| 8 | `GetVersionString()` | ✅ | Returns `"19.1.3.7"` |212| 9 | `LoadLayoutPreset(presetName)` | ✅ | Loads saved layout |213| 10 | `UpdateLayoutPreset(presetName)` | ✅ | Updates existing preset |214| 11 | `ExportLayoutPreset(presetName, presetFilePath)` | ✅ | Exports preset to file |215| 12 | `DeleteLayoutPreset(presetName)` | ✅ | Deletes preset |216| 13 | `SaveLayoutPreset(presetName)` | ⚠️ | API accepts; returns `False` when preset name conflicts |217| 14 | `ImportLayoutPreset(presetFilePath, presetName)` | ✅ | Imports preset from file |218| 15 | `Quit()` | ✅ | Quits DaVinci Resolve |219| 16 | `ImportRenderPreset(presetPath)` | ⚠️ | API accepts; needs valid preset file |220| 17 | `ExportRenderPreset(presetName, exportPath)` | ⚠️ | API accepts; needs valid preset name |221| 18 | `ImportBurnInPreset(presetPath)` | ⚠️ | API accepts; needs valid preset file |222| 19 | `ExportBurnInPreset(presetName, exportPath)` | ⚠️ | API accepts; needs valid preset name |223| 20 | `GetKeyframeMode()` | ✅ | Returns keyframe mode |224| 21 | `SetKeyframeMode(keyframeMode)` | ⚠️ | API accepts; mode must match valid enum |225226### ProjectManager227228| # | Method | Status | Test Result / Notes |229|---|--------|--------|---------------------|230| 1 | `ArchiveProject(projectName, filePath, ...)` | ⚠️ | API accepts; archiving is slow |231| 2 | `CreateProject(projectName)` | ✅ | Creates new project |232| 3 | `DeleteProject(projectName)` | ⚠️ | Returns `False` if project is open |233| 4 | `LoadProject(projectName)` | ✅ | Returns Project object |234| 5 | `GetCurrentProject()` | ✅ | Returns current Project |235| 6 | `SaveProject()` | ✅ | Saves current project |236| 7 | `CloseProject(project)` | ✅ | Closes project |237| 8 | `CreateFolder(folderName)` | ✅ | Creates project folder |238| 9 | `DeleteFolder(folderName)` | ✅ | Deletes project folder |239| 10 | `GetProjectListInCurrentFolder()` | ✅ | Returns project name list |240| 11 | `GetFolderListInCurrentFolder()` | ✅ | Returns folder name list |241| 12 | `GotoRootFolder()` | ✅ | Navigates to root |242| 13 | `GotoParentFolder()` | ✅ | Returns `False` at root (expected) |243| 14 | `GetCurrentFolder()` | ✅ | Returns current folder name |244| 15 | `OpenFolder(folderName)` | ✅ | Opens folder |245| 16 | `ImportProject(filePath, projectName)` | ✅ | Imports .drp file |246| 17 | `ExportProject(projectName, filePath, ...)` | ✅ | Exports .drp file |247| 18 | `RestoreProject(filePath, projectName)` | ⚠️ | API accepts; needs backup archive |248| 19 | `GetCurrentDatabase()` | ✅ | Returns `{DbType, DbName}` |249| 20 | `GetDatabaseList()` | ✅ | Returns list of databases |250| 21 | `SetCurrentDatabase({dbInfo})` | ✅ | Switches database |251| 22 | `CreateCloudProject({cloudSettings})` | ☁️ | Requires cloud infrastructure |252| 23 | `LoadCloudProject({cloudSettings})` | ☁️ | Requires cloud infrastructure |253| 24 | `ImportCloudProject(filePath, {cloudSettings})` | ☁️ | Requires cloud infrastructure |254| 25 | `RestoreCloudProject(folderPath, {cloudSettings})` | ☁️ | Requires cloud infrastructure |255256### Project257258| # | Method | Status | Test Result / Notes |259|---|--------|--------|---------------------|260| 1 | `GetMediaPool()` | ✅ | Returns MediaPool object |261| 2 | `GetTimelineCount()` | ✅ | Returns integer count |262| 3 | `GetTimelineByIndex(idx)` | ✅ | Returns Timeline object |263| 4 | `GetCurrentTimeline()` | ✅ | Returns current Timeline |264| 5 | `SetCurrentTimeline(timeline)` | ✅ | Sets active timeline |265| 6 | `GetGallery()` | ✅ | Returns Gallery object |266| 7 | `GetName()` | ✅ | Returns project name |267| 8 | `SetName(projectName)` | ⚠️ | Returns `False` on open project |268| 9 | `GetPresetList()` | ✅ | Returns preset list with dimensions |269| 10 | `SetPreset(presetName)` | ⚠️ | API accepts; preset must exist |270| 11 | `AddRenderJob()` | ✅ | Returns job ID string |271| 12 | `DeleteRenderJob(jobId)` | ✅ | Deletes render job |272| 13 | `DeleteAllRenderJobs()` | ✅ | Clears render queue |273| 14 | `GetRenderJobList()` | ✅ | Returns job list |274| 15 | `GetRenderPresetList()` | ✅ | Returns preset names |275| 16 | `StartRendering(...)` | ✅ | Starts render |276| 17 | `StopRendering()` | ✅ | Stops render |277| 18 | `IsRenderingInProgress()` | ✅ | Returns `False` when idle |278| 19 | `LoadRenderPreset(presetName)` | ✅ | Loads render preset |279| 20 | `SaveAsNewRenderPreset(presetName)` | ✅ | Creates render preset |280| 21 | `DeleteRenderPreset(presetName)` | ✅ | Deletes render preset |281| 22 | `SetRenderSettings({settings})` | ✅ | Applies render settings |282| 23 | `GetRenderJobStatus(jobId)` | ✅ | Returns `{JobStatus, CompletionPercentage}` |283| 24 | `GetQuickExportRenderPresets()` | ✅ | Returns preset names |284| 25 | `RenderWithQuickExport(preset, {params})` | ✅ | Initiates quick export |285| 26 | `GetSetting(settingName)` | ✅ | Returns project settings dict |286| 27 | `SetSetting(settingName, settingValue)` | ✅ | Sets project setting |287| 28 | `GetRenderFormats()` | ✅ | Returns format map |288| 29 | `GetRenderCodecs(renderFormat)` | ✅ | Returns codec map |289| 30 | `GetCurrentRenderFormatAndCodec()` | ✅ | Returns `{format, codec}` |290| 31 | `SetCurrentRenderFormatAndCodec(format, codec)` | ✅ | Sets format and codec |291| 32 | `GetCurrentRenderMode()` | ✅ | Returns mode integer |292| 33 | `SetCurrentRenderMode(renderMode)` | ✅ | Sets render mode |293| 34 | `GetRenderResolutions(format, codec)` | ✅ | Returns resolution list |294| 35 | `RefreshLUTList()` | ✅ | Refreshes LUT list |295| 36 | `GetUniqueId()` | ✅ | Returns UUID string |296| 37 | `InsertAudioToCurrentTrackAtPlayhead(...)` | ⚠️ | Tested; needs Fairlight page context |297| 38 | `LoadBurnInPreset(presetName)` | ⚠️ | API accepts; preset must exist |298| 39 | `ExportCurrentFrameAsStill(filePath)` | ⚠️ | API accepts; needs valid playhead position |299| 40 | `GetColorGroupsList()` | ✅ | Returns color group list |300| 41 | `AddColorGroup(groupName)` | ✅ | Returns ColorGroup object |301| 42 | `DeleteColorGroup(colorGroup)` | ✅ | Deletes color group |302303### MediaStorage304305| # | Method | Status | Test Result / Notes |306|---|--------|--------|---------------------|307| 1 | `GetMountedVolumeList()` | ✅ | Returns mounted volume paths |308| 2 | `GetSubFolderList(folderPath)` | ✅ | Returns subfolder paths |309| 3 | `GetFileList(folderPath)` | ✅ | Returns file paths |310| 4 | `RevealInStorage(path)` | ✅ | Reveals path in Media Storage |311| 5 | `AddItemListToMediaPool(...)` | ✅ | Imports media, returns clips |312| 6 | `AddClipMattesToMediaPool(item, [paths], eye)` | ✅ | Adds clip mattes |313| 7 | `AddTimelineMattesToMediaPool([paths])` | ✅ | Returns MediaPoolItem list |314315### MediaPool316317| # | Method | Status | Test Result / Notes |318|---|--------|--------|---------------------|319| 1 | `GetRootFolder()` | ✅ | Returns root Folder |320| 2 | `AddSubFolder(folder, name)` | ✅ | Creates subfolder |321| 3 | `RefreshFolders()` | ✅ | Refreshes folder list |322| 4 | `CreateEmptyTimeline(name)` | ✅ | Creates timeline |323| 5 | `AppendToTimeline(...)` | ✅ | Appends clips, returns TimelineItems |324| 6 | `CreateTimelineFromClips(name, ...)` | ✅ | Creates timeline from clips |325| 7 | `ImportTimelineFromFile(filePath, {options})` | ✅ | Imports AAF/EDL/XML |326| 8 | `DeleteTimelines([timeline])` | ✅ | Deletes timelines |327| 9 | `GetCurrentFolder()` | ✅ | Returns current Folder |328| 10 | `SetCurrentFolder(folder)` | ✅ | Sets current folder |329| 11 | `DeleteClips([clips])` | ✅ | Deletes clips |330| 12 | `ImportFolderFromFile(filePath)` | ✅ | Imports DRB folder |331| 13 | `DeleteFolders([subfolders])` | ✅ | Deletes folders |332| 14 | `MoveClips([clips], targetFolder)` | ✅ | Moves clips |333| 15 | `MoveFolders([folders], targetFolder)` | ✅ | Moves folders |334| 16 | `GetClipMatteList(item)` | ✅ | Returns matte paths |335| 17 | `GetTimelineMatteList(folder)` | ✅ | Returns matte items |336| 18 | `DeleteClipMattes(item, [paths])` | ✅ | Deletes clip mattes |337| 19 | `RelinkClips([items], folderPath)` | ⚠️ | API accepts; needs offline clips |338| 20 | `UnlinkClips([items])` | ✅ | Unlinks clips |339| 21 | `ImportMedia([items])` | ✅ | Imports media files |340| 22 | `ExportMetadata(fileName, [clips])` | ✅ | Exports metadata CSV |341| 23 | `GetUniqueId()` | ✅ | Returns UUID string |342| 24 | `CreateStereoClip(left, right)` | ✅ | Creates stereo pair |343| 25 | `AutoSyncAudio([items], {settings})` | ⚠️ | Tested; needs matching A/V clips |344| 26 | `GetSelectedClips()` | ✅ | Returns selected clips |345| 27 | `SetSelectedClip(item)` | ✅ | Selects clip |346347### Folder348349| # | Method | Status | Test Result / Notes |350|---|--------|--------|---------------------|351| 1 | `GetClipList()` | ✅ | Returns clip list |352| 2 | `GetName()` | ✅ | Returns folder name |353| 3 | `GetSubFolderList()` | ✅ | Returns subfolder list |354| 4 | `GetIsFolderStale()` | ✅ | Returns `False` |355| 5 | `GetUniqueId()` | ✅ | Returns UUID string |356| 6 | `Export(filePath)` | ✅ | Exports DRB file |357| 7 | `TranscribeAudio()` | ✅ | Starts audio transcription |358| 8 | `ClearTranscription()` | ✅ | Clears transcription |359360### MediaPoolItem361362| # | Method | Status | Test Result / Notes |363|---|--------|--------|---------------------|364| 1 | `GetName()` | ✅ | Returns clip name |365| 2 | `GetMetadata(metadataType)` | ✅ | Returns metadata dict |366| 3 | `SetMetadata(type, value)` | ✅ | Sets metadata |367| 4 | `GetThirdPartyMetadata(type)` | ✅ | Returns third-party metadata |368| 5 | `SetThirdPartyMetadata(type, value)` | ✅ | Sets third-party metadata |369| 6 | `GetMediaId()` | ✅ | Returns media UUID |370| 7 | `AddMarker(frameId, color, name, note, duration, customData)` | ✅ | Adds marker |371| 8 | `GetMarkers()` | ✅ | Returns marker dict |372| 9 | `GetMarkerByCustomData(customData)` | ✅ | Finds marker by data |373| 10 | `UpdateMarkerCustomData(frameId, customData)` | ✅ | Updates marker data |374| 11 | `GetMarkerCustomData(frameId)` | ✅ | Returns custom data string |375| 12 | `DeleteMarkersByColor(color)` | ✅ | Deletes markers by color |376| 13 | `DeleteMarkerAtFrame(frameNum)` | ⚠️ | Returns `False` if no marker at frame |377| 14 | `DeleteMarkerByCustomData(customData)` | ⚠️ | Returns `False` if no match |378| 15 | `AddFlag(color)` | ✅ | Adds flag |379| 16 | `GetFlagList()` | ✅ | Returns flag colors |380| 17 | `ClearFlags(color)` | ✅ | Clears flags |381| 18 | `GetClipColor()` | ✅ | Returns clip color |382| 19 | `SetClipColor(colorName)` | ✅ | Sets clip color |383| 20 | `ClearClipColor()` | ✅ | Clears clip color |384| 21 | `GetClipProperty(propertyName)` | ✅ | Returns property dict |385| 22 | `SetClipProperty(propertyName, value)` | ⚠️ | API accepts; some properties read-only |386| 23 | `LinkProxyMedia(proxyMediaFilePath)` | ✅ | Links proxy media |387| 24 | `UnlinkProxyMedia()` | ✅ | Unlinks proxy media |388| 25 | `ReplaceClip(filePath)` | ✅ | Replaces clip source |389| 26 | `GetUniqueId()` | ✅ | Returns UUID string |390| 27 | `TranscribeAudio()` | ✅ | Starts audio transcription |391| 28 | `ClearTranscription()` | ✅ | Clears transcription |392| 29 | `GetAudioMapping()` | ✅ | Returns JSON audio mapping |393| 30 | `GetMarkInOut()` | ✅ | Returns mark in/out dict |394| 31 | `SetMarkInOut(in, out, type)` | ✅ | Sets mark in/out |395| 32 | `ClearMarkInOut(type)` | ✅ | Clears mark in/out |396397### Timeline398399| # | Method | Status | Test Result / Notes |400|---|--------|--------|---------------------|401| 1 | `GetName()` | ✅ | Returns timeline name |402| 2 | `SetName(timelineName)` | ⚠️ | Returns `False` on active timeline |403| 3 | `GetStartFrame()` | ✅ | Returns start frame |404| 4 | `GetEndFrame()` | ✅ | Returns end frame |405| 5 | `SetStartTimecode(timecode)` | ✅ | Sets start timecode |406| 6 | `GetStartTimecode()` | ✅ | Returns `"01:00:00:00"` |407| 7 | `GetTrackCount(trackType)` | ✅ | Returns track count |408| 8 | `AddTrack(trackType, subTrackType)` | ✅ | Adds track |409| 9 | `DeleteTrack(trackType, trackIndex)` | ✅ | Deletes track |410| 10 | `GetTrackSubType(trackType, trackIndex)` | ✅ | Returns sub-type (e.g. `"stereo"`) |411| 11 | `SetTrackEnable(trackType, trackIndex, enabled)` | ✅ | Enables/disables track |412| 12 | `GetIsTrackEnabled(trackType, trackIndex)` | ✅ | Returns enabled state |413| 13 | `SetTrackLock(trackType, trackIndex, locked)` | ✅ | Locks/unlocks track |414| 14 | `GetIsTrackLocked(trackType, trackIndex)` | ✅ | Returns lock state |415| 15 | `DeleteClips([timelineItems], ripple)` | ✅ | Deletes clips from timeline |416| 16 | `SetClipsLinked([timelineItems], linked)` | ✅ | Links/unlinks clips |417| 17 | `GetItemListInTrack(trackType, index)` | ✅ | Returns items on track |418| 18 | `AddMarker(frameId, color, name, note, duration, customData)` | ✅ | Adds timeline marker |419| 19 | `GetMarkers()` | ✅ | Returns marker dict |420| 20 | `GetMarkerByCustomData(customData)` | ✅ | Finds marker by data |421| 21 | `UpdateMarkerCustomData(frameId, customData)` | ✅ | Updates marker data |422| 22 | `GetMarkerCustomData(frameId)` | ✅ | Returns custom data |423| 23 | `DeleteMarkersByColor(color)` | ✅ | Deletes markers by color |424| 24 | `DeleteMarkerAtFrame(frameNum)` | ⚠️ | Returns `False` if no marker at frame |425| 25 | `DeleteMarkerByCustomData(customData)` | ⚠️ | Returns `False` if no match |426| 26 | `GetCurrentTimecode()` | ✅ | Returns timecode string |427| 27 | `SetCurrentTimecode(timecode)` | ⚠️ | Returns `False` if playback not active |428| 28 | `GetCurrentVideoItem()` | ✅ | Returns item at playhead |429| 29 | `GetCurrentClipThumbnailImage()` | ✅ | Returns thumbnail data |430| 30 | `GetTrackName(trackType, trackIndex)` | ✅ | Returns track name |431| 31 | `SetTrackName(trackType, trackIndex, name)` | ✅ | Sets track name |432| 32 | `DuplicateTimeline(timelineName)` | ✅ | Duplicates timeline |433| 33 | `CreateCompoundClip([items], {clipInfo})` | ✅ | Returns compound clip item |434| 34 | `CreateFusionClip([timelineItems])` | ✅ | Returns Fusion clip item |435| 35 | `ImportIntoTimeline(filePath, {options})` | ⚠️ | Tested; result depends on file format |436| 36 | `Export(fileName, exportType, exportSubtype)` | ✅ | Exports EDL/XML/AAF |437| 37 | `GetSetting(settingName)` | ✅ | Returns settings dict |438| 38 | `SetSetting(settingName, settingValue)` | ⚠️ | API accepts; some settings read-only |439| 39 | `InsertGeneratorIntoTimeline(name)` | ✅ | Inserts generator |440| 40 | `InsertFusionGeneratorIntoTimeline(name)` | ✅ | Inserts Fusion generator |441| 41 | `InsertFusionCompositionIntoTimeline()` | ✅ | Inserts Fusion composition |442| 42 | `InsertOFXGeneratorIntoTimeline(name)` | ⚠️ | API accepts; needs valid OFX plugin |443| 43 | `InsertTitleIntoTimeline(name)` | ✅ | Inserts title |444| 44 | `InsertFusionTitleIntoTimeline(name)` | ✅ | Inserts Fusion title |445| 45 | `GrabStill()` | ✅ | Returns GalleryStill object |446| 46 | `GrabAllStills(stillFrameSource)` | ✅ | Returns list of GalleryStill objects |447| 47 | `GetUniqueId()` | ✅ | Returns UUID string |448| 48 | `CreateSubtitlesFromAudio({settings})` | ✅ | Returns `True` — creates subtitles from audio |449| 49 | `DetectSceneCuts()` | ✅ | Returns `True` — detects scene cuts in timeline |450| 50 | `ConvertTimelineToStereo()` | ✅ | Converts timeline to stereo 3D |451| 51 | `GetNodeGraph()` | ✅ | Returns Graph object |452| 52 | `AnalyzeDolbyVision([items], analysisType)` | 🔬 | Requires HDR/Dolby Vision content |453| 53 | `GetMediaPoolItem()` | ✅ | Returns MediaPoolItem for timeline |454| 54 | `GetMarkInOut()` | ✅ | Returns mark in/out dict |455| 55 | `SetMarkInOut(in, out, type)` | ✅ | Sets mark in/out |456| 56 | `ClearMarkInOut(type)` | ✅ | Clears mark in/out |457458### TimelineItem459460| # | Method | Status | Test Result / Notes |461|---|--------|--------|---------------------|462| 1 | `GetName()` | ✅ | Returns item name |463| 2 | `GetDuration(subframe_precision)` | ✅ | Returns duration |464| 3 | `GetEnd(subframe_precision)` | ✅ | Returns end frame |465| 4 | `GetSourceEndFrame()` | ✅ | Returns source end frame |466| 5 | `GetSourceEndTime()` | ✅ | Returns source end time |467| 6 | `GetFusionCompCount()` | ✅ | Returns comp count |468| 7 | `GetFusionCompByIndex(compIndex)` | ✅ | Returns Fusion composition |469| 8 | `GetFusionCompNameList()` | ✅ | Returns comp names |470| 9 | `GetFusionCompByName(compName)` | ✅ | Returns Fusion composition |471| 10 | `GetLeftOffset(subframe_precision)` | ✅ | Returns left offset |472| 11 | `GetRightOffset(subframe_precision)` | ✅ | Returns right offset |473| 12 | `GetStart(subframe_precision)` | ✅ | Returns start frame |474| 13 | `GetSourceStartFrame()` | ✅ | Returns source start |475| 14 | `GetSourceStartTime()` | ✅ | Returns source start time |476| 15 | `SetProperty(propertyKey, propertyValue)` | ✅ | Sets item property |477| 16 | `GetProperty(propertyKey)` | ✅ | Returns property dict |478| 17 | `AddMarker(frameId, color, name, note, duration, customData)` | ✅ | Adds marker to item |479| 18 | `GetMarkers()` | ✅ | Returns marker dict |480| 19 | `GetMarkerByCustomData(customData)` | ✅ | Finds marker by data |481| 20 | `UpdateMarkerCustomData(frameId, customData)` | ✅ | Updates marker data |482| 21 | `GetMarkerCustomData(frameId)` | ✅ | Returns custom data |483| 22 | `DeleteMarkersByColor(color)` | ✅ | Deletes markers by color |484| 23 | `DeleteMarkerAtFrame(frameNum)` | ⚠️ | Returns `False` if no marker at frame |485| 24 | `DeleteMarkerByCustomData(customData)` | ⚠️ | Returns `False` if no match |486| 25 | `AddFlag(color)` | ✅ | Adds flag |487| 26 | `GetFlagList()` | ✅ | Returns flag colors |488| 27 | `ClearFlags(color)` | ✅ | Clears flags |489| 28 | `GetClipColor()` | ✅ | Returns clip color |490| 29 | `SetClipColor(colorName)` | ✅ | Sets clip color |491| 30 | `ClearClipColor()` | ✅ | Clears clip color |492| 31 | `AddFusionComp()` | ✅ | Creates Fusion composition |493| 32 | `ImportFusionComp(path)` | ✅ | Imports .comp file |494| 33 | `ExportFusionComp(path, compIndex)` | ✅ | Exports .comp file |495| 34 | `DeleteFusionCompByName(compName)` | ⚠️ | Returns `False` if comp not found |496| 35 | `LoadFusionCompByName(compName)` | ✅ | Loads composition |497| 36 | `RenameFusionCompByName(oldName, newName)` | ✅ | Renames composition |498| 37 | `AddVersion(versionName, versionType)` | ✅ | Adds grade version |499| 38 | `GetCurrentVersion()` | ✅ | Returns version info |500| 39 | `DeleteVersionByName(versionName, versionType)` | ⚠️ | Returns `False` if version not found |501| 40 | `LoadVersionByName(versionName, versionType)` | ✅ | Loads grade version |502| 41 | `RenameVersionByName(oldName, newName, type)` | ✅ | Renames version |503| 42 | `GetVersionNameList(versionType)` | ✅ | Returns version names |504| 43 | `GetMediaPoolItem()` | ✅ | Returns source MediaPoolItem |505| 44 | `GetStereoConvergenceValues()` | ✅ | Returns stereo keyframes |506| 45 | `GetStereoLeftFloatingWindowParams()` | ✅ | Returns stereo params |507| 46 | `GetStereoRightFloatingWindowParams()` | ✅ | Returns stereo params |508| 47 | `SetCDL([CDL map])` | ✅ | Sets CDL values |509| 48 | `AddTake(mediaPoolItem, startFrame, endFrame)` | ✅ | Adds take |510| 49 | `GetSelectedTakeIndex()` | ✅ | Returns selected take index |511| 50 | `GetTakesCount()` | ✅ | Returns take count |512| 51 | `GetTakeByIndex(idx)` | ✅ | Returns take info |513| 52 | `DeleteTakeByIndex(idx)` | ✅ | Deletes take |514| 53 | `SelectTakeByIndex(idx)` | ✅ | Selects take |515| 54 | `FinalizeTake()` | ⚠️ | Returns `False` when no take selected |516| 55 | `CopyGrades([tgtTimelineItems])` | ⚠️ | API accepts; needs matching items |517| 56 | `SetClipEnabled(enabled)` | ✅ | Enables/disables clip |518| 57 | `GetClipEnabled()` | ✅ | Returns enabled state |519| 58 | `UpdateSidecar()` | ⚠️ | Returns `False` for non-BRAW clips |520| 59 | `GetUniqueId()` | ✅ | Returns UUID string |521| 60 | `LoadBurnInPreset(presetName)` | ⚠️ | API accepts; preset must exist |522| 61 | `CreateMagicMask(mode)` | ⚠️ | Tested; needs DaVinci Neural Engine + Color page context |523| 62 | `RegenerateMagicMask()` | ⚠️ | Tested; needs existing mask |524| 63 | `Stabilize()` | ✅ | Returns `True` on supported clips |525| 64 | `SmartReframe()` | ⚠️ | Tested; needs specific aspect ratio setup |526| 65 | `GetNodeGraph(layerIdx)` | ✅ | Returns Graph object |527| 66 | `GetColorGroup()` | ✅ | Returns ColorGroup |528| 67 | `AssignToColorGroup(colorGroup)` | ✅ | Assigns to group |529| 68 | `RemoveFromColorGroup()` | ⚠️ | Returns `False` if not in group |530| 69 | `ExportLUT(exportType, path)` | ✅ | Exports LUT file |531| 70 | `GetLinkedItems()` | ✅ | Returns linked items |532| 71 | `GetTrackTypeAndIndex()` | ✅ | Returns `[trackType, trackIndex]` |533| 72 | `GetSourceAudioChannelMapping()` | ✅ | Returns audio mapping |534| 73 | `GetIsColorOutputCacheEnabled()` | ✅ | Returns cache state |535| 74 | `GetIsFusionOutputCacheEnabled()` | ✅ | Returns cache state |536| 75 | `SetColorOutputCache(cache_value)` | ⚠️ | Tested; needs active color pipeline |537| 76 | `SetFusionOutputCache(cache_value)` | ⚠️ | Tested; needs active Fusion pipeline |538539### Gallery540541| # | Method | Status | Test Result / Notes |542|---|--------|--------|---------------------|543| 1 | `GetAlbumName(galleryStillAlbum)` | ✅ | Returns album name |544| 2 | `SetAlbumName(galleryStillAlbum, albumName)` | ✅ | Sets album name |545| 3 | `GetCurrentStillAlbum()` | ✅ | Returns GalleryStillAlbum |546| 4 | `SetCurrentStillAlbum(galleryStillAlbum)` | ✅ | Sets current album |547| 5 | `GetGalleryStillAlbums()` | ✅ | Returns album list |548| 6 | `GetGalleryPowerGradeAlbums()` | ✅ | Returns power grade albums |549| 7 | `CreateGalleryStillAlbum()` | ✅ | Creates still album |550| 8 | `CreateGalleryPowerGradeAlbum()` | ✅ | Creates power grade album |551552### GalleryStillAlbum553554| # | Method | Status | Test Result / Notes |555|---|--------|--------|---------------------|556| 1 | `GetStills()` | ✅ | Returns list of GalleryStill objects |557| 2 | `GetLabel(galleryStill)` | ✅ | Returns label string |558| 3 | `SetLabel(galleryStill, label)` | ⚠️ | API accepts; may not persist in all versions |559| 4 | `ImportStills([filePaths])` | ✅ | Imports DRX still files (requires Color page) |560| 5 | `ExportStills([stills], folderPath, prefix, format)` | ✅ | Exports stills as DRX+DPX (requires Color page) |561| 6 | `DeleteStills([galleryStill])` | ✅ | Deletes stills from album |562563### Graph564565| # | Method | Status | Test Result / Notes |566|---|--------|--------|---------------------|567| 1 | `GetNumNodes()` | ✅ | Returns node count (via ColorGroup pre/post graphs) |568| 2 | `SetLUT(nodeIndex, lutPath)` | ✅ | Sets LUT on node |569| 3 | `GetLUT(nodeIndex)` | ✅ | Returns LUT path |570| 4 | `SetNodeCacheMode(nodeIndex, cache_value)` | ✅ | Returns `True` |571| 5 | `GetNodeCacheMode(nodeIndex)` | ✅ | Returns `-1` (no cache mode set) |572| 6 | `GetNodeLabel(nodeIndex)` | ✅ | Returns node label string |573| 7 | `GetToolsInNode(nodeIndex)` | ✅ | Returns `None` (no OFX tools in node) |574| 8 | `SetNodeEnabled(nodeIndex, isEnabled)` | ✅ | Returns `True` |575| 9 | `ApplyGradeFromDRX(path, gradeMode)` | ✅ | Applies grade from DRX file |576| 10 | `ApplyArriCdlLut()` | ✅ | Applies ARRI CDL LUT |577| 11 | `ResetAllGrades()` | ✅ | Resets all grades |578579### ColorGroup580581| # | Method | Status | Test Result / Notes |582|---|--------|--------|---------------------|583| 1 | `GetName()` | ✅ | Returns group name |584| 2 | `SetName(groupName)` | ✅ | Sets group name |585| 3 | `GetClipsInTimeline(timeline)` | ✅ | Returns clips in group |586| 4 | `GetPreClipNodeGraph()` | ✅ | Returns Graph object |587| 5 | `GetPostClipNodeGraph()` | ✅ | Returns Graph object |588589---590591## Contributing592593We welcome contributions! The following areas especially need help:594595### Help Wanted: Untested API Methods596597**5 methods** (1.5%) remain untested against a live DaVinci Resolve instance. If you have access to the required infrastructure or content, we'd love a PR with test confirmation:5985991. **Cloud Project Methods** (4 methods) — Need DaVinci Resolve cloud infrastructure:600 - `ProjectManager.CreateCloudProject`601 - `ProjectManager.LoadCloudProject`602 - `ProjectManager.ImportCloudProject`603 - `ProjectManager.RestoreCloudProject`6046052. **HDR Analysis** (1 method) — Needs specific content:606 - `Timeline.AnalyzeDolbyVision` — needs HDR/Dolby Vision content607608### How to Contribute6096101. Fork the repository6112. Create a feature branch (`git checkout -b feature/my-contribution`)6123. Run the existing test suite to ensure nothing breaks6134. Add your test results or fixes6145. Submit a pull request615616### Other Contribution Ideas617618- **Windows testing** — All tests were run on macOS; Windows verification welcome619- **Linux testing** — DaVinci Resolve supports Linux; test coverage needed620- **Resolve version compatibility** — Test against Resolve 18.x, 19.0, or newer versions621- **Bug reports** — If a tool returns unexpected results on your setup, file an issue622- **Documentation** — Improve examples, add tutorials, translate docs623624## Platform Support625626| Platform | Status | Resolve Paths Auto-Detected | Notes |627|----------|--------|----------------------------|-------|628| macOS | ✅ Tested | `/Library/Application Support/Blackmagic Design/...` | Primary development and test platform |629| Windows | ✅ Supported | `C:\ProgramData\Blackmagic Design\...` | Community-tested; PRs welcome |630| Linux | ⚠️ Experimental | `/opt/resolve/...` | Should work — testing and feedback welcome |631632## Project Structure633634```635davinci-resolve-mcp/636├── install.py # Universal installer (macOS/Windows/Linux)637├── src/638│ ├── server.py # Compound MCP server — 26 tools (default)639│ ├── resolve_mcp_server.py # Full MCP server — 342 tools (power users)640│ └── utils/ # Platform detection, Resolve connection helpers641├── tests/ # 5-phase live API test suite (319/319 pass)642├── docs/643│ └── resolve_scripting_api.txt # Official Resolve Scripting API reference644└── examples/ # Getting started, markers, media, timeline examples645```646647## License648649MIT650651## Author652653Samuel Gursky (samgursky@gmail.com)654- GitHub: [github.com/samuelgursky](https://github.com/samuelgursky)655656## Acknowledgments657658- Blackmagic Design for DaVinci Resolve and its scripting API659- The Model Context Protocol team for enabling AI assistant integration660- Anthropic for Claude Code, used extensively in development and testing661
Full transparency — inspect the skill content before installing.