---
title: Haystack
description: Give a Haystack Agent a bash tool over a Mirage workspace via MirageShellTool.
icon: /images/haystack-logo.svg
---

[Haystack](https://haystack.deepset.ai) is deepset's framework for building LLM applications and agents. The integration is maintained by deepset and lives in their [haystack-core-integrations](https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/mirage) repository, so it installs as its own package rather than as a `mirage-ai` extra.

## Install

```bash
uv add mirage-haystack
```

<Note>
  `mirage-haystack` pins an exact `mirage-ai` version rather than tracking the latest, because Mirage is pre-1.0. Check the [integration's `pyproject.toml`](https://github.com/deepset-ai/haystack-core-integrations/blob/main/integrations/mirage/pyproject.toml) for which one, since installing it may move an existing `mirage-ai` in your environment.
</Note>

## Usage

Describe the mounts, wrap the workspace in a tool, hand the tool to an `Agent`.

```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 (
    MirageMount,
    MirageShellTool,
    MirageWorkspace,
)

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)
```

The tool exposes a single `command` parameter, so the model writes ordinary bash and pipes across mounts. Its description is generated from the mount tree, so the model is told which paths exist without you writing a prompt for it.

## Guarding what the agent can do

Two controls, and they are not interchangeable.

`read_only=True` on a mount is the write boundary. Mirage refuses every write to that mount whatever command is used, so this is what prevents modification and deletion.

`allowed_commands` restricts which command names may run. It is checked against every command Mirage would execute, including ones nested in `$(...)`, backticks and subshells, so `ls "$(rm x)"` is rejected unless `rm` is allowed too. Treat it as steering, not a sandbox: allowing a command that runs other commands (`eval`, `bash`, `sh`, `source`, `xargs`) effectively allows anything.

Commands never reach the host shell either way. Mirage interprets them itself, so the blast radius is the mounts you attached.

## Exports

| Symbol | Purpose |
| --- | --- |
| `MirageWorkspace` | Declares the mount tree, and runs commands against it with `run` / `run_async`. Serializable with `to_dict` / `from_dict`. |
| `MirageMount` | One mount: `path`, `resource`, `config`, `read_only`. |
| `MirageShellTool` | The Haystack `Tool` that gives an `Agent` the bash surface. |
| `MirageError` | Base error, with `MirageConfigError` and `MirageCommandNotAllowedError`. |

## Links

- [Integration page](https://haystack.deepset.ai/integrations/mirage) on haystack.deepset.ai.
- [Source and README](https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/mirage) in `haystack-core-integrations`.
- [`mirage-haystack`](https://pypi.org/project/mirage-haystack) on PyPI.
