Flows are part of the FrontMCP execution model. They provide hook points for cross-cutting concerns like logging, caching, validation, and error handling.
Why Flows?
Flows give you fine-grained control over request processing without modifying tool, resource, or prompt code directly.
Flows are ideal for:
- Request validation — check permissions, validate inputs before execution
- Caching — intercept responses and serve from cache
- Logging and auditing — trace every request through the system
- Error handling — centralized error recovery and formatting
- Performance monitoring — measure timing across stages
Flow Lifecycle
Every request passes through these stages in order:Built-in Flows
FrontMCP provides built-in flows for all MCP protocol operations:Creating Custom Flows
Custom flows are defined with the@Flow decorator, a typed FlowPlan, and stage handlers attached via FlowHooksOf(...).Stage(...). The plan is keyed by phase (pre, execute, post, finalize) and lists the stage names that run in that phase. Each named stage in the plan is implemented by a method on the flow class decorated with @Stage('<stage-name>').
Flow Metadata
Hooking into Flows
You don’t need to create a full custom flow to customize behavior. Use hooks to intercept specific stages of existing flows.Hook Types
Applying Hooks
Hooks are method decorators returned from a flow-specific factory. Use the pre-built hook factories (e.g.ToolHook, ResourceHook) or build your own with FlowHooksOf(flowName).
FlowHooksOf(flowName):
Around Hooks
Around hooks wrap a stage completely, giving you control over whether the stage executes:Flow Control
Within flow stages, you have access to control methods:State Management
Flows exposethis.rawInput (raw request payload) and this.input (validated against inputSchema). Use this.state to pass data between stages — never mutate rawInput directly.
Never mutate
rawInput in flows — use state.set() for flow state. This ensures each stage works with clean, immutable inputs.Generating Flows
Use the Nx generator to scaffold a new flow:Best Practices
Do:- Use hooks for cross-cutting concerns instead of duplicating logic in tools
- Keep flow stages focused — each stage should have a single responsibility
- Use
state.set()/state.get()for passing data between stages - Handle errors in the
error()stage for centralized error management - Validate hook flows match their entry type (e.g., tool hooks use
tools:call-tool)
- Mutate
rawInputdirectly — use flow state instead - Create custom flows for simple operations that built-in flows already handle
- Skip the
error()stage — unhandled errors surface as generic MCP errors - Add business logic to hooks — keep hooks lightweight, delegate to providers