Skip to content

Middleware

Middleware

Base interfaces and built-in middleware.

arcade_mcp_server.middleware.base.Middleware

Base class for MCP middleware with typed handlers for each method.

Middleware can intercept and modify requests and responses at various stages of processing. Each handler receives the context and a call_next function to invoke the next handler in the chain.

__call__(context, call_next) async

Main entry point that orchestrates the middleware chain.

on_call_tool(context, call_next) async

Handle tool calls. Override to add tool-specific processing.

on_get_prompt(context, call_next) async

Handle prompt retrieval. Override to add prompt processing.

on_list_prompts(context, call_next) async

Handle prompt listing. Override to filter or modify prompt list.

on_list_resource_templates(context, call_next) async

Handle resource template listing. Override to filter or modify template list.

on_list_resources(context, call_next) async

Handle resource listing. Override to filter or modify resource list.

on_list_tools(context, call_next) async

Handle tool listing. Override to filter or modify tool list.

on_message(context, call_next) async

Handle any message. Override to add generic processing.

on_notification(context, call_next) async

Handle notification messages. Override to add notification processing.

on_read_resource(context, call_next) async

Handle resource reading. Override to add resource processing.

on_request(context, call_next) async

Handle request messages. Override to add request processing.

arcade_mcp_server.middleware.base.MiddlewareContext dataclass

Bases: Generic[T]

Context passed through the middleware chain.

Contains the message being processed and metadata about the request.

copy(**kwargs)

Create a copy with updated fields.

arcade_mcp_server.middleware.base.compose_middleware(*middleware)

Compose multiple middleware into a single handler.

The middleware are applied in reverse order, so the first middleware in the list is the outermost (runs first on request, last on response).

Built-ins

arcade_mcp_server.middleware.logging.LoggingMiddleware

Bases: Middleware

Middleware that logs all MCP messages and timing information.

__init__(log_level='INFO')

Initialize logging middleware.

Parameters:

Name Type Description Default
log_level str

The log level to use for message logging

'INFO'

on_message(context, call_next) async

Log all messages with timing information.

arcade_mcp_server.middleware.error_handling.ErrorHandlingMiddleware

Bases: Middleware

Middleware that handles errors and converts them to appropriate responses.

__init__(mask_error_details=True)

Initialize error handling middleware.

Parameters:

Name Type Description Default
mask_error_details bool

Whether to mask error details in responses

True

on_call_tool(context, call_next) async

Handle tool call errors specially.

on_message(context, call_next) async

Wrap all messages with error handling.

Examples

# Implement a custom middleware
from arcade_mcp_server.middleware.base import Middleware, MiddlewareContext

class TimingMiddleware(Middleware):
    async def __call__(self, context: MiddlewareContext, call_next):
        import time
        start = time.perf_counter()
        try:
            return await call_next(context)
        finally:
            elapsed_ms = (time.perf_counter() - start) * 1000
            # Attach timing info to context metadata
            context.metadata["elapsed_ms"] = round(elapsed_ms, 2)
# Compose middleware and create a server
from arcade_mcp_server.middleware.base import compose_middleware
from arcade_mcp_server.middleware.logging import LoggingMiddleware
from arcade_mcp_server.middleware.error_handling import ErrorHandlingMiddleware
from arcade_mcp_server.server import MCPServer
from arcade_core.catalog import ToolCatalog

middleware = compose_middleware([
    ErrorHandlingMiddleware(mask_error_details=False),
    LoggingMiddleware(log_level="INFO"),
    TimingMiddleware(),
])

server = MCPServer(catalog=ToolCatalog(), middleware=[middleware])