> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mirobody.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# The Agent

> The one agent the engine ships: what a turn can use, the keys that configure it, and how to replace it.

export const OssVersion = ({lang = "en"}) => <p className="text-sm text-gray-500 dark:text-gray-400">
    {lang === "zh" ? "对应 mirobody " : "Written for mirobody "}
    <a href="https://github.com/thetahealth/mirobody/tree/c1aae297b4f3fad5cb87c5fc5bffc5163f7758c9">
      <code>1.5.3</code>
    </a>
  </p>;

export const OssLink = ({path = "", children}) => {
  const base = "https://github.com/thetahealth/mirobody";
  const commit = "c1aae297b4f3fad5cb87c5fc5bffc5163f7758c9";
  const href = !path ? base + "/tree/" + commit : base + (path.endsWith("/") ? "/tree/" : "/blob/") + commit + "/" + path.replace(/\/$/, "");
  return <a href={href}>{children ?? <code>{path}</code>}</a>;
};

export const Fact = ({k, code = false, sep = ", "}) => {
  const facts = {
    "version": "1.5.3",
    "commit": "c1aae297b4f3fad5cb87c5fc5bffc5163f7758c9",
    "commitShort": "c1aae29",
    "repo": "https://github.com/thetahealth/mirobody",
    "python": "3.12",
    "port": 18060,
    "url": "http://localhost:18060",
    "pgPort": 18062,
    "redisPort": null,
    "account": "you@mirobody.ai",
    "accounts": ["you@mirobody.ai", "mom@mirobody.ai"],
    "code": "111111",
    "mcpUrlTtlDays": 10,
    "llmKeys": ["OPENROUTER_API_KEY", "DASHSCOPE_API_KEY", "GOOGLE_API_KEY", "OPENAI_API_KEY", "ANTHROPIC_API_KEY", "DEEPSEEK_API_KEY"],
    "cli": ["serve", "dev", "worker", "doctor", "fetch", "parse", "import", "resolve", "device-bundle", "mcp", "migrate-observations", "migrate-genotypes", "recode"],
    "tools": {
      "mcp": ["query_genetic_data", "query_health_indicators", "query_medications", "query_pharmacogenomics", "resolve_indicator", "convert_unit", "normalize_unit"],
      "gated": ["query_genetic_data", "query_pharmacogenomics", "query_health_indicators", "query_medications"]
    },
    "readme": {
      "en": {
        "tagline": "Self-hosted AI health data engine: every source, one standard, answers that cite their source.",
        "stages": [{
          "mark": "①",
          "name": "Collect",
          "what": "Lab reports, wearables, phone photos, genetic files, all pulled in. The source file is kept as it was, so every indicator points back to the page it was read from."
        }, {
          "mark": "②",
          "name": "Translate",
          "what": "One name to one code, one unit to UCUM, offline and deterministic. `A1c`, `HbA1c` and `Glycated Hemoglobin` become the same test here, and `头疼` and `headache` the same complaint (ICPC-3)."
        }, {
          "mark": "③",
          "name": "Agent",
          "what": "Ask over the coded record. Trend a value by minute, hour, day, week or month; get count, min, max, avg or change over any window in one call; compare across labs and devices, because they share one code. It charts the result in its reply, reads medications and genetic variants too, and names the file every number came from."
        }]
      },
      "zh": {
        "tagline": "自托管的 AI 原生健康数据引擎：任何来源，一套标准，每个答案都有出处。",
        "stages": [{
          "mark": "①",
          "name": "收集 Collect",
          "what": "化验单、穿戴设备、手机照片、基因文件，都收进来。源文件原样留下，每一项指标都能指回它被读出来的那一页。"
        }, {
          "mark": "②",
          "name": "转译 Translate",
          "what": "一个名字解析成一个码，一个单位统一到 UCUM，全程离线、结果确定。`A1c`、`HbA1c`、`糖化血红蛋白` 在这一层变成同一项检查，`头疼` 和 `headache` 也成了同一条主诉（ICPC-3）。"
        }, {
          "mark": "③",
          "name": "智能体 Agent",
          "what": "在编码后的记录上提问。按分钟、小时、天、周、月给出趋势，一次调用就能算出计数、最小值、最大值、均值和变化量；同一个码，跨化验所、跨设备直接比较。图表画在回复里，用药记录和基因型数据也读得了，每个数字都说明出自哪份文件。"
        }]
      }
    },
    "source": {
      "cli": "mirobody/cli.py",
      "tools": "mirobody/agent/tools"
    }
  };
  const value = k.split(".").reduce((o, p) => o == null ? undefined : o[p], facts);
  if (value === undefined) return <span>{"[unknown fact " + k + "]"}</span>;
  const items = Array.isArray(value) ? value : [value];
  return <>
      {items.map((item, i) => <span key={i}>
          {i > 0 ? sep : null}
          {code ? <code>{String(item)}</code> : String(item)}
        </span>)}
    </>;
};

<OssVersion lang="en" />

The engine ships one agent. It answers the questions asked at `/ask` in the web client and through the chat API, over the coded record of the person the question is about. Adding a capability means adding a [tool](/en/tools/adding-tools), not another agent.

Every agent runtime other than the bundled one (Claude Desktop, Cursor, an agent loop of your own) reaches the same data through the [MCP endpoint](/en/tools/mcp-integration) and needs nothing from this page.

## What a turn can use

| | What it is |
| - | - |
| Health-data tools | <Fact k="tools.mcp" code />: the same tools any MCP client sees over `/mcp`; see [Health-Data Tools](/en/tools/overview) |
| Virtual filesystem | `/uploads`, `/library` and `/memories`: read-only views of the person's files and profile, with file tools to list, read and search them |
| Code execution | An `eval` tool that runs JavaScript in a sandbox, with the data tools reachable inside it |
| One question back | `ask_user`, which pauses the turn to ask the person a question; the next message answers it. It is never exposed over MCP. |
| Memory | Conversation state per session, stored in Postgres |

Each answer streams as typed blocks (text, reasoning, tool calls and their results, usage), so a client can show the tool trace behind an answer.

<h2 id="configuration">
  Configuration
</h2>

The agent's keys have no agent-name suffix. The shipped values live in `config.llm.yaml`; override them in your overlay.

| Key | What it sets |
| - | - |
| `MODELS` | The model picker: one entry per chat model. `/api/models` lists the entries whose key is present, and a chat request picks one by name. |
| `DEFAULT_MODEL` | The picker's default; unset, the first entry whose key is present |
| `ALLOWED_TOOLS` / `DISALLOWED_TOOLS` | A tool whitelist or blacklist; the whitelist wins. Listing `eval` in `DISALLOWED_TOOLS` turns code execution off. |
| `PROMPTS` | System prompt templates, as a path or `path@name`; the first is the default |
| `AGENT_NAME` | The persona name used in the prompt |
| `AGENT_CHECKPOINTER` | `true` keeps conversation state per session; `false` makes every turn start fresh |

Model selection and keys are described in [Configuration](/en/configuration#the-model-key).

<h2 id="replacing-the-agent">
  Replacing the agent
</h2>

There is one agent slot. A replacement comes from an installed package that declares a `mirobody.agents` entry point, or from a directory listed in `AGENT_DIRS`; the first class found that defines `generate_response` becomes the agent for the process. Nothing in the package needs editing.

The contract is two methods. `generate_response` receives the user the turn is about (already authorized, and possibly a care-circle member rather than the caller), this turn's messages, and keyword arguments such as `language`, `session_id` and `timezone`; it yields the same typed blocks the shipped agent streams. Accept `**kwargs`, since the chat layer may add arguments.

```python theme={null}
class MyAgent:
    def __init__(self, **kwargs): ...

    async def generate_response(self, user_id: str, messages: list[dict], **kwargs):
        yield {"type": "text", "text": "..."}
```

To build a different harness on the shipped middleware and filesystem backends instead, install `mirobody[agent]`. The full contract, the block vocabulary and a minimal example plugin are in <OssLink path="mirobody/agent/README.md" />.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.