@mastra/mcp-docs-server 1.1.44 → 1.1.45-alpha.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.
@@ -121,7 +121,7 @@ const agent = new Agent({
121
121
  })
122
122
  ```
123
123
 
124
- Each request resolves its own filesystem at tool execution time:
124
+ Each request resolves its own filesystem for workspace tools and workspace instructions:
125
125
 
126
126
  ```typescript
127
127
  import { RequestContext } from '@mastra/core/request-context'
@@ -135,6 +135,8 @@ const viewerCtx = new RequestContext([['agent-role', 'viewer']])
135
135
  await agent.generate('Read info.txt', { requestContext: viewerCtx })
136
136
  ```
137
137
 
138
+ Workspace instructions use the same `requestContext`, so the agent sees the filesystem context for the resolved provider.
139
+
138
140
  The resolver can also be asynchronous, for example to look up configuration from a database:
139
141
 
140
142
  ```typescript
@@ -171,6 +171,23 @@ const workspace = new Workspace({
171
171
 
172
172
  One workspace instance serves all requests. The resolver runs at tool execution time, so each request gets its own filesystem. See [dynamic filesystem](https://mastra.ai/docs/workspace/filesystem) for details.
173
173
 
174
+ ### Dynamic sandbox (per-request)
175
+
176
+ Pass a resolver function to `sandbox` to return a different sandbox per request. This is useful for multi-tenant deployments where each user or role needs an isolated working directory or different execution permissions.
177
+
178
+ ```typescript
179
+ const workspace = new Workspace({
180
+ sandbox: ({ requestContext }) => {
181
+ const userId = requestContext.get('user-id') as string
182
+ return new LocalSandbox({
183
+ workingDirectory: `/workspaces/${userId}`,
184
+ })
185
+ },
186
+ })
187
+ ```
188
+
189
+ The resolver is incompatible with `mounts` and `lsp: true`, since both require a concrete sandbox instance at construction time. See [dynamic sandbox](https://mastra.ai/docs/workspace/sandbox) for details.
190
+
174
191
  ### Which pattern should I use?
175
192
 
176
193
  | Scenario | Pattern |
@@ -181,6 +198,7 @@ One workspace instance serves all requests. The resolver runs at tool execution
181
198
  | Agent reads/writes files, no command execution needed | `filesystem` only |
182
199
  | Agent runs commands, no file tools needed | `sandbox` only |
183
200
  | Multi-role or multi-tenant agent with per-request storage | `filesystem` with resolver function |
201
+ | Multi-tenant agent with per-request execution scope | `sandbox` with resolver function |
184
202
 
185
203
  ## Tool configuration
186
204
 
@@ -52,6 +52,109 @@ const response = await agent.generate('Run `ls -la` in the workspace directory')
52
52
 
53
53
  See [`LocalSandbox` reference](https://mastra.ai/reference/workspace/local-sandbox) for configuration options including environment isolation and native OS sandboxing.
54
54
 
55
+ ## Dynamic sandbox
56
+
57
+ The `sandbox` option accepts a resolver function instead of a static instance. The resolver receives `requestContext` and returns a sandbox per request, allowing a single workspace to serve different sandboxes based on the caller's identity, role, or tenant.
58
+
59
+ ```typescript
60
+ import { Agent } from '@mastra/core/agent'
61
+ import { Workspace, LocalSandbox } from '@mastra/core/workspace'
62
+
63
+ const workspace = new Workspace({
64
+ sandbox: ({ requestContext }) => {
65
+ const userId = requestContext.get('user-id') as string
66
+ return new LocalSandbox({
67
+ workingDirectory: `/workspaces/${userId}`,
68
+ })
69
+ },
70
+ })
71
+
72
+ const agent = new Agent({
73
+ id: 'multi-tenant-agent',
74
+ model: 'your-provider/your-model',
75
+ workspace,
76
+ })
77
+ ```
78
+
79
+ Each request resolves its own sandbox at tool execution time:
80
+
81
+ ```typescript
82
+ import { RequestContext } from '@mastra/core/request-context'
83
+
84
+ // User Alice — commands run in /workspaces/alice
85
+ const aliceCtx = new RequestContext([['user-id', 'alice']])
86
+ await agent.generate('List files in cwd', { requestContext: aliceCtx })
87
+
88
+ // User Bob — commands run in /workspaces/bob
89
+ const bobCtx = new RequestContext([['user-id', 'bob']])
90
+ await agent.generate('List files in cwd', { requestContext: bobCtx })
91
+ ```
92
+
93
+ By default, workspace instructions describe the dynamic sandbox with stable placeholder text. See [Workspace instructions](#workspace-instructions) to include concrete per-request details.
94
+
95
+ The resolver can also be asynchronous — for example to look up tenant configuration from a database:
96
+
97
+ ```typescript
98
+ const workspace = new Workspace({
99
+ sandbox: async ({ requestContext }) => {
100
+ const tenant = await db.getTenant(requestContext.get('tenant-id'))
101
+ return new LocalSandbox({ workingDirectory: tenant.workspacePath })
102
+ },
103
+ })
104
+ ```
105
+
106
+ ### Lifecycle ownership
107
+
108
+ When the sandbox is a static instance, `workspace.init()` calls its `start()` method and `workspace.destroy()` calls its `destroy()` method. With a resolver, the workspace has no instance to manage at construction time — the caller owns the returned sandbox's lifecycle.
109
+
110
+ The resolver must return a sandbox that is ready to use, either already started or able to handle calls without explicit startup. The caller also owns cleanup timing for returned sandboxes. Cleanup might happen per request, per tenant, per user, or as part of a long-lived sandbox pool; `workspace.destroy()` does not destroy resolver-returned sandboxes.
111
+
112
+ > **Note:** `sandbox` resolvers are incompatible with `mounts` and `lsp: true`. Both require a concrete sandbox instance at construction time, so combining them with a resolver throws an `INVALID_CONFIG` error (for `mounts`) or disables LSP with a warning (for `lsp: true`).
113
+
114
+ ### Tool registration
115
+
116
+ With a static sandbox, the workspace inspects the instance to decide which tools to register. With a resolver, the workspace assumes full capabilities and registers `execute_command` (with `background` support), `get_process_output`, and `kill_process`. If the resolved sandbox does not implement a capability, the runtime throws a clear `SandboxFeatureNotSupportedError`.
117
+
118
+ ### Background process continuity
119
+
120
+ Background processes can outlive a single tool call, so `get_process_output` and `kill_process` must reach the same sandbox that started the process. By default, a resolved sandbox is cached per request. For continuity across follow-up requests, such as a later conversation turn, set `sandboxCacheKey` to a stable identifier. The resolved sandbox is then cached by that key instead of by request:
121
+
122
+ ```typescript
123
+ const workspace = new Workspace({
124
+ sandbox: ({ requestContext }) => resolveSandbox(requestContext),
125
+ sandboxCacheKey: ({ requestContext }) => requestContext.get('thread-id') as string,
126
+ })
127
+ ```
128
+
129
+ Without a `sandboxCacheKey`, the resolver must return the same sandbox itself for follow-up calls that share a tenant, user, or session.
130
+
131
+ When a cached sandbox is no longer needed, destroy the sandbox in your own lifecycle code and call `workspace.clearSandboxCache(cacheKey)` to drop the workspace cache entry. Call `workspace.clearSandboxCache()` to clear all keyed sandbox entries.
132
+
133
+ ### Workspace instructions
134
+
135
+ Workspace instructions describe the environment in the agent's system message. With a sandbox resolver, the workspace does not call the resolver to build these instructions. It emits stable placeholder text, so constructing the prompt never provisions a caller-owned sandbox and the system message stays consistent across requests, which keeps prompt caching effective.
136
+
137
+ To include concrete per-request sandbox details, set `instructions.dynamicSandbox` to `'resolve'`:
138
+
139
+ ```typescript
140
+ const workspace = new Workspace({
141
+ sandbox: ({ requestContext }) => resolveSandbox(requestContext),
142
+ instructions: { dynamicSandbox: 'resolve' },
143
+ })
144
+ ```
145
+
146
+ `'resolve'` calls the resolver on every request, which may provision the sandbox and makes the system message request-specific. Pass a function instead to return custom text from `requestContext` without resolving the sandbox:
147
+
148
+ ```typescript
149
+ const workspace = new Workspace({
150
+ sandbox: ({ requestContext }) => resolveSandbox(requestContext),
151
+ instructions: {
152
+ dynamicSandbox: ({ requestContext }) =>
153
+ `Sandbox scoped to tenant ${requestContext.get('tenant-id')}.`,
154
+ },
155
+ })
156
+ ```
157
+
55
158
  ## Agent tools
56
159
 
57
160
  When you configure a sandbox on a workspace, agents receive the `execute_command` tool for running shell commands.
@@ -214,6 +214,10 @@ const stream = await agent.stream('message for agent')
214
214
 
215
215
  **options.versions.agents.status** (`'draft' | 'published'`): Target the latest version with this publication status.
216
216
 
217
+ **options.untilIdle** (`boolean | { maxIdleMs?: number }`): When set, keeps the stream open across background-task continuations. The agent will automatically re-invoke the LLM when background tasks complete, streaming continuation turns through the same \`fullStream\`. Pass \`true\` for default settings (5 min idle timeout), or an object with \`maxIdleMs\` to configure. Requires memory. Replaces the standalone \`streamUntilIdle()\` method.
218
+
219
+ **options.untilIdle.maxIdleMs** (`number`): Closes the outer stream after this many ms of idleness between turns. The timer only runs while the wrapper is between turns. Default: 5 minutes.
220
+
217
221
  ## Returns
218
222
 
219
223
  **stream** (`MastraModelOutput<Output>`): Returns a MastraModelOutput instance that provides access to the streaming output.
@@ -2,6 +2,17 @@
2
2
 
3
3
  **Added in:** `@mastra/core@1.29.0`
4
4
 
5
+ > **Deprecated:** `streamUntilIdle()` is deprecated. Use `stream()` with the `untilIdle` option instead:
6
+ >
7
+ > ```ts
8
+ > const result = await agent.stream('Research solana for me', {
9
+ > untilIdle: true,
10
+ > memory: { thread: 't1', resource: 'u1' },
11
+ > })
12
+ > ```
13
+ >
14
+ > Pass `untilIdle: { maxIdleMs: 60_000 }` to configure the idle timeout.
15
+
5
16
  `streamUntilIdle()` streams an agent's response and keeps the stream open until every background task dispatched during the run completes. When a task finishes, its result is written to memory and the agentic loop re-enters automatically so the LLM can react to it. The stream closes once no tasks are running and no completions are queued.
6
17
 
7
18
  Use it when the agent dispatches background tasks (typically long-running tools or subagents) and you want a single stream that spans the initial response **plus** every continuation triggered by a task completion. For foreground-only runs or if you prefer to manage the continuation manually (manually prompt agent to process the result), use [`Agent.stream()`](https://mastra.ai/reference/streaming/agents/stream).
@@ -31,7 +31,11 @@ const workspace = new Workspace({
31
31
 
32
32
  **filesystem** (`WorkspaceFilesystem | WorkspaceFilesystemResolver`): Filesystem provider instance, or a resolver function that receives \`requestContext\` and returns a filesystem per request. See \[dynamic filesystem]\(/docs/workspace/filesystem#dynamic-filesystem).
33
33
 
34
- **sandbox** (`WorkspaceSandbox`): Sandbox provider instance (e.g., LocalSandbox)
34
+ **sandbox** (`WorkspaceSandbox | WorkspaceSandboxResolver`): Sandbox provider instance, or a resolver function that receives \`requestContext\` and returns a sandbox per request. See \[dynamic sandbox]\(/docs/workspace/sandbox#dynamic-sandbox).
35
+
36
+ **instructions.dynamicSandbox** (`'placeholder' | 'resolve' | (({ requestContext }) => string)`): Controls how a resolver-backed \`sandbox\` contributes to workspace instructions. \`'placeholder'\` (default) emits stable text without calling the resolver. \`'resolve'\` calls the resolver and uses the sandbox's own instructions. A function returns custom text without resolving. Has no effect on a static sandbox. (Default: `'placeholder'`)
37
+
38
+ **sandboxCacheKey** (`({ requestContext }) => string | undefined`): Stable cache key for a resolver-backed \`sandbox\`. When set, resolved sandboxes are memoized per key instead of per \`RequestContext\` instance, so background-process tools reach the same sandbox across follow-up requests. Has no effect on a static sandbox.
35
39
 
36
40
  **bm25** (`boolean | BM25Config`): Enable BM25 keyword search. Pass true for defaults or a config object. (Default: `undefined`)
37
41
 
@@ -124,7 +128,7 @@ Tool names must be unique across all workspace tools. Setting a custom name that
124
128
 
125
129
  **filesystem** (`WorkspaceFilesystem | undefined`): The static filesystem provider. Returns \`undefined\` when a resolver function is configured — use \`hasFilesystemConfig()\` to check availability.
126
130
 
127
- **sandbox** (`WorkspaceSandbox | undefined`): The sandbox provider
131
+ **sandbox** (`WorkspaceSandbox | undefined`): The static sandbox provider. Returns \`undefined\` when a resolver function is configured — use \`hasSandboxConfig()\` to check availability.
128
132
 
129
133
  **skills** (`WorkspaceSkills | undefined`): Skills interface for accessing SKILL.md files
130
134
 
@@ -198,6 +202,20 @@ const info = await workspace.getInfo()
198
202
  // { id, name, status, createdAt, lastAccessedAt, filesystem?, sandbox? }
199
203
  ```
200
204
 
205
+ Pass `resolveDynamicProviders: false` to report resolver-backed providers as dynamic without invoking their resolver.
206
+
207
+ ```typescript
208
+ const info = await workspace.getInfo({ resolveDynamicProviders: false })
209
+ ```
210
+
211
+ **Parameters:**
212
+
213
+ **options.includeFileCount** (`boolean`): Whether to count total files. This can be slow for large workspaces.
214
+
215
+ **options.requestContext** (`RequestContext`): Passed to dynamic provider resolvers when \`resolveDynamicProviders\` is enabled.
216
+
217
+ **options.resolveDynamicProviders** (`boolean`): Whether to invoke dynamic provider resolvers. Set to \`false\` when you only need metadata and want resolver-backed providers reported as \`dynamic\`. (Default: `true`)
218
+
201
219
  #### `getInstructions(opts?)`
202
220
 
203
221
  Returns combined instructions from the filesystem and sandbox providers. Injected into the agent's system message to help it understand the execution context.
@@ -218,6 +236,20 @@ const instructions = workspace.getInstructions({ requestContext })
218
236
 
219
237
  **Returns:** `string`
220
238
 
239
+ #### `getInstructionsAsync(opts?)`
240
+
241
+ Returns combined workspace instructions. Use this when the workspace uses resolver-backed providers. A dynamic filesystem is resolved per request; a dynamic sandbox contributes stable placeholder text unless `instructions.dynamicSandbox` is set to `'resolve'`.
242
+
243
+ ```typescript
244
+ const instructions = await workspace.getInstructionsAsync({ requestContext })
245
+ ```
246
+
247
+ **Parameters:**
248
+
249
+ **opts.requestContext** (`RequestContext`): Passed to the dynamic filesystem resolver, and to the dynamic sandbox resolver when \`instructions.dynamicSandbox\` is \`'resolve'\`.
250
+
251
+ **Returns:** `Promise<string>`
252
+
221
253
  To override the default output, pass an `instructions` option to [LocalFilesystem](https://mastra.ai/reference/workspace/local-filesystem) or [LocalSandbox](https://mastra.ai/reference/workspace/local-sandbox).
222
254
 
223
255
  #### `getToolsConfig()`
@@ -282,6 +314,56 @@ const fs = await workspace.resolveFilesystem({ requestContext: ctx })
282
314
 
283
315
  **Returns:** `Promise<WorkspaceFilesystem | undefined>`
284
316
 
317
+ ### Dynamic sandbox
318
+
319
+ #### `hasSandboxConfig()`
320
+
321
+ Check whether a sandbox is configured, either as a static instance or a resolver function. Use this instead of checking `workspace.sandbox` directly, because a resolver-based workspace returns `undefined` from the `sandbox` property.
322
+
323
+ ```typescript
324
+ if (workspace.hasSandboxConfig()) {
325
+ // Sandbox tools are available
326
+ }
327
+ ```
328
+
329
+ **Returns:** `boolean`
330
+
331
+ #### `resolveSandbox({ requestContext })`
332
+
333
+ Resolve the sandbox for a given request context. When a resolver function is configured, calls it with the provided `requestContext`. When a static sandbox is configured, returns it directly. Returns `undefined` if no sandbox is configured.
334
+
335
+ ```typescript
336
+ import { RequestContext } from '@mastra/core/request-context'
337
+
338
+ const ctx = new RequestContext([['user-id', 'alice']])
339
+ const sandbox = await workspace.resolveSandbox({ requestContext: ctx })
340
+ ```
341
+
342
+ **Parameters:**
343
+
344
+ **requestContext** (`RequestContext`): The request context to pass to the resolver function.
345
+
346
+ **Returns:** `Promise<WorkspaceSandbox | undefined>`
347
+
348
+ #### `clearSandboxCache(cacheKey?)`
349
+
350
+ Clear resolver-backed sandboxes cached by `sandboxCacheKey`. Pass a cache key to clear one entry, or omit it to clear all keyed sandbox entries.
351
+
352
+ This method does not clear the per-`RequestContext` weak cache. Those entries are garbage-collection managed.
353
+
354
+ The workspace does not own resolver-returned sandboxes. This method only drops workspace references. Destroy the sandbox in your own lifecycle code.
355
+
356
+ ```typescript
357
+ workspace.clearSandboxCache('thread-123')
358
+ workspace.clearSandboxCache()
359
+ ```
360
+
361
+ **Parameters:**
362
+
363
+ **cacheKey** (`string`): The cache key to clear. Omit this value to clear all keyed sandbox entries.
364
+
365
+ **Returns:** `void`
366
+
285
367
  ## Agent tools
286
368
 
287
369
  A workspace provides tools to agents based on what's configured.
@@ -333,6 +415,8 @@ Added when a sandbox is configured:
333
415
  | `mastra_workspace_get_process_output` | Get stdout, stderr, and status of a background process by PID. Accepts `tail` to limit output lines and `wait: true` to block until exit. Only available when the sandbox has a process manager. |
334
416
  | `mastra_workspace_kill_process` | Kill a background process by PID. Returns the last 50 lines of output. Only available when the sandbox has a process manager. |
335
417
 
418
+ With a static sandbox, capability checks (`executeCommand`, `processes`) decide which tool variants are exposed. With a [dynamic sandbox](https://mastra.ai/docs/workspace/sandbox), all sandbox tools are registered and the runtime throws a clear error if the resolved sandbox does not implement a requested capability.
419
+
336
420
  The `execute_command` tool accepts a `backgroundProcesses` option for lifecycle callbacks on background processes:
337
421
 
338
422
  **backgroundProcesses** (`BackgroundProcessesConfig`): Configuration for handling background processes. Only applicable if the sandbox supports background execution.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # @mastra/mcp-docs-server
2
2
 
3
+ ## 1.1.45-alpha.0
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [[`f82cc72`](https://github.com/mastra-ai/mastra/commit/f82cc72edca0ce636fe18abaf2598d89a0c6bcca), [`fcf6027`](https://github.com/mastra-ai/mastra/commit/fcf602747f6771731dda268ff3493b836f9f0ee9)]:
8
+ - @mastra/core@1.41.0-alpha.0
9
+
3
10
  ## 1.1.44
4
11
 
5
12
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mastra/mcp-docs-server",
3
- "version": "1.1.44",
3
+ "version": "1.1.45-alpha.0",
4
4
  "description": "MCP server for accessing Mastra.ai documentation, changelogs, and news.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -28,7 +28,7 @@
28
28
  "jsdom": "^26.1.0",
29
29
  "local-pkg": "^1.1.2",
30
30
  "zod": "^4.4.3",
31
- "@mastra/core": "1.40.0",
31
+ "@mastra/core": "1.41.0-alpha.0",
32
32
  "@mastra/mcp": "^1.9.1"
33
33
  },
34
34
  "devDependencies": {
@@ -46,8 +46,8 @@
46
46
  "typescript": "^6.0.3",
47
47
  "vitest": "4.1.5",
48
48
  "@internal/lint": "0.0.102",
49
- "@mastra/core": "1.40.0",
50
- "@internal/types-builder": "0.0.77"
49
+ "@internal/types-builder": "0.0.77",
50
+ "@mastra/core": "1.41.0-alpha.0"
51
51
  },
52
52
  "homepage": "https://mastra.ai",
53
53
  "repository": {