MCP media_import: test the paths schema and param mapping; update docs/agents.md (#499)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Brandon Thomas 2026-10-10 05:49:28 -04:00
parent 8454634fb8
commit e0d2c9c73b
2 changed files with 29 additions and 9 deletions

View File

@ -151,6 +151,18 @@ pub struct MediaImportParams {
pub image_sequence: Option<bool>,
}
/// `file.import` params for a `media_import` call.
fn media_import_params(p: MediaImportParams) -> Value {
let mut params = json!({"paths": p.paths});
if let Some(bin) = p.bin {
params["bin"] = json!(bin);
}
if let Some(seq) = p.image_sequence {
params["imageSequence"] = json!(seq);
}
params
}
#[derive(Debug, Default, Deserialize, JsonSchema)]
pub struct ElementsParams {
/// Only element ids starting with this prefix (e.g. `timeline.clip.`).
@ -394,14 +406,7 @@ impl FilmcraftMcp {
annotations(read_only_hint = false, destructive_hint = false, idempotent_hint = false, open_world_hint = false)
)]
async fn media_import(&self, Parameters(p): Parameters<MediaImportParams>) -> Result<CallToolResult, McpError> {
let mut params = json!({"paths": p.paths});
if let Some(bin) = p.bin {
params["bin"] = json!(bin);
}
if let Some(seq) = p.image_sequence {
params["imageSequence"] = json!(seq);
}
wrap(self.run("file.import", params).await)
wrap(self.run("file.import", media_import_params(p)).await)
}
#[tool(
@ -753,6 +758,21 @@ mod tests {
}
}
/// #499: `media_import` takes `paths` (a required array), and forwards `bin` / `image_sequence` to `file.import`.
#[test]
fn media_import_schema_and_params() {
let m = FilmcraftMcp::headless(Session::default());
let def = m.tool_router.get("media_import").unwrap();
let schema = Value::Object((*def.input_schema).clone());
assert_eq!(schema["properties"]["paths"]["type"], "array", "{schema}");
assert!(schema["required"].as_array().unwrap().iter().any(|r| r == "paths"), "{schema}");
let p: MediaImportParams = serde_json::from_value(json!({"paths": ["/a.png"], "bin": 7, "image_sequence": true})).unwrap();
assert_eq!(media_import_params(p), json!({"paths": ["/a.png"], "bin": 7, "imageSequence": true}));
let p: MediaImportParams = serde_json::from_value(json!({"paths": ["/a.wav"]})).unwrap();
assert_eq!(media_import_params(p), json!({"paths": ["/a.wav"]}));
assert!(serde_json::from_value::<MediaImportParams>(json!({"text": "/a.wav"})).is_err());
}
fn demo() -> Session {
let mut s = Session::default();
s.execute("file.openDemoProject", json!({})).unwrap();

View File

@ -61,7 +61,7 @@ claude mcp add filmcraft -- filmcraft-cli mcp
| `render_preview` | both | same as `render_frame` |
| `project_inspect` | both | bins and items with ids, types, durations; active sequence |
| `sequence_inspect` | both | the active sequence: tracks, clips (`start` / `end` and `sourceIn` / `sourceOut` in ticks, frames, `speed`, `reverse`: the clip plays its `sourceIn` to `sourceOut` stretch backward, `gainDb`), effects, transitions, markers (name and `comment`), playhead, selection |
| `media_import` | both | import files by absolute path (`text`, one path per line) |
| `media_import` | both | import files by absolute path (`paths`, an array; optional `bin`, `image_sequence`) |
| `render_frame` | both | PNG of the program frame at `seconds` (headless renders; bridge screenshots the Program monitor). Read-only: the playhead and selection are left as they were |
| `ui_inspect` | bridge | UI state: tool, workspace, panels, zoom, playback, fps |
| `ui_elements` | bridge | on-screen interactive elements with id, label and rect (`prefix` filter) |