@guillem_puche/mastra-effect 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":["Data","HttpServerRespondable","Effect","MastraServerBase","normalizeQueryParams","redactStreamChunk","serializeStreamChunk","toReqRes","applyMcpRequestAuth","toFetchResponse","checkRouteFGA","isZodError","getCustomHTTPExceptionResponse","HttpRouter","Option","findMatchingCustomRoute","isProtectedCustomRoute","HttpServerResponse","Cookies","HttpServerRequest","Exit","HttpServerError","stream","effect","Layer"],"sources":["../src/index.ts"],"sourcesContent":["import type { ToolsInput } from '@mastra/core/agent';\nimport type { RequestContext } from '@mastra/core/request-context';\nimport { findMatchingCustomRoute, isProtectedCustomRoute } from '@mastra/server/auth';\nimport type { MCPHttpTransportResult, MCPSseTransportResult } from '@mastra/server/handlers/mcp';\nimport type { ParsedRequestParams, ServerRoute } from '@mastra/server/server-adapter';\nimport {\n MastraServer as MastraServerBase,\n applyMcpRequestAuth,\n checkRouteFGA,\n getCustomHTTPExceptionResponse,\n isZodError,\n normalizeQueryParams,\n redactStreamChunk,\n serializeStreamChunk,\n} from '@mastra/server/server-adapter';\nimport { Data, Effect, Exit, Layer, type ManagedRuntime, Option, type Scope, type Tracer } from 'effect';\nimport type { FindMyWay } from 'effect/unstable/http';\nimport {\n Cookies,\n HttpRouter,\n HttpServerError,\n HttpServerRequest,\n HttpServerRespondable,\n HttpServerResponse,\n} from 'effect/unstable/http';\nimport { toFetchResponse, toReqRes } from 'fetch-to-node';\n\nexport type EffectRouter = HttpRouter.HttpRouter;\n\n/**\n * Everything the adapter threads through a single request.\n *\n * Bound to both `TRequest` and `TResponse`. Effect has no mutable response object, and the base\n * class never calls `sendResponse` itself — this adapter does, from `handleRoute` — so the slot\n * carries the request-side data `sendResponse` needs, the same way `@mastra/elysia` carries its\n * framework context there. Passing an explicit object (rather than stashing properties on the\n * `Request`) keeps `getParams` and `sendResponse` honest when called outside `handleRoute`.\n */\nexport interface EffectRequestContext {\n readonly request: Request;\n readonly pathParams: Record<string, string>;\n /** Parsed once per request so nothing downstream re-reads the body. */\n readonly body: unknown;\n readonly bodyParseError?: { message: string };\n /** The request's Mastra context, which MCP transports need to learn who the caller is. */\n readonly requestContext?: RequestContext;\n}\n\ntype HasPermissionFn = (userPerms: string[], required: string) => boolean;\n\n/** Methods that may carry a request body, per Mastra's route table. */\nconst BODY_METHODS = new Set(['POST', 'PUT', 'PATCH', 'DELETE']);\n/** Methods whose JSON body may carry a `requestContext` envelope. */\nconst CONTEXT_BODY_METHODS = new Set(['POST', 'PUT', 'PATCH']);\n/** What `effect/unstable/http`'s `HttpRouter.add` accepts, besides the `*` wildcard. */\nconst EFFECT_METHODS = new Set(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS', 'QUERY']);\n\nlet hasPermissionPromise: Promise<HasPermissionFn | undefined> | undefined;\nfunction loadHasPermission(): Promise<HasPermissionFn | undefined> {\n hasPermissionPromise ??= import('@mastra/core/auth/ee')\n .then(m => m.hasPermission)\n .catch(() => {\n console.error(\n '[@guillem_puche/mastra-effect] Auth features require @mastra/core >= 1.68.0. Please upgrade: npm install @mastra/core@latest',\n );\n return undefined;\n });\n return hasPermissionPromise;\n}\n\nconst JSON_HEADERS = { 'Content-Type': 'application/json' } as const;\n\nfunction json(body: unknown, status = 200, headers?: Record<string, string>): Response {\n return new Response(JSON.stringify(body), { status: toHttpStatus(status), headers: { ...JSON_HEADERS, ...headers } });\n}\n\n/**\n * Adds headers to a response, appending `Set-Cookie` so a refreshed session cookie joins the ones the\n * route set instead of replacing them.\n */\nfunction withHeaders(response: Response, headers: Record<string, string>): Response {\n const entries = Object.entries(headers);\n if (entries.length === 0) return response;\n // A Response from `fetch`, or one a route built with frozen headers, cannot be edited in place.\n const editable = new Response(response.body, response);\n for (const [key, value] of entries) {\n if (key.toLowerCase() === 'set-cookie') editable.headers.append(key, value);\n else editable.headers.set(key, value);\n }\n return editable;\n}\n\n/** `new Response` throws outside 200-599, and this runs on the last-resort error path. */\nfunction toHttpStatus(status: unknown): number {\n return typeof status === 'number' && Number.isInteger(status) && status >= 200 && status <= 599 ? status : 500;\n}\n\n/**\n * Forwards a body but swallows a mid-stream error, closing instead of erroring, so chunks already\n * delivered survive an upstream failure rather than tearing down the whole response.\n */\nfunction createSafeReadableStream(body: ReadableStream<Uint8Array> | null): ReadableStream<Uint8Array> | null {\n if (!body) return null;\n\n let reader: ReadableStreamDefaultReader<Uint8Array> | undefined;\n\n return new ReadableStream<Uint8Array>({\n async start(controller) {\n reader = body.getReader();\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n controller.enqueue(value);\n }\n } catch {\n // Preserve chunks already sent before the upstream stream errored.\n } finally {\n // Throws if the consumer already cancelled this stream, which is not an error here.\n try {\n controller.close();\n } catch {\n // Already closed or errored.\n }\n try {\n reader.releaseLock();\n } catch {\n // A read was still in flight; the cancel below owns the reader instead.\n }\n }\n },\n // Without this the client disconnecting leaves the loop above draining the upstream forever.\n cancel(reason) {\n return reader?.cancel(reason);\n },\n });\n}\n\n/** `fetch-to-node` needs a body it can replay; a consumed request must be rebuilt before forwarding. */\nasync function createForwardRequest(request: Request, parsedBody: unknown): Promise<Request> {\n if (request.method === 'GET' || request.method === 'HEAD') return request;\n\n const headers = new Headers(request.headers);\n let body: string | ArrayBuffer | undefined;\n\n if (parsedBody !== undefined) {\n // Always re-encode: a parsed scalar string must go back out as JSON (`\"ping\"`), not as raw\n // bytes, or the receiving MCP server fails to parse what it is handed.\n body = JSON.stringify(parsedBody);\n if (!headers.has('content-type')) headers.set('content-type', 'application/json');\n } else if (!request.bodyUsed) {\n const buffer = await request.clone().arrayBuffer();\n if (buffer.byteLength > 0) body = buffer;\n }\n\n headers.delete('content-length');\n\n return new Request(request.url, {\n method: request.method,\n headers,\n body,\n signal: request.signal,\n ...(body ? { duplex: 'half' } : {}),\n } as RequestInit);\n}\n\n/** Strips hop-by-hop framing the outer fetch layer re-applies; leaving it causes double-chunking. */\nfunction forwardResponse(source: Response): Response {\n return new Response(createSafeReadableStream(source.body), {\n status: source.status,\n statusText: source.statusText,\n headers: withoutTransferEncoding(source.headers),\n });\n}\n\nfunction withoutTransferEncoding(source: Headers): Headers {\n const headers = new Headers(source);\n headers.delete('Transfer-Encoding');\n return headers;\n}\n\n/**\n * Forwards what an MCP transport wrote, and tells the transport when the client goes away.\n *\n * `fetch-to-node` builds the body from the simulated Node response but never learns that the\n * stream it handed back was cancelled. Nothing then tells the transport the client is gone, so its\n * keep-alive timer keeps firing, and the next tick writes into a closed stream — an\n * `ERR_INVALID_STATE` thrown from a timer, which nothing can catch and which ends the process.\n * Emitting `close` on the response is what the MCP transport listens for: it stops, and ends the\n * response itself.\n *\n * The bridge stream is drained rather than cancelled, for the same reason: `fetch-to-node` flushes\n * buffered writes from a timer, and cancelling its stream leaves that flush writing into a closed\n * one. Ported from `@mastra/hono`'s `propagateClientDisconnect` (see NOTICE).\n */\nfunction forwardMcpResponse(source: Response, res: { emit: (event: string) => unknown }): Response {\n const init = { status: source.status, statusText: source.statusText, headers: withoutTransferEncoding(source.headers) };\n const upstream = source.body;\n if (!upstream) return new Response(null, init);\n\n const reader = upstream.getReader();\n let disconnected = false;\n\n const body = new ReadableStream<Uint8Array>({\n async pull(controller) {\n try {\n const { done, value } = await reader.read();\n if (done) controller.close();\n else controller.enqueue(value);\n } catch {\n // Keep what was already delivered, as forwardResponse does.\n controller.close();\n }\n },\n cancel() {\n if (disconnected) return;\n disconnected = true;\n try {\n res.emit('close');\n } catch {\n // Already torn down: the transport has nothing left to stop.\n }\n void reader.read().then(\n function drain({ done }): unknown {\n return done ? undefined : reader.read().then(drain);\n },\n () => {},\n );\n },\n });\n\n return new Response(body, init);\n}\n\n/** Maps a Mastra route method onto what Effect's router accepts, rather than asserting it blindly. */\nfunction effectMethod(method: string, path: string): 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS' | '*' {\n const upper = method.toUpperCase();\n if (upper === 'ALL') return '*';\n if (!EFFECT_METHODS.has(upper)) {\n throw new Error(\n `[@guillem_puche/mastra-effect] Unsupported HTTP method \"${method}\" for route ${path}. ` +\n `effect/unstable/http accepts ${[...EFFECT_METHODS].join(', ')} or ALL.`,\n );\n }\n return upper as 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS';\n}\n\n/**\n * A Mastra route that failed with a server error (5xx), surfaced in Effect's error channel.\n *\n * It answers with exactly the response Mastra built, so clients see no difference. What changes is\n * what the app's Effect code sees: middleware can `catchTag('MastraRouteError')`, Effect's logger\n * records the cause, and a tracer marks the request span as failed. 4xx answers stay successes —\n * a refused or invalid request is not a server failure.\n */\nexport class MastraRouteError\n extends Data.TaggedError('MastraRouteError')<{\n /** The HTTP status Mastra answered with. */\n readonly status: number;\n /**\n * What the route threw. When Mastra answered the failure before it reached the adapter — a custom\n * route's handler, whose errors Mastra's own sub-app answers, or an MCP transport that failed to\n * start — an `Error` naming the request and the status instead.\n */\n readonly cause: unknown;\n /** The response Mastra built for it, sent unchanged. */\n readonly response: HttpServerResponse.HttpServerResponse;\n }>\n implements HttpServerRespondable.Respondable\n{\n override get message(): string {\n return this.cause instanceof Error ? this.cause.message : String(this.cause);\n }\n\n [HttpServerRespondable.symbol]() {\n return Effect.succeed(this.response);\n }\n}\n\n/** The error a route threw, keyed by the response it was turned into. */\nconst routeErrors = new WeakMap<Response, unknown>();\n\n/**\n * The key under which the request's Effect span is stored in Mastra's `RequestContext`.\n *\n * Stored as a function on purpose: `RequestContext` drops functions when it is serialised, so the span\n * never lands in a workflow snapshot, and a client sending `requestContext` as JSON cannot supply one.\n */\nconst REQUEST_SPAN_KEY = 'mastraEffect.requestSpan';\n\n/** Everything `new MastraServer(...)` takes: Mastra's adapter options, plus this adapter's own. */\nexport type MastraServerOptions = ConstructorParameters<\n typeof MastraServerBase<EffectRouter, EffectRequestContext, EffectRequestContext>\n>[0] & {\n /**\n * Fail the Effect request with a `MastraRouteError` when a route answers with a server error\n * (5xx), so the app's Effect error handling, logs and traces see it. The response is the same\n * either way. Defaults to `true`.\n */\n readonly errorChannel?: boolean;\n};\n\n/**\n * Mastra server adapter for Effect's HTTP layer.\n *\n * `TApp` is a live `HttpRouter` service instance rather than a Layer: Mastra's `registerRoutes()`\n * awaits ~400 sequential registrations against one fixed app, which the Layer-based\n * `HttpRouter.add` cannot express but the service's effectful `add` can.\n */\nexport class MastraServer extends MastraServerBase<EffectRouter, EffectRequestContext, EffectRequestContext> {\n private contextMiddleware?: (request: Request, parsedBody?: unknown) => Promise<RequestContext>;\n private readonly errorChannel: boolean;\n\n constructor(options: MastraServerOptions) {\n const { errorChannel, ...base } = options;\n super(base);\n this.errorChannel = errorChannel ?? true;\n }\n\n /**\n * Builds the per-request Mastra context. Effect has no `derive`, so routes call this directly.\n *\n * `parsedBody` lets the request path reuse the body it already parsed; omitting it (as the\n * multipart conformance suite does, fetching this middleware standalone) falls back to reading.\n */\n createContextMiddleware(): (request: Request, parsedBody?: unknown) => Promise<RequestContext> {\n this.contextMiddleware ??= async (request: Request, parsedBody?: unknown): Promise<RequestContext> => {\n let bodyRequestContext: Record<string, any> | undefined;\n let paramsRequestContext: Record<string, any> | undefined;\n\n const method = request.method.toUpperCase();\n if (CONTEXT_BODY_METHODS.has(method)) {\n let body = parsedBody;\n if (body === undefined && request.headers.get('content-type')?.includes('application/json')) {\n try {\n body = await request.clone().json();\n } catch {\n // Not valid JSON — the route's own body parsing reports this.\n }\n }\n if (body && typeof body === 'object' && 'requestContext' in body) {\n bodyRequestContext = (body as { requestContext?: Record<string, any> }).requestContext;\n }\n }\n\n // POST too, as in @mastra/hono: a client may put the context in the query whatever the method.\n if (method === 'GET' || method === 'POST') {\n const encoded = new URL(request.url).searchParams.get('requestContext');\n if (encoded) {\n try {\n paramsRequestContext = JSON.parse(encoded);\n } catch {\n try {\n paramsRequestContext = JSON.parse(Buffer.from(encoded, 'base64').toString('utf-8'));\n } catch {\n // Neither JSON nor base64(JSON); ignore.\n }\n }\n }\n }\n\n const requestContext = this.mergeRequestContext({ paramsRequestContext, bodyRequestContext });\n this.applyRequestMetadataToContext({\n requestContext,\n getHeader: name => request.headers.get(name) ?? undefined,\n });\n return requestContext;\n };\n\n return this.contextMiddleware;\n }\n\n async getParams(route: ServerRoute, ctx: EffectRequestContext): Promise<ParsedRequestParams> {\n const url = new URL(ctx.request.url);\n // Every value of a repeated key (`?tags=a&tags=b`), which Mastra's schemas accept as an array;\n // `normalizeQueryParams` turns a key given once back into a plain string.\n const query: Record<string, string[]> = {};\n for (const key of new Set(url.searchParams.keys())) query[key] = url.searchParams.getAll(key);\n return {\n urlParams: ctx.pathParams,\n queryParams: normalizeQueryParams(query),\n body: ctx.body,\n bodyParseError: ctx.bodyParseError,\n };\n }\n\n /**\n * Reads and parses the body exactly once.\n *\n * `oversize` rather than a body when the limit is blown: a chunked request declares no\n * `Content-Length`, so counting what actually arrives is the only way to enforce the cap — for\n * uploads as much as for JSON.\n */\n private async readBody(\n route: ServerRoute,\n request: Request,\n maxSize?: number,\n ): Promise<{ body?: unknown; bodyParseError?: { message: string }; oversize?: true }> {\n if (!BODY_METHODS.has(route.method.toUpperCase())) return {};\n\n const contentType = request.headers.get('content-type') ?? '';\n const isMultipart = contentType.includes('multipart/form-data');\n if (!isMultipart && !contentType.includes('application/json')) return {};\n\n const bytes = await readBytesWithin(request.clone(), maxSize);\n if (bytes === undefined) return { oversize: true };\n\n if (isMultipart) {\n try {\n const form = await new Response(bytes, { headers: { 'content-type': contentType } }).formData();\n return { body: await this.parseFormData(form) };\n } catch (error) {\n this.mastra.getLogger()?.error('Failed to parse multipart form data', {\n error: error instanceof Error ? { message: error.message, stack: error.stack } : error,\n });\n return {\n bodyParseError: { message: error instanceof Error ? error.message : 'Failed to parse multipart form data' },\n };\n }\n }\n\n const text = new TextDecoder().decode(bytes);\n if (text.trim().length === 0) return {};\n\n try {\n return { body: JSON.parse(text) };\n } catch (error) {\n return { bodyParseError: { message: error instanceof Error ? error.message : 'Invalid JSON in request body' } };\n }\n }\n\n /** Files arrive as Node Buffers; the conformance multipart suite asserts `Buffer.isBuffer`. */\n private async parseFormData(data: FormData): Promise<Record<string, unknown>> {\n const result: Record<string, unknown> = {};\n for (const [key, value] of data.entries()) {\n if (typeof value === 'string') {\n try {\n result[key] = JSON.parse(value);\n } catch {\n result[key] = value;\n }\n } else {\n result[key] = Buffer.from(await value.arrayBuffer());\n }\n }\n return result;\n }\n\n async stream(\n route: ServerRoute,\n _ctx: EffectRequestContext,\n result: { fullStream: ReadableStream },\n ): Promise<Response> {\n const streamFormat = route.streamFormat || 'stream';\n const encoder = new TextEncoder();\n\n const headers: Record<string, string> =\n streamFormat === 'sse'\n ? {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n 'X-Accel-Buffering': 'no',\n }\n : { 'Content-Type': 'text/plain' };\n\n // `sendResponse` casts rather than checks, so a route handler that returns a bare stream instead\n // of the `{ fullStream }` shape still arrives here — and so can one that returns no stream at all,\n // which must fail as a 500 now rather than as a 200 whose body then breaks.\n const source: unknown = result instanceof ReadableStream ? result : result?.fullStream;\n if (!(source instanceof ReadableStream)) {\n throw new Error(`Route ${route.path} declares a stream response but returned no stream`);\n }\n let reader: ReadableStreamDefaultReader | undefined;\n\n const stream = new ReadableStream<Uint8Array>({\n start: async controller => {\n reader = source.getReader();\n\n try {\n if (streamFormat === 'sse' && route.sseFlushOnConnect) {\n controller.enqueue(encoder.encode(': connected\\n\\n'));\n }\n\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n if (!value) continue;\n\n if (streamFormat === 'sse' && typeof value === 'string' && value.startsWith(':')) {\n controller.enqueue(encoder.encode(value));\n continue;\n }\n\n const outputValue = (this.streamOptions?.redact ?? true) ? redactStreamChunk(value) : value;\n const serialized = serializeStreamChunk(outputValue);\n if (!serialized.ok) {\n this.mastra.getLogger()?.error('Failed to serialize stream chunk, skipping', {\n path: route.path,\n chunkType: (outputValue as { type?: string })?.type,\n error: serialized.error.message,\n });\n continue;\n }\n\n controller.enqueue(\n encoder.encode(streamFormat === 'sse' ? `data: ${serialized.json}\\n\\n` : `${serialized.json}\\x1E`),\n );\n }\n\n if (streamFormat === 'sse') controller.enqueue(encoder.encode('data: [DONE]\\n\\n'));\n controller.close();\n } catch (error) {\n this.mastra.getLogger()?.error('Error in stream processing', {\n error: error instanceof Error ? { message: error.message, stack: error.stack } : error,\n });\n // Closed rather than errored, as in @mastra/hono, so the chunks already sent are delivered the\n // same way on every transport. Throws if the consumer already cancelled, which is fine.\n try {\n controller.close();\n } catch {\n // Already closed or errored.\n }\n } finally {\n await reader.cancel().catch(() => {});\n }\n },\n cancel(reason) {\n return reader?.cancel(reason);\n },\n });\n\n return new Response(stream, { headers });\n }\n\n async sendResponse(\n route: ServerRoute,\n ctx: EffectRequestContext,\n result: unknown,\n prefix?: string,\n ): Promise<Response> {\n const resolvedPrefix = prefix ?? this.prefix ?? '';\n\n // Transparent session refresh smuggles Set-Cookie back through the result object.\n const refreshHeaders: Record<string, string> = {};\n if (result && typeof result === 'object' && '__refreshHeaders' in result) {\n Object.assign(refreshHeaders, (result as any).__refreshHeaders as Record<string, string>);\n delete (result as any).__refreshHeaders;\n }\n\n switch (route.responseType) {\n case 'json':\n return json(result ?? null, 200, refreshHeaders);\n\n case 'stream':\n return this.stream(route, ctx, result as { fullStream: ReadableStream });\n\n case 'datastream-response':\n if (!(result instanceof Response)) {\n throw new Error(`Route ${route.path} declares a Response but returned something else`);\n }\n return forwardResponse(result);\n\n case 'mcp-http': {\n const { server, httpPath, mcpOptions: routeMcpOptions } = result as MCPHttpTransportResult;\n // `setRequestAuth` is the adapter's hook, not a transport option — the transport rejects it.\n const { setRequestAuth, ...options } = { ...this.mcpOptions, ...routeMcpOptions };\n\n return this.bridgeMcpTransport(ctx, {\n label: '[MCP HTTP] Error in background startHTTP',\n setRequestAuth,\n // The client speaks JSON-RPC, so the failure is answered in it.\n failureBody: { jsonrpc: '2.0', error: { code: -32603, message: 'Internal server error' }, id: null },\n start: ({ url, req, res }) =>\n server.startHTTP({\n url,\n httpPath: `${resolvedPrefix}${httpPath}`,\n req,\n res,\n options: Object.keys(options).length > 0 ? options : undefined,\n }),\n });\n }\n\n case 'mcp-sse': {\n const { server, ssePath, messagePath } = result as MCPSseTransportResult;\n\n return this.bridgeMcpTransport(ctx, {\n label: '[MCP SSE] Error in background startSSE',\n setRequestAuth: this.mcpOptions?.setRequestAuth,\n failureBody: { error: 'Error handling MCP SSE request' },\n start: ({ url, req, res }) =>\n server.startSSE({\n url,\n ssePath: `${resolvedPrefix}${ssePath}`,\n messagePath: `${resolvedPrefix}${messagePath}`,\n req,\n res,\n }),\n });\n }\n\n default:\n return new Response(null, { status: 500 });\n }\n }\n\n /**\n * Runs an MCP transport against a replayable copy of the request and returns what it wrote to the\n * Node response object.\n *\n * `start` is deliberately not awaited: it resolves when the body finishes, while `toFetchResponse`\n * resolves once headers are sent. Awaiting here would stall SSE.\n */\n private async bridgeMcpTransport(\n ctx: EffectRequestContext,\n transport: {\n readonly label: string;\n readonly setRequestAuth?: Parameters<typeof applyMcpRequestAuth>[0]['setRequestAuth'];\n readonly failureBody: unknown;\n readonly start: (transport: ReturnType<typeof toReqRes> & { url: URL }) => Promise<unknown>;\n },\n ): Promise<Response> {\n const forwardRequest = await createForwardRequest(ctx.request, ctx.body);\n const { req, res } = toReqRes(forwardRequest);\n\n // `toReqRes` builds a fresh Node request, so the caller Mastra's auth resolved never reaches the\n // transport — and so never reaches the tools, as `authInfo` — unless it is carried over here.\n await applyMcpRequestAuth({ req, requestContext: ctx.requestContext, setRequestAuth: transport.setRequestAuth });\n\n void transport.start({ url: new URL(forwardRequest.url), req, res }).catch((error: unknown) => {\n this.mastra.getLogger()?.error(transport.label, {\n error: error instanceof Error ? { message: error.message, stack: error.stack } : error,\n });\n // `toFetchResponse` below waits for headers, and a transport that failed before writing any\n // never will: answer, or the client waits forever.\n try {\n if (!res.headersSent) {\n res.writeHead(500, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(transport.failureBody));\n }\n } catch {\n // Already closed or destroyed — nothing left to answer.\n }\n });\n\n return forwardMcpResponse(await toFetchResponse(res), res);\n }\n\n /**\n * Never rejects — the caller runs it through `Effect.promise`. A route that threw is still answered\n * with a response; what it threw is kept in `routeErrors` for `toEffectOutcome`.\n */\n private async handleRoute(\n route: ServerRoute,\n prefix: string,\n request: Request,\n pathParams: Record<string, string>,\n span: Tracer.AnySpan | undefined,\n ): Promise<Response> {\n // Outside the try, so a refreshed session reaches the client even when the route then fails.\n let refreshHeaders: Record<string, string> = {};\n try {\n const maxSize = route.maxBodySize ?? this.bodyLimitOptions?.maxSize;\n\n // Gate one, on headers alone, so an oversized body is rejected before it is read.\n if (this.exceedsDeclaredLimit(route, request, maxSize)) return this.bodyLimitResponse(route);\n\n const parsed = await this.readBody(route, request, maxSize);\n // Gate two, on what actually arrived, for requests that declared no Content-Length.\n if (parsed.oversize) return this.bodyLimitResponse(route);\n\n const ctx: EffectRequestContext = {\n request,\n pathParams,\n body: parsed.body,\n bodyParseError: parsed.bodyParseError,\n };\n\n const requestContext = await this.createContextMiddleware()(request, parsed.body);\n if (span) requestContext.set(REQUEST_SPAN_KEY, () => span);\n const url = new URL(request.url);\n\n const authError = await this.checkRouteAuth(route, {\n path: url.pathname,\n method: request.method,\n getHeader: name => request.headers.get(name) ?? undefined,\n getQuery: name => url.searchParams.get(name) ?? undefined,\n requestContext,\n request,\n buildAuthorizeContext: () => request,\n });\n\n if (authError?.error) {\n return json({ error: authError.error }, authError.status, authError.headers);\n }\n\n refreshHeaders = authError?.headers ?? {};\n\n const permissionError = await this.resolvePermissionError(route, requestContext);\n if (permissionError) {\n return json(\n { error: permissionError.error, message: permissionError.message },\n permissionError.status,\n refreshHeaders,\n );\n }\n\n const params = await this.getParams(route, ctx);\n\n if (params.bodyParseError) {\n return json(\n { error: 'Invalid request body', issues: [{ field: 'body', message: params.bodyParseError.message }] },\n 400,\n refreshHeaders,\n );\n }\n\n const validated = await this.validateParams(route, params, refreshHeaders);\n if ('response' in validated) return validated.response;\n\n const fgaError = await checkRouteFGA(this.mastra, route, requestContext, {\n ...params.urlParams,\n ...params.queryParams,\n ...(typeof params.body === 'object' ? params.body : {}),\n });\n if (fgaError) {\n return json({ error: fgaError.error, message: fgaError.message }, fgaError.status, refreshHeaders);\n }\n\n const result = await route.handler({\n ...params.urlParams,\n ...params.queryParams,\n ...(typeof params.body === 'object' ? params.body : {}),\n requestContext,\n mastra: this.mastra,\n registeredTools: this.tools ?? {},\n taskStore: this.taskStore,\n abortSignal: request.signal,\n routePrefix: prefix,\n request,\n });\n\n const response = await this.sendResponse(route, { ...ctx, body: params.body, requestContext }, result, prefix);\n return withHeaders(response, refreshHeaders);\n } catch (error) {\n const response = withHeaders(this.toErrorResponse(error, route), refreshHeaders);\n routeErrors.set(response, error);\n return response;\n }\n }\n\n private exceedsDeclaredLimit(route: ServerRoute, request: Request, maxSize?: number): boolean {\n if (maxSize === undefined || !BODY_METHODS.has(route.method.toUpperCase())) return false;\n const contentLength = request.headers.get('content-length');\n return contentLength !== null && Number.parseInt(contentLength, 10) > maxSize;\n }\n\n private bodyLimitResponse(route: ServerRoute): Response {\n let errorResponse: unknown = { error: 'Request body too large' };\n // A route-level cap is the route's own policy; only the global limit runs the global onError.\n if (route.maxBodySize === undefined && this.bodyLimitOptions) {\n try {\n errorResponse = this.bodyLimitOptions.onError(errorResponse);\n } catch {\n // Fall back to the default error response.\n }\n }\n return json(errorResponse, 413);\n }\n\n /**\n * Mastra's RBAC check, which only applies once an auth provider is configured.\n *\n * `hasPermission` lives in an enterprise-only module, so `loadHasPermission` can come back empty;\n * the request then proceeds unchecked rather than being refused.\n */\n private async resolvePermissionError(\n route: ServerRoute,\n requestContext: RequestContext,\n ): Promise<{ status: number; error: string; message: string } | null> {\n if (!this.mastra.getStudio?.()?.auth && !this.mastra.getServer()?.auth) return null;\n\n const hasPermission = await loadHasPermission();\n if (!hasPermission) return null;\n\n const userPermissions = requestContext.get('mastra__userPermissions') as string[] | undefined;\n return this.checkRoutePermission(route, userPermissions, hasPermission, requestContext);\n }\n\n private async validateParams(\n route: ServerRoute,\n params: ParsedRequestParams,\n refreshHeaders: Record<string, string>,\n ): Promise<{ ok: true } | { response: Response }> {\n const steps: Array<{ context: 'query' | 'body' | 'path'; run: () => Promise<void>; fallback: string }> = [\n {\n context: 'query',\n fallback: 'Invalid query parameters',\n run: async () => {\n if (params.queryParams) params.queryParams = await this.parseQueryParams(route, params.queryParams);\n },\n },\n {\n context: 'body',\n fallback: 'Invalid request body',\n run: async () => {\n if (params.body !== undefined || route.bodySchema) params.body = await this.parseBody(route, params.body);\n },\n },\n {\n context: 'path',\n fallback: 'Invalid path parameters',\n run: async () => {\n if (params.urlParams) params.urlParams = await this.parsePathParams(route, params.urlParams);\n },\n },\n ];\n\n for (const step of steps) {\n try {\n await step.run();\n } catch (error) {\n this.mastra.getLogger()?.error(`Error parsing ${step.context} params`, {\n error: error instanceof Error ? { message: error.message, stack: error.stack } : error,\n });\n if (isZodError(error)) {\n const resolved = this.resolveValidationError(route, error, step.context);\n return { response: json(resolved.body, resolved.status, refreshHeaders) };\n }\n return {\n response: json(\n {\n error: step.fallback,\n issues: [{ field: 'unknown', message: error instanceof Error ? error.message : 'Unknown error' }],\n },\n 400,\n refreshHeaders,\n ),\n };\n }\n }\n\n return { ok: true };\n }\n\n private toErrorResponse(error: unknown, route?: ServerRoute): Response {\n const status = (error as any)?.status ?? (error as any)?.details?.status;\n const isClientError = typeof status === 'number' && status >= 400 && status < 500;\n\n if (!isClientError) {\n this.mastra.getLogger()?.error('Error calling handler', {\n error: error instanceof Error ? { message: error.message, stack: error.stack } : error,\n path: route?.path,\n method: route?.method,\n });\n }\n\n const customResponse = getCustomHTTPExceptionResponse(error);\n if (customResponse) return customResponse;\n\n // Which items failed, for an error that says — Studio's dataset form reads `cause.failingItems`.\n // Only that field, so nothing else an error carries in its cause leaks to the client.\n const cause = error instanceof Error ? error.cause : undefined;\n const failingItems =\n typeof status === 'number' && cause && typeof cause === 'object' && 'failingItems' in cause\n ? (cause as { failingItems?: unknown }).failingItems\n : undefined;\n\n return json(\n {\n error: error instanceof Error ? error.message : 'Unknown error',\n ...(Array.isArray(failingItems) ? { cause: { failingItems } } : {}),\n },\n toHttpStatus(status),\n );\n }\n\n async registerRoute(\n app: EffectRouter,\n route: ServerRoute,\n { prefix: prefixParam }: { prefix?: string } = {},\n ): Promise<void> {\n const prefix = prefixParam ?? this.prefix ?? '';\n const fullPath = `${prefix}${route.path}`;\n\n // rc.117's vendored FindMyWay binds the correct param name per route even when two routes\n // differ only in their param name at the same segment, so paths register verbatim — no\n // positional rewrite is needed here (see src/router-collision.test.ts).\n const handler = this.toEffectHandler((request, pathParams, span) =>\n this.handleRoute(route, prefix, request, pathParams, span),\n );\n\n await Effect.runPromise(addRoute(app, effectMethod(route.method, route.path), fullPath as `/${string}`, handler));\n }\n\n async registerCustomApiRoutes(): Promise<void> {\n const routes = await this.registerSchemaApiRoutes();\n if (!(await this.buildCustomRouteHandler(routes))) return;\n\n const handler = this.toEffectHandler((request, _pathParams, span) => this.handleCustomRoute(request, span));\n for (const route of routes) {\n await Effect.runPromise(addRoute(this.app, effectMethod(route.method, route.path), route.path as `/${string}`, handler));\n }\n }\n\n /**\n * An Effect route handler around one of the adapter's Web entry points. The entry point gets the\n * request with a body it can read and a signal that aborts if the client leaves, plus the path\n * parameters and the request's span; what it answers becomes the Effect result.\n */\n private toEffectHandler(\n answer: (request: Request, pathParams: Record<string, string>, span: Tracer.AnySpan | undefined) => Promise<Response>,\n ) {\n return (serverRequest: HttpServerRequest.HttpServerRequest) =>\n Effect.flatMap(\n Effect.all([HttpRouter.params, abortOnDisconnect, Effect.option(Effect.currentParentSpan)]),\n ([pathParams, signal, span]) =>\n // A request that cannot be materialized is a defect, not a route error.\n Effect.flatMap(toReadableWebRequest(serverRequest, signal), request =>\n Effect.flatMap(\n Effect.promise(() => answer(request, pathParams as Record<string, string>, Option.getOrUndefined(span))),\n response => this.toEffectOutcome(request, response),\n ),\n ),\n );\n }\n\n private async handleCustomRoute(request: Request, span: Tracer.AnySpan | undefined): Promise<Response> {\n try {\n const url = new URL(request.url);\n const path = url.pathname;\n const method = request.method;\n\n // `readBodyFields` parses a copy, only to read fields from. The route itself gets the original\n // body: custom routes are often webhooks that verify a signature over the exact bytes sent,\n // and parsing and re-serialising changes them — whitespace, duplicate keys, large integers,\n // binary data.\n const body = await readBodyFields(request);\n\n const requestContext = await this.createContextMiddleware()(request, body.json);\n if (span) requestContext.set(REQUEST_SPAN_KEY, () => span);\n\n const matchedRoute = findMatchingCustomRoute(\n path,\n method,\n this.customApiRoutes ?? this.mastra.getServer()?.apiRoutes,\n );\n const shouldRunAuth = isProtectedCustomRoute(path, method, this.customRouteAuthConfig);\n const shouldRunFGA = !!matchedRoute?.route.fga;\n\n if (shouldRunAuth || shouldRunFGA) {\n const serverRoute: ServerRoute = {\n method: (matchedRoute?.route.method ?? method) as any,\n path: matchedRoute?.route.path ?? path,\n responseType: 'json',\n handler: async () => {},\n requiresAuth: matchedRoute?.route.requiresAuth,\n requiresPermission: matchedRoute?.route.requiresPermission,\n fga: matchedRoute?.route.fga,\n };\n\n if (shouldRunAuth) {\n const authError = await this.checkRouteAuth(serverRoute, {\n path,\n method,\n getHeader: name => request.headers.get(name) ?? undefined,\n getQuery: name => url.searchParams.get(name) ?? undefined,\n requestContext,\n request,\n buildAuthorizeContext: () => request,\n });\n if (authError?.error) return json({ error: authError.error }, authError.status, authError.headers);\n\n const permissionError = await this.resolvePermissionError(serverRoute, requestContext);\n if (permissionError) {\n return json({ error: permissionError.error, message: permissionError.message }, permissionError.status);\n }\n }\n\n const fgaError = await checkRouteFGA(this.mastra, serverRoute, requestContext, {\n ...matchedRoute?.params,\n ...Object.fromEntries(url.searchParams),\n ...body.fields,\n });\n if (fgaError) return json({ error: fgaError.error, message: fgaError.message }, fgaError.status);\n }\n\n const headers: Record<string, string | string[] | undefined> = {};\n request.headers.forEach((value, key) => {\n headers[key] = value;\n });\n\n // Not a fetch handler despite the shape: it takes loose primitives and builds the Request\n // itself. `null` means no custom route matched.\n const response = await this.handleCustomRouteRequest(\n request.url,\n request.method,\n headers,\n // The stream itself, which the base forwards byte for byte, as @mastra/hono does.\n request.body ?? undefined,\n requestContext,\n request.signal,\n );\n\n return response ?? json({ error: 'Not Found' }, 404);\n } catch (error) {\n const response = this.toErrorResponse(error);\n routeErrors.set(response, error);\n return response;\n }\n }\n\n /**\n * The Effect result of a route: its response, or — for a server error (5xx), when the error\n * channel is on — a `MastraRouteError` that answers with that same response.\n *\n * Decided by the status, not by whether the adapter caught something: not every server error\n * passes through its `catch`. A custom route's handler that throws is answered by Mastra's own\n * sub-app, and an MCP transport that fails to start writes its 500 itself.\n */\n private toEffectOutcome(\n request: Request,\n response: Response,\n ): Effect.Effect<HttpServerResponse.HttpServerResponse, MastraRouteError> {\n const converted = this.toEffectResponse(response);\n if (!this.errorChannel || response.status < 500) return Effect.succeed(converted);\n const cause = routeErrors.has(response)\n ? routeErrors.get(response)\n : new Error(`Mastra answered ${request.method} ${new URL(request.url).pathname} with ${response.status}`);\n return Effect.fail(new MastraRouteError({ status: response.status, cause, response: converted }));\n }\n\n /**\n * Converts a Web response for Effect to send, keeping every `Set-Cookie`.\n *\n * `HttpServerResponse.fromWeb` files cookies by name, so two sharing a name — one cleared at two\n * paths, say — collapse into the last, and one that cannot be serialised fails the response when\n * it is sent, leaving the client waiting. Each cookie gets its own entry here instead, and one\n * that could never be sent is dropped with a warning. Attributes Effect does not model (anything\n * but Domain, Path, Expires, Max-Age, HttpOnly, Secure, SameSite, Priority, Partitioned) are lost.\n */\n private toEffectResponse(response: Response): HttpServerResponse.HttpServerResponse {\n const setCookies = response.headers.getSetCookie();\n const converted = HttpServerResponse.fromWeb(response);\n if (setCookies.length === 0) return converted;\n\n const cookies: Record<string, Cookies.Cookie> = {};\n setCookies.forEach((header, index) => {\n for (const cookie of Object.values(Cookies.fromSetCookie(header).cookies)) {\n try {\n Cookies.serializeCookie(cookie);\n cookies[`${index}:${cookie.name}`] = cookie;\n } catch (error) {\n this.mastra.getLogger()?.warn('Dropped a Set-Cookie header that cannot be sent', {\n cookie: cookie.name,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n });\n return HttpServerResponse.replaceCookies(converted, Cookies.fromReadonlyRecord(cookies));\n }\n\n /**\n * Context is built per route because Effect has no `derive`-style request hook. What does need\n * every request, matched or not, is the warning for a channel webhook nobody registered, which\n * shows up as a 404 no route would ever log — so it runs here, as in `@mastra/hono`.\n *\n * It wraps the app's own routes too, since they share the router, so it steps aside at once for\n * anything that cannot be such a webhook: every method but POST, and a target with no parsable path.\n */\n registerContextMiddleware(): void {\n Effect.runSync(\n this.app.addGlobalMiddleware(httpEffect =>\n Effect.flatMap(HttpServerRequest.HttpServerRequest, serverRequest => {\n if (serverRequest.method.toUpperCase() !== 'POST') return httpEffect;\n const url = parseTarget(serverRequest.url);\n if (!url) return httpEffect;\n return Effect.onExit(httpEffect, exit =>\n Effect.map(statusOf(exit), status =>\n this.warnIfUnregisteredChannelWebhook(url.pathname, serverRequest.method, status),\n ),\n );\n }),\n ),\n );\n }\n\n /** Auth is resolved per route, matching every other adapter. */\n registerAuthMiddleware(): void {}\n\n registerHttpLoggingMiddleware(): void {\n if (!this.httpLoggingConfig?.enabled) return;\n\n // runSync, not runPromise: the base calls this synchronously inside init() and immediately\n // registers routes, so a floating promise would race registration and hide rejections.\n Effect.runSync(\n this.app.addGlobalMiddleware(httpEffect =>\n Effect.flatMap(HttpServerRequest.HttpServerRequest, serverRequest => {\n const url = parseTarget(serverRequest.url);\n // A target with no parsable path is logged as it arrived.\n const path = url?.pathname ?? serverRequest.url;\n if (!this.shouldLogRequest(path)) return httpEffect;\n\n const start = Date.now();\n // On exit rather than on success, so a request no route matched — which fails instead of\n // producing a response — is logged with the 404 it is answered with, as in @mastra/hono.\n return Effect.onExit(httpEffect, exit =>\n Effect.map(statusOf(exit), status => {\n const duration = Date.now() - start;\n const level = this.httpLoggingConfig?.level || 'info';\n const logData: Record<string, any> = {\n method: serverRequest.method,\n path,\n status,\n duration: `${duration}ms`,\n };\n\n if (this.httpLoggingConfig?.includeQueryParams) {\n logData.query = Object.fromEntries(url?.searchParams ?? []);\n }\n\n if (this.httpLoggingConfig?.includeHeaders) {\n const headers: Record<string, unknown> = { ...serverRequest.headers };\n for (const header of this.httpLoggingConfig.redactHeaders ?? []) {\n if (headers[header.toLowerCase()] !== undefined) headers[header.toLowerCase()] = '[REDACTED]';\n }\n logData.headers = headers;\n }\n\n this.logger[level](`${serverRequest.method} ${path} ${status} ${duration}ms`, logData);\n }),\n );\n }),\n ),\n );\n }\n}\n\n/**\n * The request as a Web `Request` whose body can still be read.\n *\n * `HttpServerRequest.toWeb` hands over the original body. Once a middleware has read it\n * (`request.json`, `request.text`…) that body is empty on a Node server and unusable in a fetch\n * handler, so Mastra would see no input at all. Effect keeps what the middleware read, and the body\n * is rebuilt from that: the bytes where Effect kept them, otherwise the text, which is what a fetch\n * handler keeps for JSON and forms. A body nobody has read passes through untouched, still streaming.\n */\nconst toReadableWebRequest = (\n serverRequest: HttpServerRequest.HttpServerRequest,\n signal: AbortSignal,\n): Effect.Effect<Request> =>\n // The signal only takes effect where Effect builds the Request (a Node server). A fetch handler\n // passes the host's own Request through, whose signal already aborts when the client leaves.\n Effect.flatMap(Effect.orDie(HttpServerRequest.toWeb(serverRequest, { signal })), request => {\n // GET and HEAD carry no body, and a Request with those methods refuses one.\n if (request.method === 'GET' || request.method === 'HEAD' || !bodyWasRead(serverRequest.source)) {\n return Effect.succeed(request);\n }\n // Read some other way — the raw stream, say — nothing kept the bytes. Failing says so; passing\n // the empty body on would surface as a baffling validation error far from the cause.\n if (!effectKeptBody(serverRequest)) {\n return Effect.die(\n new Error(\n `The body of ${request.method} ${new URL(request.url).pathname} was consumed before Mastra's route ran, ` +\n 'and not through request.text/json/arrayBuffer, so it cannot be recovered',\n ),\n );\n }\n return serverRequest.arrayBuffer.pipe(\n Effect.map(bytes => new Uint8Array(bytes)),\n Effect.catch(() => Effect.map(serverRequest.text, text => new TextEncoder().encode(text))),\n Effect.orDie,\n Effect.map(\n body => new Request(request.url, { method: request.method, headers: request.headers, body, signal: request.signal }),\n ),\n );\n });\n\n/**\n * A signal that aborts when the request ends without its response having been sent: the client\n * disconnected, or the server shut down first. Mastra hands it to routes as `abortSignal`, which is\n * what stops an agent's model calls — without it they run to the end after the client has left.\n *\n * Tied to the request's scope, which Effect keeps open until the response body has been written,\n * so it also covers a streamed answer the client stops reading partway.\n */\nconst abortOnDisconnect: Effect.Effect<AbortSignal, never, Scope.Scope> = Effect.suspend(() => {\n const controller = new AbortController();\n return Effect.as(\n Effect.addFinalizer(exit => (Exit.isSuccess(exit) ? Effect.void : Effect.sync(() => controller.abort()))),\n controller.signal,\n );\n});\n\n/**\n * The target a request asked for, as a URL, for reading its path and query. `url` may be\n * origin-relative, so URL gets a base purely to parse it.\n *\n * `undefined` for a target URL cannot parse, such as `//[`, which Node still accepts. Throwing\n * instead would fail the request as a defect, so the router's 404 for it would become a 500.\n */\nconst parseTarget = (url: string): URL | undefined => {\n try {\n return new URL(url, 'http://localhost');\n } catch {\n return undefined;\n }\n};\n\n/**\n * The status a request is answered with, including when it failed instead of producing a response\n * — asked of the failure the same way Effect's server asks when it answers, so a request no route\n * matched reports its 404.\n */\nconst statusOf = (exit: Exit.Exit<HttpServerResponse.HttpServerResponse, unknown>): Effect.Effect<number> =>\n Exit.isSuccess(exit)\n ? Effect.succeed(exit.value.status)\n : Effect.map(HttpServerError.causeResponse(exit.cause), ([response]) => response.status);\n\n/**\n * The body, or `undefined` once it passes `maxSize`. Counted as it arrives, because a chunked\n * request declares no length; reading stops at the limit instead of buffering the rest.\n */\nasync function readBytesWithin(request: Request, maxSize?: number): Promise<Uint8Array | undefined> {\n if (maxSize === undefined) return new Uint8Array(await request.arrayBuffer());\n\n const reader = request.body?.getReader();\n if (!reader) return new Uint8Array();\n\n const chunks: Uint8Array[] = [];\n let size = 0;\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n size += value.byteLength;\n if (size > maxSize) {\n // Not awaited: this is a clone, and a clone's cancel only settles once the original is\n // cancelled too, which never happens here.\n void reader.cancel().catch(() => {});\n return undefined;\n }\n chunks.push(value);\n }\n\n const bytes = new Uint8Array(size);\n let offset = 0;\n for (const chunk of chunks) {\n bytes.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return bytes;\n}\n\n/** Whether something already read the body of a Web `Request` or a Node `IncomingMessage`. */\nconst bodyWasRead = (source: unknown): boolean => {\n if (source instanceof Request) return source.bodyUsed;\n const stream = source as { readonly readableDidRead?: unknown } | null;\n return stream?.readableDidRead === true;\n};\n\n/**\n * Whether Effect kept a copy of the body when it was read.\n *\n * Checked before asking for that copy, because on Node, asking Effect for the bytes of a stream\n * something else already drained waits forever. The fields are Effect's private caches, the same on\n * the Node and the fetch implementations. Should a release rename them, this answers false and the\n * regression tests for a body read by middleware fail — never a hang.\n */\nconst effectKeptBody = (serverRequest: HttpServerRequest.HttpServerRequest): boolean => {\n const caches = serverRequest as unknown as { readonly arrayBufferEffect?: unknown; readonly textEffect?: unknown };\n return caches.arrayBufferEffect !== undefined || caches.textEffect !== undefined;\n};\n\n/**\n * The fields a custom route's permission check may read from the body, mirroring @mastra/hono: the\n * members of a JSON object, or the entries of a form. `json` is the parsed JSON, for the request\n * context. Anything unreadable contributes nothing — the route still receives the body and reports\n * the problem itself.\n *\n * Reads a copy, leaving `request`'s own body for the route, and copies only a body it will read: an\n * unread copy of a large upload would hold every byte of it until the request is gone.\n */\nasync function readBodyFields(request: Request): Promise<{ json?: unknown; fields: Record<string, unknown> }> {\n const contentType = request.headers.get('content-type') ?? '';\n try {\n if (contentType.includes('application/json')) {\n const parsed: unknown = await request.clone().json();\n const isObject = typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed);\n return { json: parsed, fields: isObject ? (parsed as Record<string, unknown>) : {} };\n }\n if (contentType.includes('application/x-www-form-urlencoded') || contentType.includes('multipart/form-data')) {\n return { fields: Object.fromEntries(await request.clone().formData()) };\n }\n } catch {\n // Not what the content type claims.\n }\n return { fields: {} };\n}\n\n/**\n * Creates an empty router to hand to `new MastraServer({ app })`.\n *\n * `ignoreDuplicateSlashes` is forced off because Effect's vendored FindMyWay defaults it on\n * (FindMyWay/internal/router.ts:54), unlike stock find-my-way. Left on, `/api//agents` would serve\n * `/api/agents` instead of 404ing, which diverges from every other Mastra adapter.\n */\nexport const createRouter = (config?: Partial<FindMyWay.RouterConfig>): EffectRouter =>\n Effect.runSync(\n Effect.provideService(HttpRouter.make, HttpRouter.RouterConfig, {\n ignoreDuplicateSlashes: false,\n ...config,\n }),\n );\n\n/**\n * Registers a route whose handler may fail with `MastraRouteError`.\n *\n * `HttpRouter.add` records a handler's error type as a requirement, so middleware can promise to\n * handle it. Nothing needs to: unhandled, the error answers with its own response (it is\n * `Respondable`). So the requirement is dropped here, which keeps registration runnable on its own.\n */\nconst addRoute = (\n app: EffectRouter,\n method: Parameters<EffectRouter['add']>[0],\n path: `/${string}`,\n handler: (\n request: HttpServerRequest.HttpServerRequest,\n ) => Effect.Effect<HttpServerResponse.HttpServerResponse, MastraRouteError, Scope.Scope | HttpRouter.RouteContext>,\n): Effect.Effect<void> => app.add(method, path, handler) as Effect.Effect<void>;\n\n/**\n * The Effect span of the HTTP request a Mastra tool or workflow step is running for, when the app\n * has a tracer installed. `undefined` otherwise, and for a workflow resumed by a later request.\n */\nexport function requestSpan(requestContext: RequestContext | undefined): Tracer.AnySpan | undefined {\n const getSpan = requestContext?.get(REQUEST_SPAN_KEY);\n return typeof getSpan === 'function' ? (getSpan as () => Tracer.AnySpan)() : undefined;\n}\n\n/**\n * Runs an Effect from inside a Mastra tool or workflow step.\n *\n * Mastra runs tools and steps as plain promises, outside the Effect request that triggered them.\n * This gives the Effect the services `runtime` provides, makes the HTTP request's span its parent,\n * and interrupts it when Mastra's abort signal fires. Pass the context Mastra hands the tool or\n * step — it carries both the request context and that signal:\n *\n * ```ts\n * execute: (input, context) => runInRequest(runtime, Users.find(input.id), context)\n * ```\n *\n * Two things follow from running through `runtime` rather than the request:\n *\n * - The signal is whatever Mastra hands over. For a tool an agent calls, it aborts when the client\n * leaves. A workflow step's is the run's own: it aborts when the run is cancelled, not when the\n * request that started it is abandoned.\n * - The spans the Effect creates are made by `runtime`'s tracer. Give the runtime the app's tracer\n * as well, or they have the right parent but are never exported.\n */\nexport function runInRequest<A, E, R>(\n runtime: ManagedRuntime.ManagedRuntime<R, never>,\n effect: Effect.Effect<A, E, R>,\n context: { readonly requestContext?: RequestContext; readonly abortSignal?: AbortSignal } = {},\n): Promise<A> {\n const span = requestSpan(context.requestContext);\n // The parent is set explicitly. With @effect/opentelemetry and a context manager, an Effect span\n // started here should join the active OpenTelemetry span by itself, but it starts a new trace\n // instead: https://github.com/Effect-TS/effect/issues/8489. Once fixed, this still matters for apps\n // without a context manager, so it stays.\n return runtime.runPromise(span ? Effect.withParentSpan(effect, span) : effect, { signal: context.abortSignal });\n}\n\n/**\n * Turns a populated router into a fetch-compatible handler plus its teardown.\n *\n * Effect logs every request it answers; pass `{ disableLogger: true }` to stop that. `dispose`\n * releases what the handler holds, not Mastra. For a tracer or other services, call\n * `HttpRouter.toWebHandler` with your own layer merged in.\n */\nexport const toWebHandler = (router: EffectRouter, options?: { readonly disableLogger?: boolean }) =>\n HttpRouter.toWebHandler(Layer.succeed(HttpRouter.HttpRouter)(router), options);\n\n/** Everything `MastraServer` takes except the router, which `createMastraServer` builds for you. */\nexport type CreateMastraServerOptions = Omit<MastraServerOptions, 'app'>;\n\n/** Convenience wrapper: build a router, register every Mastra route onto it, return both. */\nexport async function createMastraServer(\n options: CreateMastraServerOptions,\n): Promise<{ router: EffectRouter; adapter: MastraServer }> {\n const router = createRouter();\n const adapter = new MastraServer({ ...options, app: router });\n await adapter.init();\n return { router, adapter };\n}\n\nexport type { ToolsInput };\n"],"mappings":"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"}
@@ -0,0 +1,222 @@
1
+ import { ToolsInput } from "@mastra/core/agent";
2
+ import { RequestContext } from "@mastra/core/request-context";
3
+ import { MastraServer as MastraServer$1, ParsedRequestParams, ServerRoute } from "@mastra/server/server-adapter";
4
+ import { Effect, ManagedRuntime, Tracer } from "effect";
5
+ import { FindMyWay, HttpRouter, HttpServerRespondable, HttpServerResponse } from "effect/unstable/http";
6
+ //#region src/index.d.ts
7
+ export type EffectRouter = HttpRouter.HttpRouter;
8
+ /**
9
+ * Everything the adapter threads through a single request.
10
+ *
11
+ * Bound to both `TRequest` and `TResponse`. Effect has no mutable response object, and the base
12
+ * class never calls `sendResponse` itself — this adapter does, from `handleRoute` — so the slot
13
+ * carries the request-side data `sendResponse` needs, the same way `@mastra/elysia` carries its
14
+ * framework context there. Passing an explicit object (rather than stashing properties on the
15
+ * `Request`) keeps `getParams` and `sendResponse` honest when called outside `handleRoute`.
16
+ */
17
+ export interface EffectRequestContext {
18
+ readonly request: Request;
19
+ readonly pathParams: Record<string, string>;
20
+ /** Parsed once per request so nothing downstream re-reads the body. */
21
+ readonly body: unknown;
22
+ readonly bodyParseError?: {
23
+ message: string;
24
+ };
25
+ /** The request's Mastra context, which MCP transports need to learn who the caller is. */
26
+ readonly requestContext?: RequestContext;
27
+ }
28
+ declare const MastraRouteError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
29
+ readonly _tag: "MastraRouteError";
30
+ } & Readonly<A>;
31
+ /**
32
+ * A Mastra route that failed with a server error (5xx), surfaced in Effect's error channel.
33
+ *
34
+ * It answers with exactly the response Mastra built, so clients see no difference. What changes is
35
+ * what the app's Effect code sees: middleware can `catchTag('MastraRouteError')`, Effect's logger
36
+ * records the cause, and a tracer marks the request span as failed. 4xx answers stay successes —
37
+ * a refused or invalid request is not a server failure.
38
+ */
39
+ export declare class MastraRouteError extends MastraRouteError_base<{
40
+ /** The HTTP status Mastra answered with. */
41
+ readonly status: number;
42
+ /**
43
+ * What the route threw. When Mastra answered the failure before it reached the adapter — a custom
44
+ * route's handler, whose errors Mastra's own sub-app answers, or an MCP transport that failed to
45
+ * start — an `Error` naming the request and the status instead.
46
+ */
47
+ readonly cause: unknown;
48
+ /** The response Mastra built for it, sent unchanged. */
49
+ readonly response: HttpServerResponse.HttpServerResponse;
50
+ }> implements HttpServerRespondable.Respondable {
51
+ get message(): string;
52
+ [HttpServerRespondable.symbol](): Effect.Effect<HttpServerResponse.HttpServerResponse, never, never>;
53
+ }
54
+ /** Everything `new MastraServer(...)` takes: Mastra's adapter options, plus this adapter's own. */
55
+ export type MastraServerOptions = ConstructorParameters<typeof MastraServer$1<EffectRouter, EffectRequestContext, EffectRequestContext>>[0] & {
56
+ /**
57
+ * Fail the Effect request with a `MastraRouteError` when a route answers with a server error
58
+ * (5xx), so the app's Effect error handling, logs and traces see it. The response is the same
59
+ * either way. Defaults to `true`.
60
+ */
61
+ readonly errorChannel?: boolean;
62
+ };
63
+ /**
64
+ * Mastra server adapter for Effect's HTTP layer.
65
+ *
66
+ * `TApp` is a live `HttpRouter` service instance rather than a Layer: Mastra's `registerRoutes()`
67
+ * awaits ~400 sequential registrations against one fixed app, which the Layer-based
68
+ * `HttpRouter.add` cannot express but the service's effectful `add` can.
69
+ */
70
+ export declare class MastraServer extends MastraServer$1<EffectRouter, EffectRequestContext, EffectRequestContext> {
71
+ private contextMiddleware?;
72
+ private readonly errorChannel;
73
+ constructor(options: MastraServerOptions);
74
+ /**
75
+ * Builds the per-request Mastra context. Effect has no `derive`, so routes call this directly.
76
+ *
77
+ * `parsedBody` lets the request path reuse the body it already parsed; omitting it (as the
78
+ * multipart conformance suite does, fetching this middleware standalone) falls back to reading.
79
+ */
80
+ createContextMiddleware(): (request: Request, parsedBody?: unknown) => Promise<RequestContext>;
81
+ getParams(route: ServerRoute, ctx: EffectRequestContext): Promise<ParsedRequestParams>;
82
+ /**
83
+ * Reads and parses the body exactly once.
84
+ *
85
+ * `oversize` rather than a body when the limit is blown: a chunked request declares no
86
+ * `Content-Length`, so counting what actually arrives is the only way to enforce the cap — for
87
+ * uploads as much as for JSON.
88
+ */
89
+ private readBody;
90
+ /** Files arrive as Node Buffers; the conformance multipart suite asserts `Buffer.isBuffer`. */
91
+ private parseFormData;
92
+ stream(route: ServerRoute, _ctx: EffectRequestContext, result: {
93
+ fullStream: ReadableStream;
94
+ }): Promise<Response>;
95
+ sendResponse(route: ServerRoute, ctx: EffectRequestContext, result: unknown, prefix?: string): Promise<Response>;
96
+ /**
97
+ * Runs an MCP transport against a replayable copy of the request and returns what it wrote to the
98
+ * Node response object.
99
+ *
100
+ * `start` is deliberately not awaited: it resolves when the body finishes, while `toFetchResponse`
101
+ * resolves once headers are sent. Awaiting here would stall SSE.
102
+ */
103
+ private bridgeMcpTransport;
104
+ /**
105
+ * Never rejects — the caller runs it through `Effect.promise`. A route that threw is still answered
106
+ * with a response; what it threw is kept in `routeErrors` for `toEffectOutcome`.
107
+ */
108
+ private handleRoute;
109
+ private exceedsDeclaredLimit;
110
+ private bodyLimitResponse;
111
+ /**
112
+ * Mastra's RBAC check, which only applies once an auth provider is configured.
113
+ *
114
+ * `hasPermission` lives in an enterprise-only module, so `loadHasPermission` can come back empty;
115
+ * the request then proceeds unchecked rather than being refused.
116
+ */
117
+ private resolvePermissionError;
118
+ private validateParams;
119
+ private toErrorResponse;
120
+ registerRoute(app: EffectRouter, route: ServerRoute, { prefix: prefixParam }?: {
121
+ prefix?: string;
122
+ }): Promise<void>;
123
+ registerCustomApiRoutes(): Promise<void>;
124
+ /**
125
+ * An Effect route handler around one of the adapter's Web entry points. The entry point gets the
126
+ * request with a body it can read and a signal that aborts if the client leaves, plus the path
127
+ * parameters and the request's span; what it answers becomes the Effect result.
128
+ */
129
+ private toEffectHandler;
130
+ private handleCustomRoute;
131
+ /**
132
+ * The Effect result of a route: its response, or — for a server error (5xx), when the error
133
+ * channel is on — a `MastraRouteError` that answers with that same response.
134
+ *
135
+ * Decided by the status, not by whether the adapter caught something: not every server error
136
+ * passes through its `catch`. A custom route's handler that throws is answered by Mastra's own
137
+ * sub-app, and an MCP transport that fails to start writes its 500 itself.
138
+ */
139
+ private toEffectOutcome;
140
+ /**
141
+ * Converts a Web response for Effect to send, keeping every `Set-Cookie`.
142
+ *
143
+ * `HttpServerResponse.fromWeb` files cookies by name, so two sharing a name — one cleared at two
144
+ * paths, say — collapse into the last, and one that cannot be serialised fails the response when
145
+ * it is sent, leaving the client waiting. Each cookie gets its own entry here instead, and one
146
+ * that could never be sent is dropped with a warning. Attributes Effect does not model (anything
147
+ * but Domain, Path, Expires, Max-Age, HttpOnly, Secure, SameSite, Priority, Partitioned) are lost.
148
+ */
149
+ private toEffectResponse;
150
+ /**
151
+ * Context is built per route because Effect has no `derive`-style request hook. What does need
152
+ * every request, matched or not, is the warning for a channel webhook nobody registered, which
153
+ * shows up as a 404 no route would ever log — so it runs here, as in `@mastra/hono`.
154
+ *
155
+ * It wraps the app's own routes too, since they share the router, so it steps aside at once for
156
+ * anything that cannot be such a webhook: every method but POST, and a target with no parsable path.
157
+ */
158
+ registerContextMiddleware(): void;
159
+ /** Auth is resolved per route, matching every other adapter. */
160
+ registerAuthMiddleware(): void;
161
+ registerHttpLoggingMiddleware(): void;
162
+ }
163
+ /**
164
+ * Creates an empty router to hand to `new MastraServer({ app })`.
165
+ *
166
+ * `ignoreDuplicateSlashes` is forced off because Effect's vendored FindMyWay defaults it on
167
+ * (FindMyWay/internal/router.ts:54), unlike stock find-my-way. Left on, `/api//agents` would serve
168
+ * `/api/agents` instead of 404ing, which diverges from every other Mastra adapter.
169
+ */
170
+ export declare const createRouter: (config?: Partial<FindMyWay.RouterConfig>) => EffectRouter;
171
+ /**
172
+ * The Effect span of the HTTP request a Mastra tool or workflow step is running for, when the app
173
+ * has a tracer installed. `undefined` otherwise, and for a workflow resumed by a later request.
174
+ */
175
+ export declare function requestSpan(requestContext: RequestContext | undefined): Tracer.AnySpan | undefined;
176
+ /**
177
+ * Runs an Effect from inside a Mastra tool or workflow step.
178
+ *
179
+ * Mastra runs tools and steps as plain promises, outside the Effect request that triggered them.
180
+ * This gives the Effect the services `runtime` provides, makes the HTTP request's span its parent,
181
+ * and interrupts it when Mastra's abort signal fires. Pass the context Mastra hands the tool or
182
+ * step — it carries both the request context and that signal:
183
+ *
184
+ * ```ts
185
+ * execute: (input, context) => runInRequest(runtime, Users.find(input.id), context)
186
+ * ```
187
+ *
188
+ * Two things follow from running through `runtime` rather than the request:
189
+ *
190
+ * - The signal is whatever Mastra hands over. For a tool an agent calls, it aborts when the client
191
+ * leaves. A workflow step's is the run's own: it aborts when the run is cancelled, not when the
192
+ * request that started it is abandoned.
193
+ * - The spans the Effect creates are made by `runtime`'s tracer. Give the runtime the app's tracer
194
+ * as well, or they have the right parent but are never exported.
195
+ */
196
+ export declare function runInRequest<A, E, R>(runtime: ManagedRuntime.ManagedRuntime<R, never>, effect: Effect.Effect<A, E, R>, context?: {
197
+ readonly requestContext?: RequestContext;
198
+ readonly abortSignal?: AbortSignal;
199
+ }): Promise<A>;
200
+ /**
201
+ * Turns a populated router into a fetch-compatible handler plus its teardown.
202
+ *
203
+ * Effect logs every request it answers; pass `{ disableLogger: true }` to stop that. `dispose`
204
+ * releases what the handler holds, not Mastra. For a tracer or other services, call
205
+ * `HttpRouter.toWebHandler` with your own layer merged in.
206
+ */
207
+ export declare const toWebHandler: (router: EffectRouter, options?: {
208
+ readonly disableLogger?: boolean;
209
+ }) => {
210
+ readonly handler: (request: globalThis.Request, context?: import("effect/Context").Context<never> | undefined) => Promise<Response>;
211
+ readonly dispose: () => Promise<void>;
212
+ };
213
+ /** Everything `MastraServer` takes except the router, which `createMastraServer` builds for you. */
214
+ export type CreateMastraServerOptions = Omit<MastraServerOptions, 'app'>;
215
+ /** Convenience wrapper: build a router, register every Mastra route onto it, return both. */
216
+ export declare function createMastraServer(options: CreateMastraServerOptions): Promise<{
217
+ router: EffectRouter;
218
+ adapter: MastraServer;
219
+ }>;
220
+ //#endregion
221
+ export type { ToolsInput };
222
+ //# sourceMappingURL=index.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;;YA2BY,eAAe,WAAW;;;;;;;;;;iBAWrB;WACN,SAAS;WACT,YAAY;;WAEZ;WACA;IAAmB;;;WAEnB,iBAAiB;;;;;;;;;;;;;qBAkNf,yBACH;;WAEG;;;;;;WAMA;;WAEA,UAAU,mBAAmB;cAE7B,sBAAsB;MAEpB;GAIZ,sBAAsB,WAAO,OAAA,OAAA,mBAAA;;;YAiBpB,sBAAsB,6BACzB,eAAiB,cAAc,sBAAsB;;;;;;WAOnD;;;;;;;;;qBAUE,qBAAqB,eAAiB,cAAc,sBAAsB;UAC7E;mBACS;EAEjB,YAAY,SAAS;;;;;;;EAYrB,4BAA4B,SAAS,SAAS,yBAAyB,QAAQ;EA+CzE,UAAU,OAAO,aAAa,KAAK,uBAAuB,QAAQ;;;;;;;;UAqB1D;;UAuCA;EAgBR,OACJ,OAAO,aACP,MAAM,sBACN;IAAU,YAAY;MACrB,QAAQ;EAmFL,aACJ,OAAO,aACP,KAAK,sBACL,iBACA,kBACC,QAAQ;;;;;;;;UA0EG;;;;;UAuCA;UAmGN;UAMA;;;;;;;UAmBM;UAaA;UAwDN;EAgCF,cACJ,KAAK,cACL,OAAO,eACL,QAAQ;IAAiB;MAC1B;EAcG,2BAA2B;;;;;;UAezB;UAiBM;;;;;;;;;UA6FN;;;;;;;;;;UAqBA;;;;;;;;;EA8BR;;EAkBA;EAEA;;;;;;;;;qBAwNW,eAAY,SAAa,QAAQ,UAAU,kBAAgB;;;;;wBA4BxD,YAAY,gBAAgB,6BAA6B,OAAO;;;;;;;;;;;;;;;;;;;;;wBAyBhE,aAAa,GAAG,GAAG,GACjC,SAAS,eAAe,eAAe,WACvC,QAAQ,OAAO,OAAO,GAAG,GAAG,IAC5B;WAAoB,iBAAiB;WAAyB,cAAc;IAC3E,QAAQ;;;;;;;;qBAgBE,eAAY,QAAY,cAAY;WAAuB;;;;;;YAI5D,4BAA4B,KAAK;;wBAGvB,mBACpB,SAAS,4BACR;EAAU,QAAQ;EAAc,SAAS"}
@@ -0,0 +1,222 @@
1
+ import { MastraServer as MastraServer$1, ParsedRequestParams, ServerRoute } from "@mastra/server/server-adapter";
2
+ import { Effect, ManagedRuntime, Tracer } from "effect";
3
+ import { FindMyWay, HttpRouter, HttpServerRespondable, HttpServerResponse } from "effect/unstable/http";
4
+ import { ToolsInput } from "@mastra/core/agent";
5
+ import { RequestContext } from "@mastra/core/request-context";
6
+ //#region src/index.d.ts
7
+ export type EffectRouter = HttpRouter.HttpRouter;
8
+ /**
9
+ * Everything the adapter threads through a single request.
10
+ *
11
+ * Bound to both `TRequest` and `TResponse`. Effect has no mutable response object, and the base
12
+ * class never calls `sendResponse` itself — this adapter does, from `handleRoute` — so the slot
13
+ * carries the request-side data `sendResponse` needs, the same way `@mastra/elysia` carries its
14
+ * framework context there. Passing an explicit object (rather than stashing properties on the
15
+ * `Request`) keeps `getParams` and `sendResponse` honest when called outside `handleRoute`.
16
+ */
17
+ export interface EffectRequestContext {
18
+ readonly request: Request;
19
+ readonly pathParams: Record<string, string>;
20
+ /** Parsed once per request so nothing downstream re-reads the body. */
21
+ readonly body: unknown;
22
+ readonly bodyParseError?: {
23
+ message: string;
24
+ };
25
+ /** The request's Mastra context, which MCP transports need to learn who the caller is. */
26
+ readonly requestContext?: RequestContext;
27
+ }
28
+ declare const MastraRouteError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
29
+ readonly _tag: "MastraRouteError";
30
+ } & Readonly<A>;
31
+ /**
32
+ * A Mastra route that failed with a server error (5xx), surfaced in Effect's error channel.
33
+ *
34
+ * It answers with exactly the response Mastra built, so clients see no difference. What changes is
35
+ * what the app's Effect code sees: middleware can `catchTag('MastraRouteError')`, Effect's logger
36
+ * records the cause, and a tracer marks the request span as failed. 4xx answers stay successes —
37
+ * a refused or invalid request is not a server failure.
38
+ */
39
+ export declare class MastraRouteError extends MastraRouteError_base<{
40
+ /** The HTTP status Mastra answered with. */
41
+ readonly status: number;
42
+ /**
43
+ * What the route threw. When Mastra answered the failure before it reached the adapter — a custom
44
+ * route's handler, whose errors Mastra's own sub-app answers, or an MCP transport that failed to
45
+ * start — an `Error` naming the request and the status instead.
46
+ */
47
+ readonly cause: unknown;
48
+ /** The response Mastra built for it, sent unchanged. */
49
+ readonly response: HttpServerResponse.HttpServerResponse;
50
+ }> implements HttpServerRespondable.Respondable {
51
+ get message(): string;
52
+ [HttpServerRespondable.symbol](): Effect.Effect<HttpServerResponse.HttpServerResponse, never, never>;
53
+ }
54
+ /** Everything `new MastraServer(...)` takes: Mastra's adapter options, plus this adapter's own. */
55
+ export type MastraServerOptions = ConstructorParameters<typeof MastraServer$1<EffectRouter, EffectRequestContext, EffectRequestContext>>[0] & {
56
+ /**
57
+ * Fail the Effect request with a `MastraRouteError` when a route answers with a server error
58
+ * (5xx), so the app's Effect error handling, logs and traces see it. The response is the same
59
+ * either way. Defaults to `true`.
60
+ */
61
+ readonly errorChannel?: boolean;
62
+ };
63
+ /**
64
+ * Mastra server adapter for Effect's HTTP layer.
65
+ *
66
+ * `TApp` is a live `HttpRouter` service instance rather than a Layer: Mastra's `registerRoutes()`
67
+ * awaits ~400 sequential registrations against one fixed app, which the Layer-based
68
+ * `HttpRouter.add` cannot express but the service's effectful `add` can.
69
+ */
70
+ export declare class MastraServer extends MastraServer$1<EffectRouter, EffectRequestContext, EffectRequestContext> {
71
+ private contextMiddleware?;
72
+ private readonly errorChannel;
73
+ constructor(options: MastraServerOptions);
74
+ /**
75
+ * Builds the per-request Mastra context. Effect has no `derive`, so routes call this directly.
76
+ *
77
+ * `parsedBody` lets the request path reuse the body it already parsed; omitting it (as the
78
+ * multipart conformance suite does, fetching this middleware standalone) falls back to reading.
79
+ */
80
+ createContextMiddleware(): (request: Request, parsedBody?: unknown) => Promise<RequestContext>;
81
+ getParams(route: ServerRoute, ctx: EffectRequestContext): Promise<ParsedRequestParams>;
82
+ /**
83
+ * Reads and parses the body exactly once.
84
+ *
85
+ * `oversize` rather than a body when the limit is blown: a chunked request declares no
86
+ * `Content-Length`, so counting what actually arrives is the only way to enforce the cap — for
87
+ * uploads as much as for JSON.
88
+ */
89
+ private readBody;
90
+ /** Files arrive as Node Buffers; the conformance multipart suite asserts `Buffer.isBuffer`. */
91
+ private parseFormData;
92
+ stream(route: ServerRoute, _ctx: EffectRequestContext, result: {
93
+ fullStream: ReadableStream;
94
+ }): Promise<Response>;
95
+ sendResponse(route: ServerRoute, ctx: EffectRequestContext, result: unknown, prefix?: string): Promise<Response>;
96
+ /**
97
+ * Runs an MCP transport against a replayable copy of the request and returns what it wrote to the
98
+ * Node response object.
99
+ *
100
+ * `start` is deliberately not awaited: it resolves when the body finishes, while `toFetchResponse`
101
+ * resolves once headers are sent. Awaiting here would stall SSE.
102
+ */
103
+ private bridgeMcpTransport;
104
+ /**
105
+ * Never rejects — the caller runs it through `Effect.promise`. A route that threw is still answered
106
+ * with a response; what it threw is kept in `routeErrors` for `toEffectOutcome`.
107
+ */
108
+ private handleRoute;
109
+ private exceedsDeclaredLimit;
110
+ private bodyLimitResponse;
111
+ /**
112
+ * Mastra's RBAC check, which only applies once an auth provider is configured.
113
+ *
114
+ * `hasPermission` lives in an enterprise-only module, so `loadHasPermission` can come back empty;
115
+ * the request then proceeds unchecked rather than being refused.
116
+ */
117
+ private resolvePermissionError;
118
+ private validateParams;
119
+ private toErrorResponse;
120
+ registerRoute(app: EffectRouter, route: ServerRoute, { prefix: prefixParam }?: {
121
+ prefix?: string;
122
+ }): Promise<void>;
123
+ registerCustomApiRoutes(): Promise<void>;
124
+ /**
125
+ * An Effect route handler around one of the adapter's Web entry points. The entry point gets the
126
+ * request with a body it can read and a signal that aborts if the client leaves, plus the path
127
+ * parameters and the request's span; what it answers becomes the Effect result.
128
+ */
129
+ private toEffectHandler;
130
+ private handleCustomRoute;
131
+ /**
132
+ * The Effect result of a route: its response, or — for a server error (5xx), when the error
133
+ * channel is on — a `MastraRouteError` that answers with that same response.
134
+ *
135
+ * Decided by the status, not by whether the adapter caught something: not every server error
136
+ * passes through its `catch`. A custom route's handler that throws is answered by Mastra's own
137
+ * sub-app, and an MCP transport that fails to start writes its 500 itself.
138
+ */
139
+ private toEffectOutcome;
140
+ /**
141
+ * Converts a Web response for Effect to send, keeping every `Set-Cookie`.
142
+ *
143
+ * `HttpServerResponse.fromWeb` files cookies by name, so two sharing a name — one cleared at two
144
+ * paths, say — collapse into the last, and one that cannot be serialised fails the response when
145
+ * it is sent, leaving the client waiting. Each cookie gets its own entry here instead, and one
146
+ * that could never be sent is dropped with a warning. Attributes Effect does not model (anything
147
+ * but Domain, Path, Expires, Max-Age, HttpOnly, Secure, SameSite, Priority, Partitioned) are lost.
148
+ */
149
+ private toEffectResponse;
150
+ /**
151
+ * Context is built per route because Effect has no `derive`-style request hook. What does need
152
+ * every request, matched or not, is the warning for a channel webhook nobody registered, which
153
+ * shows up as a 404 no route would ever log — so it runs here, as in `@mastra/hono`.
154
+ *
155
+ * It wraps the app's own routes too, since they share the router, so it steps aside at once for
156
+ * anything that cannot be such a webhook: every method but POST, and a target with no parsable path.
157
+ */
158
+ registerContextMiddleware(): void;
159
+ /** Auth is resolved per route, matching every other adapter. */
160
+ registerAuthMiddleware(): void;
161
+ registerHttpLoggingMiddleware(): void;
162
+ }
163
+ /**
164
+ * Creates an empty router to hand to `new MastraServer({ app })`.
165
+ *
166
+ * `ignoreDuplicateSlashes` is forced off because Effect's vendored FindMyWay defaults it on
167
+ * (FindMyWay/internal/router.ts:54), unlike stock find-my-way. Left on, `/api//agents` would serve
168
+ * `/api/agents` instead of 404ing, which diverges from every other Mastra adapter.
169
+ */
170
+ export declare const createRouter: (config?: Partial<FindMyWay.RouterConfig>) => EffectRouter;
171
+ /**
172
+ * The Effect span of the HTTP request a Mastra tool or workflow step is running for, when the app
173
+ * has a tracer installed. `undefined` otherwise, and for a workflow resumed by a later request.
174
+ */
175
+ export declare function requestSpan(requestContext: RequestContext | undefined): Tracer.AnySpan | undefined;
176
+ /**
177
+ * Runs an Effect from inside a Mastra tool or workflow step.
178
+ *
179
+ * Mastra runs tools and steps as plain promises, outside the Effect request that triggered them.
180
+ * This gives the Effect the services `runtime` provides, makes the HTTP request's span its parent,
181
+ * and interrupts it when Mastra's abort signal fires. Pass the context Mastra hands the tool or
182
+ * step — it carries both the request context and that signal:
183
+ *
184
+ * ```ts
185
+ * execute: (input, context) => runInRequest(runtime, Users.find(input.id), context)
186
+ * ```
187
+ *
188
+ * Two things follow from running through `runtime` rather than the request:
189
+ *
190
+ * - The signal is whatever Mastra hands over. For a tool an agent calls, it aborts when the client
191
+ * leaves. A workflow step's is the run's own: it aborts when the run is cancelled, not when the
192
+ * request that started it is abandoned.
193
+ * - The spans the Effect creates are made by `runtime`'s tracer. Give the runtime the app's tracer
194
+ * as well, or they have the right parent but are never exported.
195
+ */
196
+ export declare function runInRequest<A, E, R>(runtime: ManagedRuntime.ManagedRuntime<R, never>, effect: Effect.Effect<A, E, R>, context?: {
197
+ readonly requestContext?: RequestContext;
198
+ readonly abortSignal?: AbortSignal;
199
+ }): Promise<A>;
200
+ /**
201
+ * Turns a populated router into a fetch-compatible handler plus its teardown.
202
+ *
203
+ * Effect logs every request it answers; pass `{ disableLogger: true }` to stop that. `dispose`
204
+ * releases what the handler holds, not Mastra. For a tracer or other services, call
205
+ * `HttpRouter.toWebHandler` with your own layer merged in.
206
+ */
207
+ export declare const toWebHandler: (router: EffectRouter, options?: {
208
+ readonly disableLogger?: boolean;
209
+ }) => {
210
+ readonly handler: (request: globalThis.Request, context?: import("effect/Context").Context<never> | undefined) => Promise<Response>;
211
+ readonly dispose: () => Promise<void>;
212
+ };
213
+ /** Everything `MastraServer` takes except the router, which `createMastraServer` builds for you. */
214
+ export type CreateMastraServerOptions = Omit<MastraServerOptions, 'app'>;
215
+ /** Convenience wrapper: build a router, register every Mastra route onto it, return both. */
216
+ export declare function createMastraServer(options: CreateMastraServerOptions): Promise<{
217
+ router: EffectRouter;
218
+ adapter: MastraServer;
219
+ }>;
220
+ //#endregion
221
+ export type { ToolsInput };
222
+ //# sourceMappingURL=index.d.ts.map