Building on Mirobody
Development Setup
Run the Mirobody Python engine from a source checkout: venv, editable install, Postgres and Redis in Docker, and the test suites.
Mirobody is a Python service (requires-python = ">=3.12"). Developing on it means creating a virtual environment, installing the package in editable mode, starting Postgres and Redis with Compose, and running mirobody serve. There is no compile step.
Prerequisites
Section titled “Prerequisites”pyproject.toml declares requires-python = ">=3.12"
For the Postgres (pgvector/pgvector:pg17-trixie) and Redis (redis:7.0-alpine) containers
mirobody/res/*.bin, *.npz, *.npy and *.gz are LFS objects
Set up a working tree
Section titled “Set up a working tree”Clone the repository
git clone https://github.com/thetahealth/mirobody.gitcd mirobodyStart Postgres and Redis
compose.yaml defines four services — pg, redis, mirobody and mirobody_worker. For local development you only want the two backing stores; the application runs on your host:
docker compose up -d pg redisThey publish 18082 → Postgres and 18089 → Redis on the host.
Create a virtual environment and install
python3 -m venv venvsource venv/bin/activate # Windows: venv\Scripts\activatepip install --upgrade pippip install -e .pip install -e . gets the engine — ① Collect and ② Standardize as a library, with no database driver and no HTTP server. Working on the chat server or the agents means the [agents] extra, which pulls [server] in with it:
pip install -e '.[agents,test]' # what you want for developmentpip install -e '.[cn]' # Aliyun OSS + Volcengine Arkpip install -e '.[indicator-build]' # only to regenerate the terminology bundlesThere is no Node.js dependency anywhere in the repository.
Write .env and your config override
.env carries exactly two variables: which config file to load, and the key used to encrypt sensitive values at rest.
echo "ENV=localdb" > .envecho "CONFIG_ENCRYPTION_KEY=$(openssl rand -hex 16)" >> .envconfig.yaml is the read-only template shipped with the repository. Your edits go into config.<ENV>.yaml — with ENV=localdb, that is config.localdb.yaml:
JWT_KEY: 'a 32-byte random string'
EMAIL_PREDEFINE_CODES: exp1@mirobody.ai: '111111'
OPENROUTER_API_KEY: 'sk-or-...'Any key whose name contains _KEY, _PASSWORD, _PASS, _PWD, _SECRET, _SK or _TOKEN is encrypted automatically on first load, using CONFIG_ENCRYPTION_KEY. The full key list is in Configuration.
Point the app at the published ports
config.yaml ships the container-network addresses (PG_HOST: 10.108.0.2, REDIS_HOST: 10.108.0.9) because that is where the app container finds them. A process running on your host reaches the same containers through the published ports instead, so override them:
PG_HOST: localhostPG_PORT: 18082REDIS_HOST: localhostREDIS_PORT: 18089Run
mirobody serveThe server listens on http://localhost:18080. Log in with exp1@mirobody.ai and the code you put in EMAIL_PREDEFINE_CODES.
Background work — the IndicatorSync and ProfileRefresh task queues — runs in a second process, not in the web server:
mirobody workerExtension directories
Section titled “Extension directories”Five config keys tell the engine where to look for your own code. Each is a list of directories shipping exactly one entry — the packaged one. Add your own and list it first, so it is scanned ahead of the built-in one:
| Key | Default |
|---|---|
MCP_TOOL_DIRS | mirobody/agent/tools |
MCP_RESOURCE_DIRS | mirobody/agent/resources |
AGENT_DIRS | mirobody/agent |
PROVIDER_DIRS | mirobody/pulse/providers |
SKILL_DIRS | mirobody/agent/skills |
Because everything is discovered at runtime, adding a tool, agent or provider means adding a file and restarting the process — there is no registry to edit and nothing to rebuild.
Running tests
Section titled “Running tests”Tests live beside the code they cover — the test for mirobody/mcp/service.py is mirobody/mcp/test_protocol.py — and testpaths is set, so the whole suite is bare pytest:
pip install -e '.[agents,test]'pytest# passes with no failuresNo database, no network, no API key. '.[test]' without [agents] is a supported smaller install: it runs the engine tests and prints a header saying the agent-layer tests were skipped.
Two suites carry the project’s public claims, and they are the ones to run after touching either half of ② Standardize:
| Suite | Covers |
|---|---|
mirobody/test_engine.py | golden LOINC codes — pins the whole chain: alias index → commonness prior → axis table |
mirobody/test_engine_coverage.py | the published accuracy number, 116/116, with a coverage floor that fails the build if it drops |
mirobody/mcp/test_protocol.py | MCP wire behaviour: version negotiation, resultType, server/discover |
mirobody/pulse/gate_tests/ | one snapshot per vendor payload → StandardPulseData |
mirobody/pulse/aggregate/ | daily rollups, CGM indicators, source priority — the only suite that touches config |
Additional checks
Section titled “Additional checks”lint-imports # the two import-linter contractslint-imports enforces the two import-linter contracts that keep langchain*, deepagents and langgraph out of everything except agent/ and server/ — see The Engine as a Library.
Pulse gate tests
Section titled “Pulse gate tests”The provider pipeline’s acceptance suite at mirobody/pulse/gate_tests/ replays recorded vendor payloads through format_data() and compares the resulting StandardPulseData against stored snapshots — no server, no database, no network:
pytest mirobody/pulse/gate_tests -vpytest mirobody/pulse/gate_tests --update-snapshots # after an intentional shape changeDetails, including how to add a case, are in Provider Testing.
Repository layout
Section titled “Repository layout”The annotated tree — packages, the extension directories, and the deployment files — is in Installation. It is the single copy, so this page doesn’t repeat it.
Next steps
Section titled “Next steps”Branches, style, and what a PR needs
Add a new health data source
Gate tests and the live Pulse routes
Run the whole stack in containers