fix(core): default approval behavior for mcp missing annotations (#15519)

- Changed `requires_mcp_tool_approval` to apply MCP spec defaults when
annotations are missing.
- Unannotated tools now default to:
  - `readOnlyHint = false`
  - `destructiveHint = true`
  - `openWorldHint = true`
- This means unannotated MCP tools now go through approval/ARC
monitoring instead of silently bypassing it.
- Explicitly read-only tools still skip approval unless they are also
explicitly marked destructive.

**Previous behavior**
Failed open for missing annotations, which was unsafe for custom MCP
tools that omitted or forgot annotations.

---------

Co-authored-by: colby-oai <228809017+colby-oai@users.noreply.github.com>
This commit is contained in:
Fouad Matin
2026-03-25 07:55:41 -07:00
committed by GitHub
parent 047ea642d2
commit 32c4993c8a
5 changed files with 127 additions and 15 deletions

View File

@@ -22,6 +22,7 @@ use rmcp::model::ResourceTemplate;
use rmcp::model::ServerCapabilities;
use rmcp::model::ServerInfo;
use rmcp::model::Tool;
use rmcp::model::ToolAnnotations;
use serde::Deserialize;
use serde_json::json;
use tokio::task;
@@ -85,11 +86,13 @@ impl TestToolServer {
}))
.expect("echo tool schema should deserialize");
Tool::new(
let mut tool = Tool::new(
Cow::Borrowed(name),
Cow::Borrowed(description),
Arc::new(schema),
)
);
tool.annotations = Some(ToolAnnotations::new().read_only(true));
tool
}
fn image_tool() -> Tool {
@@ -101,11 +104,13 @@ impl TestToolServer {
}))
.expect("image tool schema should deserialize");
Tool::new(
let mut tool = Tool::new(
Cow::Borrowed("image"),
Cow::Borrowed("Return a single image content block."),
Arc::new(schema),
)
);
tool.annotations = Some(ToolAnnotations::new().read_only(true));
tool
}
/// Tool intended for manual testing of Codex TUI rendering for MCP image tool results.
@@ -154,13 +159,15 @@ impl TestToolServer {
}))
.expect("image_scenario tool schema should deserialize");
Tool::new(
let mut tool = Tool::new(
Cow::Borrowed("image_scenario"),
Cow::Borrowed(
"Return content blocks for manual testing of MCP image rendering scenarios.",
),
Arc::new(schema),
)
);
tool.annotations = Some(ToolAnnotations::new().read_only(true));
tool
}
fn memo_resource() -> Resource {

View File

@@ -38,6 +38,7 @@ use rmcp::model::ResourceTemplate;
use rmcp::model::ServerCapabilities;
use rmcp::model::ServerInfo;
use rmcp::model::Tool;
use rmcp::model::ToolAnnotations;
use rmcp::transport::StreamableHttpServerConfig;
use rmcp::transport::StreamableHttpService;
use rmcp::transport::streamable_http_server::session::local::LocalSessionManager;
@@ -84,11 +85,13 @@ impl TestToolServer {
}))
.expect("echo tool schema should deserialize");
Tool::new(
let mut tool = Tool::new(
Cow::Borrowed("echo"),
Cow::Borrowed("Echo back the provided message and include environment data."),
Arc::new(schema),
)
);
tool.annotations = Some(ToolAnnotations::new().read_only(true));
tool
}
fn memo_resource() -> Resource {