Skip to content

postToolUse hook not dispatched for web_fetch tool results #3665

@pat-nel87

Description

@pat-nel87

Summary

postToolUse hooks are not dispatched when the model uses the web_fetch
tool. This breaks the universal-interception promise of the hook system for
HTTP responses — which are often the largest single category by bytes in a
diagnostic session.

Repro

  1. Install any working postToolUse hook. A minimal one for the test —
    write ~/.copilot/hooks-bin/probe.sh:

    #!/usr/bin/env bash
    echo "[$(date -Iseconds)] post-tool fired with: $(cat | jq -c '.toolName')" >> ~/.copilot/hook-probe.log
    echo '{}'

    And ~/.copilot/hooks/probe.json:

    {
      "version": 1,
      "hooks": {
        "postToolUse": [
          { "type": "command", "bash": "/path/to/probe.sh" }
        ]
      }
    }
  2. Run a session that mixes shell and web_fetch calls. For example:

    copilot --log-level debug -p "Fetch https://api.github.com/repos/microsoft/typescript using web_fetch and also run 'date' in powershell. Summarize both."
    
  3. After the session:

    cat ~/.copilot/hook-probe.log

Expected

One […] post-tool fired with: "<tool>" line per successful tool
invocation, including the web_fetch calls — consistent with how the hook
fires for bash, powershell, view, grep, MCP-provided tool results,
etc.

Actual

Lines for powershell and any other tools used, but no lines for
web_fetch
. The hook process is never spawned for web_fetch results.

Empirically confirmed by counting Executing hook.*post-tool lines in
~/.copilot/logs/process-*.log vs unique tool_use_ids in the same log.
In a captured session with 2 web_fetch calls and 33 other tool calls,
exactly 33 post-tool hook executions appeared. The absence is structural,
not conditional on resultType — even errored web_fetch results
(404s) get zero dispatches.

preToolUse does fire for web_fetch (35/35 in the same capture), so
the gap is specifically on the post-tool side.

Impact

HTTP responses fetched via web_fetch — often the largest single category
by bytes in real diagnostic sessions (probing internal APIs, fetching MCP
server status pages, GitHub API queries) — bypass any post-tool hook the
user has installed. That includes:

  • Context-compression tools (e.g. coagula,
    which surfaced this; the hook silently misses ~60% of bytes on
    HTTP-heavy workflows)
  • Audit/logging hooks for compliance
  • PII or secret redactors

The current workaround is to nudge the model toward Invoke-RestMethod,
Invoke-WebRequest, or curl invoked through the powershell tool —
those go through the dispatched hook chain. But the model often prefers
web_fetch for HTTP work since it's the dedicated tool for that, and
prompting around tool preference isn't reliable.

Suggested fix

Dispatch postToolUse for web_fetch results the same way it's
dispatched for every other tool. The hook can then decide what to do
with the response (no-op, transform via modifiedResult, log, etc.) —
matching the pattern documented in the hooks reference for other tools.

Environment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:pluginsPlugin system, marketplace, hooks, skills, extensions, and custom agentsarea:toolsBuilt-in tools: file editing, shell, search, LSP, git, and tool call behavior

    Type

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions