Hooks
Automation rules that run at three points in an AI session: when the assistant connects to a service, before a tool call, and after a tool call. Define them once — the gateway enforces them automatically.
Overview
Hooks fire at three points in an AI session:
- Session-hint hooks — Run once per session, when the AI client opens a connection (MCP
initialize). They contribute per-service text to the session'sinstructions— the AI sees them before any tool call, regardless of which action it ends up using. - Pre-hooks — Run before each action executes. Validate inputs, inject defaults, transform data, or block the request entirely.
- Post-hooks — Run after each action completes. Add instructions for the AI client, trigger notifications, or enrich responses.
All hooks are YAML-configured and hot-reloaded — no restart, no deployment for personal hooks.
Two scopes
Company hooks (admin)
Deployed with the gateway in config/hooks/admin/*.yaml. Apply to all users. Admin-scope validate hooks block the action.
Examples:
- New Jira issues automatically get a structured description template
- Labels with spaces are auto-replaced with hyphens
- GitLab MR descriptions must contain a Jira link
- Destructive actions (delete, archive) require confirmation
User hooks (personal)
Created per user via the AI assistant or MCP tools. Stored in config/hooks/users/{hash}/*.yaml. User-scope validate hooks warn but don't block.
Creating user hooks
Just tell your AI assistant what rule you want. It creates a persistent hook that applies to every future request — active immediately, no deployment needed.
Label automation
"From now on, automatically add the label team-backend to all my GitLab issues" Creates a rule that fires on every create_issue and create_merge_request. The label is injected before the request reaches GitLab. Works with Jira and GitLab.
Field validation
"Always warn me when I create an issue without a due date" Creates a validation rule that checks every new issue. If the due date is missing, the response includes a warnings field — the AI client sees it and can ask you to add one. The action still goes through: user hooks warn, they don't block.
Required fields and format checks
Validate that issues meet your team's structural standards — without dictating style:
"Warn me whenever my Jira issue description doesn't contain a checkbox (- [ ])" This creates a validate step that flags issues without acceptance criteria. The action still goes through (user-scope hooks warn, they don't block), but the AI client sees the warning and can ask you to fix it.
Style and templates belong in Skills, not Hooks. If you want to also dictate how ACs are phrased (user perspective, Given/When/Then, sentence length), that's preference-shaped instruction. The right home in the MCP standard is a Skill. Hooks enforce that the field has the right shape; Skills teach the agent how to compose it. mcpgate's Skill support is on the roadmap as SEP-2076 and the Skills Over MCP charter stabilize.
Custom rules via raw YAML
"Create a rule that warns whenever my GitLab MR title doesn't start with [Bug], [Feature], or [Chore]" The assistant generates a validate hook with a regex pattern. Anything the engine supports can be created via natural language.
All hooks stay active until you disable or delete them. Manage them via the dashboard or ask your assistant: "Show me my hooks" or "Delete my Slack signature hook".
Available presets
| Preset | Service | What it does |
|---|---|---|
add_label | Jira, GitLab | Auto-add a label to new issues/MRs |
add_signature | Slack | Append a signature to posted messages |
require_field | Any | Warn if a field is empty |
set_default | Any | Auto-fill a field if not provided |
add_instruction | Any | Show a reminder after the action |
For advanced use cases, pass raw YAML via the yaml parameter instead of a preset.
Managing hooks
Use gateway_hooks_read_actions to list hooks and presets. Use gateway_hooks_write_actions to create, toggle, or delete hooks. The dashboard also shows active hooks per service and allows enable/disable/delete.
Hook engine primitives
Each hook consists of one or more steps. The engine supports these step types:
| Type | Phase | SEP-1763 type | What it does |
|---|---|---|---|
validate | pre | validation | Check a field against a rule. Admin scope blocks, user scope warns. |
inject | pre | mutation | Set, append, or prepend a value into a field. |
transform | pre | mutation | Modify a field in-place (replace, regex, lowercase, etc.). |
instruct | post, session_hint | observability / initialize | Add a message for the AI client. In post it lands in the per-call instructions; in session_hint it lands in the session-level instructions on initialize. |
notify | post | observability | Send a notification (e.g. Slack message) as a side effect. |
Session-hint hooks
Session-hint hooks are a separate category from pre/post. They don't intercept a tool call — they contribute text to the session's instructions when the AI client connects to a service. The text is visible to the AI before any action runs, so it's useful for per-service context that should always be in scope: a styleguide URL for Transifex, workflow conventions for Jira, MR-template rules for GitLab.
Constraints enforced by the loader:
phase: session_hint- Only
instructsteps — novalidate,inject,transform, ornotify trigger.actionsmust be["*"]or omitted — session-hint hooks are bound to the service, not to specific actions- Both scopes apply: admin hints are joined first, then user hints layer on top
Session-hint hooks fire once per initialize, not per tool call. They cost on the order of 25–35 tokens per active hint — a one-time cost at session start, not a per-call tax.
Validation rules
The validate step supports these rules:
not_empty/required— Field must not be empty or nullmin_length— Minimum string lengthequals/not_equals— Exact value matchcontains/not_contains— Substring or list membershipmatches/not_matches— Regex pattern match
Execution order
On initialize (session start)
For each connected service:
Admin session_hint hooks (joined in file order)
→ User session_hint hooks (joined in file order)
→ appended to the session's instructions Admin hints are ground truth; user hints layer on top. A failing session-hint hook is logged and skipped — it never blocks the connection.
On a tool call
Company Hooks (pre)
→ User Hooks (pre)
→ Built-in Middleware (pre)
→ HTTP API Call
→ Built-in Middleware (post)
→ User Hooks (post)
→ Company Hooks (post) Company hooks run first on the way in and last on the way out. Built-in middleware handles technical transformations (Markdown to ADF, Cloud ID resolution, etc.) that are not configurable.
YAML format
name: require-jira-link-in-mr
description: Warn if MR description doesn't contain a Jira ticket link
version: 1
scope: user # or "admin"
enabled: true
trigger:
service: gitlab
actions:
- create_merge_request
phase: pre
steps:
- type: validate
field: params.description
rule: matches
value: "[A-Z]+-\\d+"
message: "Please include a Jira ticket (e.g. PBE-123) in the MR description" Multi-step example
name: enrich-new-issues
description: Add default label and remind about acceptance criteria
version: 1
scope: user
enabled: true
trigger:
service: gitlab
actions: [create_issue]
phase: pre
steps:
- type: inject
field: params.labels
op: append
value: ",team-backend"
default: "team-backend"
- type: validate
field: params.description
rule: min_length
value: 20
message: "Description seems too short — consider adding acceptance criteria" Session-hint example
name: transifex-styleguide
description: Tell connected MCP clients where to find the translation styleguide before they touch Transifex strings
version: 1
scope: admin
enabled: true
trigger:
service: transifex
phase: session_hint
steps:
- type: instruct
message: |
Transifex translations follow a project styleguide. Before drafting or
editing strings, fetch the styleguide URL configured in your workspace
and apply its tone/terminology rules. Note the absence of trigger.actions — session-hint hooks are service-scoped, not action-scoped, and the loader rejects anything else.
Response integration
Pre/post hook results are included in the tool response:
{
"success": true,
"data": { ... },
"applied_rules": [
{"name": "my-hook", "description": "What it does"}
],
"warnings": ["Field X is empty"],
"instructions": ["Remember to update the docs"]
} applied_rules— Which user hooks ran (transparency)warnings— Validation warnings from user-scope hooksinstructions— Post-action reminders for the AI client
Session-hint output is appended to the instructions field of the MCP initialize response, alongside the gateway's persona text. The AI client ingests it once at session start, so it's in scope for every subsequent tool call without per-call overhead.
Built-in middleware
In addition to configurable YAML hooks, the gateway includes built-in middleware that runs automatically:
- Markdown to ADF — Converts Markdown to Jira's Atlassian Document Format
- Text normalization — Fixes
\nliterals, escaped characters from AI clients - Destructive action confirmation — Requires
confirmed: truefor delete/archive operations - Response size capping — Limits large responses to prevent token overflow
- Auth error normalization — Converts service errors to actionable reconnect messages
- Cross-service automation — E.g. desk booking triggers office preheating
These are Python-based and not user-configurable. They handle technical concerns that YAML hooks don't need to worry about.
MCP tools
| Tool | Actions |
|---|---|
gateway_hooks_read_actions | list_hooks, list_presets |
gateway_hooks_write_actions | create_hook, delete_hook, toggle_hook |
Use list_presets with a service parameter to see available presets, actions, and field names for any connected service.