The Engine
Architecture Overview
What the Mirobody Python engine is made of: three steps side by side over one shared infrastructure layer and one thin HTTP layer, running as two processes.
Mirobody is a Python engine (3.12 and up). It is made of three parts, one per step: Collect takes data in, Standardize resolves readings to canonical codes, and Answers is the agent working on top of them. All three share one infrastructure layer (accounts, task queue, MCP, configuration), with a thin HTTP layer above.
It runs as two processes over one PostgreSQL database and one Redis: mirobody serve serves HTTP, mirobody worker consumes background queues. All the code lives in one importable package, so a deployment is a config file and one docker compose up — there is nothing to build.
Layering rule
Section titled “Layering rule”LangChain and its related dependencies are allowed only inside the Answers part, enforced at build time by an import contract. That is why the Collect and Standardize halves installed by pip install mirobody bring numpy as their only third-party package — and why they work as an ordinary library. See The Engine as a Library.
Two processes
Section titled “Two processes”The split is deliberate: an embedding sweep that runs for minutes has no business running in the process that serves HTTP requests.
| Process | Command | What it does |
|---|---|---|
| HTTP service | mirobody serve | Loads configuration, bootstraps the database schema, assembles routes and middleware, and serves on HTTP_HOST:HTTP_PORT. |
| Background worker | mirobody worker | Reads the same configuration and consumes background tasks off Redis: backfilling indicator codes and embeddings, rebuilding the user profile. It listens on no port. |
Both need the [agents] extra; without it the command prints one line about what to install and exits. On SIGINT / SIGTERM the worker finishes what it holds before exiting.
In compose.yaml these are two containers: one publishes 18080 and runs the HTTP service, the other publishes no port and runs the worker.
Middleware a request passes through
Section titled “Middleware a request passes through”In the order a request crosses them, with the behaviour and the config key each one reads:
- Response compression Responses over 10,000 bytes are gzipped.
- CORS Read from
HTTP_HEADERS. SettingAccess-Control-Allow-Origin: *together with credentials logs a warning, because browsers reject that combination. - JWT Verifies the bearer token and resolves the caller (0 when anonymous), and takes
language,timezoneandtrace_idfrom the headers. Enabled only whenJWT_KEYis set. - Rate limiting Counts per user per path per minute; over the threshold it returns
429withRetry-After. Driven byREQUEST_RATE_LIMITER, default/api/chat: 6and/api/session: 6. - User preference write-back For the paths listed in
USER_INFO_UPDATER, writes the caller's language and timezone back to their user record.
At startup, unless ENV is one of TEST / GRAY / PROD / TEST-INLOCAL, the engine creates the schemas named in the configuration and runs the bundled table scripts. That is the local bootstrap path; production migrations are done by hand.
The three steps and shared infrastructure
Section titled “The three steps and shared infrastructure”| Part | Directory | What is in it |
|---|---|---|
| ① Collect | pulse/ | Device providers, on-device batch import, file parsing, the normalized write path, daily rollups and the insight engine. See Data Flow. |
| ② Standardize | indicator/ | The concept graph, embedding-based resolution, UCUM unit families and clinical taxonomies. See Health Indicators. |
| ③ Answers | agent/ | The two agents, the /api/* chat surface, the MCP tool surface, Agent Skills and the prompts. See Agent Types. |
| Shared infrastructure | server/ · mcp/ · task/ · user/ · utils/ | HTTP assembly, the MCP service, background tasks, accounts and auth, configuration and database access. |
Five config keys are directory-driven: MCP_TOOL_DIRS, AGENT_DIRS, PROVIDER_DIRS, MCP_RESOURCE_DIRS and SKILL_DIRS each name directories scanned at startup. Adding a tool, an agent, a provider or a skill means dropping a file in and restarting — nothing has to be registered anywhere. Each key defaults to the one directory inside the package; to use your own, list it first.
HTTP routes
Section titled “HTTP routes”The two route families follow different prefix rules, which matters before you put the engine behind a path-routing proxy:
- Chat, MCP, login and OAuth routes take the prefix when
HTTP_URI_PREFIXis set. - Pulse, sharing, files and indicators declare their prefixes themselves and are not affected by
HTTP_URI_PREFIX.
The OAuth discovery documents, the SPA fallback, /charts and /mirobody.json are unprefixed either way.
Full route list
| Area | Routes |
|---|---|
| Health check | GET /api/health |
| Chat | GET /api/models · /api/agents · /api/providers · /api/prompts, POST /api/session, GET /api/history · /api/history_by_person, POST /api/history/delete · /api/rating · /api/chat, GET /api/beneficiary-users |
| Per-user configuration | GET / POST /api/user/mcp, POST /api/user/mcp/set · /api/user/mcp/delete, GET / POST /api/user/prompt, POST /api/user/prompt/set · /api/user/prompt/delete |
| MCP | POST / GET /mcp, /mcp/{secret}, POST /personal/mcp |
| OAuth | /.well-known/oauth-authorization-server, /.well-known/oauth-authorization-server/mcp, /.well-known/mcp-configuration, /oauth/register · /oauth/authorize · /oauth/token · /oauth/introspect, /oauth2/authorize, /oauth2/check_state/{state} |
| Login | POST /email/login · /email/verify · /email/bind, POST /apple/verify · /google/verify, POST /user/del · /user/update_name |
| WebAuthn | /auth/webauthn/{register,login,upgrade}/{options,verify}, /auth/session/renew, /auth/session/reauth/{options,verify} |
| Files | POST /files/upload, GET /files/{file_path}, GET /api/v1/data/uploaded-files · /api/v1/data/data-distribution, POST /api/v1/data/delete-files, WebSocket /ws/upload-health-report |
| Indicators | GET /api/v1/health-indicators, POST /api/v1/health-indicators/reading |
| Pulse (public) | GET /api/v1/pulse/providers · /user/providers · /user-data-sources · /user/insights · /indicators · /units, POST /api/v1/pulse/user/providers/link · /unlink, POST /api/v1/pulse/{platform}/webhook · /{platform}/token, GET /api/v1/pulse/{platform}/{provider}/callback |
| On-device batches | POST /apple/health · /apple/statistics · /apple/cda — also mounted under /api/v1/pulse/apple/*, since an uploader may point at either |
| Pulse (management) | /api/v1/manage/pulse/* (indicators, units, user-health-data, monitor, insight, data-quality), /api/v1/manage/theta/pull/*, POST /api/v1/manage/aggregate/recalculate-range |
| Sharing | POST /api/share/create, GET /api/share/{share_session_id}, /invitation/shared-by-me/*, /invitation/shared-with-me/*, /invitation/permissions/list |
| User settings | GET / POST /api/user/settings, POST /api/user/virtual |
| Web client | Anything unmatched above and outside the backend’s own prefixes falls back to the SPA shell; /mirobody.json, /__/auth/init.json and /charts are registered explicitly |
The SPA fallback returns 404 for unmatched GETs under /api, /mcp, /oauth, /oauth2, /invitation, /apple, /google, /email, /personal, /auth/session, /auth/webauthn and /.well-known rather than the shell: a mistyped backend path is an error, not a client deep link.
Infrastructure
Section titled “Infrastructure”- Database: PostgreSQL with the
vector,pg_trgmandpgcryptoextensions, default schematheta_ai. The compose file pinspgvector/pgvector:pg17-trixieand publishes18082:5432. Connection keys arePG_HOST/PG_PORT/PG_USER/PG_PASSWORD/PG_DBNAME/PG_SCHEMA/PG_MIN_CONNECTION/PG_MAX_CONNECTION. - Cache and queues: Redis (
redis:7.0-alpine, published on18089:6379). It backs rate limiting, the background task queues and the locks provider pulls take. - Object storage: a configured cloud backend when there is one, otherwise local disk. The S3 backend reads
S3_KEY/S3_TOKEN/S3_REGION/S3_BUCKET/S3_PREFIX/S3_CDN. - Configuration: three layers — the repository template
config.yaml, your ownconfig.{env}.yaml, and.env(ENV+CONFIG_ENCRYPTION_KEY). Environment variables win over both YAML files.CONFIG_SERVER+CONFIG_TOKENpoint at a remote config service. See Configuration. - Auth: multi-user JWT, an OAuth 2.0 authorization server with discovery documents for MCP clients, email one-time codes, Apple and Google sign-in, and WebAuthn.
- Web client: a prebuilt SPA served from
HTTP_ROOT, read from disk per request, so replacing the build needs no restart. It sits outside the Python package on purpose: the wheel installs the engine, not the JavaScript. Its pages are/data(documents and readings) and/ask(the agent), plus Care Circle sharing.
Next steps
Section titled “Next steps”The three intake paths health data takes, and where it lands
The registry, unit conversion and semantic search
How runtime tool discovery and the MCP surface fit together
deploy.sh, compose.yaml and the two containers