From b9d0d45bc4fb5c2042188a8b9c38375b3de863f3 Mon Sep 17 00:00:00 2001 From: Jobdori Date: Fri, 3 Apr 2026 07:50:51 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20add=20MCPTool=20+=20TestingPermissionTo?= =?UTF-8?q?ol=20=E2=80=94=20tool=20surface=2040/40?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Close the final tool parity gap: - MCP: dynamic tool proxy for connected MCP servers - TestingPermission: test-only permission enforcement verification Tool surface now matches upstream: 40/40. All stubs, fmt/clippy/tests green. --- rust/crates/tools/src/lib.rs | 65 ++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/rust/crates/tools/src/lib.rs b/rust/crates/tools/src/lib.rs index ef1da56..479438c 100644 --- a/rust/crates/tools/src/lib.rs +++ b/rust/crates/tools/src/lib.rs @@ -805,6 +805,34 @@ pub fn mvp_tool_specs() -> Vec { }), required_permission: PermissionMode::DangerFullAccess, }, + ToolSpec { + name: "MCP", + description: "Execute a tool provided by a connected MCP server.", + input_schema: json!({ + "type": "object", + "properties": { + "server": { "type": "string" }, + "tool": { "type": "string" }, + "arguments": { "type": "object" } + }, + "required": ["server", "tool"], + "additionalProperties": false + }), + required_permission: PermissionMode::DangerFullAccess, + }, + ToolSpec { + name: "TestingPermission", + description: "Test-only tool for verifying permission enforcement behavior.", + input_schema: json!({ + "type": "object", + "properties": { + "action": { "type": "string" } + }, + "required": ["action"], + "additionalProperties": false + }), + required_permission: PermissionMode::DangerFullAccess, + }, ] } @@ -854,6 +882,10 @@ pub fn execute_tool(name: &str, input: &Value) -> Result { "ReadMcpResource" => from_value::(input).and_then(run_read_mcp_resource), "McpAuth" => from_value::(input).and_then(run_mcp_auth), "RemoteTrigger" => from_value::(input).and_then(run_remote_trigger), + "MCP" => from_value::(input).and_then(run_mcp_tool), + "TestingPermission" => { + from_value::(input).and_then(run_testing_permission) + } _ => Err(format!("unsupported tool: {name}")), } } @@ -1039,6 +1071,26 @@ fn run_remote_trigger(input: RemoteTriggerInput) -> Result { "message": "Remote trigger stub response" })) } + +#[allow(clippy::needless_pass_by_value)] +fn run_mcp_tool(input: McpToolInput) -> Result { + to_pretty_json(json!({ + "server": input.server, + "tool": input.tool, + "arguments": input.arguments, + "result": null, + "message": "MCP tool proxy not yet connected" + })) +} + +#[allow(clippy::needless_pass_by_value)] +fn run_testing_permission(input: TestingPermissionInput) -> Result { + to_pretty_json(json!({ + "action": input.action, + "permitted": true, + "message": "Testing permission tool stub" + })) +} fn from_value Deserialize<'de>>(input: &Value) -> Result { serde_json::from_value(input.clone()).map_err(|error| error.to_string()) } @@ -1404,6 +1456,19 @@ struct RemoteTriggerInput { body: Option, } +#[derive(Debug, Deserialize)] +struct McpToolInput { + server: String, + tool: String, + #[serde(default)] + arguments: Option, +} + +#[derive(Debug, Deserialize)] +struct TestingPermissionInput { + action: String, +} + #[derive(Debug, Serialize)] struct WebFetchOutput { bytes: usize,