Skip to main content

自定义适配器

🌐 Custom Adapters

当你需要在 Hono 或 Express 以外的框架上运行 Mastra 时,请创建自定义适配器。如果你有 @mastra/hono@mastra/express 不支持的特定请求/响应处理需求,这可能是必要的。

🌐 Create a custom adapter when you need to run Mastra with a framework other than Hono or Express. This might be necessary if you have specific request/response handling requirements that @mastra/hono and @mastra/express don't support.

自定义适配器在 Mastra 的路由定义和你的框架路由系统之间进行转换。你将实现一些方法,通过你的框架 API 来注册中间件、处理请求和发送响应。

🌐 A custom adapter translates between Mastra's route definitions and your framework's routing system. You'll implement methods that register middleware, handle requests, and send responses using your framework's APIs.

info

使用以下任意一个预构建的服务器适配器:

🌐 Use any of these prebuilt server adapters:

抽象类
Direct link to 抽象类

🌐 Abstract class

@mastra/server/server-adapterMastraServer 抽象类为所有适配器提供了基础。它处理路由注册逻辑、参数验证以及其他共享功能。你的自定义适配器继承该类并实现框架特定的部分。

🌐 The MastraServer abstract class from @mastra/server/server-adapter provides the foundation for all adapters. It handles route registration logic, parameter validation, and other shared functionality. Your custom adapter extends this class and implements the framework-specific parts.

该类接受三个类型参数,代表你的框架类型:

🌐 The class takes three type parameters that represent your framework's types:

my-framework-adapter.ts
import { MastraServer } from '@mastra/server/server-adapter';

export class MyFrameworkServer extends MastraServer<
// Your framework's app type (e.g., FastifyInstance)
MyApp,
// Your framework's request type (e.g., FastifyRequest)
MyRequest,
// Your framework's response type (e.g., FastifyReply)
MyResponse
> {
// Implement abstract methods
}

这些类型参数确保在整个适配器实现过程中类型安全,并在访问特定框架的 API 时启用正确的类型。

🌐 These type parameters ensure type safety throughout your adapter implementation and enable proper typing when accessing framework-specific APIs.

必需的方法
Direct link to 必需的方法

🌐 Required methods

你必须实现这六个抽象方法。每个方法处理请求生命周期的特定部分,从附加上下文到发送响应。

🌐 You must implement these six abstract methods. Each handles a specific part of the request lifecycle, from attaching context to sending responses.

registerContextMiddleware()
Direct link to registerContextMiddleware()

此方法首先运行,并将 Mastra 上下文附加到每个传入请求上。路由处理程序需要访问 Mastra 实例、工具和其他上下文才能正常工作。具体如何附加此上下文取决于你的框架——Express 使用 res.locals,Hono 使用 c.set(),其他框架有各自的模式。

🌐 This method runs first and attaches Mastra context to every incoming request. Route handlers need access to the Mastra instance, tools, and other context to function. How you attach this context depends on your framework — Express uses res.locals, Hono uses c.set(), and other frameworks have their own patterns.

registerContextMiddleware(): void {
this.app.use('*', (req, res, next) => {
// Attach context to your framework's request/response
res.locals.mastra = this.mastra;
res.locals.requestContext = new RequestContext();
res.locals.tools = this.tools;
res.locals.abortSignal = createAbortSignal(req);
next();
});
}

要附加的内容:

🌐 Context to attach:

类型描述
mastraMastraMastra 实例
requestContextRequestContext请求作用域的上下文映射
toolsRecord<string, Tool>可用工具
abortSignalAbortSignal请求取消信号
taskStoreInMemoryTaskStoreA2A 任务存储(如果已配置)

registerAuthMiddleware()
Direct link to registerAuthMiddleware()

注册认证和授权中间件。此方法应检查 Mastra 实例是否配置了认证,如果未配置,则完全跳过注册。当配置了认证时,通常会注册两个中间件函数:一个用于认证(验证令牌并设置用户),另一个用于授权(检查用户是否可以访问请求的资源)。

🌐 Register authentication and authorization middleware. This method should check if authentication is configured on the Mastra instance and skip registration entirely if not. When auth is configured, you'll typically register two middleware functions: one for authentication (validating tokens and setting the user) and one for authorization (checking if the user can access the requested resource).

registerAuthMiddleware(): void {
const authConfig = this.mastra.getServer()?.auth;
if (!authConfig) return;

// Register authentication (validate token, set user)
this.app.use('*', async (req, res, next) => {
const token = extractToken(req);
const user = await authConfig.authenticateToken?.(token, req);
if (!user) {
return res.status(401).json({ error: 'Unauthorized' });
}
res.locals.user = user;
next();
});

// Register authorization (check permissions)
this.app.use('*', async (req, res, next) => {
const allowed = await authConfig.authorize?.(
req.path,
req.method,
res.locals.user,
res
);
if (!allowed) {
return res.status(403).json({ error: 'Forbidden' });
}
next();
});
}

registerRoute()
Direct link to registerRoute()

在你的框架中注册一个单独的路由。此方法在初始化期间会为每个 Mastra 路由调用一次。它接收一个包含路径、HTTP 方法、处理函数以及用于验证的 Zod 模式的 ServerRoute 对象。你的实现应将其连接到框架的路由系统中。

🌐 Register a single route with your framework. This method is called once for each Mastra route during initialization. It receives a ServerRoute object containing the path, HTTP method, handler function, and Zod schemas for validation. Your implementation should wire this up to your framework's routing system.

async registerRoute(
app: MyApp,
route: ServerRoute,
{ prefix }: { prefix?: string }
): Promise<void> {
const path = `${prefix || ''}${route.path}`;
const method = route.method.toLowerCase();

app[method](path, async (req, res) => {
try {
// 1. Extract parameters
const params = await this.getParams(route, req);

// 2. Validate with Zod schemas
const queryParams = await this.parseQueryParams(route, params.queryParams);
const body = await this.parseBody(route, params.body);

// 3. Build handler params
const handlerParams = {
...params.urlParams,
...queryParams,
...(typeof body === 'object' ? body : {}),
mastra: this.mastra,
requestContext: res.locals.requestContext,
tools: res.locals.tools,
abortSignal: res.locals.abortSignal,
taskStore: this.taskStore,
};

// 4. Call handler
const result = await route.handler(handlerParams);

// 5. Send response
return this.sendResponse(route, res, result);
} catch (error) {
const status = error.status ?? error.details?.status ?? 500;
return res.status(status).json({ error: error.message });
}
});
}

getParams()
Direct link to getParams()

从传入的请求中提取 URL 参数、查询参数和请求正文。不同的框架以不同的方式暴露这些值——Express 使用 req.paramsreq.queryreq.body,而其他框架可能使用不同的属性名或需要调用方法。此方法为你的框架统一了提取方式。

🌐 Extract URL parameters, query parameters, and request body from the incoming request. Different frameworks expose these values in different ways—Express uses req.params, req.query, and req.body, while other frameworks may use different property names or require method calls. This method normalizes the extraction for your framework.

async getParams(
route: ServerRoute,
request: MyRequest
): Promise<{
urlParams: Record<string, string>;
queryParams: Record<string, string>;
body: unknown;
}> {
return {
// From route path (e.g., :agentId)
urlParams: request.params,
// From URL query string
queryParams: request.query,
// From request body
body: request.body,
};
}

sendResponse()
Direct link to sendResponse()

根据路由的响应类型将响应发送回客户端。Mastra 路由可以返回不同的响应类型:大多数 API 响应为 JSON,代理生成为流,MCP 传输为特殊类型。你的实现应根据你的框架适当地处理每种类型。

🌐 Send the response back to the client based on the route's response type. Mastra routes can return different response types: JSON for most API responses, streams for agent generation, and special types for MCP transports. Your implementation should handle each type appropriately for your framework.

async sendResponse(
route: ServerRoute,
response: MyResponse,
result: unknown
): Promise<unknown> {
switch (route.responseType) {
case 'json':
return response.json(result);

case 'stream':
return this.stream(route, response, result);

case 'datastream-response':
// Return AI SDK Response directly
return result;

case 'mcp-http':
// Handle MCP HTTP transport
return this.handleMcpHttp(response, result);

case 'mcp-sse':
// Handle MCP SSE transport
return this.handleMcpSse(response, result);

default:
return response.json(result);
}
}

stream()
Direct link to stream()

处理代理生成的流式响应。当代理生成响应时,会产生一个响应块的流,这些块应在可用时发送给客户端。此方法从流中读取数据,可选择性地进行编辑以隐藏敏感信息,并以适当的格式(SSE或换行分隔的JSON)将数据块写入响应。

🌐 Handle streaming responses for agent generation. When an agent generates a response, it produces a stream of chunks that should be sent to the client as they become available. This method reads from the stream, optionally applies redaction to hide sensitive data, and writes chunks to the response in the appropriate format (SSE or newline-delimited JSON).

async stream(
route: ServerRoute,
response: MyResponse,
result: unknown
): Promise<unknown> {
const isSSE = route.streamFormat === 'sse';

// Set streaming headers based on format
response.setHeader('Content-Type', isSSE ? 'text/event-stream' : 'text/plain');
response.setHeader('Transfer-Encoding', 'chunked');

const reader = result.fullStream.getReader();

try {
while (true) {
const { done, value } = await reader.read();
if (done) break;

// Apply redaction if enabled
const chunk = this.streamOptions.redact
? redactChunk(value)
: value;

// Format based on stream format
if (isSSE) {
response.write(`data: ${JSON.stringify(chunk)}\n\n`);
} else {
response.write(JSON.stringify(chunk) + '\x1E');
}
}

// Send completion marker (SSE uses data: [DONE], other formats use record separator)
if (isSSE) {
response.write('data: [DONE]\n\n');
}
response.end();
} catch (error) {
reader.cancel();
throw error;
}
}

辅助方法
Direct link to 辅助方法

🌐 Helper methods

基类提供了可以在实现中使用的辅助方法。这些方法处理常见任务,如参数验证和路由注册,因此你无需重新实现它们:

🌐 The base class provides helper methods you can use in your implementation. These handle common tasks like parameter validation and route registration, so you don't need to reimplement them:

方法描述
parsePathParams(route, params)使用 Zod 模式验证路径参数
parseQueryParams(route, params)使用 Zod 模式验证查询参数
parseBody(route, body)使用 Zod 模式验证请求体
mergeRequestContext({ paramsRequestContext, bodyRequestContext })合并来自多个来源的请求上下文
registerRoutes()注册所有 Mastra 路由(为每个路由调用 registerRoute
registerOpenAPIRoute(app, config, { prefix })注册 OpenAPI 规范端点

parse* 方法使用在每个路由上定义的 Zod 模式来验证输入并返回类型化结果。如果验证失败,它们会抛出包含错误详细信息的异常。

🌐 The parse* methods use Zod schemas defined on each route to validate input and return typed results. If validation fails, they throw an error with details about what went wrong.

构造函数
Direct link to 构造函数

🌐 Constructor

你的适配器构造函数应接受与基类相同的选项,并将它们传递给 super()。如果需要,你可以添加额外的特定框架选项:

🌐 Your adapter's constructor should accept the same options as the base class and pass them to super(). You can add additional framework-specific options if needed:

constructor(options: {
app: MyApp;
mastra: Mastra;
prefix?: string;
openapiPath?: string;
bodyLimitOptions?: BodyLimitOptions;
streamOptions?: StreamOptions;
customRouteAuthConfig?: Map<string, boolean>;
}) {
super(options);
}

请参阅 服务器适配器 了解每个选项的完整文档。

🌐 See Server Adapters for full documentation on each option.

完整示例
Direct link to 完整示例

🌐 Full example

这是一个展示所有必需方法的骨架实现。框架特定的部分使用了伪代码——请替换为你所使用框架的实际 API:

🌐 Here's a skeleton implementation showing all the required methods. This uses pseudocode for framework-specific parts—replace with your framework's actual APIs:

my-framework-adapter.ts
import { MastraServer, ServerRoute } from '@mastra/server/server-adapter';
import type { Mastra } from '@mastra/core';

export class MyFrameworkServer extends MastraServer<MyApp, MyRequest, MyResponse> {
constructor(options: { app: MyApp; mastra: Mastra; prefix?: string }) {
super(options);
}

registerContextMiddleware(): void {
this.app.use('*', (req, res, next) => {
res.locals.mastra = this.mastra;
res.locals.requestContext = this.mergeRequestContext({
paramsRequestContext: req.query.requestContext,
bodyRequestContext: req.body?.requestContext,
});
res.locals.tools = this.tools ?? {};
res.locals.abortSignal = createAbortSignal(req);
next();
});
}

registerAuthMiddleware(): void {
const authConfig = this.mastra.getServer()?.auth;
if (!authConfig) return;
// ... implement auth middleware
}

async registerRoute(app: MyApp, route: ServerRoute, { prefix }: { prefix?: string }): Promise<void> {
// ... implement route registration
}

async getParams(route: ServerRoute, request: MyRequest) {
return {
urlParams: request.params,
queryParams: request.query,
body: request.body,
};
}

async sendResponse(route: ServerRoute, response: MyResponse, result: unknown) {
if (route.responseType === 'stream') {
return this.stream(route, response, result);
}
return response.json(result);
}

async stream(route: ServerRoute, response: MyResponse, result: unknown) {
// ... implement streaming
}
}

用法
Direct link to 用法

🌐 Usage

一旦你的适配器实现完成,就像使用提供的适配器一样使用它:

🌐 Once your adapter is implemented, use it the same way as the provided adapters:

server.ts
import { MyFrameworkServer } from './my-framework-adapter';
import { mastra } from './mastra';

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

await server.init();

app.listen(4111);
tip

现有的 @mastra/hono@mastra/express 实现是构建自定义适配器时的良好参考。它们展示了如何处理框架特定的模式,例如上下文存储、中间件注册和响应处理。

🌐 The existing @mastra/hono and @mastra/express implementations are good references when building your custom adapter. They show how to handle framework-specific patterns for context storage, middleware registration, and response handling.

🌐 Related