mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Support MCP tools in hooks (#18385)
## Summary Lifecycle hooks currently treat `PreToolUse`, `PostToolUse`, and `PermissionRequest` as Bash-only flows - hook schema constrains `tool_name` to `Bash` - hook input assumes a command-shaped `tool_input` - core hook dispatch path passes only shell command strings That means hooks cannot target MCP tools even though MCP tool names are model-visible and stable This change generalizes those hook paths so they can match and receive payloads for MCP tools while preserving the existing Bash behavior. ## Reviewer Notes I think these are the key files - `codex-rs/core/src/tools/handlers/mcp.rs` - `codex-rs/core/src/mcp_tool_call.rs` Otherwise the changes across apply_patch, shell, and unified_exec are mainly to rewire everything to be `tool_input` based instead of just `command` so that it'll make sense for MCP tools. ## Changes - Allow `PreToolUse`, `PostToolUse`, and `PermissionRequest` hook inputs to carry arbitrary `tool_name` and `tool_input` values instead of hard-coding `Bash` and command-only payloads. - Add MCP hook payload support through `McpHandler`, using the model-visible tool name from `ToolInvocation` and the raw MCP arguments as `tool_input`. - Include MCP tool responses in `PostToolUse` by serializing `McpToolOutput` into the hook response payload. - Run `PermissionRequest` hooks for MCP approval requests after remembered approval checks and before falling back to user-facing MCP elicitation. - Preserve exact matching for literal hook matchers like `Bash` and `mcp__memory__create_entities`, while keeping regex matcher support for patterns like `mcp__memory__.*` and `mcp__.*__write.*`. --------- Co-authored-by: Andrei Eternal <eternal@openai.com> Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
co-authored by
Andrei Eternal
Codex
parent
8bc667b07b
commit
305825abd9
@@ -125,7 +125,7 @@ with Path(r"{log_path}").open("a", encoding="utf-8") as handle:
|
||||
tool_name: "Bash".to_string(),
|
||||
matcher_aliases: Vec::new(),
|
||||
tool_use_id: "tool-1".to_string(),
|
||||
command: "echo hello".to_string(),
|
||||
tool_input: serde_json::json!({ "command": "echo hello" }),
|
||||
});
|
||||
assert_eq!(preview.len(), 1);
|
||||
assert_eq!(preview[0].source_path, managed_dir);
|
||||
@@ -141,7 +141,7 @@ with Path(r"{log_path}").open("a", encoding="utf-8") as handle:
|
||||
tool_name: "Bash".to_string(),
|
||||
matcher_aliases: Vec::new(),
|
||||
tool_use_id: "tool-1".to_string(),
|
||||
command: "echo hello".to_string(),
|
||||
tool_input: serde_json::json!({ "command": "echo hello" }),
|
||||
})
|
||||
.await;
|
||||
|
||||
@@ -212,7 +212,7 @@ fn requirements_managed_hooks_warn_when_managed_dir_is_missing() {
|
||||
tool_name: "Bash".to_string(),
|
||||
matcher_aliases: Vec::new(),
|
||||
tool_use_id: "tool-1".to_string(),
|
||||
command: "echo hello".to_string(),
|
||||
tool_input: serde_json::json!({ "command": "echo hello" }),
|
||||
})
|
||||
.is_empty()
|
||||
);
|
||||
@@ -318,7 +318,7 @@ fn discovers_hooks_from_json_and_toml_in_the_same_layer() {
|
||||
tool_name: "Bash".to_string(),
|
||||
matcher_aliases: Vec::new(),
|
||||
tool_use_id: "tool-1".to_string(),
|
||||
command: "echo hello".to_string(),
|
||||
tool_input: serde_json::json!({ "command": "echo hello" }),
|
||||
});
|
||||
assert_eq!(preview.len(), 2);
|
||||
assert!(engine.handlers.iter().all(|handler| !handler.is_managed));
|
||||
|
||||
Reference in New Issue
Block a user