@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.
- package/.docs/docs/datasets/running-experiments.md +86 -1
- package/.docs/docs/mastra-platform/deploy.md +3 -1
- package/.docs/docs/mastra-platform/workspaces.md +6 -3
- package/.docs/docs/sandbox/filesystem.md +120 -139
- package/.docs/docs/sandbox/lsp.md +195 -143
- package/.docs/docs/sandbox/overview.md +103 -69
- package/.docs/docs/sandbox/search.md +172 -153
- package/.docs/docs/sandbox/skills.md +94 -151
- package/.docs/integrations/deploy/render.md +136 -89
- package/.docs/integrations/observability/arize.md +8 -6
- package/.docs/models/index.md +1 -1
- package/.docs/models/providers/edenai.md +2 -3
- package/.docs/models/providers/empiriolabs.md +1 -1
- package/.docs/models/providers/kilo.md +2 -2
- package/.docs/models/providers/llmgateway.md +2 -1
- package/.docs/models/providers/nano-gpt.md +3 -1
- package/.docs/models/providers/ofox.md +1 -1
- package/.docs/models/providers/opencode.md +65 -65
- package/.docs/reference/cli/mastra.md +2 -2
- package/.docs/reference/client-js/datasets.md +146 -0
- package/.docs/reference/configuration.md +58 -0
- package/.docs/reference/datasets/createExperiment.md +76 -0
- package/.docs/reference/datasets/finalizeExperiment.md +43 -0
- package/.docs/reference/datasets/runExperimentItem.md +55 -0
- package/.docs/reference/datasets/submitExperimentResult.md +56 -0
- package/.docs/reference/index.md +5 -0
- package/.docs/reference/observability/tracing/exporters/langfuse.md +2 -0
- package/.docs/reference/pubsub/redis-streams.md +11 -1
- package/.docs/reference/rag/metadata-filters.md +16 -8
- package/.docs/reference/rag/retrieval.md +113 -5
- package/.docs/reference/server/routes.md +111 -0
- package/CHANGELOG.md +14 -0
- 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
|
|
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
|
|
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
|
|
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
|
-
- `
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
7
|
+
Configure files in two ways:
|
|
8
8
|
|
|
9
|
-
|
|
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
|
-
|
|
12
|
+
Configure either `filesystem` or `mounts`, not both. Configuring both throws a `WorkspaceError` with the code `INVALID_CONFIG`.
|
|
15
13
|
|
|
16
|
-
|
|
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
|
-
|
|
16
|
+
Configuring a filesystem gives the agent these tools:
|
|
19
17
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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)
|
|
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
|
|
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
|
-
|
|
50
|
+
Every provider supports file tools through `filesystem`. Sandbox mounting depends on both the filesystem provider and sandbox backend.
|
|
63
51
|
|
|
64
|
-
##
|
|
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
|
-
|
|
56
|
+
Give an agent file tools for a local knowledge base:
|
|
67
57
|
|
|
68
|
-
|
|
58
|
+
```typescript
|
|
59
|
+
import { LocalFilesystem, Workspace } from '@mastra/core/workspace'
|
|
69
60
|
|
|
70
|
-
|
|
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:
|
|
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
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
|
|
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
|
-
|
|
117
|
+
## Composite filesystem
|
|
111
118
|
|
|
112
|
-
|
|
119
|
+
The `mounts` option creates a `CompositeFilesystem`. This TypeScript path router presents several providers as one virtual directory tree.
|
|
113
120
|
|
|
114
|
-
### Multiple
|
|
121
|
+
### Multiple filesystems
|
|
115
122
|
|
|
116
|
-
|
|
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
|
-
-
|
|
141
|
-
-
|
|
142
|
-
-
|
|
143
|
-
-
|
|
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
|
-
|
|
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
|
-
|
|
154
|
+
### Without a sandbox
|
|
148
155
|
|
|
149
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
'/
|
|
157
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
185
|
+
Built-in sandbox mounting currently includes:
|
|
200
186
|
|
|
201
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
198
|
+
## Multi-tenant filesystems
|
|
213
199
|
|
|
214
|
-
`
|
|
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:
|
|
221
|
-
|
|
222
|
-
|
|
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
|
-
|
|
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
|
-
|
|
220
|
+
## Policies and containment
|
|
230
221
|
|
|
231
|
-
|
|
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
|
-
|
|
230
|
+
allowedPaths: ['../shared-policies'],
|
|
238
231
|
}),
|
|
239
232
|
})
|
|
240
233
|
```
|
|
241
234
|
|
|
242
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
- [
|
|
266
|
-
- [
|
|
267
|
-
- [
|
|
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)
|