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's instructions — 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

PresetServiceWhat it does
add_labelJira, GitLabAuto-add a label to new issues/MRs
add_signatureSlackAppend a signature to posted messages
require_fieldAnyWarn if a field is empty
set_defaultAnyAuto-fill a field if not provided
add_instructionAnyShow 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:

TypePhaseSEP-1763 typeWhat it does
validateprevalidationCheck a field against a rule. Admin scope blocks, user scope warns.
injectpremutationSet, append, or prepend a value into a field.
transformpremutationModify a field in-place (replace, regex, lowercase, etc.).
instructpost, session_hintobservability / initializeAdd 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.
notifypostobservabilitySend 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 instruct steps — no validate, inject, transform, or notify
  • trigger.actions must 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 null
  • min_length — Minimum string length
  • equals / not_equals — Exact value match
  • contains / not_contains — Substring or list membership
  • matches / 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 hooks
  • instructions — 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 \n literals, escaped characters from AI clients
  • Destructive action confirmation — Requires confirmed: true for 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

ToolActions
gateway_hooks_read_actionslist_hooks, list_presets
gateway_hooks_write_actionscreate_hook, delete_hook, toggle_hook

Use list_presets with a service parameter to see available presets, actions, and field names for any connected service.