afterMCPExecution return value is discarded, and postToolUse carries no MCP server identity

There is no post-execution hook that can both (a) tell which MCP server produced a tool result and (b) act on it. The two halves live on two different hooks.

Detail

At the MCP tool-call site, two hooks fire back to back:

  • afterMCPExecution receives mcp_server_name and mcp_server_url, but its return value is discarded.
  • postToolUse honors updated_mcp_tool_output (replaces the result the model sees), but its payload carries no MCP server fields — only tool_name, tool_input, tool_output, duration, tool_use_id.

From cursor-agent 2026.07.23-e383d2b:

// payload `i` spreads `b` = { mcp_server_name, mcp_server_url }
yield this.hookExecutor.executeHookForStep(u._E.afterMCPExecution, i);   // return value not assigned
const a = yield this.firePostToolUse(e, l, f, p, n, s, d, h);
if (void 0 !== a?.updated_mcp_tool_output) { /* result is substituted */ }

firePostToolUse builds its payload as {...t, tool_name, tool_input, tool_output, duration, tool_use_id}b, which holds the server fields, is not spread in.

beforeMCPExecution has both halves (it gets mcp_server_name / mcp_server_url / url / command and honors permission: "deny"), so the gap is only on the post-execution side.

Why this matters

Policy about MCP tool results is per-server: whether a result is acceptable depends on which server returned it. A tool result is also an injection vector into the model’s context, so the moment before the model reads it is where that policy has to apply. Today the decision can be made on afterMCPExecution but not enforced, or enforced on postToolUse but not made.

To be clear, this is not a request to un-run the call — it has already executed and that can’t be reversed. It is only about withholding or replacing the result before the model reads it, which postToolUse already supports.

Either change closes the gap

  1. Honor updated_mcp_tool_output from afterMCPExecution’s return value, as postToolUse already does; or
  2. Add mcp_server_name / mcp_server_url to the postToolUse payload for MCP tools.

(2) looks like the smaller change.

Environment

cursor-agent 2026.07.23-e383d2b, macOS.

Also filed at Hooks: `afterMCPExecution` return value is discarded, and `postToolUse` carries no MCP server identity · Issue #50 · cursor/cookbook · GitHub for visibility.

Hey there,

To confirm each side:

  • beforeMCPExecution is the one hook today that carries both the server identity (mcp_server_name / mcp_server_url) and the ability to act (permission: "deny"), so per-server gating before a call runs works now.
  • Post-execution, afterMCPExecution is observation-only, and postToolUse is the hook that can substitute a result (updated_mcp_tool_output) - but it doesn’t currently carry the MCP server fields, so per-server policy on the result isn’t expressible today. That lines up with what you found.

Between your two options, adding mcp_server_name / mcp_server_url to the postToolUse payload is the cleaner fit, since the after-hooks are intentionally observation-only.

We’ll be tracking this thread to gauge interest, which helps our team prioritize - so if others want per-server result policies, please chime in. No timeline to share yet, but I’ll post back here when there’s an update. (And thanks for cross-filing on the cookbook.)