feat(ai_client): gate mutating MCP tools through pre_tool_callback in all 4 providers

This commit is contained in:
2026-03-02 16:50:47 -05:00
parent 8e6462d10b
commit e5e35f78dd
2 changed files with 81 additions and 4 deletions

View File

@@ -805,7 +805,12 @@ def _send_gemini(md_content: str, user_message: str, base_dir: str,
events.emit("tool_execution", payload={"status": "started", "tool": name, "args": args, "round": r_idx})
if name in mcp_client.TOOL_NAMES:
_append_comms("OUT", "tool_call", {"name": name, "args": args})
out = mcp_client.dispatch(name, args)
if name in mcp_client.MUTATING_TOOLS and pre_tool_callback:
desc = f"# MCP MUTATING TOOL: {name}\n" + "\n".join(f"# {k}: {repr(v)}" for k, v in args.items())
_res = pre_tool_callback(desc, base_dir, qa_callback)
out = "USER REJECTED: tool execution cancelled" if _res is None else mcp_client.dispatch(name, args)
else:
out = mcp_client.dispatch(name, args)
elif name == TOOL_NAME:
scr = args.get("script", "")
_append_comms("OUT", "tool_call", {"name": TOOL_NAME, "script": scr})
@@ -927,7 +932,12 @@ def _send_gemini_cli(md_content: str, user_message: str, base_dir: str,
events.emit("tool_execution", payload={"status": "started", "tool": name, "args": args, "round": r_idx})
if name in mcp_client.TOOL_NAMES:
_append_comms("OUT", "tool_call", {"name": name, "id": call_id, "args": args})
out = mcp_client.dispatch(name, args)
if name in mcp_client.MUTATING_TOOLS and pre_tool_callback:
desc = f"# MCP MUTATING TOOL: {name}\n" + "\n".join(f"# {k}: {repr(v)}" for k, v in args.items())
_res = pre_tool_callback(desc, base_dir, qa_callback)
out = "USER REJECTED: tool execution cancelled" if _res is None else mcp_client.dispatch(name, args)
else:
out = mcp_client.dispatch(name, args)
elif name == TOOL_NAME:
scr = args.get("script", "")
_append_comms("OUT", "tool_call", {"name": TOOL_NAME, "id": call_id, "script": scr})
@@ -1343,7 +1353,12 @@ def _send_anthropic(md_content: str, user_message: str, base_dir: str, file_item
events.emit("tool_execution", payload={"status": "started", "tool": b_name, "args": b_input, "round": round_idx})
if b_name in mcp_client.TOOL_NAMES:
_append_comms("OUT", "tool_call", {"name": b_name, "id": b_id, "args": b_input})
output = mcp_client.dispatch(b_name, b_input)
if b_name in mcp_client.MUTATING_TOOLS and pre_tool_callback:
desc = f"# MCP MUTATING TOOL: {b_name}\n" + "\n".join(f"# {k}: {repr(v)}" for k, v in b_input.items())
_res = pre_tool_callback(desc, base_dir, qa_callback)
output = "USER REJECTED: tool execution cancelled" if _res is None else mcp_client.dispatch(b_name, b_input)
else:
output = mcp_client.dispatch(b_name, b_input)
_append_comms("IN", "tool_result", {"name": b_name, "id": b_id, "output": output})
elif b_name == TOOL_NAME:
script = b_input.get("script", "")
@@ -1596,7 +1611,12 @@ def _send_deepseek(md_content: str, user_message: str, base_dir: str,
events.emit("tool_execution", payload={"status": "started", "tool": tool_name, "args": tool_args, "round": round_idx})
if tool_name in mcp_client.TOOL_NAMES:
_append_comms("OUT", "tool_call", {"name": tool_name, "id": tool_id, "args": tool_args})
tool_output = mcp_client.dispatch(tool_name, tool_args)
if tool_name in mcp_client.MUTATING_TOOLS and pre_tool_callback:
desc = f"# MCP MUTATING TOOL: {tool_name}\n" + "\n".join(f"# {k}: {repr(v)}" for k, v in tool_args.items())
_res = pre_tool_callback(desc, base_dir, qa_callback)
tool_output = "USER REJECTED: tool execution cancelled" if _res is None else mcp_client.dispatch(tool_name, tool_args)
else:
tool_output = mcp_client.dispatch(tool_name, tool_args)
elif tool_name == TOOL_NAME:
script = tool_args.get("script", "")
_append_comms("OUT", "tool_call", {"name": TOOL_NAME, "id": tool_id, "script": script})