Workspace class
Added in: @mastra/core@1.1.0
The Workspace class combines a filesystem and sandbox to provide agents with file storage and command execution capabilities. It also supports BM25 and vector search for indexed content.
Usage exampleDirect link to Usage example
import { Workspace, LocalFilesystem, LocalSandbox } from '@mastra/core/workspace'
const workspace = new Workspace({
id: 'my-workspace',
name: 'My Workspace',
filesystem: new LocalFilesystem({
basePath: './workspace',
}),
sandbox: new LocalSandbox({
workingDirectory: './workspace',
}),
bm25: true,
autoIndexPaths: ['docs'],
})
Constructor parametersDirect link to Constructor parameters
id?:
name?:
filesystem?:
requestContext and returns a filesystem per request. See filesystems per user or thread.sandbox?:
requestContext and returns a sandbox per request. See sandboxes per user or thread.instructions.dynamicSandbox?:
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.sandboxCacheKey?:
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.bm25?:
vectorStore?:
embedder?:
vectorStore is set. Accepts either a single-text function (text: string) => Promise<number[]> or a batch-capable function (texts: string[]) => Promise<number[][]> that has a batch: true property and an optional maxBatchSize. See Batch embedding.autoIndexPaths?:
skills?:
skillSource?:
onMount?:
searchIndexName?:
tools?:
enabled?:
requireApproval?:
writeLockTimeoutMs?:
hooks?:
operationTimeout?:
For a resolver-backed sandbox, the default 'placeholder' mode avoids provisioning a sandbox while workspace instructions are built. Set instructions.dynamicSandbox to 'resolve' when the instructions must come from the resolved sandbox:
const workspace = new Workspace({
sandbox: ({ requestContext }) => resolveSandbox(requestContext),
instructions: { dynamicSandbox: 'resolve' },
})
This calls the resolver while building instructions. To describe a request-specific environment without provisioning it, pass a function that returns instruction text from requestContext instead.
Tool configurationDirect link to Tool configuration
The tools option accepts a WorkspaceToolsConfig object that controls which workspace tools are enabled and their safety settings.
import { Workspace } from '@mastra/core/workspace'
import { WORKSPACE_TOOLS } from '@mastra/core/workspace'
const workspace = new Workspace({
id: 'my-workspace',
name: 'My Workspace',
tools: {
// Global defaults (apply to all tools)
enabled: true,
requireApproval: false,
// Per-tool overrides using WORKSPACE_TOOLS constants
[WORKSPACE_TOOLS.FILESYSTEM.WRITE_FILE]: {
requireApproval: true,
},
},
})
The config object has two types of settings:
- Global settings:
enabledandrequireApprovalapply to all workspace tools unless overridden.hooksandwriteLockTimeoutMsare global-only and can't be configured per tool. - Per-tool overrides: Use
WORKSPACE_TOOLSconstants as keys to configure individual tools
Per-tool overrides accept the following options:
enabled?:
requireApproval?:
name?:
mastra_workspace_* name.requireReadBeforeWrite?:
maxOutputTokens?:
ToolConfigContext contains { requestContext, workspace }. Execution-time policies use ToolConfigWithArgsContext, which also contains the tool's args.
import { Workspace, WORKSPACE_TOOLS } from '@mastra/core/workspace'
const workspace = new Workspace({
id: 'my-workspace',
tools: {
enabled: ({ requestContext }) => requestContext.get('workspace-tools') === true,
[WORKSPACE_TOOLS.FILESYSTEM.WRITE_FILE]: {
requireApproval: ({ args }) => (args.path as string).startsWith('/protected'),
requireReadBeforeWrite: true,
},
[WORKSPACE_TOOLS.SANDBOX.EXECUTE_COMMAND]: {
maxOutputTokens: 5000,
},
},
})
See Filesystem tools, Sandbox tools, LSP inspection, and Search tools for capability-specific configuration.
Tool name remappingDirect link to Tool name remapping
Rename workspace tools by setting the name property on individual tool configs. The config key remains the original constant: only the name exposed to the agent changes.
import { Workspace } from '@mastra/core/workspace'
import { WORKSPACE_TOOLS } from '@mastra/core/workspace'
const workspace = new Workspace({
id: 'my-workspace',
name: 'My Workspace',
tools: {
[WORKSPACE_TOOLS.FILESYSTEM.READ_FILE]: { name: 'view' },
[WORKSPACE_TOOLS.FILESYSTEM.GREP]: { name: 'search_content' },
},
})
Tool names must be unique across all workspace tools. Setting a custom name that conflicts with another tool's default or custom name throws an error.
Tool hooksDirect link to Tool hooks
Set tools.hooks to run logic before and after every enabled workspace tool call. Hooks run after name remapping, so the context includes both the exposed toolName and the original workspaceToolName.
import { Workspace } from '@mastra/core/workspace'
const workspace = new Workspace({
id: 'my-workspace',
tools: {
hooks: {
beforeToolCall: ({ toolName, workspaceToolName, input }) => {
console.log(`Running ${toolName} (${workspaceToolName})`, input)
},
afterToolCall: ({ toolName, output, error }) => {
console.log(`Finished ${toolName}`, { output, error })
},
},
},
})
beforeToolCall?:
{ toolName, workspaceToolName, input, context }. Return { proceed: false, output } to skip the tool call and use output as its result.afterToolCall?:
{ toolName, workspaceToolName, input, context, output, error }. output is undefined when the tool throws, and error is set instead.If the owning agent also defines tool hooks, workspace hooks run inside the agent hook wrapper. The order is agent beforeToolCall → workspace beforeToolCall → tool → workspace afterToolCall → agent afterToolCall.
PropertiesDirect link to Properties
id:
name:
status:
filesystem:
undefined when a resolver function is configured — use hasFilesystemConfig() to check availability.sandbox:
undefined when a resolver function is configured — use hasSandboxConfig() to check availability.skills:
canBM25:
canVector:
canHybrid:
MethodsDirect link to Methods
LifecycleDirect link to Lifecycle
init()Direct link to init
Initialize the workspace and prepare resources.
await workspace.init()
Calling init() is optional in most cases:
- Sandbox: Auto-starts on first
executeCommand()call. Useinit()to avoid first-command latency. - Filesystem: Creates the base directory and runs any provider-specific setup. Some providers auto-create the directory on first operation.
- Search: Required only if using
autoIndexPathsfor auto-indexing.
Initialization performs:
- Starts the filesystem provider (creates base directory if needed)
- Starts the sandbox provider (creates working directory, sets up isolation if configured)
- Indexes files from
autoIndexPathsfor search
stop()Direct link to stop
Stop the workspace's live resources without destroying them.
await workspace.stop()
stop() shuts down language servers and closes the browser, then stops the sandbox provider. Remote sandbox providers suspend or pause the sandbox so it can resume later. LocalSandbox kills its background processes and unmounts filesystems. Stopping isn't a teardown. The filesystem, search index, and skills keep working, and the next sandbox operation starts the sandbox again.
mastra.shutdown() calls stop() for registered workspaces, so a process restart suspends remote sandboxes instead of deleting them. To fully tear a workspace down, call destroy() or mastra.removeWorkspace(id, { destroy: true }) explicitly.
destroy()Direct link to destroy
Destroy the workspace and clean up resources.
await workspace.destroy()
destroy() closes workspace-owned resources in order: language servers, browsers, sandbox providers, and filesystem providers. It also clears cached sandbox references.
Call destroy() when your application is done with a workspace and its sandbox. To remove a workspace from the Mastra registry, use mastra.removeWorkspace().
LocalFilesystem.destroy() doesn't delete files on disk. Resolver-backed filesystem and sandbox providers are owned by your application and must be cleaned up by your application.
Search operationsDirect link to Search operations
index(path, content, options?)Direct link to indexpath-content-options
Index content for search.
await workspace.index('/docs/guide.md', 'Guide content...')
search(query, options?)Direct link to searchquery-options
Search indexed content.
const results = await workspace.search('password reset', {
topK: 10,
mode: 'hybrid',
})
UtilityDirect link to Utility
getInfo()Direct link to getinfo
Get workspace information.
const info = await workspace.getInfo()
// { id, name, status, createdAt, lastAccessedAt, filesystem?, sandbox? }
Pass resolveDynamicProviders: false to report resolver-backed providers as runtime-defined without invoking their resolver.
const info = await workspace.getInfo({ resolveDynamicProviders: false })
Parameters:
options.includeFileCount?:
options.requestContext?:
resolveDynamicProviders is enabled.options.resolveDynamicProviders?:
false when you only need metadata and want resolver-backed providers reported as dynamic.getInstructions(opts?)Direct link to getinstructionsopts
Returns combined instructions from the filesystem and sandbox providers. Injected into the agent's system message to help it understand the execution context.
const instructions = workspace.getInstructions()
Pass requestContext to enable per-request customization when a provider's instructions option is a function:
const instructions = workspace.getInstructions({ requestContext })
Parameters:
opts.requestContext?:
instructions function on the filesystem or sandbox provider, if one is configured.Returns: string
getInstructionsAsync(opts?)Direct link to getinstructionsasyncopts
Returns combined workspace instructions. Use this when the workspace uses resolver-backed providers. A runtime-defined filesystem is resolved per request. A runtime-defined sandbox contributes stable placeholder text unless instructions.dynamicSandbox is set to 'resolve'.
const instructions = await workspace.getInstructionsAsync({ requestContext })
Parameters:
opts.requestContext?:
instructions.dynamicSandbox is 'resolve'.Returns: Promise<string>
To override the default output, pass an instructions option to LocalFilesystem or LocalSandbox.
getToolsConfig()Direct link to gettoolsconfig
Get the current tools configuration.
const config = workspace.getToolsConfig()
Returns: WorkspaceToolsConfig | undefined
setToolsConfig(config?)Direct link to settoolsconfigconfig
Replace the per-tool configuration at runtime. This performs a full replacement: it doesn't merge with the previous config. Pass undefined to reset to defaults. Changes take effect on the next agent interaction (the next createWorkspaceTools() call).
import { WORKSPACE_TOOLS } from '@mastra/core/workspace'
// Disable write tools for read-only mode
workspace.setToolsConfig({
[WORKSPACE_TOOLS.FILESYSTEM.WRITE_FILE]: { enabled: false },
[WORKSPACE_TOOLS.FILESYSTEM.EDIT_FILE]: { enabled: false },
})
// Reset to defaults
workspace.setToolsConfig(undefined)
Parameters:
config?:
Dynamic filesystemDirect link to Dynamic filesystem
hasFilesystemConfig()Direct link to hasfilesystemconfig
Check whether a filesystem is configured, either as a static instance or a resolver function. Use this instead of checking workspace.filesystem directly, because a resolver-based workspace returns undefined from the filesystem property.
if (workspace.hasFilesystemConfig()) {
// Filesystem tools are available
}
Returns: boolean
resolveFilesystem({ requestContext })Direct link to resolvefilesystem-requestcontext-
Resolve the filesystem for a request context. When a resolver function is configured, calls it with the provided requestContext. When a static filesystem is configured, returns it directly. Returns undefined if no filesystem is configured.
import { RequestContext } from '@mastra/core/request-context'
const ctx = new RequestContext([['agent-role', 'admin']])
const fs = await workspace.resolveFilesystem({ requestContext: ctx })
Parameters:
requestContext:
Returns: Promise<WorkspaceFilesystem | undefined>
Dynamic sandboxDirect link to Dynamic sandbox
hasSandboxConfig()Direct link to hassandboxconfig
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.
if (workspace.hasSandboxConfig()) {
// Sandbox tools are available
}
Returns: boolean
resolveSandbox({ requestContext })Direct link to resolvesandbox-requestcontext-
Resolve the sandbox for a 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.
import { RequestContext } from '@mastra/core/request-context'
const ctx = new RequestContext([['user-id', 'alice']])
const sandbox = await workspace.resolveSandbox({ requestContext: ctx })
Parameters:
requestContext:
Returns: Promise<WorkspaceSandbox | undefined>
clearSandboxCache(cacheKey?)Direct link to clearsandboxcachecachekey
Clear resolver-backed sandboxes cached by sandboxCacheKey. Pass a cache key to clear one entry, or omit it to clear all keyed sandbox entries.
This method doesn't clear the per-RequestContext weak cache. Those entries are garbage-collection managed.
The workspace doesn't own resolver-returned sandboxes. This method only drops workspace references. Destroy the sandbox in your own lifecycle code.
workspace.clearSandboxCache('thread-123')
workspace.clearSandboxCache()
Parameters:
cacheKey?:
Returns: void
Agent toolsDirect link to Agent tools
A workspace provides tools to agents based on what's configured.
Filesystem toolsDirect link to Filesystem tools
Added when a filesystem is configured:
| Tool | Description |
|---|---|
mastra_workspace_read_file | Read file contents. Text files return as text (with optional line range). Images and PDFs return as native media parts the model can view directly. Other binaries return metadata only unless an explicit encoding is passed. |
mastra_workspace_write_file | Create or overwrite a file with new content. Creates parent directories automatically. |
mastra_workspace_edit_file | Edit an existing file by finding and replacing text. Useful for targeted changes without rewriting the entire file. |
mastra_workspace_list_files | List directory contents as a tree structure. Supports recursive listing with depth limits, glob patterns, and .gitignore filtering (enabled by default). |
mastra_workspace_delete | Delete a file or directory. Supports recursive deletion for directories. |
mastra_workspace_file_stat | Get metadata about a file or directory including size, type, and modification time. |
mastra_workspace_mkdir | Create a directory. Creates parent directories automatically if they don't exist. |
mastra_workspace_grep | Search file contents using regex patterns. Supports glob filtering, context lines, and case-insensitive search. |
With a static filesystem, write tools (write_file, edit_file, delete, mkdir) are excluded when the filesystem is in read-only mode. With a runtime-defined filesystem, write tools are always included and read-only is enforced at runtime.
The read_file tool accepts mediaTypes and maxMediaBytes options to control which mime types are surfaced to the model as native media parts and how large those files can be:
mediaTypes?:
['image/*']), a custom predicate function, or false to disable media detection. Defaults to the cross-provider-safe intersection of image formats plus PDF. Only applies when the caller doesn't pass an explicit encoding.maxMediaBytes?:
const workspace = new Workspace({
filesystem: new LocalFilesystem({ basePath: './workspace' }),
tools: {
[WORKSPACE_TOOLS.FILESYSTEM.READ_FILE]: {
// Broaden to any image (including SVG, BMP, HEIC) — may fail on some providers
mediaTypes: ['image/*'],
// Raise the inline-media cap to 25 MiB
maxMediaBytes: 25 * 1024 * 1024,
},
},
})
Sandbox toolsDirect link to Sandbox tools
Added when a sandbox is configured:
| Tool | Description |
|---|---|
mastra_workspace_execute_command | Execute a shell command. Returns stdout, stderr, and exit code. When the sandbox has a process manager, accepts background: true to spawn a long-running process and return a PID. |
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. |
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. |
With a static sandbox, capability checks (executeCommand, processes) decide which tool variants are exposed. With a runtime-defined sandbox, all sandbox tools are registered and the runtime throws a clear error if the resolved sandbox doesn't implement a requested capability.
The execute_command tool accepts a backgroundProcesses option for lifecycle callbacks on background processes:
backgroundProcesses?:
onStdout?:
onStderr?:
onExit?:
abortSignal?:
See Background processes for callback examples.
Search toolsDirect link to Search tools
Added when BM25 or vector search is configured:
| Tool | Description |
|---|---|
mastra_workspace_search | Search indexed content using keyword (BM25), semantic (vector), or hybrid search. Returns ranked results with scores. |
mastra_workspace_index | Index content for search. Associates content with a path for later retrieval. |
The index tool is excluded when the filesystem is in read-only mode.
Skill toolsDirect link to Skill tools
Added when skills are configured:
| Tool | Description |
|---|---|
skill | Activate a skill by name or path. Returns the skill's full instructions, references, scripts, and assets. |
skill_search | Search across skill content. Accepts an optional list of skill names to filter and a topK parameter. |
skill_read | Read a specific file (reference, script, or asset) from a skill directory. |
When multiple skills share the same name, list() returns all of them. get() with a name applies tie-breaking (local > managed > external). If two skills share the same name and source type, get() throws an error. Pass a skill's full path to get() to bypass tie-breaking. See Skills with the same name for details.