Compare commits

...
Author SHA1 Message Date
l0ng-ai 314997cdd7 feat(mcp): run the IDE's tools without confirmation in brave mode
Settings › Agents › IDE MCP Server gets Run tools without confirmation
(brave mode): builds, run configurations and the other tools that change
a project start without the confirmation, so an unattended agent isn't
left waiting on it until the request times out. It is read from the
IDE's own settings only; a project's settings can't hold it.

Fixes #2660
2026-11-11 21:16:36 +08:00
8 changed files with 66 additions and 3 deletions

No files matched your search

+3
View File
@@ -261,6 +261,9 @@ pub enum WhileRunning {
pub struct McpServerSettings {
/// Serve the tools on 127.0.0.1 while the IDE runs.
pub enabled: bool,
/// Run the tools that change the project or start processes without asking first
/// (IDEA's brave mode). Read from the IDE's own settings only, never a project's.
pub run_without_confirmation: bool,
}
/// Absent profiles retain automatic discovery for existing projects.
+5
View File
@@ -51,7 +51,12 @@ impl Call {
pub async fn ask(&self, request: ide_core::event::McpUiRequest) -> IdeResult<()> {
self.host.mcp_requests.ask(self, request).await
}
/// Asks the user before a tool changes the project or starts a process, unless brave mode
/// is on — read from the IDE's own settings only, which a project's settings can't set.
pub async fn confirm(&self, subject: &str, detail: Option<String>) -> IdeResult<()> {
if self.host.global_settings.effective().mcp_server.run_without_confirmation {
return self.project.require_trust();
}
self.ask(ide_core::event::McpUiRequest::Confirm {
client: self.client.clone(),
tool: self.tool.clone(),
+37
View File
@@ -416,3 +416,40 @@ async fn rust_references_and_rename_through_mcp() {
assert!(project.documents.text_snapshot(doc.id).unwrap().text.contains("format_amount"));
assert!(std::fs::read_to_string(h.root.join("src/main.rs")).unwrap().contains("format_amount"));
}
#[tokio::test(flavor = "multi_thread")]
async fn brave_mode_runs_tools_without_asking_only_from_the_global_settings() {
let h = Harness::new();
h.trust().await;
h.host.mcp_requests.set_ready("main", true);
let p = h.host.project("main").unwrap();
// A project's own settings can't turn the confirmations off.
let project_layer =
p.settings.set(ide_workspace::SettingsScope::Project, "mcpServer.runWithoutConfirmation", json!(true));
assert!(project_layer.is_err());
let denied = h.error("build_project", json!({}));
let answer = async {
let id = h.request().await;
h.host.mcp_requests.answer("main", id, false, None).unwrap();
};
let (error, ()) = tokio::join!(denied, answer);
assert!(error.contains("denied by the user"), "{error}");
h.events.lock().unwrap().clear();
h.host
.global_settings
.set(ide_workspace::SettingsScope::Global, "mcpServer.runWithoutConfirmation", json!(true))
.unwrap();
let (host, root) = (h.host.clone(), h.root.clone());
let result = tokio::time::timeout(
std::time::Duration::from_secs(60),
tokio::task::spawn_blocking(move || {
Registry::new(host).call(&root, "build_project", "{}", CancellationToken::new())
}),
)
.await
.expect("ran without waiting for an answer")
.unwrap();
assert!(!format!("{result:?}").contains("denied"), "{result:?}");
assert!(!h.events.lock().unwrap().iter().any(|e| matches!(e, IdeEvent::McpRequest { .. })), "nothing asked");
}
+5
View File
@@ -6665,6 +6665,11 @@ export type McpServerInput = {
export type McpServerSettings = {
/** Serve the tools on 127.0.0.1 while the IDE runs. */
enabled?: boolean,
/**
* Run the tools that change the project or start processes without asking first
* (IDEA's brave mode). Read from the IDE's own settings only, never a project's.
*/
runWithoutConfirmation?: boolean,
};
/** What Settings › Agents › MCP Server shows. */
+6 -3
View File
@@ -104,7 +104,7 @@ export interface ValidSettings extends WorkflowSettings {
/** Tools › Agents › Chat: the meters in an agent task's header. */
agents: Required<AgentSettings>;
/** Agents › IDE MCP Server (global). */
mcpServer: { enabled: boolean };
mcpServer: { enabled: boolean; runWithoutConfirmation: boolean };
}
/** One `languageServers.<name>` entry; every field optional, like in settings.toml. */
@@ -193,7 +193,7 @@ export const DEFAULT_SETTINGS: ValidSettings = {
defaults: {},
mcpServers: {},
},
mcpServer: { enabled: false },
mcpServer: { enabled: false, runWithoutConfirmation: false },
...DEFAULT_WORKFLOW,
};
@@ -359,7 +359,10 @@ export function validateSettings(raw: Settings | unknown): ValidSettings {
defaults: agentDefaults(agents.defaults),
mcpServers: agentMcpServers(agents.mcpServers),
},
mcpServer: { enabled: bool(obj(root.mcpServer).enabled, d.mcpServer.enabled) },
mcpServer: {
enabled: bool(obj(root.mcpServer).enabled, d.mcpServer.enabled),
runWithoutConfirmation: bool(obj(root.mcpServer).runWithoutConfirmation, d.mcpServer.runWithoutConfirmation),
},
...validateWorkflow(root),
};
}
@@ -128,6 +128,12 @@ export const MCP_SERVER_PAGE: PageSpec = {
label: "Enable MCP server",
hint: "Agents can read, edit, and run projects open in OpenIDEA through its built-in tools. Run tools ask for confirmation. The server listens on 127.0.0.1 only and requires a token.",
},
{
kind: "toggle",
key: "mcpServer.runWithoutConfirmation",
label: "Run tools without confirmation (brave mode)",
hint: "Run configurations, terminal commands and other tools that change a project start without asking, for unattended agents. Only the IDE's own settings can turn this on, not a project's.",
},
{
kind: "custom",
label: "Endpoint",
+2
View File
@@ -673,6 +673,8 @@
"Gradle Tasks": "Gradle Tasks",
"IDE MCP Server": "IDE MCP Server",
"Enable MCP server": "Enable MCP server",
"Run tools without confirmation (brave mode)": "Run tools without confirmation (brave mode)",
"Run configurations, terminal commands and other tools that change a project start without asking, for unattended agents. Only the IDE's own settings can turn this on, not a project's.": "Run configurations, terminal commands and other tools that change a project start without asking, for unattended agents. Only the IDE's own settings can turn this on, not a project's.",
"Status": "Status",
"Running on port {port}": "Running on port {port}",
"Failed to start: {error}": "Failed to start: {error}",
+2
View File
@@ -7015,6 +7015,8 @@
"Gradle Tasks": "Gradle 任务",
"IDE MCP Server": "IDE MCP 服务器",
"Enable MCP server": "启用 MCP 服务器",
"Run tools without confirmation (brave mode)": "运行工具时不再确认(勇敢模式)",
"Run configurations, terminal commands and other tools that change a project start without asking, for unattended agents. Only the IDE's own settings can turn this on, not a project's.": "运行配置、终端命令以及其他会修改项目的工具直接执行、不再询问,适合无人值守的智能体。只能在 IDE 自身的设置里开启,项目设置无法开启。",
"Running on port {port}": "正在端口 {port} 上运行",
"Failed to start: {error}": "启动失败:{error}",
"Not running": "未运行",