自訂 Adapter
當預先建置的 Server Adapter(Hono、Express、Fastify、Koa)不支援你的框架,或你有特定的請求/回應處理需求時,請建立自訂 Adapter。
自訂 Adapter 會在 Mastra 的路由定義與框架的路由系統之間進行轉換。你需要使用框架的 API 實作註冊 Middleware、處理請求及傳送回應的方法。
你可以使用下列任一預先建置的 Server Adapter:
抽象類別「抽象類別」的直接連結
@mastra/server/server-adapter 的 MastraServer 抽象類別是所有 Adapter 的基礎。它會處理路由註冊邏輯、參數驗證及其他共用功能。你的自訂 Adapter 會擴充此類別,並實作框架專用的部分。
此類別接受三個型別參數,分別代表框架的型別:
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
}
這些型別參數可確保整個 Adapter 實作的型別安全,並在存取框架專用 API 時提供正確的型別。
必要方法「必要方法」的直接連結
你必須實作以下六個抽象方法。每個方法分別處理請求生命週期的特定部分,從附加 context 到傳送回應。
registerContextMiddleware()「registercontextmiddleware」的直接連結
此方法會最先執行,並將 Mastra context 附加至每個傳入的請求。路由處理常式需要存取 Mastra 執行個體、Tool 及其他 context 才能運作。附加此 context 的方式取決於你的框架: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();
});
}
要附加的 context:
| 索引鍵 | 型別 | 說明 |
|---|---|---|
mastra | Mastra | Mastra 執行個體 |
requestContext | RequestContext | 請求範圍的 context map |
tools | Record<string, Tool> | 可用的 Tool |
abortSignal | AbortSignal | 請求取消訊號 |
taskStore | InMemoryTaskStore | A2A 任務儲存空間(若已設定) |
registerAuthMiddleware()「registerauthmiddleware」的直接連結
註冊驗證與授權 Middleware。此方法應檢查 Mastra 執行個體是否已設定驗證;若未設定,則完全略過註冊。設定驗證後,通常會註冊兩個 Middleware 函式:一個用於驗證(驗證 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>;
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 }) | 合併來自多個來源的請求 context |
registerRoutes() | 註冊所有 Mastra 路由(對每個路由呼叫 registerRoute) |
registerOpenAPIRoute(app, config, { prefix }) | 註冊 OpenAPI 規格端點 |
parse* 方法會使用每個路由上定義的 Zod Schema 驗證輸入,並傳回具型別的結果。若驗證失敗,這些方法會擲回包含失敗詳細資訊的錯誤。
建構函式「建構函式」的直接連結
Adapter 的建構函式應接受與基底類別相同的選項,並將其傳給 super()。如有需要,你也可以加入其他框架專用選項:
constructor(options: {
app: MyApp;
mastra: Mastra;
prefix?: string;
openapiPath?: string;
bodyLimitOptions?: BodyLimitOptions;
streamOptions?: StreamOptions;
customRouteAuthConfig?: Map<string, boolean>;
}) {
super(options);
}
如需各選項的完整文件,請參閱 Server Adapter。
完整範例「完整範例」的直接連結
以下基礎實作展示所有必要方法。框架專用部分使用虛擬程式碼,請替換為框架的實際 API:
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
}
}
使用方式「使用方式」的直接連結
完成 Adapter 實作後,請以與現有 Adapter 相同的方式使用:
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)
建立自訂 Adapter 時,現有的 @mastra/hono 與 @mastra/express 實作是很好的參考。這些實作展示如何處理 context 儲存、Middleware 註冊及回應處理等框架專用模式。
如果想搭配 Server Adapter 使用 Studio,請使用 mastra studio,只啟動 Studio UI。
相關資源「相關資源」的直接連結
- Server Adapter:概觀與共用概念
- Hono Adapter:參考實作
- Express Adapter:參考實作
- MastraServer 參考文件:完整 API 參考文件
- createRoute() 參考文件:建立型別安全的自訂路由