Skip to main content
Flows are named execution pipelines that define how requests are processed through a series of lifecycle stages. Every MCP operation (calling a tool, reading a resource, getting a prompt) passes through a flow that controls pre-processing, execution, post-processing, and finalization.
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

Define custom flows with the @Flow decorator:

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

Use the @Hooks decorator on tools, resources, or prompts:

Around Hooks

Around hooks wrap a stage completely, giving you control over whether the stage executes:
For a complete guide to hooks, see the Hooks decorator reference.

Flow Control

Within flow stages, you have access to control methods:

State Management

Flows use a state object to pass data between stages. Never mutate rawInput directly — use state.set() instead:
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:
This generates a flow class with all lifecycle methods:

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)
Don’t:
  • Mutate rawInput directly — 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