mirror of
https://github.com/storytold/designcraft.git
synced 2026-11-14 22:35:26 +00:00
MCP: complete tool hints and check nested argument keys
Extend the existing MCP tools instead of adding a second set (#259): - every tool states destructiveHint and idempotentHint next to readOnlyHint/openWorldHint; input schemas are closed - the #776 unknown-key check walks the schema, so a misspelt key in a batch step, a pointer event or mods is -32602 with its path instead of being ignored - script::parse refuses JSON step keys other than command/params - a panic outside a command (rendering, inspection, the app connection) is a tool error and the server keeps running Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
438d7b6d79
commit
ecb2fc1676
6 files changed
+171
-10
No files matched your search
@@ -11,7 +11,7 @@
|
||||
//! **Script text** ([`parse`]): a JSON array of steps, JSON lines (`{"command": …, "params": …}`),
|
||||
//! or compact lines `command.id {json}` / `command.id={json}` / `command.id`; blank lines and
|
||||
//! lines starting with `#` or `//` are ignored. A leading byte order mark and CRLF line endings are
|
||||
//! accepted.
|
||||
//! accepted. A JSON step takes only `command` and `params`; any other key is an error.
|
||||
|
||||
use serde_json::{Value, json};
|
||||
|
||||
@@ -59,6 +59,10 @@ pub fn parse(text: &str) -> Result<Vec<Step>, String> {
|
||||
}
|
||||
|
||||
fn step_of(v: &Value) -> Result<Step, String> {
|
||||
// A misspelt key (`parms`) would otherwise run the command with its defaults.
|
||||
if let Some(key) = v.as_object().and_then(|o| o.keys().find(|k| !matches!(k.as_str(), "command" | "params"))) {
|
||||
return Err(format!("unknown key `{key}` (a step takes `command` and `params`)"));
|
||||
}
|
||||
let command = v.get("command").and_then(Value::as_str).filter(|s| !s.is_empty()).ok_or("missing `command`")?.to_string();
|
||||
let params = v.get("params").cloned().filter(|p| !p.is_null()).unwrap_or(json!({}));
|
||||
if !params.is_object() {
|
||||
@@ -195,6 +199,16 @@ mod tests {
|
||||
assert!(parse("frame.create {bad").unwrap_err().contains("line 1"));
|
||||
}
|
||||
|
||||
/// A misspelt step key ran the command with its defaults.
|
||||
#[test]
|
||||
fn a_step_with_an_unknown_key_is_refused() {
|
||||
for text in [r#"[{"command": "file.new", "parms": {"width": 300}}]"#, r#"{"command": "file.new", "parms": {"width": 300}}"#] {
|
||||
let e = parse(text).unwrap_err();
|
||||
assert!(e.contains("unknown key `parms`"), "{e}");
|
||||
}
|
||||
assert!(parse(r#"[{"command": "file.new", "params": {"width": 300}}]"#).is_ok());
|
||||
}
|
||||
|
||||
/// Windows editors and PowerShell 5.1 save "UTF-8 with BOM" with CRLF line endings; the BOM
|
||||
/// became part of the first command id (#654).
|
||||
#[test]
|
||||
|
||||
Reference in new issue
Block a user