mirror of
https://github.com/storytold/craft-anything.git
synced 2026-11-05 20:47:45 +01:00
Restrict Claude Code tools and require approval for unknown tools (#637)
Claude Code chats get no built-in tools and builders an explicit list (commands, files, search, todos), so tools that message other sessions, publish or schedule work never start; in Auto mode a tool without an approval rule is refused (fixes #635).
This commit is contained in:
@@ -290,6 +290,8 @@ impl AgentAdapter for ClaudeCode {
|
||||
if let Some(r) = cx.resume {
|
||||
a.extend(["--resume".into(), r.to_string()]);
|
||||
}
|
||||
let tools = if cx.builder { "Bash,PowerShell,Read,Write,Edit,MultiEdit,Glob,Grep,TodoWrite,NotebookEdit" } else { "" };
|
||||
a.extend(["--tools".into(), tools.into()]);
|
||||
if cx.builder {
|
||||
// Questions go through our core_person_ask tool, which the app shows as buttons.
|
||||
a.extend(["--permission-prompt-tool".into(), format!("mcp__{MCP_NAME}__{APPROVAL_TOOL}"), "--disallowedTools".into(), "AskUserQuestion".into()]);
|
||||
|
||||
@@ -1146,6 +1146,14 @@ mod tests {
|
||||
let pos = |f: &str| a.iter().position(|x| x == f).unwrap();
|
||||
assert_eq!(a[pos("--output-format") + 1], "stream-json");
|
||||
assert!(a.contains(&"--verbose".to_string()));
|
||||
assert_eq!(a[pos("--tools") + 1], "", "built-in tools are opt-in");
|
||||
for access in [Access::Build, Access::CleanRoomBuild] {
|
||||
let builder = build_args(adapter_for("claude-code").as_ref(), &BackendOptions::default(), "build it", "PRE", &mcp(), None, access);
|
||||
let builder_pos = builder.iter().position(|x| x == "--tools").unwrap();
|
||||
let tools = &builder[builder_pos + 1];
|
||||
assert!(tools.split(',').any(|tool| tool == "Bash"), "{access:?}: {tools}");
|
||||
assert!(tools.split(',').any(|tool| tool == "Write"), "{access:?}: {tools}");
|
||||
}
|
||||
assert_eq!(a[pos("--resume") + 1], "sess-1");
|
||||
assert_eq!(a[pos("--allowedTools") + 1], "mcp__craft-anything__*");
|
||||
assert_eq!(a[pos("--model") + 1], "claude-opus-5-5", "Opus 5.5 unless configured otherwise");
|
||||
|
||||
@@ -539,7 +539,7 @@ pub fn never_unasked(ws: Option<&Workspace>, tool: &str, input: &Value) -> Optio
|
||||
}
|
||||
None
|
||||
}
|
||||
_ => None,
|
||||
_ => refuse("uses a tool that has no automatic approval rule"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -991,6 +991,14 @@ mod tests {
|
||||
assert_eq!((back["questions"].as_str(), back["chosen"].is_null(), back["app_default"].as_str()), (Some("ask"), true, Some("ask")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn auto_mode_refuses_unknown_tools_that_may_publish_or_schedule() {
|
||||
for tool in ["Artifact", "CronCreate", "ListAgents"] {
|
||||
let result = never_unasked(None, tool, &json!({}));
|
||||
assert!(result.is_some(), "{tool} must require the person's approval");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn auto_mode_keeps_going_but_never_does_the_dangerous_things() {
|
||||
let (_d, w) = ws();
|
||||
|
||||
Reference in New Issue
Block a user