@mastra/mcp-docs-server 1.2.19-alpha.0 → 1.2.19-alpha.14

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.
Files changed (76) hide show
  1. package/.docs/docs/channels.md +1 -0
  2. package/.docs/docs/deployment/mastra-server.md +19 -0
  3. package/.docs/docs/deployment/workers.md +2 -2
  4. package/.docs/docs/evals/built-in-scorers.md +1 -0
  5. package/.docs/docs/evals/multi-turn.md +84 -1
  6. package/.docs/docs/evals/overview.md +63 -1
  7. package/.docs/docs/harness/durable-agents.md +1 -1
  8. package/.docs/docs/mastra-platform/deploy.md +101 -0
  9. package/.docs/docs/mastra-platform/server.md +6 -11
  10. package/.docs/docs/mastra-platform/studio.md +8 -10
  11. package/.docs/docs/observability/integrations/exporters/mastra-storage.md +19 -14
  12. package/.docs/docs/sandbox/filesystem.md +8 -8
  13. package/.docs/docs/sandbox/overview.md +33 -66
  14. package/.docs/docs/server/middleware.md +4 -0
  15. package/.docs/docs/server/server-adapters.md +12 -8
  16. package/.docs/docs/storage.md +2 -0
  17. package/.docs/integrations/channels/imessage.md +150 -8
  18. package/.docs/integrations/databases/elasticsearch.md +156 -0
  19. package/.docs/integrations/databases/libsql.md +16 -0
  20. package/.docs/integrations/databases/mongodb.md +1 -1
  21. package/.docs/integrations/databases/postgresql.md +26 -0
  22. package/.docs/integrations/databases/valkey.md +99 -0
  23. package/.docs/integrations/deploy/render.md +47 -61
  24. package/.docs/integrations/sandboxes/e2b.md +2 -0
  25. package/.docs/integrations/tools/parallel.md +240 -0
  26. package/.docs/integrations.md +3 -0
  27. package/.docs/models/environment-variables.md +2 -0
  28. package/.docs/models/gateways/merge-gateway.md +2 -1
  29. package/.docs/models/gateways/netlify.md +10 -5
  30. package/.docs/models/gateways/openrouter.md +8 -9
  31. package/.docs/models/gateways/vercel.md +7 -6
  32. package/.docs/models/index.md +1 -1
  33. package/.docs/models/providers/agentrouter.md +17 -34
  34. package/.docs/models/providers/aki-io.md +14 -13
  35. package/.docs/models/providers/chutes.md +2 -2
  36. package/.docs/models/providers/crof.md +3 -8
  37. package/.docs/models/providers/crossmodel.md +56 -55
  38. package/.docs/models/providers/deepseek.md +8 -7
  39. package/.docs/models/providers/digitalocean.md +11 -11
  40. package/.docs/models/providers/edenai.md +14 -14
  41. package/.docs/models/providers/google.md +3 -3
  42. package/.docs/models/providers/hyper.md +7 -7
  43. package/.docs/models/providers/inceptron.md +1 -1
  44. package/.docs/models/providers/kilo.md +24 -20
  45. package/.docs/models/providers/llmgateway-providers.md +17 -8
  46. package/.docs/models/providers/llmgateway.md +3 -5
  47. package/.docs/models/providers/nano-gpt.md +24 -13
  48. package/.docs/models/providers/nvidia.md +3 -1
  49. package/.docs/models/providers/ofox.md +114 -110
  50. package/.docs/models/providers/opencode-go.md +26 -23
  51. package/.docs/models/providers/opencode.md +1 -1
  52. package/.docs/models/providers/opper.md +112 -0
  53. package/.docs/models/providers/requesty.md +1 -1
  54. package/.docs/models/providers/scaleway.md +2 -1
  55. package/.docs/models/providers.md +2 -0
  56. package/.docs/reference/agents/channels.md +1 -1
  57. package/.docs/reference/ai-sdk/handle-chat-stream.md +11 -0
  58. package/.docs/reference/ai-sdk/with-sse-heartbeat.md +47 -0
  59. package/.docs/reference/core/mastra-class.md +1 -1
  60. package/.docs/reference/evals/checks.md +6 -0
  61. package/.docs/reference/evals/multi-turn-judge.md +101 -0
  62. package/.docs/reference/index.md +4 -0
  63. package/.docs/reference/pubsub/valkey-streams.md +84 -0
  64. package/.docs/reference/rag/vector-databases.md +4 -4
  65. package/.docs/reference/server/express-adapter.md +6 -8
  66. package/.docs/reference/server/hono-adapter.md +19 -6
  67. package/.docs/reference/storage/turso.md +88 -0
  68. package/.docs/reference/streaming/ChunkType.md +29 -1
  69. package/.docs/reference/streaming/agents/stream.md +1 -3
  70. package/.docs/reference/tools/mcp-client.md +41 -9
  71. package/.docs/reference/vectors/mongodb.md +11 -11
  72. package/.docs/reference/vectors/pg.md +2 -0
  73. package/.docs/reference/workspace/sandbox.md +29 -1
  74. package/.docs/reference/workspace/workspace-class.md +15 -3
  75. package/CHANGELOG.md +66 -0
  76. package/package.json +5 -5
@@ -7,7 +7,7 @@ A filesystem gives an agent tools for reading, writing, listing, and [searching]
7
7
  Configure files in two ways:
8
8
 
9
9
  - [Direct filesystem access](#direct-filesystem-access) uses `filesystem` with one filesystem provider or a [`CompositeFilesystem`](#manual-composition) that you create yourself.
10
- - [Mounts](#mounts) uses `mounts` to create a `CompositeFilesystem` from path-prefixed providers. When the workspace also has a static sandbox, Mastra attempts to mount each provider at its configured path inside the sandbox so commands can access the same files. Remote sandbox mounts typically use Filesystem in Userspace (FUSE).
10
+ - [Mounts](#mounts) uses `mounts` to create a `CompositeFilesystem` from path-prefixed providers. When a static sandbox and filesystem provider support mounting, Mastra automatically mounts the provider at its configured path. Remote sandbox mounts typically use Filesystem in Userspace (FUSE).
11
11
 
12
12
  Configure either `filesystem` or `mounts`, not both. Configuring both throws a `WorkspaceError` with the code `INVALID_CONFIG`.
13
13
 
@@ -71,7 +71,7 @@ See [Search](https://mastra.ai/docs/sandbox/search) to index the files for keywo
71
71
 
72
72
  ## Mounts
73
73
 
74
- Use `mounts` when programs inside a sandbox need to access persistent files by path. The agent still receives file tools, while command tools can run commands such as `ls`, `cat`, or `python` against the same files.
74
+ Use `mounts` when programs inside a sandbox need to access persistent files by path. Mastra creates the mount automatically when the sandbox and filesystem provider support it. The agent still receives file tools, while command tools can run commands such as `ls`, `cat`, or `python` against the same files.
75
75
 
76
76
  For example, mount an S3 bucket at `/workspace` inside a Daytona sandbox:
77
77
 
@@ -180,7 +180,7 @@ Use manual composition when another part of your application needs the composite
180
180
 
181
181
  ## Mount availability
182
182
 
183
- File tools and composite routing work without FUSE. Remote sandboxes can use FUSE to make cloud storage visible to commands. `LocalSandbox` uses symlinks instead.
183
+ File tools and composite routing work without a sandbox mount. When a remote sandbox and filesystem provider support mounting, `mounts` automatically uses FUSE to make the files visible to commands. `LocalSandbox` uses symlinks instead.
184
184
 
185
185
  Built-in sandbox mounting currently includes:
186
186
 
@@ -195,27 +195,27 @@ Remote mounts may require `s3fs`, `gcsfuse`, or `blobfuse2` inside the sandbox.
195
195
 
196
196
  If a sandbox mount is unavailable or fails, the workspace remains usable. File tools continue to access the provider through its SDK, but commands can't see that path. Mastra describes these providers to the agent as available through file tools only.
197
197
 
198
- ## Multi-tenant filesystems
198
+ ## Filesystems per user or thread
199
199
 
200
200
  The `filesystem` option accepts a resolver when storage should vary by request, user, role, or tenant:
201
201
 
202
202
  ```typescript
203
203
  const workspace = new Workspace({
204
204
  filesystem: ({ requestContext }) => {
205
- const tenantId = requestContext.get('tenant-id') as string
205
+ const userId = requestContext.get('user-id') as string
206
206
 
207
207
  return new S3Filesystem({
208
208
  bucket: process.env.S3_BUCKET!,
209
209
  region: process.env.S3_REGION!,
210
- prefix: `tenants/${tenantId}`,
210
+ prefix: `users/${userId}`,
211
211
  })
212
212
  },
213
213
  })
214
214
  ```
215
215
 
216
- Each tenant gets its own filesystem view, and file tools resolve the provider from the request context automatically.
216
+ Each user gets a separate filesystem view, and file tools resolve the provider from the request context automatically.
217
217
 
218
- `mounts` doesn't accept a resolver and can't be combined with a sandbox resolver. When each user or thread needs separate storage inside a separate sandbox, create and mount the provider inside the sandbox resolver. See [Multi-tenant sandboxes](https://mastra.ai/docs/sandbox/overview).
218
+ `mounts` doesn't accept a resolver and can't be combined with a sandbox resolver. When each user or thread needs separate storage inside a separate sandbox, create and mount the provider inside the sandbox resolver. See [Sandboxes per user or thread](https://mastra.ai/docs/sandbox/overview).
219
219
 
220
220
  ## Policies and containment
221
221
 
@@ -2,27 +2,27 @@
2
2
 
3
3
  # Sandboxes and filesystems
4
4
 
5
- A sandbox gives your agent an environment where it can run commands, execute code, install dependencies, and manage processes. Sandboxes can isolate potentially risky agent-run code from your host, and they can also provide a place to run longer-lived or more resource-intensive work outside your application process.
5
+ A sandbox gives your agent an isolated environment where it can run commands, execute code, install dependencies, and manage processes. This lets agents perform work that would be risky, resource-intensive, or impractical to run directly inside your application.
6
6
 
7
- [Filesystems](https://mastra.ai/docs/sandbox/filesystem) give the agent files it can read, write, and [search](https://mastra.ai/docs/sandbox/search). They can provide a knowledge base for your agent, or be shared with a sandbox so commands can work with the same files and keep data beyond the sandbox's lifetime.
7
+ Sandboxes are often temporary, so files created inside them may disappear when the environment stops. A [filesystem](https://mastra.ai/docs/sandbox/filesystem) gives the agent a place to read, write, and [search](https://mastra.ai/docs/sandbox/search) files that can outlive the sandbox. You can use one to keep outputs between runs, seed a new sandbox with existing files, or give the agent documents it can search while working. Filesystems also work without a sandbox, for example when an agent only needs a knowledge base or access to files in a service such as Google Drive.
8
8
 
9
9
  ## When to use sandboxes
10
10
 
11
- Sandboxes work well for:
11
+ Use a sandbox when an agent needs to:
12
12
 
13
- - **Coding agents and software factories**: Clone repositories and run shell, Git, build, or test workflows without giving autonomous agents access to the host system.
14
- - **Deep research and analysis**: Use specialized libraries to process downloaded PDFs or presentations and produce new artifacts.
15
- - **Long-running and parallel tasks**: Run work in separate environments without tying it to one request or sharing files and processes.
13
+ - Clone repositories and run shell, Git, build, or test workflows in a separate environment.
14
+ - Process PDFs, presentations, or other files with specialized libraries and produce new artifacts.
15
+ - Run long-lived or parallel work in separate environments with isolated files and processes.
16
16
 
17
17
  If an agent only needs to read, write, or search files, configure [direct filesystem access](https://mastra.ai/docs/sandbox/filesystem).
18
18
 
19
19
  ## Quickstart
20
20
 
21
- Give an agent a local sandbox and filesystem:
21
+ Give an agent a local sandbox:
22
22
 
23
23
  ```typescript
24
24
  import { Agent } from '@mastra/core/agent'
25
- import { LocalFilesystem, LocalSandbox, Workspace } from '@mastra/core/workspace'
25
+ import { LocalSandbox, Workspace } from '@mastra/core/workspace'
26
26
 
27
27
  export const codingAgent = new Agent({
28
28
  id: 'coding-agent',
@@ -33,20 +33,15 @@ export const codingAgent = new Agent({
33
33
  sandbox: new LocalSandbox({
34
34
  workingDirectory: './workspace',
35
35
  }),
36
- filesystem: new LocalFilesystem({
37
- basePath: './workspace',
38
- }),
39
36
  }),
40
37
  })
41
38
 
42
39
  await codingAgent.generate('List the files in the sandbox directory')
43
40
  ```
44
41
 
45
- > **Warning:** `LocalSandbox` is the quickest sandbox to set up, but commands run on the host by default. Enable [native isolation](#localsandbox) whenever possible. For applications exposed to untrusted users, use a remote or container backend with a stronger isolation boundary instead.
46
-
47
- To use a different sandbox for each user, tenant, or thread, configure a [sandbox resolver](#multi-tenant-sandboxes) instead.
42
+ > **Warning:** `LocalSandbox` runs commands on the application host by default and isn't isolated or secure. Enable [native isolation](#localsandbox), or use a remote or container sandbox when running untrusted code.
48
43
 
49
- The `LocalSandbox` above is one static instance. Every request and memory thread that uses `codingAgent` shares its files, processes, and lifecycle state. A memory thread doesn't create an isolated sandbox by itself.
44
+ A static sandbox is shared across every request and memory thread that uses the agent. Use a [resolver](#sandboxes-per-user-or-thread) when each user or thread needs a separate environment. See [Lifecycle and persistence](#lifecycle-and-persistence) for sharing and cleanup details.
50
45
 
51
46
  ## Using the sandbox
52
47
 
@@ -74,7 +69,7 @@ const workspace = new Workspace({
74
69
  })
75
70
  ```
76
71
 
77
- Omitting a tool entry keeps its capability-driven default. Set `{ enabled: false }` on one tool to remove it, or set the top-level `enabled: false` to disable generated tools by default. A per-tool `{ enabled: true }` overrides that global setting. The top-level `requireApproval` policy applies to every generated tool unless a per-tool entry overrides it.
72
+ Set `{ enabled: false }` on one tool to remove it, or set the top-level `enabled: false` to disable generated tools by default. A per-tool `{ enabled: true }` overrides that global setting. The top-level `requireApproval` policy applies to every generated tool unless a per-tool entry overrides it.
78
73
 
79
74
  See the [sandbox tools reference](https://mastra.ai/reference/workspace/workspace-class) for all generated tools and the [tool configuration reference](https://mastra.ai/reference/workspace/workspace-class) for approvals, output limits, and hooks.
80
75
 
@@ -109,17 +104,12 @@ export const startServerTool = createTool({
109
104
  })
110
105
  ```
111
106
 
112
- ## Supported backends
107
+ ## Sandboxes
113
108
 
114
109
  ### `LocalSandbox`
115
110
 
116
111
  [`LocalSandbox`](https://mastra.ai/reference/workspace/local-sandbox) executes commands on the same machine as your Mastra application. By default, commands run directly on the host with the permissions of the application process.
117
112
 
118
- Enable native isolation to restrict filesystem and network access at the operating-system level:
119
-
120
- - **macOS**: Seatbelt (`sandbox-exec`)
121
- - **Linux**: Bubblewrap (`bwrap`)
122
-
123
113
  ```typescript
124
114
  const sandbox = new LocalSandbox({
125
115
  workingDirectory: './workspace',
@@ -131,11 +121,13 @@ const sandbox = new LocalSandbox({
131
121
  })
132
122
  ```
133
123
 
134
- Use `LocalSandbox.detectIsolation()` to check whether Seatbelt or Bubblewrap is available on the current operating system. The [`nativeSandbox` options](https://mastra.ai/reference/workspace/local-sandbox) control network access, read-only or writable paths, write access to the working directory, and system binaries. You can also provide a custom Seatbelt profile or Bubblewrap arguments. See [`LocalSandbox`](https://mastra.ai/reference/workspace/local-sandbox) for the full configuration.
124
+ Enable native isolation to restrict filesystem and network access at the operating-system level. On macOS, native isolation uses Seatbelt (`sandbox-exec`). On Linux, it uses Bubblewrap (`bwrap`). Use `LocalSandbox.detectIsolation()` to check whether Seatbelt or Bubblewrap is available on the current operating system.
135
125
 
136
- ### Other backends
126
+ The [`nativeSandbox` options](https://mastra.ai/reference/workspace/local-sandbox) control network access, read-only or writable paths, write access to the working directory, and system binaries. You can also provide a custom Seatbelt profile or Bubblewrap arguments. See [`LocalSandbox`](https://mastra.ai/reference/workspace/local-sandbox) for the full configuration.
137
127
 
138
- Use a remote or container backend when commands need a stronger boundary from the host application or when workloads need to scale beyond the resources of your application server. Each backend has its own isolation, persistence, networking, and mount behavior:
128
+ ### Remote sandboxes
129
+
130
+ Use a remote or container sandbox when commands need a stronger boundary from the host application or when workloads need to scale beyond the resources of your application server. Each sandbox has its own isolation, persistence, networking, and mount behavior:
139
131
 
140
132
  - [AgentCore](https://mastra.ai/integrations/sandboxes/agentcore)
141
133
  - [Apple Container](https://mastra.ai/integrations/sandboxes/apple-container)
@@ -149,47 +141,40 @@ Use a remote or container backend when commands need a stronger boundary from th
149
141
  - [Railway](https://mastra.ai/integrations/sandboxes/railway)
150
142
  - [Vercel](https://mastra.ai/integrations/sandboxes/vercel)
151
143
 
152
- If Mastra doesn't support your execution backend, implement the [sandbox provider interface](https://mastra.ai/reference/workspace/sandbox) to add it.
144
+ If Mastra doesn't support your sandbox provider, implement the [sandbox provider interface](https://mastra.ai/reference/workspace/sandbox) to add it.
153
145
 
154
146
  ## Filesystem
155
147
 
156
- Each sandbox has a native filesystem that commands can use. Treat it as ephemeral because its contents follow the sandbox's lifecycle.
148
+ Every sandbox has a filesystem for commands. `LocalSandbox` uses the host filesystem, while remote sandboxes have isolated filesystems that are often temporary. For files that need to outlive a sandbox, use external storage.
149
+
150
+ Mastra filesystems connect agents and sandboxes to provider-backed storage such as Amazon S3, Google Cloud Storage, or Google Drive. Configuring one gives the agent tools to read, write, and search files. When a remote sandbox and provider support mounting, Mastra automatically mounts the storage through Filesystem in Userspace (FUSE). Commands use normal file paths while the provider stores changes outside the sandbox.
157
151
 
158
- To seed a sandbox with files or keep files between runs, give the agent a [filesystem](https://mastra.ai/docs/sandbox/filesystem). Use mounts when sandbox commands need access to the same files.
152
+ See [Filesystem](https://mastra.ai/docs/sandbox/filesystem) for providers, file tools, and mounts.
159
153
 
160
- ## Multi-tenant sandboxes
154
+ ## Sandboxes per user or thread
161
155
 
162
156
  Use a **resolver** when each user, tenant, or thread needs a separate sandbox. Set `sandboxCacheKey` to the identity that owns the sandbox so later requests reuse the same live environment.
163
157
 
164
158
  This example creates and caches one Daytona sandbox per user:
165
159
 
166
160
  ```typescript
167
- import type { RequestContext } from '@mastra/core/request-context'
168
161
  import { Workspace } from '@mastra/core/workspace'
169
162
  import { DaytonaSandbox } from '@mastra/daytona'
170
163
 
171
- const getUserId = (requestContext: RequestContext) => {
172
- const userId = requestContext.get('user-id')
173
- if (typeof userId !== 'string' || !userId) {
174
- throw new Error('A user ID is required to resolve this sandbox')
175
- }
176
-
177
- return userId
178
- }
179
-
180
164
  const workspace = new Workspace({
181
165
  sandbox: async ({ requestContext }) => {
182
- const sandbox = new DaytonaSandbox({ id: `user-${getUserId(requestContext)}` })
166
+ const userId = requestContext.get('user-id') as string
167
+ const sandbox = new DaytonaSandbox({ id: `user-${userId}` })
183
168
  await sandbox.start()
184
169
  return sandbox
185
170
  },
186
- sandboxCacheKey: ({ requestContext }) => getUserId(requestContext),
171
+ sandboxCacheKey: ({ requestContext }) => requestContext.get('user-id') as string,
187
172
  })
188
173
  ```
189
174
 
190
175
  The first request from a user creates the sandbox. Later requests with the same user ID reuse it.
191
176
 
192
- For a sandbox with persistent storage, create the filesystem and mount it inside the resolver. This example creates one Daytona sandbox and one S3 storage prefix per memory thread, then mounts the storage at `/workspace`:
177
+ For a sandbox with persistent storage, create a [filesystem](https://mastra.ai/docs/sandbox/filesystem) and mount it inside the resolver. This example creates one Daytona sandbox and one S3 storage prefix per memory thread, then mounts the storage at `/workspace`:
193
178
 
194
179
  ```typescript
195
180
  import { MASTRA_THREAD_ID_KEY, type RequestContext } from '@mastra/core/request-context'
@@ -197,14 +182,8 @@ import { Workspace } from '@mastra/core/workspace'
197
182
  import { DaytonaSandbox } from '@mastra/daytona'
198
183
  import { S3Filesystem } from '@mastra/s3'
199
184
 
200
- const getThreadId = (requestContext: RequestContext) => {
201
- const threadId = requestContext.get(MASTRA_THREAD_ID_KEY)
202
- if (typeof threadId !== 'string' || !threadId) {
203
- throw new Error('A memory thread is required to resolve this sandbox')
204
- }
205
-
206
- return threadId
207
- }
185
+ const getThreadId = (requestContext: RequestContext) =>
186
+ requestContext.get(MASTRA_THREAD_ID_KEY) as string
208
187
 
209
188
  const createThreadFilesystem = (threadId: string) =>
210
189
  new S3Filesystem({
@@ -234,25 +213,13 @@ The first request in a thread runs the resolver and starts the sandbox. Later re
234
213
 
235
214
  The example mounts the filesystem inside the resolver because static `mounts` can't be combined with a sandbox resolver. Generated tools resolve the filesystem and sandbox from the request context automatically.
236
215
 
237
- ### Resolver ownership
238
-
239
- You are responsible for every sandbox returned by a resolver. It must be ready to use when returned. Keep track of it and destroy it through your application lifecycle code when it's no longer needed. `workspace.destroy()` doesn't destroy resolver-returned sandboxes.
240
-
241
- Resolvers are incompatible with `mounts` and [`lsp: true`](https://mastra.ai/docs/sandbox/lsp), because both require a static sandbox during configuration. Using a resolver with `mounts` throws an `INVALID_CONFIG` error. With `lsp: true`, Mastra disables LSP and logs a warning.
216
+ > **Warning:** Your application owns sandboxes returned by a resolver. Destroy them and call `workspace.clearSandboxCache(cacheKey)` when the user, thread, or session ends. `workspace.destroy()` doesn't destroy resolver-returned sandboxes.
242
217
 
243
218
  ### Tool availability
244
219
 
245
- With a static sandbox, Mastra knows which capabilities the backend supports and only gives the agent the corresponding tools. Application code should also check that an optional capability is available before using it:
246
-
247
- ```typescript
248
- const processManager = workspace.sandbox?.processes
249
-
250
- if (processManager) {
251
- await processManager.spawn('pnpm dev')
252
- }
253
- ```
220
+ With a static sandbox, Mastra knows which capabilities the backend supports and only gives the agent the corresponding tools. Application code should also check that an optional capability is available before using it.
254
221
 
255
- With a [resolver-backed sandbox](#multi-tenant-sandboxes), the backend isn't known until a request runs, so Mastra initially makes all sandbox tools available. If the resolved backend doesn't support the tool the agent calls, the call fails with `SandboxFeatureNotSupportedError`.
222
+ With a [resolver-backed sandbox](#sandboxes-per-user-or-thread), the backend isn't known until a request runs, so Mastra initially makes all sandbox tools available. If the resolved backend doesn't support the tool the agent calls, the call fails with `SandboxFeatureNotSupportedError`.
256
223
 
257
224
  ## Background processes
258
225
 
@@ -292,7 +259,7 @@ Who shares a sandbox depends on where you configure it and whether you use a res
292
259
  | Resource-scoped resolver | The resolver caches one sandbox for each resource ID. |
293
260
  | Thread-scoped resolver | A memory thread keeps its sandbox across requests in that thread. |
294
261
 
295
- A static sandbox isn't automatically scoped to the current resource or memory thread. For resource or thread scope, use a resolver and set `sandboxCacheKey` to the corresponding ID. See [Multi-tenant sandboxes](#multi-tenant-sandboxes).
262
+ A static sandbox isn't automatically scoped to the current resource or memory thread. For resource or thread scope, use a resolver and set `sandboxCacheKey` to the corresponding ID. See [Sandboxes per user or thread](#sandboxes-per-user-or-thread).
296
263
 
297
264
  ### Start
298
265
 
@@ -6,6 +6,10 @@ Mastra servers can execute custom middleware functions before or after an API ro
6
6
 
7
7
  A middleware receives the [Hono](https://hono.dev) `Context` (`c`) and a `next` function. If it returns a `Response` the request is short-circuited. Calling `next()` continues processing the next middleware or route handler.
8
8
 
9
+ > **Note:** Middleware handlers use Hono's signature, so they run on Hono-based serving paths: `mastra dev` and `mastra build`, the [Hono adapter](https://mastra.ai/reference/server/hono-adapter), and adapters built on it such as `@mastra/next` and `@mastra/tanstack-start`. Adapters for other frameworks (Express, Fastify, Koa) can't run Hono handlers and log a warning at startup when middleware is configured. Register middleware through the framework's own API there instead.
10
+ >
11
+ > User middleware never runs on routes declared public with `requiresAuth: false`, so it can't block endpoints the framework needs to keep reachable, such as the Studio sign-in routes.
12
+
9
13
  ```typescript
10
14
  import { Mastra } from '@mastra/core'
11
15
 
@@ -424,11 +424,12 @@ See the [NestJS Adapter](https://mastra.ai/reference/server/nestjs-adapter) docu
424
424
 
425
425
  ## Initialization flow
426
426
 
427
- Calling `init()` runs three steps in order. Understanding this flow helps when you need to insert your own middleware at specific points.
427
+ Calling `init()` runs four steps in order. Understanding this flow helps when you need to insert your own middleware at specific points.
428
428
 
429
429
  1. `registerContextMiddleware()`: Attaches the Mastra instance, request context, tools, and abort signal to every request. This makes Mastra available to all subsequent middleware and route handlers.
430
430
  2. `registerAuthMiddleware()`: Runs the adapter auth hook during initialization. Official adapters enforce auth inline when Mastra registers built-in routes and `registerApiRoute()` routes, so raw framework routes should use the adapter's exported `createAuthMiddleware()` helper when they need Mastra auth.
431
- 3. `registerRoutes()`: Registers all Mastra API routes for agents, workflows, and other features. Also registers MCP routes if MCP servers are configured.
431
+ 3. `registerUserMiddleware()`: Registers [middleware](https://mastra.ai/docs/server/middleware) from the `server.middleware` config and `mastra.setServerMiddleware()`. Middleware handlers use Hono's signature, so Hono-based adapters (`@mastra/hono` and the adapters built on it) mount them; other adapters log a warning instead of silently ignoring the config.
432
+ 4. `registerRoutes()`: Registers all Mastra API routes for agents, workflows, and other features. Also registers MCP routes if MCP servers are configured.
432
433
 
433
434
  ### Manual initialization
434
435
 
@@ -445,6 +446,9 @@ server.registerContextMiddleware();
445
446
  // Middleware that needs Mastra context
446
447
  app.use(customMiddleware);
447
448
 
449
+ // Registers `server.middleware` and `setServerMiddleware()` middleware
450
+ server.registerUserMiddleware();
451
+
448
452
  await server.registerRoutes();
449
453
 
450
454
  // Routes after Mastra
@@ -566,11 +570,12 @@ When using server adapters, configuration comes from two places: the Mastra `ser
566
570
 
567
571
  The adapter reads these settings from `mastra.getServer()`:
568
572
 
569
- | Option | Description |
570
- | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
571
- | `auth` | Authentication config, used by `registerAuthMiddleware()`. |
572
- | `bodySizeLimit` | Default body size limit in bytes. Can be overridden per-adapter via `bodyLimitOptions`. |
573
- | `onError` | Custom error handler called when an unhandled error occurs in a route handler. See [server.onError](https://mastra.ai/reference/configuration). |
573
+ | Option | Description |
574
+ | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
575
+ | `auth` | Authentication config, used by `registerAuthMiddleware()`. |
576
+ | `bodySizeLimit` | Default body size limit in bytes. Can be overridden per-adapter via `bodyLimitOptions`. |
577
+ | `onError` | Custom error handler called when an unhandled error occurs in a route handler. See [server.onError](https://mastra.ai/reference/configuration). |
578
+ | `middleware` | User [middleware](https://mastra.ai/docs/server/middleware), registered by `registerUserMiddleware()`. Hono-based adapters run it; Express, Fastify, and Koa log a warning because Hono handlers can't run there. |
574
579
 
575
580
  ### Adapter constructor only
576
581
 
@@ -595,7 +600,6 @@ These `server` config options are only used by `mastra build` and have no effect
595
600
  | `cors` | `mastra build` adds CORS middleware |
596
601
  | `timeout` | `mastra build` |
597
602
  | `apiRoutes` | `registerApiRoute()` for `mastra build` |
598
- | `middleware` | Middleware config for `mastra build` |
599
603
 
600
604
  When using adapters, configure these features directly with your framework. For example, add CORS middleware using Hono's or Express's built-in CORS packages, and set the port when calling your framework's listen function.
601
605
 
@@ -197,6 +197,7 @@ Each provider page includes installation instructions, configuration parameters,
197
197
  - [Convex](https://mastra.ai/integrations/databases/convex)
198
198
  - [DuckDB](https://mastra.ai/integrations/databases/duckdb)
199
199
  - [DynamoDB](https://mastra.ai/integrations/databases/dynamodb)
200
+ - [Elasticsearch](https://mastra.ai/integrations/databases/elasticsearch)
200
201
  - [Google Cloud Spanner](https://mastra.ai/integrations/databases/spanner)
201
202
  - [LanceDB](https://mastra.ai/integrations/databases/lancedb)
202
203
  - [libSQL](https://mastra.ai/integrations/databases/libsql)
@@ -207,6 +208,7 @@ Each provider page includes installation instructions, configuration parameters,
207
208
  - [OracleDB](https://mastra.ai/integrations/databases/oracledb)
208
209
  - [PostgreSQL](https://mastra.ai/integrations/databases/postgresql)
209
210
  - [Redis](https://mastra.ai/integrations/databases/redis)
211
+ - [Valkey](https://mastra.ai/integrations/databases/valkey)
210
212
  - [Upstash](https://mastra.ai/integrations/databases/upstash)
211
213
 
212
214
  ## Next steps
@@ -2,10 +2,19 @@
2
2
 
3
3
  # iMessage
4
4
 
5
- iMessage channels let a Mastra agent receive direct messages and group messages from iMessage. Mastra handles the agent wiring, the webhook route, and the gateway listener; the Photon iMessage adapter docs cover number provisioning, credentials, and webhook registration.
5
+ iMessage channels let a Mastra agent receive direct messages and group messages from iMessage. Two vendor-maintained adapters connect Mastra to iMessage: [Photon](https://app.photon.codes) and [Linq](https://linqapp.com). Mastra handles the agent wiring and the webhook route; the adapter docs cover number provisioning, credentials, and webhook registration.
6
+
7
+ ## Choose an adapter
8
+
9
+ Both adapters follow the same Mastra wiring, so pick the provider first:
10
+
11
+ - **Photon**: Hosted or self-hosted iMessage service. Supports webhooks and a [gateway listener](#gateway-listener) that streams messages over an open connection.
12
+ - **Linq**: Hosted iMessage, RCS, and SMS API. Webhook-driven, with tapback reactions and media mapped in both directions.
6
13
 
7
14
  ## Install the adapter
8
15
 
16
+ **Photon**:
17
+
9
18
  Install the Photon iMessage adapter:
10
19
 
11
20
  **npm**:
@@ -32,9 +41,87 @@ yarn add @photon-ai/chat-adapter-imessage
32
41
  bun add @photon-ai/chat-adapter-imessage
33
42
  ```
34
43
 
44
+ **Linq**:
45
+
46
+ ```bash
47
+ npm install @photon-ai/chat-adapter-imessage
48
+ ```
49
+
50
+ **Tab 3**:
51
+
52
+ ```bash
53
+ pnpm add @photon-ai/chat-adapter-imessage
54
+ ```
55
+
56
+ **Tab 4**:
57
+
58
+ ```bash
59
+ yarn add @photon-ai/chat-adapter-imessage
60
+ ```
61
+
62
+ **Tab 5**:
63
+
64
+ ```bash
65
+ bun add @photon-ai/chat-adapter-imessage
66
+ ```
67
+
68
+ **Tab 6**:
69
+
70
+ Install the Linq Chat SDK adapter:
71
+
72
+ **npm**:
73
+
74
+ ```bash
75
+ npm install @linqapp/chat-sdk-adapter
76
+ ```
77
+
78
+ **pnpm**:
79
+
80
+ ```bash
81
+ pnpm add @linqapp/chat-sdk-adapter
82
+ ```
83
+
84
+ **Yarn**:
85
+
86
+ ```bash
87
+ yarn add @linqapp/chat-sdk-adapter
88
+ ```
89
+
90
+ **Bun**:
91
+
92
+ ```bash
93
+ bun add @linqapp/chat-sdk-adapter
94
+ ```
95
+
96
+ **Tab 7**:
97
+
98
+ ```bash
99
+ npm install @linqapp/chat-sdk-adapter
100
+ ```
101
+
102
+ **Tab 8**:
103
+
104
+ ```bash
105
+ pnpm add @linqapp/chat-sdk-adapter
106
+ ```
107
+
108
+ **Tab 9**:
109
+
110
+ ```bash
111
+ yarn add @linqapp/chat-sdk-adapter
112
+ ```
113
+
114
+ **Tab 10**:
115
+
116
+ ```bash
117
+ bun add @linqapp/chat-sdk-adapter
118
+ ```
119
+
35
120
  ## Agent configuration
36
121
 
37
- Add `createiMessageAdapter()` to the agent's `channels.adapters` object:
122
+ Add the adapter factory to the agent's `channels.adapters` object:
123
+
124
+ **Photon**:
38
125
 
39
126
  ```typescript
40
127
  import { Agent } from '@mastra/core/agent'
@@ -57,6 +144,35 @@ export const imessageAgent = new Agent({
57
144
  })
58
145
  ```
59
146
 
147
+ `threadContext: { maxMessages: 0 }` skips the platform history lookup Mastra runs when an agent is first mentioned in a group chat, which the Photon adapter can't perform.
148
+
149
+ **Linq**:
150
+
151
+ ```typescript
152
+ import { Agent } from '@mastra/core/agent'
153
+ import { createLinqAdapter } from '@linqapp/chat-sdk-adapter'
154
+
155
+ export const imessageAgent = new Agent({
156
+ id: 'imessage-agent',
157
+ name: 'iMessage Agent',
158
+ instructions: 'Answer questions and help with tasks over iMessage.',
159
+ model: 'openai/gpt-5.6-sol',
160
+ channels: {
161
+ adapters: {
162
+ imessage: {
163
+ adapter: createLinqAdapter({
164
+ apiKey: process.env.LINQ_API_KEY!,
165
+ signingSecret: process.env.LINQ_WEBHOOK_SECRET!,
166
+ }),
167
+ toolDisplay: 'text',
168
+ },
169
+ },
170
+ },
171
+ })
172
+ ```
173
+
174
+ `createLinqAdapter()` takes the credentials directly: `apiKey` is your Linq API key and `signingSecret` comes from the [webhook subscription](#webhook-url). The Linq adapter can fetch thread history, so the default `threadContext` behavior works.
175
+
60
176
  Register the agent on the Mastra instance:
61
177
 
62
178
  ```typescript
@@ -70,10 +186,14 @@ export const mastra = new Mastra({
70
186
 
71
187
  Use `imessage` as the adapter key. Mastra derives the webhook path and the `platform` value on `requestContext` from this key.
72
188
 
73
- `toolDisplay: 'text'` describes tool calls in the message, since iMessage has no interactive cards for Approve and Deny actions. `threadContext: { maxMessages: 0 }` skips the platform history lookup Mastra runs when an agent is first mentioned in a group chat, which the adapter can't perform. Both override defaults that assume platform features iMessage lacks.
189
+ `toolDisplay: 'text'` describes tool calls in the message, since iMessage has no interactive cards for Approve and Deny actions. Both adapters need it: Photon has no card rendering, and Linq flattens cards to plain text where buttons show their labels but can't trigger actions.
190
+
191
+ > **Warning:** Text rendering can't submit approval decisions. A tool with `requireApproval: true` stays suspended until a UI or API action, such as Studio, submits an explicit approval or decline, so avoid approval-gated tools on iMessage agents unless another surface handles the decision. See [Tool approval](https://mastra.ai/docs/channels).
74
192
 
75
193
  ## Adapter setup
76
194
 
195
+ **Photon**:
196
+
77
197
  Follow the [Photon iMessage adapter docs](https://github.com/photon-hq/vercel-chat-adapter-imessage) for iMessage-specific setup, including number provisioning, hosted and self-hosted modes, and webhook registration. The adapter picks its mode from the environment variables you set.
78
198
 
79
199
  For the hosted service, create a project at [app.photon.codes](https://app.photon.codes) and use the project credentials:
@@ -94,6 +214,17 @@ IMESSAGE_PHONE=+15551234567
94
214
 
95
215
  `IMESSAGE_PHONE` is optional and routes messages when a self-hosted server has several numbers. You can also pass these values to `createiMessageAdapter()` directly, including a `credentials` function that resolves the project ID and secret at first use from a secret store.
96
216
 
217
+ **Linq**:
218
+
219
+ Follow the [Linq API docs](https://docs.linqapp.com/) for Linq-specific setup, including phone number provisioning and API keys. Create a Linq account, copy your API key, and set the credentials the agent configuration reads:
220
+
221
+ ```bash
222
+ LINQ_API_KEY=your-linq-api-key
223
+ LINQ_WEBHOOK_SECRET=your-webhook-signing-secret
224
+ ```
225
+
226
+ `LINQ_WEBHOOK_SECRET` is the signing secret returned when you create a webhook subscription in the next section. The adapter also accepts a `baseURL` option to target a different Linq API base URL, such as a sandbox.
227
+
97
228
  ## Webhook URL
98
229
 
99
230
  Mastra generates the iMessage webhook route from the agent ID and adapter key:
@@ -108,13 +239,25 @@ Use your public Mastra server URL as the base URL:
108
239
  https://your-app.example.com/api/agents/imessage-agent/channels/imessage/webhook
109
240
  ```
110
241
 
242
+ **Photon**:
243
+
111
244
  Register this URL in the [Photon dashboard](https://app.photon.codes), then set the signing secret it returns as `IMESSAGE_WEBHOOK_SECRET`. The secret is shown once at registration. The adapter verifies the signature on every delivery and rejects requests that don't match. Webhooks are available in hosted mode only.
112
245
 
113
- > **Note:** Photon delivers to public HTTPS endpoints only. It won't deliver to `http://`, to private addresses like `localhost`, or through a redirect. For local development, use a tunnel as described in the [Channels overview](https://mastra.ai/docs/channels).
246
+ **Linq**:
247
+
248
+ Create a [webhook subscription](https://docs.linqapp.com/guides/webhooks/subscriptions/) with this URL as the target and subscribe to at least these events:
249
+
250
+ - `message.received`
251
+ - `reaction.added`
252
+ - `reaction.removed`
253
+
254
+ Set the `signing_secret` the subscription returns as `LINQ_WEBHOOK_SECRET`. The secret is shown once at creation and can't be retrieved later. The adapter verifies the HMAC signature on every delivery, checks for replayed requests, and rejects requests that don't match.
255
+
256
+ > **Note:** Both providers deliver to public HTTPS endpoints only, not to `http://` or private addresses like `localhost`. For local development, use a tunnel as described in the [Channels overview](https://mastra.ai/docs/channels).
114
257
 
115
258
  ## Duplicate deliveries
116
259
 
117
- Photon retries failed deliveries with backoff and delivers at least once, so the same message can arrive twice. Chat SDK drops the repeat using the channel state adapter, and Mastra's default keeps those dedup keys in memory. That covers a single long-running server.
260
+ Photon and Linq retry failed deliveries with backoff and deliver at least once, so the same message can arrive twice. Chat SDK drops the repeat using the channel state adapter, and Mastra's default keeps those dedup keys in memory. That covers a single long-running server.
118
261
 
119
262
  A repeat can still reach the agent after a restart, or on serverless where the retry is routed to a different instance. Pass a shared state adapter on `channels.state` so dedup keys are visible everywhere. Install one alongside the adapter:
120
263
 
@@ -154,7 +297,6 @@ channels: {
154
297
  toolDisplay: 'text',
155
298
  },
156
299
  },
157
- threadContext: { maxMessages: 0 },
158
300
  state: createRedisState(),
159
301
  },
160
302
  ```
@@ -163,13 +305,13 @@ This matters most for tools with side effects, where handling the same message t
163
305
 
164
306
  ## Read receipts
165
307
 
166
- iPhone Messages sends a read receipt for every message the agent posts, and the adapter delivers those receipts as inbound messages with no text and no attachments. Mastra skips them, so the agent doesn't answer its own reply in a loop. Their message IDs carry a `:read:` suffix, and they show up as skipped messages at `debug` log level.
308
+ iPhone Messages sends a read receipt for every message the agent posts, and the Photon adapter delivers those receipts as inbound messages with no text and no attachments. Mastra skips them, so the agent doesn't answer its own reply in a loop. Their message IDs carry a `:read:` suffix, and they show up as skipped messages at `debug` log level.
167
309
 
168
310
  The same rule applies to every adapter: an inbound message with neither text nor attachments never starts an agent run. A custom `onDirectMessage`, `onMention`, or `onSubscribedMessage` handler still receives it and can act on it before calling `defaultHandler`.
169
311
 
170
312
  ## Gateway listener
171
313
 
172
- The adapter can hold an open connection and stream messages in real time instead of receiving webhooks. This works in both hosted and self-hosted modes.
314
+ The Photon adapter can hold an open connection and stream messages in real time instead of receiving webhooks. This works in both hosted and self-hosted modes. The Linq adapter is webhook-driven and has no gateway mode.
173
315
 
174
316
  Mastra starts this listener during initialization and reconnects it if it drops, so no cron job or extra route is needed on a long-running server. Set `gateway: false` on the adapter config to turn it off when you use webhooks:
175
317