Skip to main content
Version: 2.27

Mirage

haystack_integrations.tools.mirage.shell_tool

MirageShellTool

Bases: Tool

A Haystack Tool that lets an Agent run bash commands across a Mirage virtual filesystem.

Mirage mounts heterogeneous backends (object storage, databases, SaaS apps, local disk) as one filesystem; this tool exposes Mirage's single execute surface to an Agent as one well-described tool with a command parameter. Output is normalized to text and truncated before it reaches the model.

Security model

Mirage never shells out to the host: every command runs inside Mirage's own virtual-filesystem interpreter, so the blast radius is confined to the mounts you attach. Two controls shape what an Agent can do:

  • Per-mount read-only mode (MirageMount(..., read_only=True)) is the authoritative write boundary. Mirage refuses any write to a read-only mount regardless of the command used, so this -- not the allowlist -- is how you prevent modification or deletion. Mount anything the Agent should not change as read-only.
  • The command allowlist (allowed_commands) restricts which commands may run. It is enforced against every command Mirage would execute, including commands nested inside $(...), backticks, <(...) and subshells, so ls "$(rm x)" is rejected unless rm is also allowed. Treat it as a best-effort filter to steer the Agent, not a sandbox: allowing a command that itself runs other commands (eval, bash, sh, source, xargs, timeout) effectively allows anything, so do not list those for untrusted/hosted use.
  • denied_paths rejects any command whose text references one of the given path substrings.

Usage example

python
from haystack.components.agents import Agent
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.dataclasses import ChatMessage
from haystack_integrations.tools.mirage import MirageWorkspace, MirageMount, MirageShellTool

workspace = MirageWorkspace([
MirageMount(path="/data", resource="ram"),
MirageMount(path="/s3", resource="s3", config={"bucket": "my-bucket"}, read_only=True),
])
tool = MirageShellTool(workspace, allowed_commands=["ls", "cat", "grep", "head", "wc", "cp"])

agent = Agent(chat_generator=OpenAIChatGenerator(model="gpt-4o-mini"), tools=[tool])
result = agent.run(messages=[ChatMessage.from_user("How many lines in /s3/log.txt mention 'alert'?")])
print(result["messages"][-1].text)

init

python
__init__(
workspace: MirageWorkspace,
*,
name: str = "mirage_shell",
description: str | None = None,
invocation_timeout: float = 60.0,
max_output_chars: int = 20000,
allowed_commands: list[str] | None = None,
denied_paths: list[str] | None = None
) -> None

Initialize the Mirage shell tool.

Parameters:

  • workspace (MirageWorkspace) – The :class:MirageWorkspace describing the mount tree.
  • name (str) – Tool name exposed to the LLM.
  • description (str | None) – Custom description. If None, one is generated from the mount tree.
  • invocation_timeout (float) – Maximum seconds to wait for a command to finish.
  • max_output_chars (int) – Truncate command output to this many characters before returning it.
  • allowed_commands (list[str] | None) – If set, only these command names may run, e.g. ["ls", "cat", "grep", "head", "wc"]. The allowlist is enforced against every command Mirage would execute -- including commands nested in substitutions/subshells -- so ls "$(rm x)" is rejected unless rm is also allowed. It is a filter over Mirage's virtual commands to steer the Agent, not a security sandbox; the write boundary is per-mount read_only (see the class "Security model" section). If None, any command is allowed (not recommended for untrusted/hosted use).
  • denied_paths (list[str] | None) – If set, any command referencing one of these path substrings is rejected.

warm_up

python
warm_up() -> None

Build the underlying live workspace eagerly. Called by Agent.warm_up()/Pipeline.warm_up().

to_dict

python
to_dict() -> dict[str, Any]

Serialize the tool to a dictionary in the {"type": ..., "data": ...} format.

from_dict

python
from_dict(data: dict[str, Any]) -> MirageShellTool

Deserialize the tool from a dictionary.

close

python
close() -> None

Close the underlying workspace.

haystack_integrations.tools.mirage.workspace

MirageMount

Declarative description of a single backend mounted into a :class:MirageWorkspace.

A mount is the serializable unit of a Mirage workspace: it names where a backend is mounted (path), which backend it is (resource, a Mirage registry name such as "s3" or "gdrive"), and how to configure it (config).

config values may be plain values, Haystack Secret objects for credentials, or an OAuth token source (e.g. OAuthRefreshTokenSource) for backends whose config accepts a token-provider callable (such as Mirage's OneDrive access_token). Secrets and token sources are resolved only when the live workspace is built.

Every backend is created the same way. Use the Mirage registry name and the config keys that backend expects (discover names with MirageMount.available_resources(); config keys come from the backend's Mirage config class):

python
from haystack.utils import Secret

MirageMount(path="/data", resource="ram") # in-memory scratch
MirageMount(path="/local", resource="disk", config={"root": "/srv/data"}) # local disk
MirageMount(path="/s3", resource="s3", config={"bucket": "my-bucket"}, read_only=True)
MirageMount(
path="/drive",
resource="gdrive",
config={"client_id": "...", "refresh_token": Secret.from_env_var("GDRIVE_REFRESH_TOKEN")},
read_only=True,
)

Parameters:

  • path (str) – Mount point in the virtual filesystem, e.g. "/s3".
  • resource (str) – Mirage registry name of the backend, e.g. "ram", "disk", "s3", "gdrive". See mirage.resource.registry.REGISTRY or MirageMount.available_resources() for the full list.
  • config (dict[str, Any]) – Keyword arguments passed to the backend's Mirage config. Values may be Secrets, or an OAuth token source that is turned into a token-provider callable when the workspace is built.
  • read_only (bool) – If True, the mount is mounted in Mirage's READ mode and writes are rejected by Mirage itself.

available_resources

python
available_resources() -> list[str]

Return the Mirage registry names usable as resource.

These are short backend names such as "s3", "gdrive", "postgres". Pass one to MirageMount(resource=...); the config keys each backend expects come from its Mirage config class.

MirageWorkspace

A description of a Mirage mount tree that lazily builds a live mirage.Workspace.

MirageWorkspace is the shared backend behind the Mirage tools and components: it holds the list of :class:MirageMounts and the cache configuration, serializes cleanly (resolving Secrets only at build time), and constructs the live workspace on first use via Mirage's resource registry.

Usage example

python
from haystack.utils import Secret
from haystack_integrations.tools.mirage import MirageWorkspace, MirageMount

ws = MirageWorkspace(
mounts=[
MirageMount(path="/data", resource="ram"),
MirageMount(path="/s3", resource="s3", config={"bucket": "my-bucket"}, read_only=True),
]
)
print(ws.run("ls /s3"))

init

python
__init__(
mounts: list[MirageMount], *, cache_limit: str | int = "512MB"
) -> None

Initialize the workspace description.

Parameters:

  • mounts (list[MirageMount]) – The backends to mount, as a list of :class:MirageMount.
  • cache_limit (str | int) – Mirage file-cache size limit (e.g. "512MB" or an int byte count).

Raises:

  • MirageConfigError – If no mounts are provided or mount paths are not unique.

warm_up

python
warm_up() -> None

Build the live mirage.Workspace eagerly. Idempotent.

close

python
close() -> None

Close the live workspace and release its resources, if it was built. Thread-safe.

run

python
run(
command: str, *, timeout: float = 60.0, max_chars: int | None = None
) -> str

Run a bash command against the mount tree from a synchronous context and return its output.

Parameters:

  • command (str) – A bash command line, e.g. "grep -r alert /s3/logs | wc -l".
  • timeout (float) – Maximum seconds to wait for the command.
  • max_chars (int | None) – If set, truncate the returned text to this many characters.

Returns:

  • str – Combined stdout (plus a trailing error note on non-zero exit) as a string.

run_async

python
run_async(
command: str, *, timeout: float = 60.0, max_chars: int | None = None
) -> str

Async counterpart of :meth:run.

to_dict

python
to_dict() -> dict[str, Any]

Serialize the workspace description to a dictionary (Secret-safe).

from_dict

python
from_dict(data: dict[str, Any]) -> MirageWorkspace

Deserialize a workspace description from a dictionary.

describe

python
describe() -> str

Return a human/LLM-readable summary of the mount tree (used in tool descriptions).