DEV Community

Mason Delan
Mason Delan

Posted on Fully Autonomous

MCP connected, hooks inactive: lessons from five coding-agent adapters

An agent can successfully call an MCP server while its lifecycle hooks never run. The server connection and the hook process have separate configuration, execution environments, and trust checks.

That distinction mattered while extending Selvedge's native hooks beyond Claude Code in version 0.3.15. The release adds adapters for Codex, Cursor, Gemini CLI, VS Code Copilot's Local harness, and Windsurf/Cascade. The useful engineering lesson is how to separate shared behavior from each client's protocol—and how to prove the integration actually ran.

Disclosure: I maintain Selvedge. This article was drafted with AI assistance and checked against the released implementation and recorded test evidence.

Keep the decision logic separate from the protocol

The feature in this example checks whether an agent has consulted recorded decisions before a supported edit to a watched schema or migration path. A saved rejection might explain that a column cannot be removed because an importer still needs it.

The adapter has three jobs:

native client payload
  -> normalize project, session, tool and affected paths
  -> evaluate the shared decision check
  -> encode the result in the client's response format
Enter fullscreen mode Exit fullscreen mode

The shared check shouldn't need to know whether a client calls its shell tool Bash, Shell, or run_shell_command. Conversely, the response encoder must know which fields—or exit status—the client recognizes.

The released adapters make these mappings explicit. For example, the Codex apply_patch payload supplies patch text in tool_input.command; the VS Code Local apply_patch payload uses tool_input.input. Treating those tools as interchangeable would lose the files the check needs to inspect.

Similar event names don't imply identical configuration

These are the project files and pre-edit events used by the five new adapters:

Client target Project hook configuration Event used for the edit check
Codex .codex/hooks.json PreToolUse
Cursor .cursor/hooks.json preToolUse
VS Code Copilot, Local .github/hooks/selvedge.json PreToolUse
Gemini CLI .gemini/settings.json BeforeTool
Windsurf/Cascade .windsurf/hooks.json, or existing .devin/hooks.json pre_write_code, pre_run_command

The table describes this implementation's supported targets, not every harness sold under those product names. In particular, VS Code Local support doesn't establish support for Copilot CLI, cloud sessions, or Agent Host.

Configuration shape also differs: copying the same event name into each JSON file is insufficient. Installers need to validate existing structures, preserve unrelated hooks, back up modified files, and surface conflicts rather than silently replacing customized commands.

Project identity deserves the same care. If a multi-root workspace payload doesn't identify an unambiguous current directory, checking another repository's decisions would be worse than reporting that the check couldn't be applied.

A notification isn't saved context

All five new adapters implement the watched-edit check. Four also provide a startup digest and a user notification before compaction. The Windsurf adapter provides neither startup context nor a compaction notification.

Those are different capabilities:

  • A startup digest supplies selected saved context at a supported startup event.
  • A pre-edit check can delay a supported tool call until the lookup requirement is satisfied.
  • A compaction notification tells the user something. It doesn't make the model save a decision, and it isn't model-context injection.

Calling all three “memory preservation” hides the distinction users need when debugging. Explicitly recorded rationale is also different from a model's hidden reasoning; these adapters don't inspect hidden reasoning or read chat transcripts.

Test installation, invocation, and behavior separately

A generated hook file proves configuration was written. A successful direct invocation proves a script accepts a particular payload. Neither proves the client will launch it.

For a real-client test, use a disposable project and inspect this sequence:

  1. Seed one synthetic rejection against a watched file.
  2. Review and enable the hooks through the client's normal trust controls.
  3. Attempt the relevant edit before retrieving history; inspect the actual denial.
  4. Retrieve the saved decision, retry, and inspect the final file.
  5. Test an ordinary source edit and a read operation independently.

The recorded native test used Codex CLI 0.154.0-alpha.6.2. It observed an apply_patch denial, a real prior-attempts CLI lookup, then an allowed retry with the resulting file checked. It used normal hook trust and an absolute executable path with a local payload recorder. Earlier isolated runs that ignored user configuration never invoked the project hooks; they were excluded from the successful result.

The other four new clients have automated payload, configuration, SQLite, and subprocess coverage, but haven't been verified end to end in those clients. Startup and compaction behavior also remains contract-tested. That's the difference between implementing an adapter and observing it work in a particular client version.

State the limits of the check

This check covers known mutating tool schemas and configured watched paths. Shell write detection is heuristic. Unknown payloads, missing project databases, and internal evaluation errors fail open, so it is workflow assistance rather than a security boundary.

The existing lookup window can also be satisfied by a recent query from another session. An allowed edit therefore doesn't prove this particular agent understood the decision—or that the old constraint still applies. Retrieval, correct application, and a successful code change need separate evidence.

The next diagnostic improvement is to report configuration and observed hook execution separately. A single “connected” status cannot establish both.

Protocol references: Codex, Cursor, VS Code Local, Gemini CLI, and Cascade. The contracts cited by this implementation were checked September 24, 2026; client versions can change them.

Top comments (0)