Merge pull request #595 from MarekSpodBiedry/issue-476

Reject unknown mcp options instead of silently ignoring them (#476)
This commit is contained in:
Brandon Thomas 2026-10-10 14:52:19 -04:00 committed by GitHub
commit 539759ef18
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 20 additions and 0 deletions

View File

@ -60,6 +60,11 @@ impl Args {
self.opts.iter().any(|(n, _)| n == k)
}
/// The first option that is not in `allowed`, if any.
pub fn unknown_opt(&self, allowed: &[&str]) -> Option<&str> {
self.opts.iter().map(|(n, _)| n.as_str()).find(|n| !allowed.contains(n))
}
/// Command params from positionals `from..`: one JSON object, or `key=value` pairs
/// (dotted keys nest: `color.r=1`).
pub fn params_from(&self, from: usize) -> Result<Value, String> {
@ -135,6 +140,14 @@ mod tests {
assert_eq!(a.params_from(2).unwrap(), json!({"a": {"b": [1]}}));
}
#[test]
fn unknown_opt_reports_first_unlisted_option() {
let allowed = ["--bridge", "--project"];
assert_eq!(args("mcp --automation-read-root X").unknown_opt(&allowed), Some("--automation-read-root"));
assert_eq!(args("mcp --bridge=a --typo=1").unknown_opt(&allowed), Some("--typo"));
assert_eq!(args("mcp --project p --bridge a").unknown_opt(&allowed), None);
}
#[test]
fn double_dash_ends_options() {
let a = args("import -- --weird-name.mov a.mov");

View File

@ -462,6 +462,9 @@ async fn cli() {
diag(format_args!("rendered {}x{} in {:.1} ms → {out}", img.w, img.h, dt.as_secs_f64() * 1000.0));
}
"mcp" => {
if let Some(opt) = a.unknown_opt(&["--bridge", "--project", "--demo", "--data-dir"]) {
usage(format!("unknown mcp option `{opt}`"));
}
let server = match a.opt("--bridge") {
Some(addr) => filmcraft_automation::FilmcraftMcp::bridge(addr).unwrap_or_else(|e| fail(e)),
None => match Backend::open(&a) {

View File

@ -14,6 +14,10 @@ FilmCraft from an agent. Part 2 covers developing FilmCraft as an agent.
| Headless | `filmcraft-cli mcp --demo` or `--project p.fcproj` (neither = empty project) | an in-process engine session, no window |
| Bridge | `filmcraft-cli mcp --bridge 127.0.0.1:9876` | the running desktop app started with `filmcraft --control 9876` |
Headless mode also takes `--data-dir DIR` (user presets). Any other option is rejected (exit 2):
there are no folder-confinement flags such as `--automation-read-root`, so the server can read
and write wherever the user can.
**Claude Code.** The repository's `.mcp.json` registers both servers (`filmcraft` = bridge,
`filmcraft-headless` = demo). Both point at `target/release/filmcraft-cli`, so build it first: