claude: Respect always allow setting (#36450)
Claude will now respect the `agent.always_allow_tool_actions` setting and will set it when "Always Allow" is clicked. Release Notes: - N/A
This commit is contained in:
parent
33fbe53d48
commit
b578031120
4 changed files with 64 additions and 11 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -258,6 +258,7 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"acp_thread",
|
"acp_thread",
|
||||||
"agent-client-protocol",
|
"agent-client-protocol",
|
||||||
|
"agent_settings",
|
||||||
"agentic-coding-protocol",
|
"agentic-coding-protocol",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"collections",
|
"collections",
|
||||||
|
|
|
@ -19,6 +19,7 @@ doctest = false
|
||||||
[dependencies]
|
[dependencies]
|
||||||
acp_thread.workspace = true
|
acp_thread.workspace = true
|
||||||
agent-client-protocol.workspace = true
|
agent-client-protocol.workspace = true
|
||||||
|
agent_settings.workspace = true
|
||||||
agentic-coding-protocol.workspace = true
|
agentic-coding-protocol.workspace = true
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
collections.workspace = true
|
collections.workspace = true
|
||||||
|
|
|
@ -111,7 +111,8 @@ impl AgentConnection for ClaudeAgentConnection {
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let (mut thread_tx, thread_rx) = watch::channel(WeakEntity::new_invalid());
|
let (mut thread_tx, thread_rx) = watch::channel(WeakEntity::new_invalid());
|
||||||
let permission_mcp_server = ClaudeZedMcpServer::new(thread_rx.clone(), cx).await?;
|
let fs = project.read_with(cx, |project, _cx| project.fs().clone())?;
|
||||||
|
let permission_mcp_server = ClaudeZedMcpServer::new(thread_rx.clone(), fs, cx).await?;
|
||||||
|
|
||||||
let mut mcp_servers = HashMap::default();
|
let mut mcp_servers = HashMap::default();
|
||||||
mcp_servers.insert(
|
mcp_servers.insert(
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::claude::tools::{ClaudeTool, EditToolParams, ReadToolParams};
|
use crate::claude::tools::{ClaudeTool, EditToolParams, ReadToolParams};
|
||||||
use acp_thread::AcpThread;
|
use acp_thread::AcpThread;
|
||||||
use agent_client_protocol as acp;
|
use agent_client_protocol as acp;
|
||||||
|
use agent_settings::AgentSettings;
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use context_server::listener::{McpServerTool, ToolResponse};
|
use context_server::listener::{McpServerTool, ToolResponse};
|
||||||
|
@ -11,8 +13,11 @@ use context_server::types::{
|
||||||
ToolAnnotations, ToolResponseContent, ToolsCapabilities, requests,
|
ToolAnnotations, ToolResponseContent, ToolsCapabilities, requests,
|
||||||
};
|
};
|
||||||
use gpui::{App, AsyncApp, Task, WeakEntity};
|
use gpui::{App, AsyncApp, Task, WeakEntity};
|
||||||
|
use project::Fs;
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use settings::{Settings as _, update_settings_file};
|
||||||
|
use util::debug_panic;
|
||||||
|
|
||||||
pub struct ClaudeZedMcpServer {
|
pub struct ClaudeZedMcpServer {
|
||||||
server: context_server::listener::McpServer,
|
server: context_server::listener::McpServer,
|
||||||
|
@ -23,6 +28,7 @@ pub const SERVER_NAME: &str = "zed";
|
||||||
impl ClaudeZedMcpServer {
|
impl ClaudeZedMcpServer {
|
||||||
pub async fn new(
|
pub async fn new(
|
||||||
thread_rx: watch::Receiver<WeakEntity<AcpThread>>,
|
thread_rx: watch::Receiver<WeakEntity<AcpThread>>,
|
||||||
|
fs: Arc<dyn Fs>,
|
||||||
cx: &AsyncApp,
|
cx: &AsyncApp,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let mut mcp_server = context_server::listener::McpServer::new(cx).await?;
|
let mut mcp_server = context_server::listener::McpServer::new(cx).await?;
|
||||||
|
@ -30,6 +36,7 @@ impl ClaudeZedMcpServer {
|
||||||
|
|
||||||
mcp_server.add_tool(PermissionTool {
|
mcp_server.add_tool(PermissionTool {
|
||||||
thread_rx: thread_rx.clone(),
|
thread_rx: thread_rx.clone(),
|
||||||
|
fs: fs.clone(),
|
||||||
});
|
});
|
||||||
mcp_server.add_tool(ReadTool {
|
mcp_server.add_tool(ReadTool {
|
||||||
thread_rx: thread_rx.clone(),
|
thread_rx: thread_rx.clone(),
|
||||||
|
@ -102,6 +109,7 @@ pub struct McpServerConfig {
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct PermissionTool {
|
pub struct PermissionTool {
|
||||||
|
fs: Arc<dyn Fs>,
|
||||||
thread_rx: watch::Receiver<WeakEntity<AcpThread>>,
|
thread_rx: watch::Receiver<WeakEntity<AcpThread>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,6 +149,24 @@ impl McpServerTool for PermissionTool {
|
||||||
input: Self::Input,
|
input: Self::Input,
|
||||||
cx: &mut AsyncApp,
|
cx: &mut AsyncApp,
|
||||||
) -> Result<ToolResponse<Self::Output>> {
|
) -> Result<ToolResponse<Self::Output>> {
|
||||||
|
if agent_settings::AgentSettings::try_read_global(cx, |settings| {
|
||||||
|
settings.always_allow_tool_actions
|
||||||
|
})
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
let response = PermissionToolResponse {
|
||||||
|
behavior: PermissionToolBehavior::Allow,
|
||||||
|
updated_input: input.input,
|
||||||
|
};
|
||||||
|
|
||||||
|
return Ok(ToolResponse {
|
||||||
|
content: vec![ToolResponseContent::Text {
|
||||||
|
text: serde_json::to_string(&response)?,
|
||||||
|
}],
|
||||||
|
structured_content: (),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let mut thread_rx = self.thread_rx.clone();
|
let mut thread_rx = self.thread_rx.clone();
|
||||||
let Some(thread) = thread_rx.recv().await?.upgrade() else {
|
let Some(thread) = thread_rx.recv().await?.upgrade() else {
|
||||||
anyhow::bail!("Thread closed");
|
anyhow::bail!("Thread closed");
|
||||||
|
@ -148,8 +174,10 @@ impl McpServerTool for PermissionTool {
|
||||||
|
|
||||||
let claude_tool = ClaudeTool::infer(&input.tool_name, input.input.clone());
|
let claude_tool = ClaudeTool::infer(&input.tool_name, input.input.clone());
|
||||||
let tool_call_id = acp::ToolCallId(input.tool_use_id.context("Tool ID required")?.into());
|
let tool_call_id = acp::ToolCallId(input.tool_use_id.context("Tool ID required")?.into());
|
||||||
let allow_option_id = acp::PermissionOptionId("allow".into());
|
|
||||||
let reject_option_id = acp::PermissionOptionId("reject".into());
|
const ALWAYS_ALLOW: &'static str = "always_allow";
|
||||||
|
const ALLOW: &'static str = "allow";
|
||||||
|
const REJECT: &'static str = "reject";
|
||||||
|
|
||||||
let chosen_option = thread
|
let chosen_option = thread
|
||||||
.update(cx, |thread, cx| {
|
.update(cx, |thread, cx| {
|
||||||
|
@ -157,12 +185,17 @@ impl McpServerTool for PermissionTool {
|
||||||
claude_tool.as_acp(tool_call_id).into(),
|
claude_tool.as_acp(tool_call_id).into(),
|
||||||
vec![
|
vec![
|
||||||
acp::PermissionOption {
|
acp::PermissionOption {
|
||||||
id: allow_option_id.clone(),
|
id: acp::PermissionOptionId(ALWAYS_ALLOW.into()),
|
||||||
|
name: "Always Allow".into(),
|
||||||
|
kind: acp::PermissionOptionKind::AllowAlways,
|
||||||
|
},
|
||||||
|
acp::PermissionOption {
|
||||||
|
id: acp::PermissionOptionId(ALLOW.into()),
|
||||||
name: "Allow".into(),
|
name: "Allow".into(),
|
||||||
kind: acp::PermissionOptionKind::AllowOnce,
|
kind: acp::PermissionOptionKind::AllowOnce,
|
||||||
},
|
},
|
||||||
acp::PermissionOption {
|
acp::PermissionOption {
|
||||||
id: reject_option_id.clone(),
|
id: acp::PermissionOptionId(REJECT.into()),
|
||||||
name: "Reject".into(),
|
name: "Reject".into(),
|
||||||
kind: acp::PermissionOptionKind::RejectOnce,
|
kind: acp::PermissionOptionKind::RejectOnce,
|
||||||
},
|
},
|
||||||
|
@ -172,16 +205,33 @@ impl McpServerTool for PermissionTool {
|
||||||
})??
|
})??
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let response = if chosen_option == allow_option_id {
|
let response = match chosen_option.0.as_ref() {
|
||||||
PermissionToolResponse {
|
ALWAYS_ALLOW => {
|
||||||
|
cx.update(|cx| {
|
||||||
|
update_settings_file::<AgentSettings>(self.fs.clone(), cx, |settings, _| {
|
||||||
|
settings.set_always_allow_tool_actions(true);
|
||||||
|
});
|
||||||
|
})?;
|
||||||
|
|
||||||
|
PermissionToolResponse {
|
||||||
|
behavior: PermissionToolBehavior::Allow,
|
||||||
|
updated_input: input.input,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ALLOW => PermissionToolResponse {
|
||||||
behavior: PermissionToolBehavior::Allow,
|
behavior: PermissionToolBehavior::Allow,
|
||||||
updated_input: input.input,
|
updated_input: input.input,
|
||||||
}
|
},
|
||||||
} else {
|
REJECT => PermissionToolResponse {
|
||||||
debug_assert_eq!(chosen_option, reject_option_id);
|
|
||||||
PermissionToolResponse {
|
|
||||||
behavior: PermissionToolBehavior::Deny,
|
behavior: PermissionToolBehavior::Deny,
|
||||||
updated_input: input.input,
|
updated_input: input.input,
|
||||||
|
},
|
||||||
|
opt => {
|
||||||
|
debug_panic!("Unexpected option: {}", opt);
|
||||||
|
PermissionToolResponse {
|
||||||
|
behavior: PermissionToolBehavior::Deny,
|
||||||
|
updated_input: input.input,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue