跳至主要內容

自訂轉接器

當預先建置的伺服器轉接器(Hono、Express、Fastify、Koa)不支援你的框架,或你對請求/回應處理有特定要求時,便可建立自訂轉接器。

自訂轉接器會在 Mastra 的路由定義與你的框架路由系統之間進行轉換。你需要使用框架的 API,實作註冊中介軟件、處理請求和傳送回應的方法。

資訊

你可以使用以下任何一個預先建置的伺服器轉接器:

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

MastraServer 抽象類別來自 @mastra/server/server-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
}

這些類型參數可確保整個轉接器實作的類型安全,並讓你在存取框架專用 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:

Key類型說明
mastraMastraMastra 執行個體
requestContextRequestContext以請求為作用域的 context map
toolsRecord<string, Tool>可用的 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.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 驗證輸入,並傳回具有類型的結果。如果驗證失敗,這些方法會擲回錯誤,並提供出錯詳情。

建構函式
建構函式 的直接連結

你的轉接器建構函式應接受與基礎類別相同的選項,並將它們傳遞給 super()。如有需要,你亦可加入其他框架專用選項:

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

請參閱伺服器轉接器,了解每個選項的完整文件。

完整範例
完整範例 的直接連結

以下骨架實作展示所有必須實作的方法。框架專用部分使用虛擬程式碼,請替換為框架實際使用的 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/hono@mastra/express 實作是很好的參考。它們展示如何處理框架專用的 context 儲存和中介軟件註冊模式,以及回應處理。

如要配合你的伺服器轉接器使用 Studio,請使用 mastra studio,只啟動 Studio UI。