Draft / RFC — 2026-04-14 · TrueFoundry Engineering. Internal design reference; endpoints and behavior may change before general availability.
1. Executive Summary
AI coding assistants (Cursor, Claude Code, and others emerging) now support hooks — lifecycle callbacks that let external systems observe, control, and extend the agent loop. TrueFoundry’s AI Gateway already has a working Cursor hooks endpoint (/cursor/hooks/validate). This document maps the exact API surface of each client, identifies gaps, and proposes a unified architecture to support all clients through the gateway’s guardrail engine.
2. Current State (What We Have)
Existing Endpoint
- Router:
src/routers/cursorHooks.ts - Handler:
src/handlers/cursorHooksHandler.ts
3. Cursor Hooks API — Complete Reference
3.1 Protocol
3.2 Manifest Format (hooks.json)
3.3 All Hook Events
3.4 Key Input/Output Schemas
Common base (all events):3.5 How Cursor Calls External Hooks (Gateway Integration Pattern)
Cursor hooks are local processes. To route through the gateway, teams deploy a thin CLI script as the hook command that:- Reads JSON from stdin
- POSTs it to
https://gateway.example.com/cursor/hooks/validate - Reads the gateway response
- Exits with code
0(allow) or2(deny)
4. Claude Code Hooks API — Complete Reference
4.1 Protocol
4.2 Manifest Format (settings.json)
4.3 All Hook Events
4.4 Key Input/Output Schemas
Common base (all events):deny > defer > ask > allow
4.5 HTTP Hook Type (Direct Gateway Integration)
This is the critical difference from Cursor. Claude Code can POST directly to our gateway:- Request body = full event JSON sent as POST body
- Response = JSON parsed for decision/output
- Non-2xx = non-blocking error (execution continues)
- Must return 2xx with JSON body containing deny decision to actually block
5. Side-by-Side Comparison
6. Engineering Design Decisions
Decision summary: We will expose a single
/guardrail API on the AI Gateway that any AI coding assistant hook can invoke. Cursor reaches it via a thin CLI bridge (because Cursor hooks are local processes), and Claude Code reaches it directly using its native HTTP hook type. The endpoint normalizes client-specific event schemas into a shared internal format, runs the existing guardrail engine, and serializes the response back into the format the calling client expects.Decision 1: Unified /guardrail Endpoint (DECIDED)
Decision: Single unified endpoint — POST /api/llm/guardrail (with the existing /cursor/hooks/validate kept as a backwards-compatible alias).
The endpoint accepts events from any supported coding assistant. A thin per-client adapter normalizes the incoming schema, the shared guardrail engine evaluates the event, and a per-client serializer formats the response.
- One auth/rate-limit/observability surface to operate.
- Guardrail resolution rules are defined once and apply to every client.
- New clients (Windsurf, Copilot, JetBrains AI, etc.) plug in by adding an adapter — no new public route.
- Cursor and Claude Code teams point their hook config at the same URL; only the request body differs.
- Do we keep
/cursor/hooks/validatemounted long-term as an alias, or deprecate it after a migration window? - Should
/api/llm/guardrailaccept aclientquery/header for unambiguous adapter selection, or rely purely on payload shape sniffing? - Is the same endpoint also the right place for non-blocking analytics events (
SessionStart,Notification, etc.), or do those go to a separate/guardrail/eventsingest path?
Decision 2: Claude Code Response Format
The current Cursor response format is flat:continue, decision, reason)? Need to test what Claude Code actually requires vs. what it tolerates.
Decision 3: Event Mapping to Guardrail Types
Current mapping (Cursor):
New events from Claude Code that need mapping:
QUESTION: Which Claude Code events do we actually want to run guardrails on? The full list has 30+ events — most are observability. Should we start with just the blocking events (
UserPromptSubmit, PreToolUse, PostToolUse) and treat the rest as analytics passthrough?
Decision 4: Input Extraction from Claude Code Events
The current handler extractstext from the request body and wraps it as:
- Option A:
JSON.stringify(tool_input)— simple but loses semantic meaning - Option B: Extract the primary field per tool type (
commandfor Bash,contentfor Write,file_pathfor Read) - Option C: Send the full structured input to guardrails and let each plugin decide what to inspect
Decision 5: Authentication for Claude Code HTTP Hooks
Claude Code supports header interpolation from env vars:- JWT flow requires the user to have a TrueFoundry account
- Hook-specific key could be scoped to just guardrail validation
- Enterprise deployment: how does the admin distribute credentials?
Decision 6: Input Mutation Support
Both Cursor and Claude Code support mutating the tool input via hook response:- Cursor:
{ "updated_input": { "command": "safe-version" } } - Claude Code:
{ "hookSpecificOutput": { "updatedInput": { "command": "safe-version" } } }
Decision 7: Streaming / Latency Requirements
Hooks are in the critical path of every tool call. If the gateway is slow:- Cursor: User sees a hang (script blocked on HTTP POST)
- Claude Code: User sees custom
statusMessagespinner
- Current guardrail execution times?
- Should we implement a fast-path for common cases (e.g., cache allow decisions for repeated tool types)?
- Should we support async guardrails that don’t block the agent?
Decision 8: Additional Context Injection
Claude Code hooks can injectadditionalContext — text that gets added to the conversation for the LLM to see. This is a powerful feature for:
- Adding security policies inline
- Warning about sensitive operations
- Providing organization-specific instructions
Decision 9: defer Permission (Claude Code Only)
Claude Code supports a 4th permission level: defer (let Claude decide, but show the user). This is between allow and ask.
QUESTION: Should we support defer in our response? This would require guardrails to have a “soft warning” mode in addition to hard block.
Decision 10: Other Clients (Future-Proofing)
Emerging AI coding assistants likely to adopt hooks:- Windsurf (Codeium) — likely similar to Cursor’s local-process model
- GitHub Copilot — may adopt HTTP hooks
- JetBrains AI — may adopt HTTP hooks
- Zed AI — early stage
- Aider, Continue.dev — OSS tools
7. Proposed Architecture
All clients call the single decided endpoint —POST /api/llm/guardrail — and the gateway picks the right adapter for each.
Internal Normalized Event Format
8. Implementation Plan
Phase 1: Claude Code HTTP Hook Endpoint (Week 1-2)
- Create
src/routers/claudeCodeHooks.tsandsrc/handlers/claudeCodeHooksHandler.ts - Mount at
/claude-code/hooks/validateand/api/llm/claude-code/hooks/validate - Handle
PreToolUse,PostToolUse,UserPromptSubmitevents - Parse Claude Code input format, map to guardrail types
- Return Claude Code response format (
hookSpecificOutput) - Auth via Bearer token header (existing JWT flow)
Phase 2: Adapter Layer Refactor (Week 2-3)
- Extract common guardrail resolution logic from
cursorHooksHandler.ts - Create
src/hooks/adapters/cursor.tsandsrc/hooks/adapters/claudeCode.ts - Create
src/hooks/normalizedEvent.tsfor internal format - Refactor both handlers to use shared core
Phase 3: Advanced Features (Week 3-4)
- Input mutation support (return modified tool inputs)
- Additional context injection
deferpermission support- Analytics passthrough for non-blocking events (SessionStart, Stop, etc.)
Phase 4: Distribution & Onboarding (Week 4-5)
- CLI bridge script for Cursor (npm package or downloadable script)
- Claude Code
.claude/settings.jsontemplate generator - TrueFoundry dashboard UI for hooks configuration
- Documentation and onboarding guide
9. Open Questions (Sorted by Priority)
P0 — Must answer before implementation
P1 — Should answer before Phase 2
P2 — Can defer to Phase 3+
10. Appendix: Client Configuration Examples
A. Cursor Setup (with CLI bridge)
.cursor/hooks.json:B. Claude Code Setup (native HTTP)
.claude/settings.json:11. References
- Cursor Hooks Docs: https://cursor.com/docs/hooks
- Claude Code Hooks: https://docs.anthropic.com/en/docs/claude-code/hooks
- Existing implementation:
src/handlers/cursorHooksHandler.ts - Guardrail engine:
src/middlewares/hooks/ - Plugin system:
src/plugins/