跳至主要內容

自訂 Adapter

當預先建置的 Server Adapter(Hono、Express、Fastify、Koa)不支援你的框架,或你有特定的請求/回應處理需求時,請建立自訂 Adapter。

自訂 Adapter 會在 Mastra 的路由定義與框架的路由系統之間進行轉換。你需要使用框架的 API 實作註冊 Middleware、處理請求及傳送回應的方法。

資訊

你可以使用下列任一預先建置的 Server Adapter:

抽象類別
「抽象類別」的直接連結

@mastra/server/server-adapterMastraServer 抽象類別是所有 Adapter 的基礎。它會處理路由註冊邏輯、參數驗證及其他共用功能。你的自訂 Adapter 會擴充此類別,並實作框架專用的部分。

此類別接受三個型別參數,分別代表框架的型別:

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
}

這些型別參數可確保整個 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:

索引鍵型別說明
mastraMastraMastra 執行個體
requestContextRequestContext請求範圍的 context map
toolsRecord<string, Tool>可用的 Tool
abortSignalAbortSignal請求取消訊號
taskStoreInMemoryTaskStoreA2A 任務儲存空間(若已設定)

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.paramsreq.queryreq.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:

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

使用方式
「使用方式」的直接連結

完成 Adapter 實作後,請以與現有 Adapter 相同的方式使用:

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)
提示

建立自訂 Adapter 時,現有的 @mastra/hono@mastra/express 實作是很好的參考。這些實作展示如何處理 context 儲存、Middleware 註冊及回應處理等框架專用模式。

如果想搭配 Server Adapter 使用 Studio,請使用 mastra studio,只啟動 Studio UI。