跳到主要内容
非官方简体中文翻译,内容版权归 Mastra 所有,遵循 Apache-2.0 许可。

Custom adapters

当预构建的服务端适配器(Hono、Express、Fastify、Koa)不支持你的框架,或者你有特定的请求/响应处理需求时,可以创建自定义适配器。

自定义适配器负责在 Mastra 的路由定义与你的框架路由系统之间进行转换。你需要使用框架 API 实现注册中间件、处理请求和发送响应的方法。

备注

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

抽象类
抽象类的直接链接

@mastra/server/server-adapter 中的 MastraServer 抽象类为所有适配器提供基础。它处理路由注册逻辑、参数校验和其他共享功能。你的自定义适配器需要扩展该类,并实现框架相关的部分。

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

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 时提供正确的类型提示。

必需的方法
必需的方法的直接链接

你必须实现以下六个抽象方法。每个方法负责请求生命周期中的特定环节,从附加上下文到发送响应。

registerContextMiddleware()
registercontextmiddleware的直接链接

该方法最先运行,为每个传入请求附加 Mastra 上下文。路由处理器需要访问 Mastra 实例、工具和其他上下文才能正常工作。附加上下文的方式取决于你的框架:Express 使用 res.locals,Hono 使用 c.set(),其他框架则有各自的模式。

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();
});
}

需要附加的上下文:

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

registerAuthMiddleware()
registerauthmiddleware的直接链接

注册认证和授权中间件。该方法应检查 Mastra 实例是否配置了认证,如果未配置则完全跳过注册。配置认证后,通常需要注册两个中间件函数:一个用于认证(校验 Token 并设置用户),另一个用于授权(检查用户能否访问所请求的资源)。

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()
registerroute的直接链接

向你的框架注册单个路由。初始化期间,Mastra 会为每个路由调用一次该方法。它接收一个 ServerRoute 对象,其中包含路径、HTTP 方法、处理函数以及用于校验的 Zod schema。你的实现应将其接入框架的路由系统。

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()
getparams的直接链接

从传入请求中提取 URL 参数、查询参数和请求体。不同框架以不同方式暴露这些值:Express 使用 req.params、req.query 和 req.body,而其他框架可能使用不同的属性名或需要调用方法。该方法为你框架归一化提取逻辑。

async getParams(
route: ServerRoute,
request: MyRequest
): Promise<{
urlParams: Record<string, string>;
queryParams: Record<string, string | string[]>; // repeated keys (?tag=a&tag=b) become arrays
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()
sendresponse的直接链接

根据路由的响应类型将响应发送回客户端。Mastra 路由可以返回不同的响应类型:大多数 API 响应使用 JSON,agent 生成使用流,此外还有 MCP 传输的特殊类型。你的实现应针对框架正确处理每种类型。

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()
stream的直接链接

处理 agent 生成的流式响应。当 agent 生成响应时,会产生一个数据块流,应随数据块的可用状态发送给客户端。该方法从流中读取数据,并可选地应用脱敏以隐藏敏感数据,然后以适当的格式(SSE 或换行分隔 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;
}
}

辅助方法
辅助方法的直接链接

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

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

parse* 方法使用每个路由上定义的 Zod schema 校验输入并返回类型化结果。如果校验失败,它们会抛出包含错误详情的异常。

构造函数
构造函数的直接链接

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

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

关于每个选项的完整文档,请参阅 Server Adapters。

完整示例
完整示例的直接链接

以下骨架实现展示了所有必需的方法。其中框架相关部分使用伪代码,请替换为你框架的实际 API:

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
}
}

使用
使用的直接链接

适配器实现完成后,使用方式与内置适配器相同:

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)

测试适配器兼容性
测试适配器兼容性的直接链接

使用 @mastra/server-adapters-test-suite 运行与 Mastra 官方适配器相同的合规性测试。在适配器项目中连同其 peer 依赖一起安装:

npm install --save-dev @mastra/server-adapters-test-suite @mastra/core @mastra/mcp @mastra/server vitest zod

为路由测试套件提供框架特定的初始化函数和请求执行函数:

src/my-framework-adapter.test.ts
import { createRouteAdapterTestSuite } from '@mastra/server-adapters-test-suite'
import { executeHttpRequest, setupAdapter } from './adapter-test-helpers'

createRouteAdapterTestSuite({
suiteName: 'My framework adapter',
setupAdapter,
executeHttpRequest,
})

该包还提供 Model Context Protocol (MCP)、multipart 请求、HTTP 日志和请求体限制测试的根导出。支持的 peer 依赖版本请参阅包 README。

提示

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

如果你想在服务端适配器中使用 Studio,请使用 mastra studio 仅启动 Studio UI。

相关内容
相关内容的直接链接