@mastra/mcp-docs-server 1.2.18-alpha.3 → 1.2.18-alpha.6

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 (33) hide show
  1. package/.docs/docs/datasets/running-experiments.md +86 -1
  2. package/.docs/docs/mastra-platform/deploy.md +3 -1
  3. package/.docs/docs/mastra-platform/workspaces.md +6 -3
  4. package/.docs/docs/sandbox/filesystem.md +120 -139
  5. package/.docs/docs/sandbox/lsp.md +195 -143
  6. package/.docs/docs/sandbox/overview.md +103 -69
  7. package/.docs/docs/sandbox/search.md +172 -153
  8. package/.docs/docs/sandbox/skills.md +94 -151
  9. package/.docs/integrations/deploy/render.md +136 -89
  10. package/.docs/integrations/observability/arize.md +8 -6
  11. package/.docs/models/index.md +1 -1
  12. package/.docs/models/providers/edenai.md +2 -3
  13. package/.docs/models/providers/empiriolabs.md +1 -1
  14. package/.docs/models/providers/kilo.md +2 -2
  15. package/.docs/models/providers/llmgateway.md +2 -1
  16. package/.docs/models/providers/nano-gpt.md +3 -1
  17. package/.docs/models/providers/ofox.md +1 -1
  18. package/.docs/models/providers/opencode.md +65 -65
  19. package/.docs/reference/cli/mastra.md +2 -2
  20. package/.docs/reference/client-js/datasets.md +146 -0
  21. package/.docs/reference/configuration.md +58 -0
  22. package/.docs/reference/datasets/createExperiment.md +76 -0
  23. package/.docs/reference/datasets/finalizeExperiment.md +43 -0
  24. package/.docs/reference/datasets/runExperimentItem.md +55 -0
  25. package/.docs/reference/datasets/submitExperimentResult.md +56 -0
  26. package/.docs/reference/index.md +5 -0
  27. package/.docs/reference/observability/tracing/exporters/langfuse.md +2 -0
  28. package/.docs/reference/pubsub/redis-streams.md +11 -1
  29. package/.docs/reference/rag/metadata-filters.md +16 -8
  30. package/.docs/reference/rag/retrieval.md +113 -5
  31. package/.docs/reference/server/routes.md +111 -0
  32. package/CHANGELOG.md +14 -0
  33. package/package.json +3 -3
@@ -254,7 +254,7 @@ The exported event types are `ExperimentEvent`, `ExperimentRunStartedEvent`, `Ex
254
254
 
255
255
  ## Lifecycle hooks
256
256
 
257
- Use lifecycle hooks to prepare state before a target runs and clean it up afterwards. This is useful when an item can't be evaluated against an empty environment. A run might need a fixture file copied into the agent's workspace, or a sandbox provisioned before the agent can touch it.
257
+ Use lifecycle hooks to prepare state before a target runs and clean it up afterward. This is useful when an item can't be evaluated against an empty environment. A run might need a fixture file copied into the agent's workspace, or a sandbox provisioned before the agent can touch it.
258
258
 
259
259
  Hooks run at two levels. `beforeAll` and `afterAll` run once per experiment, and `beforeEach` and `afterEach` run once per item:
260
260
 
@@ -430,6 +430,91 @@ while (experiment.status === 'pending' || experiment.status === 'running') {
430
430
  console.log(experiment.status) // 'completed' | 'failed'
431
431
  ```
432
432
 
433
+ ## Caller-driven experiments
434
+
435
+ `startExperiment()` puts Mastra in charge of the whole run. If a durable orchestrator (for example Temporal, Airflow, or a custom worker fleet) owns the loop instead, create the experiment first and drive it yourself in one of these shapes:
436
+
437
+ - **Caller drives the loop, Mastra runs each item.** Create the experiment with a target, then call [`runExperimentItem()`](https://mastra.ai/reference/datasets/runExperimentItem) once per item. Mastra executes the target and runs the scorers, then persists the result.
438
+ - **Caller runs everything.** Create the experiment without a target, execute and score items on your own infrastructure, and ingest each result with [`submitExperimentResult()`](https://mastra.ai/reference/datasets/submitExperimentResult).
439
+
440
+ Both shapes finish with [`finalizeExperiment()`](https://mastra.ai/reference/datasets/finalizeExperiment), and both write to the same tables as native runs, so Studio views, comparisons, and review summaries work unchanged.
441
+
442
+ ### Run items server-side
443
+
444
+ Create the experiment with [`createExperiment()`](https://mastra.ai/reference/datasets/createExperiment), passing the target and scorers. Pass your own `id` (for example a workflow run id) to make creation idempotent: retrying the call with the same id returns the existing experiment instead of failing.
445
+
446
+ ```typescript
447
+ const dataset = await mastra.datasets.get({ id: 'translation-dataset-id' })
448
+
449
+ const { experimentId, totalItems, datasetVersion } = await dataset.createExperiment({
450
+ id: 'temporal-wf-run-42', // optional: reuse your workflow run id for idempotent creates
451
+ targetType: 'agent',
452
+ targetId: 'translation-agent',
453
+ scorers: ['accuracy'],
454
+ })
455
+ ```
456
+
457
+ Then run each item from your orchestrator. Mastra resolves the item at the pinned dataset version and executes the target with the resolved scorers. The result upserts keyed by `(experimentId, itemId, attempt)`, so a retried activity converges on a single row:
458
+
459
+ ```typescript
460
+ const { result, scores } = await dataset.runExperimentItem({
461
+ experimentId,
462
+ itemId: 'item-1',
463
+ })
464
+ ```
465
+
466
+ Retries and timeouts belong to your orchestrator: each `runExperimentItem` call executes the item exactly once. Scorers resolve with the same precedence as native runs: experiment `scorers` win over item `scorerIds`, which win over dataset `scorerIds`.
467
+
468
+ ### Ingest external results
469
+
470
+ If your workers execute and score items themselves, create the experiment without a target:
471
+
472
+ ```typescript
473
+ const { experimentId } = await dataset.createExperiment({
474
+ id: 'temporal-wf-run-42',
475
+ name: 'external-eval',
476
+ })
477
+ ```
478
+
479
+ Submit one result per item with [`submitExperimentResult()`](https://mastra.ai/reference/datasets/submitExperimentResult). Submissions are upserts keyed by `(experimentId, itemId, attempt)`, so a retried worker converges on a single row instead of duplicating results:
480
+
481
+ ```typescript
482
+ await dataset.submitExperimentResult({
483
+ experimentId,
484
+ itemId: 'item-1',
485
+ output: { translation: 'Hola' },
486
+ scores: [{ scorerId: 'accuracy', score: 0.92, reason: 'Faithful translation' }],
487
+ })
488
+ ```
489
+
490
+ `input` and `groundTruth` default to the dataset item's values at the experiment's pinned dataset version. Inline `scores` are persisted to the scores store under the experiment, so they show up in comparisons alongside native scorer runs. Experiments created with a target reject `submitExperimentResult` so two writers can't race on the same rows.
491
+
492
+ ### Finalize
493
+
494
+ When all items are done, call [`finalizeExperiment()`](https://mastra.ai/reference/datasets/finalizeExperiment). Mastra computes `succeededCount`, `failedCount`, and `skippedCount` from the persisted rows, so your workers never track completion bookkeeping. Finalization is idempotent. Calling it again returns the stored record:
495
+
496
+ ```typescript
497
+ const experiment = await dataset.finalizeExperiment({ experimentId })
498
+
499
+ console.log(experiment.status) // 'completed'
500
+ console.log(experiment.succeededCount) // items with at least one attempt that didn't error
501
+ console.log(experiment.failedCount) // items where every attempt errored
502
+ console.log(experiment.skippedCount) // items never submitted
503
+ ```
504
+
505
+ The same lifecycle is available over HTTP (`POST /api/datasets/:datasetId/experiments` with `start: false`, `POST .../experiments/:experimentId/items/:itemId/run`, `POST .../experiments/:experimentId/results`, `POST .../experiments/:experimentId/finalize`, see [Server routes](https://mastra.ai/reference/server/routes)) and through `@mastra/client-js` (see the [Datasets API](https://mastra.ai/reference/client-js/datasets)).
506
+
507
+ ### Retry and idempotency contract
508
+
509
+ Caller-driven calls are designed to be retried aggressively (for example by a Temporal retry policy). The exact guarantees:
510
+
511
+ - **Create is idempotent on `id`.** Calling `createExperiment` again with the same caller-supplied `id` returns the existing experiment. Reusing an `id` that belongs to another dataset or an experiment with a different target fails with `EXPERIMENT_ID_CONFLICT` (HTTP `409`).
512
+ - **Results upsert on `(experimentId, itemId, attempt)`.** Re-submitting the same key updates the existing row, and the last write wins for all mutable fields. A retried worker never creates duplicates.
513
+ - **`attempt` separates deliberate trials from retries.** Retries of the same run should reuse the same `attempt` (default `0`) so they converge. To record repeated trials of an item as separate rows, submit with `attempt: 0`, `attempt: 1`, and so on.
514
+ - **The dataset version is pinned at creation.** Submissions are validated against the items visible at that version, so editing or deleting items afterward doesn't affect an in-flight experiment. Submitting an `itemId` that isn't visible at the pinned version fails with `404`.
515
+ - **Finalize is idempotent and terminal.** Finalizing an already-completed experiment returns the stored record without recomputing. After finalization, further submissions are rejected with `EXPERIMENT_ALREADY_FINALIZED` (HTTP `409`).
516
+ - **Counts are server-computed and per-item.** At finalize time Mastra rolls attempts up per item: `succeededCount` (at least one attempt without an error), `failedCount` (every attempt errored), `skippedCount` (never submitted). `succeededCount + failedCount + skippedCount === totalItems` always holds, and callers keep no completion bookkeeping. Attempt-level rows remain available via `listExperimentResults`.
517
+
433
518
  ## Configuration options
434
519
 
435
520
  ### Concurrency
@@ -28,7 +28,7 @@ A local `.env` file is optional. Environment variables stored on the platform ar
28
28
 
29
29
  On the first run the CLI prompts you to create the platform project (named after your `package.json`) and the `production` environment. Accept the prompts, or pass `--yes` to accept defaults without confirmation.
30
30
 
31
- 2. The CLI runs a preflight check before anything ships. Storage that would fall back to a local file path (which doesn't survive on the platform's ephemeral filesystem) would normally block the deploy:
31
+ 2. The CLI runs a preflight check before every deploy. Storage that would fall back to a local file path (which doesn't survive on the platform's ephemeral filesystem) would normally block the deploy:
32
32
 
33
33
  ```text
34
34
  file:./mastra.db will be used at runtime because TURSO_DATABASE_URL is not set
@@ -48,6 +48,8 @@ A local `.env` file is optional. Environment variables stored on the platform ar
48
48
 
49
49
  The environment slug (`production` above) matches the environment the CLI would have deployed to — this is important because `mastra env db create` requires an environment argument in non-interactive shells when the project has more than one environment.
50
50
 
51
+ Preflight also catches database URLs that point at your local machine. A `.env` file with `REDIS_URL=redis://localhost:6379` works during development, but the deployed server can't reach your laptop — so the CLI warns and offers the same managed provisioning. If you decline, the deploy continues with your value as-is; if you accept, the managed database's connection variables take precedence at deploy time while your local `.env` keeps working for development.
52
+
51
53
  > **Note:** If preflight reports a hard-coded local path instead (`Build contains a host-local storage URL`), it can't offer the inline fix — guard the path with an environment variable first so the file is only used during local development:
52
54
  >
53
55
  > ```ts
@@ -74,15 +74,18 @@ export const mastra = new Mastra({
74
74
 
75
75
  `PlatformFilesystem` and `PlatformSandbox` have different lifecycles, which matters when you design agents.
76
76
 
77
- **`PlatformFilesystem` is a long-lived handle to the environment's bucket.** All requests, all agents, and all sandboxes in the environment read and write the same object storage. Anything an agent writes is visible on the next request unless you explicitly delete it.
77
+ **`PlatformFilesystem` is a long-lived handle to the environment's bucket.** All requests and agents that use this provider read and write the same object storage. Anything an agent writes is visible on the next request unless you explicitly delete it.
78
78
 
79
79
  **`PlatformSandbox` is a client for provisioning ephemeral sandboxes.** Each `PlatformSandbox` instance owns one remote sandbox:
80
80
 
81
81
  - `start()` provisions a fresh sandbox (or reattaches when you passed `sandboxId`).
82
82
  - `executeCommand()` runs commands against it.
83
- - `destroy()` tears the sandbox down. `stop()` is an alias.
83
+ - `stop()` tears down the remote sandbox while preserving its recovery checkpoint when it has one.
84
+ - `destroy()` also releases the recovery checkpoint associated with a caller-supplied recovery `id`.
84
85
 
85
- The `sandbox` you pass to `Workspace` provides the tools an agent uses inside its own request. When your agent needs another isolated environment, for example a per-task workspace, a per-user tenant, or a background job that shouldn't touch the caller's shell state, construct another `PlatformSandbox`:
86
+ One statically configured `PlatformSandbox` is shared by every request and agent using that configuration. Requests and memory threads don't receive separate sandboxes automatically. `PlatformFilesystem` is also a separate provider: configuring both providers doesn't mount the environment bucket inside the sandbox.
87
+
88
+ When your agent needs another isolated environment, for example a per-task sandbox, a per-user tenant, or a background job that shouldn't touch shared shell state, construct another `PlatformSandbox`:
86
89
 
87
90
  ```typescript
88
91
  import { PlatformSandbox } from '@mastra/platform-workspace'
@@ -2,52 +2,40 @@
2
2
 
3
3
  # Filesystem
4
4
 
5
- Filesystems give agents persistent storage for source code, documents, datasets, and generated artifacts.
5
+ A filesystem gives an agent tools for reading, writing, listing, and [searching](https://mastra.ai/docs/sandbox/search) files. Use it as a knowledge base or to persist files between ephemeral sandbox runs.
6
6
 
7
- Mastra supports two mutually exclusive ways to add a filesystem to a workspace:
7
+ Configure files in two ways:
8
8
 
9
- | Configuration | Agent access | Sandbox access |
10
- | ------------- | ------------ | ---------------------------------------------- |
11
- | `mounts` | File tools | Files appear as local directories through FUSE |
12
- | `filesystem` | File tools | No access |
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).
13
11
 
14
- Use `mounts` when a sandbox needs to run commands against the files. The agent can use file tools, while code inside the sandbox can use shell commands and libraries against the same storage.
12
+ Configure either `filesystem` or `mounts`, not both. Configuring both throws a `WorkspaceError` with the code `INVALID_CONFIG`.
15
13
 
16
- Use `filesystem` when the agent only needs direct file tools or the sandbox backend doesn't support mounts. The agent acts as the driver: it can read a file and pass its contents to another tool, but the file doesn't exist inside the sandbox. A command such as `cat`, `grep`, or `python script.py` can't access it unless your application passes the content as input.
14
+ Both configurations give the agent file tools. Sandbox commands can only see a provider when the sandbox can mount it successfully.
17
15
 
18
- You can't configure both `filesystem` and `mounts` on the same workspace.
16
+ Configuring a filesystem gives the agent these tools:
19
17
 
20
- ## When to use filesystems
21
-
22
- Mount a filesystem when you want to:
23
-
24
- - Seed an ephemeral sandbox with source code, datasets, or project files.
25
- - Persist files after the sandbox stops or is deleted.
26
- - Let commands and agent file tools work against the same storage.
27
- - Share a storage location across multiple sandbox runs.
28
-
29
- Use a workspace-only filesystem when you want to:
30
-
31
- - Give an agent access to a managed knowledge base without command execution.
32
- - Read documents uploaded by non-technical teammates to services such as S3 or Google Drive.
33
- - Use persistent storage with a sandbox backend that doesn't support mounts.
34
- - Keep the storage provider separate from the sandbox execution environment.
35
-
36
- For example, a real-estate agent could read travel-policy documents from an S3 bucket or Google Drive folder and use them while answering questions. The agent can search and read those files without needing a sandbox to execute commands against them.
37
-
38
- A data agent could also download reports from Google Drive with file tools. It can pass their contents to a sandbox for analysis and presentation generation, then upload the finished presentation for the team. This works even when the sandbox backend can't mount Google Drive because the agent transfers the input and output between the filesystem and sandbox.
18
+ | Tool | Does |
19
+ | ------------ | ---------------------------------------------------------------------------- |
20
+ | `read_file` | Reads all or part of a file. |
21
+ | `write_file` | Creates or overwrites a file. |
22
+ | `edit_file` | Replaces matching text in an existing file. |
23
+ | `list_files` | Lists files and directories. |
24
+ | `delete` | Deletes a file or directory. |
25
+ | `file_stat` | Returns metadata about a file or directory. |
26
+ | `mkdir` | Creates a directory and any missing parent directories. |
27
+ | `grep` | Searches file contents with a regular expression. |
28
+ | `ast_edit` | Applies structural code edits. Available when `@ast-grep/napi` is installed. |
39
29
 
40
30
  ## Supported filesystems
41
31
 
42
32
  ### `LocalFilesystem`
43
33
 
44
- [`LocalFilesystem`](https://mastra.ai/reference/workspace/local-filesystem) stores files in a directory on the same machine as your Mastra application. Use it for local development or when the application already has access to the files on disk.
45
-
46
- `LocalFilesystem` contains file-tool access within its configured `basePath` by default. You can allow specific paths outside that directory or disable containment when the application requires broader host access.
34
+ [`LocalFilesystem`](https://mastra.ai/reference/workspace/local-filesystem) connects to a directory on the same machine as your Mastra application. Use it for local development or files that already live on the application host.
47
35
 
48
36
  ### Other filesystems
49
37
 
50
- Use another filesystem when files need to persist outside the application host or already live in an external service:
38
+ Use another provider when files live outside the application host or already exist in an external service:
51
39
 
52
40
  - [AgentFS](https://mastra.ai/integrations/file-storage/agentfs)
53
41
  - [Amazon S3](https://mastra.ai/integrations/file-storage/amazon-s3)
@@ -59,15 +47,33 @@ Use another filesystem when files need to persist outside the application host o
59
47
  - [Mesa](https://mastra.ai/integrations/file-storage/mesa)
60
48
  - [Vercel Files](https://mastra.ai/integrations/file-storage/vercel-files)
61
49
 
62
- Workspace-only file tools work with the full provider list. FUSE mount support depends on both the filesystem and sandbox backend, so check both references before choosing a combination.
50
+ Every provider supports file tools through `filesystem`. Sandbox mounting depends on both the filesystem provider and sandbox backend.
63
51
 
64
- ## Mounts
52
+ ## Direct filesystem access
53
+
54
+ Use `filesystem` when the agent should access a provider through file tools. It can also accept a resolver or a manually created `CompositeFilesystem`.
65
55
 
66
- Use mounts when both the agent and sandbox commands need access to the same files.
56
+ Give an agent file tools for a local knowledge base:
67
57
 
68
- ### Quickstart
58
+ ```typescript
59
+ import { LocalFilesystem, Workspace } from '@mastra/core/workspace'
69
60
 
70
- Mount an S3 filesystem into a sandbox:
61
+ const workspace = new Workspace({
62
+ filesystem: new LocalFilesystem({
63
+ basePath: './knowledge-base',
64
+ }),
65
+ })
66
+ ```
67
+
68
+ The agent receives tools such as `read_file`, `write_file`, `list_files`, and `grep`. Paths are resolved under `./knowledge-base`, and no sandbox or command tools are required.
69
+
70
+ See [Search](https://mastra.ai/docs/sandbox/search) to index the files for keyword or semantic retrieval. See the [filesystem tools reference](https://mastra.ai/reference/workspace/workspace-class) for every generated tool and its policies.
71
+
72
+ ## Mounts
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.
75
+
76
+ For example, mount an S3 bucket at `/workspace` inside a Daytona sandbox:
71
77
 
72
78
  ```typescript
73
79
  import { Mastra } from '@mastra/core'
@@ -79,7 +85,7 @@ const workspace = new Workspace({
79
85
  mounts: {
80
86
  '/workspace': new S3Filesystem({
81
87
  bucket: process.env.S3_BUCKET!,
82
- region: process.env.S3_REGION!,
88
+ region: 'us-east-1',
83
89
  }),
84
90
  },
85
91
  sandbox: new DaytonaSandbox(),
@@ -88,32 +94,33 @@ const workspace = new Workspace({
88
94
  export const mastra = new Mastra({ workspace })
89
95
  ```
90
96
 
91
- Existing objects in the bucket seed the sandbox at `/workspace`. Files created there persist in S3 after the sandbox stops. All agents registered with this `Mastra` instance inherit the workspace.
92
-
93
97
  ### Using mounts
94
98
 
95
- The agent receives file tools for mounted storage:
99
+ Files already available through the provider appear under `/workspace`. The agent can call `read_file('/workspace/report.md')` or run `cat /workspace/report.md`. Files written through either interface are saved by the provider and remain available after the sandbox stops.
100
+
101
+ Without a mount, an agent can still combine file tools with sandbox commands by passing file contents between tool calls. This works for selected text files. Mounts are a better fit when programs expect a directory tree or need to process large or binary files.
102
+
103
+ > **Warning:** File-tool and command-tool policies are independent. Requiring approval for `write_file`, for example, doesn't stop the agent from using `execute_command` to write the same mounted path. If files must only be accessed through file tools, disable sandbox command tools.
104
+
105
+ ### Paths
106
+
107
+ A mount path is the path exposed to file tools and, after a successful sandbox mount, commands. The underlying provider may store those files somewhere else.
108
+
109
+ For a remote provider mounted at `/workspace`:
96
110
 
97
- | Tool | Does |
98
- | ------------ | ---------------------------------------------------------- |
99
- | `read_file` | Reads text or binary file contents. |
100
- | `write_file` | Creates or replaces a file. |
101
- | `edit_file` | Applies targeted edits to a text file. |
102
- | `list_files` | Lists files and directories, with optional glob filtering. |
103
- | `file_stat` | Returns file metadata. |
104
- | `mkdir` | Creates a directory. |
105
- | `delete` | Deletes files or directories. |
106
- | `grep` | Searches file contents with a regular expression. |
111
+ - File tools use paths such as `/workspace/report.md`.
112
+ - Remote sandbox commands generally use the same path.
113
+ - The provider maps the path to its configured bucket, prefix, or remote directory.
107
114
 
108
- Mounted files are also available to commands inside the sandbox. In the Quickstart, `read_file('/workspace/report.md')` and `cat /workspace/report.md` read the same S3 object.
115
+ `LocalSandbox` creates a symlink inside its `workingDirectory` instead. A `/workspace` mount becomes `<workingDirectory>/workspace` on the host and is usually addressed as `workspace` by commands.
109
116
 
110
- Use `WORKSPACE_TOOLS.FILESYSTEM` to require approval, disable tools, enforce read-before-write, or change output limits. See the [Workspace filesystem tools reference](https://mastra.ai/reference/workspace/workspace-class) for configuration details.
117
+ ## Composite filesystem
111
118
 
112
- > **Warning:** File-tool policies only apply to file-tool calls. Restrictions such as `allowedPaths`, approval rules, or read-before-write don't constrain shell commands running inside the sandbox. A command can access any mounted path allowed by the sandbox backend and mount configuration. If the agent must not bypass file-tool policies through the shell, disable its sandbox command tools.
119
+ The `mounts` option creates a `CompositeFilesystem`. This TypeScript path router presents several providers as one virtual directory tree.
113
120
 
114
- ### Multiple mounts
121
+ ### Multiple filesystems
115
122
 
116
- The `mounts` option creates a `CompositeFilesystem` that routes paths to storage providers by mount prefix. Supported sandbox backends expose those providers as local directories through FUSE.
123
+ Configure each provider under its mount path:
117
124
 
118
125
  ```typescript
119
126
  import { Workspace } from '@mastra/core/workspace'
@@ -137,131 +144,105 @@ const workspace = new Workspace({
137
144
 
138
145
  With this configuration:
139
146
 
140
- - Existing objects under `/data` and `/reports` seed the sandbox when the mounts become available.
141
- - Agent file tools route each path to its corresponding storage provider.
142
- - Commands inside the sandbox access the same paths.
143
- - New and updated files persist in the underlying buckets.
147
+ - `list_files('/')` returns the virtual directories `data` and `reports`.
148
+ - `read_file('/data/input.csv')` routes to the S3 provider.
149
+ - `write_file('/reports/summary.md')` routes to the Google Cloud Storage provider.
150
+ - Sandbox commands use the same paths when each sandbox mount succeeds.
144
151
 
145
- All file paths must start with a mount prefix. Listing `/` returns a virtual directory for each mount. Mount paths can't be nested, so a workspace can't mount both `/data` and `/data/archive`.
152
+ The composite strips the mount prefix before passing the remaining path to its provider. Mount paths can't be nested, so the same composite can't contain both `/data` and `/data/archive`.
146
153
 
147
- Mount support varies by sandbox backend and filesystem provider. Check both references before choosing a combination.
154
+ ### Without a sandbox
148
155
 
149
- ### Read-only mounts
156
+ You can configure `mounts` without a sandbox. Mastra still creates the `CompositeFilesystem`, and all file tools work with mount-prefixed paths. The agent receives no command or process tools because no sandbox is configured.
150
157
 
151
- Set `readOnly: true` on a filesystem provider when the sandbox and agent should only read seeded files. Mastra excludes write tools for provider objects known to be read-only, and the mount backend enforces its own write restrictions.
158
+ ### Manual composition
159
+
160
+ You can also create a `CompositeFilesystem` yourself and pass it through `filesystem` for path routing without automatic sandbox mount wiring:
152
161
 
153
162
  ```typescript
154
- const workspace = new Workspace({
163
+ import { CompositeFilesystem, LocalFilesystem, Workspace } from '@mastra/core/workspace'
164
+ import { S3Filesystem } from '@mastra/s3'
165
+
166
+ const filesystem = new CompositeFilesystem({
155
167
  mounts: {
156
- '/policies': new S3Filesystem({
157
- bucket: 'company-policies',
168
+ '/local': new LocalFilesystem({ basePath: './data' }),
169
+ '/archive': new S3Filesystem({
170
+ bucket: 'agent-archive',
158
171
  region: 'us-east-1',
159
- readOnly: true,
160
172
  }),
161
173
  },
162
- sandbox: new DaytonaSandbox(),
163
174
  })
164
- ```
165
-
166
- ### Per-user or per-thread mounts
167
-
168
- Workspace-level `mounts` require a sandbox provider object and can't be combined with a sandbox resolver. For one sandbox and storage prefix per user or thread, create and mount the filesystem inside the sandbox resolver. See [Multi-tenant sandboxes](https://mastra.ai/docs/sandbox/overview) for a complete example.
169
-
170
- ## Workspace-only filesystem
171
-
172
- Pass a provider to `filesystem` when only the agent needs file access. The agent receives file tools, but a sandbox configured on the same workspace can't see those files.
173
175
 
174
- ### Quickstart
175
-
176
- ```typescript
177
- import { LocalSandbox, Workspace } from '@mastra/core/workspace'
178
- import { GoogleDriveFilesystem } from '@mastra/google-drive'
179
-
180
- const workspace = new Workspace({
181
- filesystem: new GoogleDriveFilesystem({
182
- folderId: process.env.GOOGLE_DRIVE_FOLDER_ID!,
183
- accessToken: process.env.GOOGLE_DRIVE_ACCESS_TOKEN!,
184
- }),
185
- sandbox: new LocalSandbox({
186
- workingDirectory: './workspace',
187
- }),
188
- })
176
+ const workspace = new Workspace({ filesystem })
189
177
  ```
190
178
 
191
- The agent receives the same file tools listed under [Using mounts](#using-mounts), plus sandbox command tools. It can use both in one task. For the prompt "Format `draft.md` with Prettier and save it as `formatted.md`", the agent can:
179
+ Use manual composition when another part of your application needs the composite as a filesystem provider. For normal sandbox mounting, the `mounts` option performs the same routing setup with less configuration.
192
180
 
193
- 1. Read `draft.md` from Google Drive with `read_file`.
194
- 2. Pass the returned content to `execute_command`, for example by piping it into Prettier.
195
- 3. Write the command output to `formatted.md` with `write_file`.
181
+ ## Mount availability
196
182
 
197
- The Google Drive files never appear inside the sandbox. Only the content passed to `execute_command` crosses into the execution environment, and only the returned output is written back.
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.
198
184
 
199
- ### Seed a filesystem
185
+ Built-in sandbox mounting currently includes:
200
186
 
201
- Files already in the folder seed the workspace. You can also seed any writable provider through its API before the agent uses it:
187
+ | Sandbox backend | Filesystem providers |
188
+ | --------------- | --------------------------------------------------- |
189
+ | `LocalSandbox` | `LocalFilesystem` |
190
+ | E2B | Amazon S3, Google Cloud Storage, Azure Blob Storage |
191
+ | Daytona | Amazon S3, Google Cloud Storage, Azure Blob Storage |
192
+ | Blaxel | Amazon S3, Google Cloud Storage |
202
193
 
203
- ```typescript
204
- await workspace.filesystem?.writeFile(
205
- 'travel-policy.md',
206
- '# Travel policy\n\nEmployees may book economy flights.',
207
- )
208
- ```
194
+ Remote mounts may require `s3fs`, `gcsfuse`, or `blobfuse2` inside the sandbox. Some backends install missing software during startup, which can require network access and additional startup time. Other combinations require an [`onMount`](https://mastra.ai/reference/workspace/workspace-class) hook or provider-specific setup.
209
195
 
210
- Filesystem operations initialize the provider on first use. Writes replace existing files by default. Pass `{ overwrite: false }` when existing content must be preserved.
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.
211
197
 
212
- ### Containment
198
+ ## Multi-tenant filesystems
213
199
 
214
- `LocalFilesystem` uses contained mode by default. File tools can access `basePath` but can't traverse into unrelated host paths through absolute paths, `..`, or symlinks.
200
+ The `filesystem` option accepts a resolver when storage should vary by request, user, role, or tenant:
215
201
 
216
202
  ```typescript
217
- import { LocalFilesystem, Workspace } from '@mastra/core/workspace'
218
-
219
203
  const workspace = new Workspace({
220
- filesystem: new LocalFilesystem({
221
- basePath: './knowledge-base',
222
- allowedPaths: ['../shared-policies'],
223
- }),
204
+ filesystem: ({ requestContext }) => {
205
+ const tenantId = requestContext.get('tenant-id') as string
206
+
207
+ return new S3Filesystem({
208
+ bucket: process.env.S3_BUCKET!,
209
+ region: process.env.S3_REGION!,
210
+ prefix: `tenants/${tenantId}`,
211
+ })
212
+ },
224
213
  })
225
214
  ```
226
215
 
227
- Containment still matters even though the agent accesses files through tools. It limits what those tools can expose. Prefer `allowedPaths` for specific external directories instead of setting `contained: false`.
216
+ Each tenant gets its own filesystem view, and file tools resolve the provider from the request context automatically.
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).
228
219
 
229
- ### Read-only mode
220
+ ## Policies and containment
230
221
 
231
- Set `readOnly: true` when the agent should use seeded content without changing it:
222
+ Use individual constants under `WORKSPACE_TOOLS.FILESYSTEM`, such as `WORKSPACE_TOOLS.FILESYSTEM.WRITE_FILE`, to configure each file tool. Set approval globally or per tool. Configure read-before-write and output limits on individual filesystem tool entries. See the [filesystem tool configuration reference](https://mastra.ai/reference/workspace/workspace-class).
223
+
224
+ `LocalFilesystem` contains access within `basePath` by default. Use `allowedPaths` when the agent needs specific directories outside that root:
232
225
 
233
226
  ```typescript
234
227
  const workspace = new Workspace({
235
228
  filesystem: new LocalFilesystem({
236
229
  basePath: './knowledge-base',
237
- readOnly: true,
230
+ allowedPaths: ['../shared-policies'],
238
231
  }),
239
232
  })
240
233
  ```
241
234
 
242
- For a provider object, Mastra removes write, edit, delete, and directory-creation tools. For a resolver-backed filesystem, the tools remain registered because the provider isn't known until execution. Write attempts are rejected at runtime.
243
-
244
- ### Multi-tenant filesystems
235
+ Containment applies to filesystem operations, including file tools and search indexing. It doesn't restrict shell commands.
245
236
 
246
- Use a resolver when each request, user, role, or tenant needs different storage:
237
+ Set `readOnly: true` when the agent should use files without changing them. For a static provider, Mastra removes write-related file tools.
247
238
 
248
- ```typescript
249
- const workspace = new Workspace({
250
- filesystem: ({ requestContext }) => {
251
- const tenantId = requestContext.get('tenant-id') as string
252
- return new S3Filesystem({
253
- bucket: process.env.S3_BUCKET!,
254
- region: process.env.S3_REGION!,
255
- prefix: `tenants/${tenantId}`,
256
- })
257
- },
258
- })
259
- ```
239
+ With a filesystem resolver, write tools remain registered because the provider isn't known until the request runs. The provider rejects write attempts at runtime.
260
240
 
261
- Each tenant's prefix seeds its own workspace view. Workspace tools resolve the filesystem from the request context automatically.
241
+ For `LocalFilesystem` entries in a composite, keep `contained: true`, which is the default. Set `contained: false` only when unrestricted host filesystem access is intentional.
262
242
 
263
243
  ## Related
264
244
 
265
- - [Sandbox](https://mastra.ai/docs/sandbox/overview)
266
- - [`WorkspaceFilesystem` reference](https://mastra.ai/reference/workspace/filesystem)
267
- - [`Workspace` reference](https://mastra.ai/reference/workspace/workspace-class)
245
+ - [Sandboxes](https://mastra.ai/docs/sandbox/overview)
246
+ - [Search](https://mastra.ai/docs/sandbox/search)
247
+ - [Filesystem provider interface](https://mastra.ai/reference/workspace/filesystem)
248
+ - [Configuration API reference](https://mastra.ai/reference/workspace/workspace-class)