davinci-resolve-mcp 4.4.2 → 4.5.0

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.
@@ -61,6 +61,7 @@ def get_project_setting(setting_name: str) -> Dict[str, Any]:
61
61
 
62
62
 
63
63
  @mcp.tool(annotations=DESTRUCTIVE_TOOL)
64
+ @granular_destructive_op()
64
65
  def set_project_setting(setting_name: str, setting_value: Any) -> str:
65
66
  """Set a project setting to the specified value.
66
67
 
@@ -250,6 +251,7 @@ def save_project() -> str:
250
251
 
251
252
 
252
253
  @mcp.tool()
254
+ @granular_destructive_op()
253
255
  def close_project() -> str:
254
256
  """Close the current project.
255
257
 
@@ -306,6 +308,7 @@ def get_cache_settings() -> Dict[str, Any]:
306
308
 
307
309
 
308
310
  @mcp.tool()
311
+ @granular_destructive_op()
309
312
  def set_cache_mode(mode: str) -> str:
310
313
  """Set cache mode for the current project.
311
314
 
@@ -340,6 +343,7 @@ def set_cache_mode(mode: str) -> str:
340
343
 
341
344
 
342
345
  @mcp.tool()
346
+ @granular_destructive_op()
343
347
  def set_optimized_media_mode(mode: str) -> str:
344
348
  """Set optimized media mode for the current project.
345
349
 
@@ -374,6 +378,7 @@ def set_optimized_media_mode(mode: str) -> str:
374
378
 
375
379
 
376
380
  @mcp.tool()
381
+ @granular_destructive_op()
377
382
  def set_proxy_mode(mode: str) -> str:
378
383
  """Set proxy media mode for the current project.
379
384
 
@@ -408,6 +413,7 @@ def set_proxy_mode(mode: str) -> str:
408
413
 
409
414
 
410
415
  @mcp.tool()
416
+ @granular_destructive_op()
411
417
  def set_proxy_quality(quality: str) -> str:
412
418
  """Set proxy media quality for the current project.
413
419
 
@@ -442,6 +448,7 @@ def set_proxy_quality(quality: str) -> str:
442
448
 
443
449
 
444
450
  @mcp.tool()
451
+ @granular_destructive_op()
445
452
  def set_cache_path(path_type: str, path: str) -> str:
446
453
  """Set cache file path for the current project.
447
454
 
@@ -501,6 +508,7 @@ def create_cloud_project_tool(
501
508
 
502
509
 
503
510
  @mcp.tool()
511
+ @granular_destructive_op()
504
512
  def load_cloud_project_tool(
505
513
  project_name: Optional[str] = None,
506
514
  project_media_path: Optional[str] = None,
@@ -594,6 +602,7 @@ def get_project_property_endpoint(property_name: str) -> Dict[str, Any]:
594
602
 
595
603
 
596
604
  @mcp.tool()
605
+ @granular_destructive_op()
597
606
  def set_project_property_tool(property_name: str, property_value: Any) -> str:
598
607
  """Set a project property value.
599
608
 
@@ -624,6 +633,7 @@ def get_timeline_format() -> Dict[str, Any]:
624
633
 
625
634
 
626
635
  @mcp.tool()
636
+ @granular_destructive_op()
627
637
  def set_timeline_format_tool(width: int, height: int, frame_rate: float, interlaced: bool = False) -> str:
628
638
  """Set timeline format (resolution and frame rate).
629
639
 
@@ -657,6 +667,7 @@ def get_superscale_settings_endpoint() -> Dict[str, Any]:
657
667
 
658
668
 
659
669
  @mcp.tool()
670
+ @granular_destructive_op()
660
671
  def set_superscale_settings_tool(enabled: bool, quality: int = 0) -> str:
661
672
  """Set SuperScale settings for the current project.
662
673
 
@@ -695,6 +706,7 @@ def get_color_settings_endpoint() -> Dict[str, Any]:
695
706
 
696
707
 
697
708
  @mcp.tool()
709
+ @granular_destructive_op()
698
710
  def set_color_science_mode_tool(mode: str) -> str:
699
711
  """Set color science mode for the current project.
700
712
 
@@ -714,6 +726,7 @@ def set_color_science_mode_tool(mode: str) -> str:
714
726
 
715
727
 
716
728
  @mcp.tool()
729
+ @granular_destructive_op()
717
730
  def set_color_space_tool(color_space: str, gamma: str = None) -> str:
718
731
  """Set timeline color space and gamma.
719
732
 
@@ -776,6 +789,7 @@ def archive_project(project_name: str, archive_path: str, archive_src_media: boo
776
789
 
777
790
 
778
791
  @mcp.tool()
792
+ @granular_destructive_op()
779
793
  def delete_project(project_name: str) -> Dict[str, Any]:
780
794
  """Delete a project from the current database. WARNING: This is irreversible.
781
795
 
@@ -810,6 +824,7 @@ def create_project_folder(folder_name: str) -> Dict[str, Any]:
810
824
 
811
825
 
812
826
  @mcp.tool()
827
+ @granular_destructive_op()
813
828
  def delete_project_folder(folder_name: str) -> Dict[str, Any]:
814
829
  """Delete a folder from the current project folder location.
815
830
 
@@ -953,6 +968,7 @@ def get_database_list() -> Dict[str, Any]:
953
968
 
954
969
 
955
970
  @mcp.tool()
971
+ @granular_destructive_op()
956
972
  def set_current_database(db_info: Dict[str, str]) -> Dict[str, Any]:
957
973
  """Switch to a different database.
958
974
 
@@ -968,6 +984,7 @@ def set_current_database(db_info: Dict[str, str]) -> Dict[str, Any]:
968
984
 
969
985
 
970
986
  @mcp.tool()
987
+ @granular_destructive_op()
971
988
  def set_project_name(name: str) -> Dict[str, Any]:
972
989
  """Rename the current project.
973
990
 
@@ -1017,6 +1034,7 @@ def get_project_preset_list() -> Dict[str, Any]:
1017
1034
 
1018
1035
 
1019
1036
  @mcp.tool()
1037
+ @granular_destructive_op()
1020
1038
  def set_project_preset(preset_name: str) -> Dict[str, Any]:
1021
1039
  """Apply a project preset to the current project.
1022
1040
 
@@ -1034,6 +1052,7 @@ def set_project_preset(preset_name: str) -> Dict[str, Any]:
1034
1052
 
1035
1053
 
1036
1054
  @mcp.tool()
1055
+ @granular_destructive_op()
1037
1056
  def delete_render_job(job_id: str) -> Dict[str, Any]:
1038
1057
  """Delete a specific render job by its ID.
1039
1058
 
@@ -1085,6 +1104,7 @@ def start_rendering_jobs(job_ids: Optional[List[str]] = None, is_interactive_mod
1085
1104
 
1086
1105
 
1087
1106
  @mcp.tool()
1107
+ @granular_destructive_op()
1088
1108
  def stop_rendering() -> Dict[str, Any]:
1089
1109
  """Stop the current rendering process."""
1090
1110
  resolve = get_resolve()
@@ -1111,6 +1131,7 @@ def is_rendering_in_progress() -> Dict[str, Any]:
1111
1131
 
1112
1132
 
1113
1133
  @mcp.tool()
1134
+ @granular_destructive_op()
1114
1135
  def load_render_preset(preset_name: str) -> Dict[str, Any]:
1115
1136
  """Load a render preset by name.
1116
1137
 
@@ -1145,6 +1166,7 @@ def save_as_new_render_preset(preset_name: str) -> Dict[str, Any]:
1145
1166
 
1146
1167
 
1147
1168
  @mcp.tool()
1169
+ @granular_destructive_op()
1148
1170
  def delete_render_preset(preset_name: str) -> Dict[str, Any]:
1149
1171
  """Delete a render preset.
1150
1172
 
@@ -1162,6 +1184,7 @@ def delete_render_preset(preset_name: str) -> Dict[str, Any]:
1162
1184
 
1163
1185
 
1164
1186
  @mcp.tool()
1187
+ @granular_destructive_op()
1165
1188
  def set_render_settings(settings: Dict[str, Any]) -> Dict[str, Any]:
1166
1189
  """Set render settings for the current project.
1167
1190
 
@@ -1254,6 +1277,7 @@ def get_current_render_format_and_codec() -> Dict[str, Any]:
1254
1277
 
1255
1278
 
1256
1279
  @mcp.tool()
1280
+ @granular_destructive_op()
1257
1281
  def set_current_render_format_and_codec(format_name: str, codec_name: str) -> Dict[str, Any]:
1258
1282
  """Set the render format and codec.
1259
1283
 
@@ -1297,6 +1321,7 @@ def get_current_render_mode() -> Dict[str, Any]:
1297
1321
 
1298
1322
 
1299
1323
  @mcp.tool()
1324
+ @granular_destructive_op()
1300
1325
  def set_current_render_mode(mode: int) -> Dict[str, Any]:
1301
1326
  """Set the render mode.
1302
1327
 
@@ -1375,6 +1400,7 @@ def insert_audio_to_current_track(file_path: str) -> Dict[str, Any]:
1375
1400
 
1376
1401
 
1377
1402
  @mcp.tool()
1403
+ @granular_destructive_op()
1378
1404
  def load_burn_in_preset(preset_name: str) -> Dict[str, Any]:
1379
1405
  """Load a burn-in preset by name for the project.
1380
1406
 
@@ -1441,6 +1467,7 @@ def add_color_group(group_name: str) -> Dict[str, Any]:
1441
1467
 
1442
1468
 
1443
1469
  @mcp.tool()
1470
+ @granular_destructive_op()
1444
1471
  def delete_color_group(group_name: str) -> Dict[str, Any]:
1445
1472
  """Delete a color group from the current project.
1446
1473
 
@@ -1588,6 +1615,7 @@ def add_render_job() -> Dict[str, Any]:
1588
1615
 
1589
1616
 
1590
1617
  @mcp.tool()
1618
+ @granular_destructive_op()
1591
1619
  def load_cloud_project(project_name: str, project_media_path: str, sync_mode: str = "proxy") -> Dict[str, Any]:
1592
1620
  """Load a cloud project from DaVinci Resolve cloud.
1593
1621
 
@@ -9,6 +9,7 @@ from src.utils.resolve211_edits import validate_edit_options, validate_transitio
9
9
  from src.granular.common import (
10
10
  mcp, READ_ONLY_TOOL, WRITE_TOOL, DESTRUCTIVE_TOOL, get_resolve, get_current_project,
11
11
  _get_timeline, _get_timeline_item, _resolve_safe_dir, _find_clip_by_id, _requires_method, has_method,
12
+ granular_destructive_op,
12
13
  )
13
14
 
14
15
 
@@ -190,6 +191,7 @@ def set_timeline_item_fades(options: dict, track_type: str = "video", track_inde
190
191
 
191
192
 
192
193
  @mcp.tool(annotations=DESTRUCTIVE_TOOL)
194
+ @granular_destructive_op()
193
195
  def add_timeline_item_transition(options: dict, track_type: str = "video", track_index: int = 1, item_index: int = 0) -> dict:
194
196
  """Add a native 21.1 transition using type/category/position/alignment and optional duration in frames. Returns actual span; clip indexes can change after insertion."""
195
197
  error = validate_transition_options(options)
@@ -207,6 +209,7 @@ def add_timeline_item_transition(options: dict, track_type: str = "video", track
207
209
 
208
210
 
209
211
  @mcp.tool(annotations=DESTRUCTIVE_TOOL)
212
+ @granular_destructive_op()
210
213
  def create_multicam_clip(clip_ids: list[str], options: dict | None = None) -> dict:
211
214
  """Create native 21.1 multicam clips. Options follow MulticamOptions; named Resolve constants or numeric values accepted. Resolves every ID before writing."""
212
215
  _, p = get_current_project()
@@ -220,6 +223,7 @@ def create_multicam_clip(clip_ids: list[str], options: dict | None = None) -> di
220
223
 
221
224
 
222
225
  @mcp.tool(annotations=DESTRUCTIVE_TOOL)
226
+ @granular_destructive_op()
223
227
  def flatten_timeline_item_multicam(grade_option: str = "FLATTEN_MULTICAM_COPY_GRADE", track_type: str = "video", track_index: int = 1, item_index: int = 0) -> dict:
224
228
  """Flatten a native multicam item using COPY_GRADE or RETAIN_GRADE_FROM_ANGLE. Re-query items after replacement."""
225
229
  if track_type not in ("video", "audio") or track_index < 1 or item_index < 0:
@@ -237,6 +241,7 @@ def flatten_timeline_item_multicam(grade_option: str = "FLATTEN_MULTICAM_COPY_GR
237
241
 
238
242
 
239
243
  @mcp.tool(annotations=DESTRUCTIVE_TOOL)
244
+ @granular_destructive_op()
240
245
  def set_timeline_output_blanking(options: dict) -> dict:
241
246
  """Set native 21.1 timeline Top/Bottom/Left/Right pixel coordinates."""
242
247
  error = validate_blanking(options)
@@ -252,6 +257,7 @@ def set_timeline_output_blanking(options: dict) -> dict:
252
257
 
253
258
 
254
259
  @mcp.tool(annotations=DESTRUCTIVE_TOOL)
260
+ @granular_destructive_op()
255
261
  def set_timeline_item_output_blanking(options: dict, track_type: str = "video", track_index: int = 1, item_index: int = 0) -> dict:
256
262
  """Set native 21.1 clip pixel coordinates. Disable timeline blanking inheritance first; this call does not change inheritance."""
257
263
  error = validate_blanking(options)
@@ -269,6 +275,7 @@ def set_timeline_item_output_blanking(options: dict, track_type: str = "video",
269
275
 
270
276
 
271
277
  @mcp.tool(annotations=DESTRUCTIVE_TOOL)
278
+ @granular_destructive_op()
272
279
  def set_timeline_item_use_timeline_for_output_blanking(use_timeline: bool, track_type: str = "video", track_index: int = 1, item_index: int = 0) -> dict:
273
280
  """Explicitly enable/disable native 21.1 timeline blanking inheritance for a clip."""
274
281
  if type(use_timeline) is not bool:
@@ -285,6 +292,7 @@ def set_timeline_item_use_timeline_for_output_blanking(use_timeline: bool, track
285
292
 
286
293
 
287
294
  @mcp.tool(annotations=DESTRUCTIVE_TOOL)
295
+ @granular_destructive_op()
288
296
  def normalize_timeline_audio_level(item_ids: list[str], options: dict | None = None) -> dict:
289
297
  """Native 21.1 normalization of explicit audio timeline item IDs. Options normalizationMode, targetLevel (dBFS), targetLoudness (LKFS), setLevelMode; use get_normalize_audio_modes for names."""
290
298
  _, timeline, error = _get_timeline()
@@ -297,6 +305,7 @@ def normalize_timeline_audio_level(item_ids: list[str], options: dict | None = N
297
305
 
298
306
 
299
307
  @mcp.tool(annotations=DESTRUCTIVE_TOOL)
308
+ @granular_destructive_op()
300
309
  def auto_align_timeline_clips(item_ids: list[str], options: dict | None = None) -> dict:
301
310
  """Native 21.1 alignment of current video/audio items by unique ID. Include linked audio AND video IDs to move both; selection is not expanded. Options SyncUsing and UseTrack accept documented constant names or integral native values."""
302
311
  _, timeline, error = _get_timeline()
@@ -228,6 +228,7 @@ def save_layout_preset_tool(preset_name: str) -> Dict[str, Any]:
228
228
 
229
229
 
230
230
  @mcp.tool()
231
+ @granular_destructive_op()
231
232
  def load_layout_preset_tool(preset_name: str) -> Dict[str, Any]:
232
233
  """Load a UI layout preset.
233
234
 
@@ -282,6 +283,7 @@ def import_layout_preset_tool(import_path: str, preset_name: str = None) -> Dict
282
283
 
283
284
 
284
285
  @mcp.tool()
286
+ @granular_destructive_op()
285
287
  def delete_layout_preset_tool(preset_name: str) -> Dict[str, Any]:
286
288
  """Delete a layout preset.
287
289
 
@@ -325,6 +327,7 @@ def get_app_state_endpoint() -> Dict[str, Any]:
325
327
 
326
328
 
327
329
  @mcp.tool()
330
+ @granular_destructive_op()
328
331
  def quit_app(force: bool = False, save_project: bool = True) -> str:
329
332
  """
330
333
  Quit DaVinci Resolve application.
@@ -346,6 +349,7 @@ def quit_app(force: bool = False, save_project: bool = True) -> str:
346
349
 
347
350
 
348
351
  @mcp.tool()
352
+ @granular_destructive_op()
349
353
  def restart_app(wait_seconds: int = 5) -> str:
350
354
  """
351
355
  Restart DaVinci Resolve application.
@@ -515,6 +519,7 @@ def get_keyframe_mode() -> Dict[str, Any]:
515
519
 
516
520
 
517
521
  @mcp.tool()
522
+ @granular_destructive_op()
518
523
  def set_keyframe_mode(mode: int) -> Dict[str, Any]:
519
524
  """Set the keyframe mode in Resolve.
520
525
 
@@ -545,6 +550,7 @@ def get_fairlight_presets() -> Dict[str, Any]:
545
550
 
546
551
 
547
552
  @mcp.tool()
553
+ @granular_destructive_op()
548
554
  def quit_resolve() -> Dict[str, Any]:
549
555
  """Quit DaVinci Resolve. WARNING: This will close the application."""
550
556
  resolve = get_resolve()
@@ -591,6 +597,7 @@ def get_burn_in_preset_list() -> Dict[str, Any]:
591
597
 
592
598
 
593
599
  @mcp.tool()
600
+ @granular_destructive_op()
594
601
  def delete_burn_in_preset(preset_name: str) -> Dict[str, Any]:
595
602
  """Delete a data burn-in preset by name (Resolve 21.0.4+).
596
603
 
@@ -641,6 +648,7 @@ def save_user_preferences_preset(preset_name: str) -> Dict[str, Any]:
641
648
 
642
649
 
643
650
  @mcp.tool()
651
+ @granular_destructive_op()
644
652
  def load_user_preferences_preset(preset_name: str) -> Dict[str, Any]:
645
653
  """Load a user-preferences preset (Resolve 21.0.4+).
646
654
 
@@ -662,6 +670,7 @@ def load_user_preferences_preset(preset_name: str) -> Dict[str, Any]:
662
670
 
663
671
 
664
672
  @mcp.tool()
673
+ @granular_destructive_op()
665
674
  def delete_user_preferences_preset(preset_name: str) -> Dict[str, Any]:
666
675
  """Delete a user-preferences preset by name (Resolve 21.0.4+).
667
676
 
@@ -105,6 +105,7 @@ def create_timeline(name: str) -> str:
105
105
 
106
106
 
107
107
  @mcp.tool()
108
+ @granular_destructive_op()
108
109
  def set_current_timeline(name: str) -> str:
109
110
  """Switch to a timeline by name.
110
111
 
@@ -203,6 +204,7 @@ def list_timelines_tool() -> List[str]:
203
204
 
204
205
 
205
206
  @mcp.tool()
207
+ @granular_destructive_op()
206
208
  def timeline_set_name(name: str) -> Dict[str, Any]:
207
209
  """Rename the current timeline.
208
210
 
@@ -217,6 +219,7 @@ def timeline_set_name(name: str) -> Dict[str, Any]:
217
219
 
218
220
 
219
221
  @mcp.tool()
222
+ @granular_destructive_op()
220
223
  def timeline_set_start_timecode(timecode: str) -> Dict[str, Any]:
221
224
  """Set the start timecode of the current timeline.
222
225
 
@@ -240,6 +243,7 @@ def timeline_get_current_timecode() -> Dict[str, Any]:
240
243
 
241
244
 
242
245
  @mcp.tool()
246
+ @granular_destructive_op()
243
247
  def timeline_set_current_timecode(timecode: str) -> Dict[str, Any]:
244
248
  """Set the playhead to a specific timecode.
245
249
 
@@ -288,6 +292,7 @@ def timeline_add_track(
288
292
 
289
293
 
290
294
  @mcp.tool()
295
+ @granular_destructive_op()
291
296
  def timeline_delete_track(track_type: str, track_index: int) -> Dict[str, Any]:
292
297
  """Delete a track from the timeline.
293
298
 
@@ -318,6 +323,7 @@ def timeline_get_track_sub_type(track_type: str, track_index: int) -> Dict[str,
318
323
 
319
324
 
320
325
  @mcp.tool()
326
+ @granular_destructive_op()
321
327
  def timeline_set_track_enable(track_type: str, track_index: int, enabled: bool) -> Dict[str, Any]:
322
328
  """Enable or disable a track.
323
329
 
@@ -349,6 +355,7 @@ def timeline_get_is_track_enabled(track_type: str, track_index: int) -> Dict[str
349
355
 
350
356
 
351
357
  @mcp.tool()
358
+ @granular_destructive_op()
352
359
  def timeline_set_track_lock(track_type: str, track_index: int, locked: bool) -> Dict[str, Any]:
353
360
  """Lock or unlock a track.
354
361
 
@@ -397,6 +404,7 @@ def timeline_get_voice_isolation_state(track_index: int) -> Dict[str, Any]:
397
404
 
398
405
 
399
406
  @mcp.tool()
407
+ @granular_destructive_op()
400
408
  def timeline_set_voice_isolation_state(track_index: int, state: Dict[str, Any]) -> Dict[str, Any]:
401
409
  """Set voice isolation state for an audio track.
402
410
 
@@ -415,6 +423,7 @@ def timeline_set_voice_isolation_state(track_index: int, state: Dict[str, Any])
415
423
 
416
424
 
417
425
  @mcp.tool()
426
+ @granular_destructive_op()
418
427
  def timeline_delete_clips(clip_ids: List[str], track_type: str = "video", track_index: int = 1) -> Dict[str, Any]:
419
428
  """Delete clips from the timeline.
420
429
 
@@ -437,6 +446,7 @@ def timeline_delete_clips(clip_ids: List[str], track_type: str = "video", track_
437
446
 
438
447
 
439
448
  @mcp.tool()
449
+ @granular_destructive_op()
440
450
  def timeline_set_clips_linked(clip_ids: List[str], linked: bool, track_type: str = "video", track_index: int = 1) -> Dict[str, Any]:
441
451
  """Link or unlink clips in the timeline.
442
452
 
@@ -533,6 +543,7 @@ def timeline_get_marker_custom_data(frame_id: int) -> Dict[str, Any]:
533
543
 
534
544
 
535
545
  @mcp.tool()
546
+ @granular_destructive_op()
536
547
  def timeline_delete_markers_by_color(color: str) -> Dict[str, Any]:
537
548
  """Delete all timeline markers of a specific color.
538
549
 
@@ -547,6 +558,7 @@ def timeline_delete_markers_by_color(color: str) -> Dict[str, Any]:
547
558
 
548
559
 
549
560
  @mcp.tool()
561
+ @granular_destructive_op()
550
562
  def timeline_delete_marker_at_frame(frame_id: int) -> Dict[str, Any]:
551
563
  """Delete a timeline marker at a specific frame.
552
564
 
@@ -561,6 +573,7 @@ def timeline_delete_marker_at_frame(frame_id: int) -> Dict[str, Any]:
561
573
 
562
574
 
563
575
  @mcp.tool()
576
+ @granular_destructive_op()
564
577
  def timeline_delete_marker_by_custom_data(custom_data: str) -> Dict[str, Any]:
565
578
  """Delete a timeline marker by custom data.
566
579
 
@@ -590,6 +603,7 @@ def timeline_get_track_name(track_type: str, track_index: int) -> Dict[str, Any]
590
603
 
591
604
 
592
605
  @mcp.tool()
606
+ @granular_destructive_op()
593
607
  def timeline_set_track_name(track_type: str, track_index: int, name: str) -> Dict[str, Any]:
594
608
  """Set the name of a track.
595
609
 
@@ -882,6 +896,7 @@ def timeline_create_subtitles_from_audio(
882
896
  # the timeline by adding cuts. The compound server rates timeline_ai.detect_scene_cuts
883
897
  # destructive; this is the same Resolve call, so it carries the same hint.
884
898
  @mcp.tool(annotations=DESTRUCTIVE_TOOL)
899
+ @granular_destructive_op()
885
900
  def timeline_detect_scene_cuts() -> Dict[str, Any]:
886
901
  """Detect scene cuts in the current timeline. DESTRUCTIVE — adds cuts to the timeline."""
887
902
  _, tl, err = _get_timeline()
@@ -987,6 +1002,7 @@ def timeline_get_mark_in_out() -> Dict[str, Any]:
987
1002
 
988
1003
 
989
1004
  @mcp.tool()
1005
+ @granular_destructive_op()
990
1006
  def timeline_set_mark_in_out(mark_in: int, mark_out: int) -> Dict[str, Any]:
991
1007
  """Set mark in/out points for the current timeline.
992
1008
 
@@ -1002,6 +1018,7 @@ def timeline_set_mark_in_out(mark_in: int, mark_out: int) -> Dict[str, Any]:
1002
1018
 
1003
1019
 
1004
1020
  @mcp.tool()
1021
+ @granular_destructive_op()
1005
1022
  def timeline_clear_mark_in_out() -> Dict[str, Any]:
1006
1023
  """Clear mark in/out points for the current timeline."""
1007
1024
  _, tl, err = _get_timeline()
@@ -1084,6 +1101,7 @@ def create_timeline_from_clips(
1084
1101
 
1085
1102
 
1086
1103
  @mcp.tool()
1104
+ @granular_destructive_op()
1087
1105
  def set_timeline_setting(setting_name: str, setting_value: str) -> Dict[str, Any]:
1088
1106
  """Set a timeline setting value.
1089
1107