Explorar o código

feat: add MCPTool + TestingPermissionTool — tool surface 40/40

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.
Jobdori hai 2 meses
pai
achega
b9d0d45bc4
Modificáronse 1 ficheiros con 65 adicións e 0 borrados
  1. 65 0
      rust/crates/tools/src/lib.rs

+ 65 - 0
rust/crates/tools/src/lib.rs

@@ -805,6 +805,34 @@ pub fn mvp_tool_specs() -> Vec<ToolSpec> {
             }),
             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<String, String> {
         "ReadMcpResource" => from_value::<McpResourceInput>(input).and_then(run_read_mcp_resource),
         "McpAuth" => from_value::<McpAuthInput>(input).and_then(run_mcp_auth),
         "RemoteTrigger" => from_value::<RemoteTriggerInput>(input).and_then(run_remote_trigger),
+        "MCP" => from_value::<McpToolInput>(input).and_then(run_mcp_tool),
+        "TestingPermission" => {
+            from_value::<TestingPermissionInput>(input).and_then(run_testing_permission)
+        }
         _ => Err(format!("unsupported tool: {name}")),
     }
 }
@@ -1039,6 +1071,26 @@ fn run_remote_trigger(input: RemoteTriggerInput) -> Result<String, String> {
         "message": "Remote trigger stub response"
     }))
 }
+
+#[allow(clippy::needless_pass_by_value)]
+fn run_mcp_tool(input: McpToolInput) -> Result<String, String> {
+    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<String, String> {
+    to_pretty_json(json!({
+        "action": input.action,
+        "permitted": true,
+        "message": "Testing permission tool stub"
+    }))
+}
 fn from_value<T: for<'de> Deserialize<'de>>(input: &Value) -> Result<T, String> {
     serde_json::from_value(input.clone()).map_err(|error| error.to_string())
 }
@@ -1404,6 +1456,19 @@ struct RemoteTriggerInput {
     body: Option<String>,
 }
 
+#[derive(Debug, Deserialize)]
+struct McpToolInput {
+    server: String,
+    tool: String,
+    #[serde(default)]
+    arguments: Option<Value>,
+}
+
+#[derive(Debug, Deserialize)]
+struct TestingPermissionInput {
+    action: String,
+}
+
 #[derive(Debug, Serialize)]
 struct WebFetchOutput {
     bytes: usize,