Skip to content

postToolUse hook not dispatched for web_fetch tool results #3665

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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions