Skip to main content

Server Adapters

Server adapters let you run Mastra with your own HTTP server instead of the Hono server generated by mastra build. They provide more control over the server setup, including custom middleware ordering, authentication, logging, and deployment configuration. You can still integrate Mastra into any Node.js application without changing how agents or workflows execute.

When to use server adapters
Direct link to When to use server adapters

  • You want Mastra’s endpoints added automatically to an existing application
  • You need direct access to the server instance for custom configuration
  • Your team prefers using another server framework instead of the Hono server created by mastra build.
tip

For simple deployments without custom server requirements, use mastra build instead. It configures server setup, registers middleware, and applies deployment settings based on your project configuration. See Server Configuration.

Available adapters
Direct link to Available adapters

Mastra currently provides two official server adapters:

You can build your own adapter, read Custom Adapters for details.

Installation
Direct link to Installation

Install the adapter for the framework of your choice.

npm install @mastra/express@beta

Configuration
Direct link to Configuration

Initialize your app as usual, then create a MastraServer by passing in the app and your main mastra instance from src/mastra/index.ts. Calling init() automatically registers Mastra middleware and all available endpoints. You can continue adding your own routes as normal, either before or after init(), and they’ll run alongside Mastra’s endpoints.

src/express-server.ts
import express from "express";
import { MastraServer } from "@mastra/express";
import { mastra } from "./mastra";

const app = express();
app.use(express.json());

const server = new MastraServer({ app, mastra });

await server.init();

app.listen(4111, () => {
console.log('Server running on port 4111');
});
info

See the Express Adapter or Hono Adapter docs for full configuration options.

Initialization flow
Direct link to Initialization flow

Calling init() runs three steps in order. Understanding this flow helps when you need to insert your own middleware at specific points.

  1. registerContextMiddleware(): Attaches the Mastra instance, request context, tools, and abort signal to every request. This makes Mastra available to all subsequent middleware and route handlers.
  2. registerAuthMiddleware(): Adds authentication and authorization middleware, but only if server.auth is configured in your Mastra instance. Skipped entirely if no auth is configured.
  3. registerRoutes(): Registers all Mastra API routes for agents, workflows, and other features. Also registers MCP routes if MCP servers are configured.

Manual initialization
Direct link to Manual initialization

For custom middleware ordering, call each method separately instead of init(). This is useful when you need middleware that runs before Mastra's context is set up, or when you need to insert logic between the initialization steps.

server.ts
const server = new MastraServer({ app, mastra });

// Your middleware first
app.use(loggingMiddleware);

server.registerContextMiddleware();

// Middleware that needs Mastra context
app.use(customMiddleware);

server.registerAuthMiddleware();
await server.registerRoutes();

// Routes after Mastra
app.get('/health', ...);
tip

Use manual initialization when you need middleware that runs before Mastra's context is available, or when you need to insert middleware between the context and auth steps.

Adding custom routes
Direct link to Adding custom routes

You can add your own routes to the app alongside Mastra's routes.

  • Routes added before init() won't have Mastra context available.
  • Routes added after init() have access to the Mastra context (the Mastra instance, request context, authenticated user, etc.).
info

Visit "Adding custom routes" for Express and Hono for more information.

Route prefixes
Direct link to Route prefixes

By default, Mastra routes are registered at /api/agents, /api/workflows, etc. Use the prefix option to change this. This is useful for API versioning or when integrating with an existing app that has its own /api routes.

const server = new MastraServer({
app,
mastra,
prefix: '/api/v2',
});

With this prefix, Mastra routes become /api/v2/agents, /api/v2/workflows, etc. Custom routes you add directly to the app are not affected by this prefix.

OpenAPI spec
Direct link to OpenAPI spec

Mastra can generate an OpenAPI specification for all registered routes. This is useful for documentation, client generation, or integration with API tools. Enable it by setting the openapiPath option:

const server = new MastraServer({
app,
mastra,
openapiPath: '/openapi.json',
});

The spec is generated from the Zod schemas defined on each route and served at the specified path. It includes all Mastra routes as well as any custom routes created with createRoute().

Stream data redaction
Direct link to Stream data redaction

When streaming agent responses over HTTP, the HTTP streaming layer redacts sensitive information from stream chunks before sending them to clients. This prevents accidental exposure of:

  • System prompts and agent instructions
  • Tool definitions and their parameters
  • API keys and other credentials in request bodies
  • Internal configuration data

This redaction happens at the HTTP boundary, so internal callbacks like onStepFinish still have access to the full request data for debugging and observability purposes.

By default, redaction is enabled. Configure this behavior via streamOptions. Set redact: false only for internal services or debugging scenarios where you need access to the full request data in stream responses.

const server = new MastraServer({
app,
mastra,
streamOptions: {
redact: true, // Default
},
});
info

See MastraServer for full configuration options.

Per-route auth overrides
Direct link to Per-route auth overrides

When authentication is configured on your Mastra instance, all routes require authentication by default. Sometimes you need exceptions: public health check endpoints, webhook receivers, or admin routes that need stricter controls.

Use customRouteAuthConfig to override authentication behavior for specific routes. Keys follow the format METHOD:PATH where method is GET, POST, PUT, DELETE, or ALL. Paths support wildcards (*) for matching multiple routes. Setting a value to false makes the route public, while true requires authentication.

const server = new MastraServer({
app,
mastra,
customRouteAuthConfig: new Map([
// Public health check
['GET:/api/health', false],
// Public API spec
['GET:/api/openapi.json', false],
// Public webhook endpoints
['POST:/api/webhooks/*', false],
// Require auth even if globally disabled
['POST:/api/admin/reset', true],
// Protect all methods on internal routes
['ALL:/api/internal/*', true],
]),
});
info

See MastraServer for full configuration options.

Accessing the app
Direct link to Accessing the app

After creating the adapter, you may still need access to the underlying framework app. This is useful when passing it to a platform’s serve function or when adding routes from another module.

// Via the MastraServer instance
const app = server.getApp();

// Via the Mastra instance (available after adapter construction)
const app = mastra.getServerApp();

Both methods return the same app instance. Use whichever is more convenient based on what's in scope.

Server config vs adapter options
Direct link to Server config vs adapter options

When using server adapters, configuration comes from two places: the Mastra server config (passed to the Mastra constructor) and the adapter constructor options. Understanding which options come from where helps avoid confusion when settings don't seem to take effect.

Used by adapters
Direct link to Used by adapters

The adapter reads these settings from mastra.getServer():

OptionDescription
authAuthentication config, used by registerAuthMiddleware().
bodySizeLimitDefault body size limit in bytes. Can be overridden per-adapter via bodyLimitOptions.

Adapter constructor only
Direct link to Adapter constructor only

These options are passed directly to the adapter constructor and are not read from the Mastra config:

OptionDescription
prefixRoute path prefix
openapiPathOpenAPI spec endpoint
bodyLimitOptionsBody size limit with custom error handler
streamOptionsStream redaction settings
customRouteAuthConfigPer-route auth overrides

Not used by adapters
Direct link to Not used by adapters

These server config options are only used by mastra build and have no effect when using adapters directly:

OptionUsed by
port, hostmastra dev, mastra build
corsmastra build adds CORS middleware
timeoutmastra build
apiRoutesregisterApiRoute() for mastra build
middlewareMiddleware config for mastra build

When using adapters, configure these features directly with your framework. For example, add CORS middleware using Hono's or Express's built-in CORS packages, and set the port when calling your framework's listen function.

MCP support
Direct link to MCP support

Server adapters register MCP (Model Context Protocol) routes during registerRoutes() when MCP servers are configured in your Mastra instance. MCP allows external tools and services to connect to your Mastra server and interact with your agents.

The adapter registers routes for both HTTP and SSE (Server-Sent Events) transports, enabling different client connection patterns.

See MCP for configuration details and how to set up MCP servers.