docs/src/content/en/reference/workspace/local-filesystem.mdx
Added in: @mastra/[email protected]
Stores files in a directory on the local filesystem.
:::info
For interface details, see WorkspaceFilesystem Interface.
:::
Add a LocalFilesystem to a workspace and assign it to an agent. The agent can then read, write, and manage files as part of its tasks:
import { Agent } from '@mastra/core/agent'
import { Workspace, LocalFilesystem } from '@mastra/core/workspace'
const workspace = new Workspace({
filesystem: new LocalFilesystem({
basePath: './workspace',
}),
})
const agent = new Agent({
id: 'file-agent',
model: 'openai/gpt-5.4',
workspace,
})
// The agent now has filesystem tools available
const response = await agent.generate('List all files in the workspace')
<PropertiesTable
content={[
{
name: 'basePath',
type: 'string',
description: 'Base directory path on disk. All file paths are resolved relative to this directory.',
},
{
name: 'id',
type: 'string',
description: 'Unique identifier for this filesystem instance',
isOptional: true,
defaultValue: 'Auto-generated',
},
{
name: 'contained',
type: 'boolean',
description:
'When true, all file operations are restricted to stay within basePath. Prevents path traversal attacks and symlink escapes. See containment.',
isOptional: true,
defaultValue: 'true',
},
{
name: 'allowedPaths',
type: 'string[]',
description: 'Additional directories the agent can access outside of basePath.',
isOptional: true,
defaultValue: '[]',
},
{
name: 'instructions',
type: 'string | ((opts: { defaultInstructions: string; requestContext?: RequestContext }) => string)',
description:
'Custom instructions that override the default instructions returned by getInstructions(). Pass a string to fully replace them, or a function to extend them with access to the current requestContext for per-request customization.',
isOptional: true,
},
{
name: 'readOnly',
type: 'boolean',
description: 'When true, all write operations are blocked. Read operations are still allowed.',
isOptional: true,
defaultValue: 'false',
},
]}
/>
<PropertiesTable content={[ { name: 'id', type: 'string', description: 'Filesystem instance identifier', }, { name: 'name', type: 'string', description: "Provider name ('LocalFilesystem')", }, { name: 'provider', type: 'string', description: "Provider identifier ('local')", }, { name: 'basePath', type: 'string', description: 'The absolute base path on disk', }, { name: 'readOnly', type: 'boolean | undefined', description: 'Whether the filesystem is in read-only mode', }, { name: 'allowedPaths', type: 'readonly string[]', description: 'Current set of resolved allowed paths. These paths are permitted beyond basePath when containment is enabled.', }, ]} />
init()Initialize the filesystem. Creates the base directory if it doesn't exist.
await filesystem.init()
Called by workspace.init().
LocalFilesystem initializes on first operation if not already initialized, creating the base directory automatically. Calling init() explicitly is optional but can be useful to pre-create directories before the first operation.
destroy()Clean up filesystem resources.
await filesystem.destroy()
Called by workspace.destroy().
setAllowedPaths(pathsOrUpdater)Update the allowed paths at runtime. Accepts a new paths array (replaces current) or an updater callback that receives the current paths and returns the new set.
// Set directly
filesystem.setAllowedPaths(['/home/user/.config'])
// Update with callback
filesystem.setAllowedPaths(prev => [...prev, '/home/user/documents'])
// Clear all allowed paths
filesystem.setAllowedPaths([])
Parameters:
<PropertiesTable content={[ { name: 'pathsOrUpdater', type: 'string[] | ((current: readonly string[]) => string[])', description: 'New allowed paths array or updater function receiving current paths', }, ]} />
readFile(path, options?)Read file contents.
const content = await filesystem.readFile('/docs/guide.md')
const buffer = await filesystem.readFile('/image.png', { encoding: 'binary' })
Parameters:
<PropertiesTable content={[ { name: 'path', type: 'string', description: 'File path relative to basePath', }, { name: 'options', type: 'Options', description: 'Configuration options.', isOptional: true, properties: [ { type: 'Options', parameters: [ { name: 'encoding', type: "'utf-8' | 'binary'", description: 'Text or binary encoding', isOptional: true, defaultValue: "'utf-8'", }, ], }, ], }, ]} />
writeFile(path, content, options?)Write content to a file.
await filesystem.writeFile('/docs/new.md', '# New Document')
await filesystem.writeFile('/nested/path/file.md', content, { recursive: true })
Parameters:
<PropertiesTable content={[ { name: 'path', type: 'string', description: 'File path relative to basePath', }, { name: 'content', type: 'string | Buffer', description: 'File content', }, { name: 'options', type: 'Options', description: 'Configuration options.', isOptional: true, properties: [ { type: 'Options', parameters: [ { name: 'recursive', type: 'boolean', description: "Create parent directories if they don't exist", isOptional: true, defaultValue: 'false', }, { name: 'overwrite', type: 'boolean', description: 'Overwrite existing file', isOptional: true, defaultValue: 'true', }, { name: 'expectedMtime', type: 'Date', description: "If provided, the write fails with a StaleFileError when the file's current modification time doesn't match. Use this for optimistic concurrency control to detect external modifications between read and write.", isOptional: true, }, ], }, ], }, ]} />
appendFile(path, content)Append content to an existing file.
await filesystem.appendFile('/logs/app.log', 'New log entry\n')
Parameters:
<PropertiesTable content={[ { name: 'path', type: 'string', description: 'File path relative to basePath', }, { name: 'content', type: 'string | Buffer', description: 'Content to append', }, ]} />
deleteFile(path, options?)Delete a file.
await filesystem.deleteFile('/docs/old.md')
await filesystem.deleteFile('/docs/maybe.md', { force: true }) // Don't throw if missing
Parameters:
<PropertiesTable content={[ { name: 'path', type: 'string', description: 'File path', }, { name: 'options', type: 'Options', description: 'Configuration options.', isOptional: true, properties: [ { type: 'Options', parameters: [ { name: 'force', type: 'boolean', description: "Don't throw error if file doesn't exist", isOptional: true, defaultValue: 'false', }, ], }, ], }, ]} />
copyFile(src, dest, options?)Copy a file to a new location.
await filesystem.copyFile('/docs/template.md', '/docs/new-doc.md')
await filesystem.copyFile('/src/config.json', '/backup/config.json', { overwrite: false })
Parameters:
<PropertiesTable content={[ { name: 'src', type: 'string', description: 'Source file path', }, { name: 'dest', type: 'string', description: 'Destination file path', }, { name: 'options', type: 'Options', description: 'Configuration options.', isOptional: true, properties: [ { type: 'Options', parameters: [ { name: 'overwrite', type: 'boolean', description: 'Overwrite destination if it exists', isOptional: true, defaultValue: 'true', }, ], }, ], }, ]} />
moveFile(src, dest, options?)Move or rename a file.
await filesystem.moveFile('/docs/draft.md', '/docs/final.md')
await filesystem.moveFile('/temp/upload.txt', '/files/document.txt')
Parameters:
<PropertiesTable content={[ { name: 'src', type: 'string', description: 'Source file path', }, { name: 'dest', type: 'string', description: 'Destination file path', }, { name: 'options', type: 'Options', description: 'Configuration options.', isOptional: true, properties: [ { type: 'Options', parameters: [ { name: 'overwrite', type: 'boolean', description: 'Overwrite destination if it exists', isOptional: true, defaultValue: 'true', }, ], }, ], }, ]} />
mkdir(path, options?)Create a directory.
await filesystem.mkdir('/docs/api')
await filesystem.mkdir('/deeply/nested/path', { recursive: true })
Parameters:
<PropertiesTable content={[ { name: 'path', type: 'string', description: 'Directory path', }, { name: 'options', type: 'Options', description: 'Configuration options.', isOptional: true, properties: [ { type: 'Options', parameters: [ { name: 'recursive', type: 'boolean', description: 'Create parent directories', isOptional: true, defaultValue: 'false', }, ], }, ], }, ]} />
rmdir(path, options?)Remove a directory.
await filesystem.rmdir('/docs/old')
await filesystem.rmdir('/docs/nested', { recursive: true })
Parameters:
<PropertiesTable content={[ { name: 'path', type: 'string', description: 'Directory path', }, { name: 'options', type: 'Options', description: 'Configuration options.', isOptional: true, properties: [ { type: 'Options', parameters: [ { name: 'recursive', type: 'boolean', description: 'Remove contents recursively', isOptional: true, defaultValue: 'false', }, { name: 'force', type: 'boolean', description: "Don't throw if directory doesn't exist", isOptional: true, defaultValue: 'false', }, ], }, ], }, ]} />
readdir(path, options?)List directory contents.
const entries = await filesystem.readdir('/docs')
// [{ name: 'guide.md', type: 'file' }, { name: 'api', type: 'directory' }]
exists(path)Check if a path exists.
const exists = await filesystem.exists('/docs/guide.md')
stat(path)Get file or directory metadata.
const stat = await filesystem.stat('/docs/guide.md')
// { type: 'file', size: 1234, modifiedAt: Date, createdAt: Date, path: '/docs/guide.md' }
getInfo()Returns metadata about this filesystem instance.
const info = filesystem.getInfo()
// { id: '...', name: 'LocalFilesystem', provider: 'local', basePath: '/workspace', readOnly: false }
getInstructions(opts?)Returns a description of how paths work in this filesystem. When assigned to an agent, this is injected into the agent's system message.
const instructions = filesystem.getInstructions()
// 'Local filesystem at "/workspace". Files at workspace path "/foo" are stored at "/workspace/foo" on disk.'
Pass requestContext to enable per-request customization when the instructions constructor option is a function:
const instructions = filesystem.getInstructions({ requestContext })
Parameters:
<PropertiesTable
content={[
{
name: 'opts.requestContext',
type: 'RequestContext',
description: 'Forwarded to the instructions function if one was provided in the constructor.',
isOptional: true,
},
]}
/>
Returns: string
To override the default output, pass an instructions option to the constructor. See constructor parameters.
basePath worksThe basePath option sets the root directory for all file operations. File paths passed to methods like readFile() are resolved relative to this base:
/docs/guide.md → docs/guide.md./workspace + docs/guide.md → ./workspace/docs/guide.mdconst filesystem = new LocalFilesystem({
basePath: './workspace',
})
// These all resolve to ./workspace/docs/guide.md
await filesystem.readFile('/docs/guide.md')
await filesystem.readFile('docs/guide.md')
When you use a relative path for basePath, it resolves from process.cwd(). In Mastra projects, cwd varies depending on how you run your code:
| Context | Working directory | ./workspace resolves to |
|---|---|---|
mastra dev | ./src/mastra/public/ | ./src/mastra/public/workspace |
mastra start | ./.mastra/output/ | ./.mastra/output/workspace |
| Direct script | Where you ran the command | Relative to that location |
This can cause confusion when the same relative path resolves to different locations.
For consistent paths across all execution contexts, use an environment variable with an absolute path:
import { LocalFilesystem } from '@mastra/core/workspace'
const filesystem = new LocalFilesystem({
basePath: process.env.WORKSPACE_PATH!,
})
Set WORKSPACE_PATH in your environment to an absolute path like /home/user/my-project/workspace. This ensures the workspace path is consistent regardless of how you run your code.