ctx object plus next.
If you need finer-grained hooks (only on specific tools), see Hooks.
When to use middleware
Use middleware when you need a decision or transformation to apply to every request:- Authentication and authorization checks.
- Input normalization or validation.
- Logging, metrics, and tracing tags.
- Guardrails for destructive or high-cost tools.
proxy.tool(...), proxy.operation(...), or proxy.mcp(name).operation(...) routes to avoid slowing the full pipeline.
Basic middleware
Deny a tool
server.tool names such as github.create_issue, github.*, and *.search_*.
Server-scoped middleware
Check policy capabilities
ctx.policy.can(server, tool) checks the configured allow/deny permissions for the current subject. It does not consume rate limits, invoke manual approval callbacks, or expose credential values.
Inject guidance
Enforce plan-based policies
Validate inputs before they reach servers
You can block requests early by checking parameters. This is useful when a server does not validate inputs on its own.Normalize inputs
Middleware can also clean up incoming parameters before they reach a server.Add soft guidance for LLM clients
If your MCP clients are LLMs, you can inject guidance without blocking requests. This is useful for reminders like “do not write outside /tmp”.Pattern: lightweight rate limiting
Fentaris does not implement rate limiting directly, but you can implement a simple per-user limit inside middleware. For production, use a shared store (Redis, KV) to coordinate across multiple instances.Migrating legacy middleware
ctx.user, ctx.policyDecision, and ctx.res remain available as compatibility aliases. Prefer ctx.subject, ctx.auth, ctx.policy, ctx.credentials, ctx.response, and the root helpers in new code.
Debugging middleware
If requests are not behaving as expected, logctx.operation, ctx.server?.name, ctx.tool?.proxyName, ctx.resource?.uri, ctx.prompt?.name, and ctx.completion?.target. This will quickly tell you whether a route is matching the capability you expect.
Local capabilities
Local capabilities declared withapp.local(name) use the same middleware and operation route pipeline as upstream MCP servers.
ctx.server?.name is the local namespace and ctx.tool?.name is the unproxied local tool name. Local resources, prompts, and completions expose the same context fields as upstream operations.
Ordering rules
Middleware runs in registration order. If a middleware returns aCallToolResult, Fentaris stops the chain.
Best practices
- Keep side effects minimal; prefer deterministic checks.
- Log denied calls with the reason to ease audits.
- Avoid long-running operations; do those in the upstream server when possible.
- Use one middleware per concern (logging, policy, normalization).
- Write tests for policy middleware to avoid regressions.