Skip to content

Class: MastraServer

@mastra/express subclass that stamps RequestContext with the AppKit user, resource id, and a thread id backed by an HTTP-only session cookie (appkit_<plugin-name>_session_id).

  • MastraServer

new MastraServer(config, …args): MastraServer

MastraPluginConfig

…[{ app: Application; bodyLimitOptions?: BodyLimitOptions; customApiRoutes?: ApiRoute[]; customRouteAuthConfig?: Map<string, boolean>; mastra: Mastra; mcpOptions?: MCPOptions; openapiPath?: string; prefix?: string; streamOptions?: StreamOptions; taskStore?: InMemoryTaskStore; tools?: ToolsInput; }]

MastraServer

ExpressMastraServer.constructor

protected optional bodyLimitOptions?: BodyLimitOptions

ExpressMastraServer.bodyLimitOptions


component: RegisteredLogger

ExpressMastraServer.component


protected optional customApiRoutes?: ApiRoute[]

ExpressMastraServer.customApiRoutes


protected optional customRouteAuthConfig?: Map<string, boolean>

ExpressMastraServer.customRouteAuthConfig


protected optional httpLoggingConfig?: HttpLoggingConfig

ExpressMastraServer.httpLoggingConfig


protected logger: IMastraLogger

ExpressMastraServer.logger


protected mastra: Mastra

ExpressMastraServer.mastra


protected optional mcpOptions?: MCPOptions

ExpressMastraServer.mcpOptions


optional name?: string

ExpressMastraServer.name


protected optional openapiPath?: string

ExpressMastraServer.openapiPath


protected optional prefix?: string

ExpressMastraServer.prefix


protected streamOptions: StreamOptions

ExpressMastraServer.streamOptions


protected optional taskStore?: InMemoryTaskStore

ExpressMastraServer.taskStore


protected optional tools?: ToolsInput

ExpressMastraServer.tools

get protected app(): TApp

Protected getter for subclasses to access the app. This allows subclasses to use this.app naturally.

TApp

ExpressMastraServer.app

__setLogger(logger): void

Set the logger for the agent

IMastraLogger

void

ExpressMastraServer.__setLogger


__setRawConfig(rawConfig): void

Internal

Sets the raw storage configuration for this primitive.

Record<string, unknown>

void

ExpressMastraServer.__setRawConfig


protected applyRequestMetadataToContext(__namedParameters): void

(name) => string | undefined

RequestContext

void

ExpressMastraServer.applyRequestMetadataToContext


protected buildCustomRouteHandler(): Promise<boolean>

Creates an internal Hono sub-app with all custom API routes registered. Stores the handler on this instance for use by handleCustomRouteRequest(). Returns true if custom routes were found and registered.

Promise<boolean>

ExpressMastraServer.buildCustomRouteHandler


protected checkRouteAuth(route, context): Promise<{ error: string; headers?: Record<string, string>; status: number; } | null>

Check if the current request should be authenticated/authorized. Returns null if auth passes, or an error response if it fails.

This is a thin wrapper around coreAuthMiddleware that:

  1. Routes to the correct auth provider (studio vs server) based on request headers
  2. Handles route-level requiresAuth opt-out (not available in global middleware)
  3. Delegates all other auth logic to coreAuthMiddleware
  4. Translates the AuthResult into the {status, error} format adapters expect

Security: When x-mastra-client-type: studio header is present and studio auth is configured, we ONLY use studio auth. If authentication fails, we return 401 and redirect to login - we do NOT fall back to server auth. This prevents external users from spoofing the studio header to access Studio UI.

ServerRoute

() => unknown

Build framework-specific context for authorize() callback

(name) => string | undefined

(name) => string | undefined

string

string

Request

Raw Request object for cookie-based auth providers

RequestContext

Promise<{ error: string; headers?: Record<string, string>; status: number; } | null>

ExpressMastraServer.checkRouteAuth


protected checkRoutePermission(route, userPermissions, hasPermissionFn, requestContext?): { error: string; message: string; status: number; } | null

Check if the user has the required permission for a route.

Uses convention-based permission derivation:

  1. If route has explicit requiresPermission, use that
  2. Otherwise, derive permission from path/method (e.g., GET /agents → agents:read)
  3. Routes with requiresAuth: false skip permission checks

Permission checks use the RBAC provider that corresponds to the auth mode (studio vs server) that was used for authentication.

ServerRoute

The route being accessed

string[] | undefined

The user’s permissions from the request context

(userPerms, required) => boolean

Function to check if user permissions match required permission

RequestContext<unknown>

Request context to determine which RBAC provider to use

{ error: string; message: string; status: number; } | null

Error response if permission denied, null if allowed

ExpressMastraServer.checkRoutePermission


configureMlflowTraceId(res): void

Stamp the turn’s MLflow trace id on the response so the chat client can attach thumbs / comment feedback to it later. MLflow derives its trace id from the OpenTelemetry trace id (tr-<hex>), and every Mastra span for this request inherits the ambient OTel context (see observability.ts), so the active span’s trace id here is the id MLflow will record for the turn.

No-op unless feedback is enabled, and when no live OTel span is active (e.g. the OTLP SDK isn’t registered): in that case the header is simply absent and the client hides feedback for that message, degrading gracefully rather than emitting a bogus trace id.

Response

void


configureRequestContextModelOverride(req, requestContext): void

Request

RequestContext

void


configureRequestContextRequestId(req, res, requestContext): void

Stamp a per-request id and echo it on the response so an upstream proxy / curl client / browser-side log line can pair its view of the request with the matching trace span. Reuses X-Request-Id when the upstream already supplies one so multi-hop traces stay joined; otherwise mints a UUIDv4.

The id is surfaced as mastra__requestId span metadata via TRACE_REQUEST_CONTEXT_KEYS and as the X-Request-Id response header so dev tools can copy it from either side.

Request

Response

RequestContext

void


configureRequestContextScopes(req, requestContext): void

Stamp OAuth scopes from the forwarded access token on MASTRA_SCOPES_KEY for workspace mount gating.

Request

RequestContext

void


configureRequestContextThreadId(req, res, requestContext): void

Resolve the thread id this request targets and pin it on RequestContext (consumed by the agent stream for persistence and by the history / threads routes). Resolution order:

  1. A client-supplied thread id (the thread-selection header / ?threadId= query). This is how the chat UI references a specific conversation among the many a user owns - it picks a thread id from the /threads listing (or mints one for a new conversation) and stamps it here. The id is scoped to the caller’s resource by the recall / list routes, so a client can only ever read or write its own threads.
  2. The per-session cookie (appkit_<plugin-name>_session_id), minted on first contact. This is the default single-thread fallback for clients that don’t manage threads explicitly, so existing embeds keep one stable conversation per session with no client changes.

Request

Response

RequestContext

void


configureRequestContextUser(req, requestContext): Promise<void>

Request

RequestContext

Promise<void>


createContextMiddleware(): (req, res, next) => Promise<void>

(req, res, next) => Promise<void>

ExpressMastraServer.createContextMiddleware


getApp<T>(): T

Get the app instance.

Returns the server app that was passed to the constructor. This allows users to access the underlying server framework’s app for direct operations like calling routes via app.fetch() (Hono) or using the app for testing.

T = Application

The expected type of the app (defaults to TApp)

T

The app instance cast to T. Callers are responsible for ensuring T matches the actual app type.

const app = adapter.getApp<Hono>();
const response = await app.fetch(new Request('http://localhost/api/agents'));

ExpressMastraServer.getApp


protected getEffectiveAuthConfig(getHeader): { authConfig: unknown; authMode: MastraAuthMode; } | null

Determines which auth configuration to use for the current request.

Request routing logic:

  1. If x-mastra-client-type: studio header is present AND studio.auth is configured: → Use studio auth (for internal team members accessing Studio UI)
  2. If studio header is present but studio.auth is NOT configured: → No auth required (Studio development mode)
  3. Otherwise: → Use server auth (for external customers calling API)

Security note: The header is only for routing - auth validation happens via session cookies/tokens. If someone spoofs the studio header but doesn’t have a valid studio session, they’ll get a 401 (not fall back to server auth).

(name) => string | undefined

{ authConfig: unknown; authMode: MastraAuthMode; } | null

ExpressMastraServer.getEffectiveAuthConfig


protected getEffectiveFGAProvider(requestContext): IFGAProvider<any> | undefined

Gets the effective FGA provider for the current request based on auth mode.

RequestContext

IFGAProvider<any> | undefined

ExpressMastraServer.getEffectiveFGAProvider


protected getEffectiveRBACProvider(requestContext): IRBACProvider<any> | undefined

Gets the effective RBAC provider for the current request based on auth mode.

RequestContext

IRBACProvider<any> | undefined

ExpressMastraServer.getEffectiveRBACProvider


getParams(route, request): Promise<ParsedRequestParams>

ServerRoute

Request

Promise<ParsedRequestParams>

ExpressMastraServer.getParams


protected handleCustomRouteRequest(url, method, headers, body, requestContext?, signal?): Promise<Response | null>

Forwards a request to the internal custom route handler. Returns the Response if a custom route matched, or null to fall through.

string

string

Record<string, string | string[] | undefined>

unknown

RequestContext<unknown>

AbortSignal

Promise<Response | null>

ExpressMastraServer.handleCustomRouteRequest


init(): Promise<void>

Promise<void>

ExpressMastraServer.init


protected mergeRequestContext(__namedParameters): RequestContext

Record<string, any>

Record<string, any>

RequestContext

ExpressMastraServer.mergeRequestContext


parseBody(route, body): Promise<unknown>

ServerRoute

unknown

Promise<unknown>

ExpressMastraServer.parseBody


parsePathParams(route, params): Promise<Record<string, any>>

ServerRoute

Record<string, string>

Promise<Record<string, any>>

ExpressMastraServer.parsePathParams


parseQueryParams(route, params): Promise<Record<string, any>>

ServerRoute

Record<string, QueryParamValue>

Promise<Record<string, any>>

ExpressMastraServer.parseQueryParams


registerAuthMiddleware(): void

void

ExpressMastraServer.registerAuthMiddleware


registerContextMiddleware(): void

void

ExpressMastraServer.registerContextMiddleware


registerCustomApiRoutes(): Promise<void>

Override in adapters to register custom API routes defined via registerApiRoute(). Called by init() between registerAuthMiddleware() and registerRoutes().

Promise<void>

ExpressMastraServer.registerCustomApiRoutes


registerHttpLoggingMiddleware(): void

void

ExpressMastraServer.registerHttpLoggingMiddleware


registerOpenAPIRoute(app, config, __namedParameters): Promise<void>

Application

OpenAPIConfig | undefined

string

Promise<void>

ExpressMastraServer.registerOpenAPIRoute


registerRoute(app, route, __namedParameters?): Promise<void>

Application

ServerRoute

string

Promise<void>

ExpressMastraServer.registerRoute


registerRoutes(): Promise<void>

Promise<void>

ExpressMastraServer.registerRoutes


protected resolveValidationError(route, error, context): ValidationErrorResponse

ServerRoute

ZodError

ValidationErrorContext

ValidationErrorResponse

ExpressMastraServer.resolveValidationError


sendResponse(route, response, result, request?, prefix?): Promise<void>

ServerRoute

Response

unknown

Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>

string

Promise<void>

ExpressMastraServer.sendResponse


protected shouldLogRequest(path): boolean

Determines if a request to the given path should be logged.

string

The request path to check

boolean

true if the request should be logged, false otherwise

ExpressMastraServer.shouldLogRequest


stream(route, res, result): Promise<void>

ServerRoute

Response

ReadableStream

Promise<void>

ExpressMastraServer.stream


toRawConfig(): Record<string, unknown> | undefined

Returns the raw storage configuration this primitive was created from, or undefined if it was created from code.

Record<string, unknown> | undefined

ExpressMastraServer.toRawConfig


validateAgentBuilderLicense(): Promise<void>

Validate that an Agent Builder configuration has a valid EE license. Throws if the editor is configured with builder support but no valid EE license is available.

Promise<void>

ExpressMastraServer.validateAgentBuilderLicense


protected validateCustomRoutePaths(routes): void

Validates that no custom route path collides with the built-in route prefix. Throws if any route path starts with the server’s apiPrefix.

ApiRoute[]

void

ExpressMastraServer.validateCustomRoutePaths


validateEELicense(): Promise<void>

Validate that EE features have a valid license in production. Throws if RBAC or FGA is configured without a valid license outside dev/test environments.

Promise<void>

ExpressMastraServer.validateEELicense


validateFGAPolicyCoverage(): Promise<void>

Validate route-level FGA policy coverage when an FGA provider opts into startup checks.

Promise<void>

ExpressMastraServer.validateFGAPolicyCoverage


protected writeCustomRouteResponse(response, nodeRes, signal?): Promise<void>

Pipes a custom route Response to a Node.js ServerResponse (http.ServerResponse). Works with Koa (ctx.res), Express (res), and Fastify (reply.raw).

Response

object & WritableStream

AbortSignal

Promise<void>

ExpressMastraServer.writeCustomRouteResponse