Quickstart¶
Boot the NovoMCP engine locally in about 2 minutes. No API keys, nothing to provision — it runs on your hardware.
Requirements¶
- Python 3.10 or later. Python 3.9 hit end-of-life October 2025; several transitive deps require 3.10+.
Check what you have:
If it says Python 3.10.x or newer, skip ahead. Otherwise install a supported version via any of these paths:
Download from python.org/downloads.
Download from python.org/downloads, or use WSL and follow the Linux instructions.
All fine — any Python 3.10+ works. python:3.11-slim is a good Docker base.
Install and run¶
Fastest — uvx (no clone, no venv)¶
Run the engine straight from PyPI in one command:
uvx fetches the novomcp package into a throwaway environment and launches the engine — nothing to clone or install. The first run downloads the dependency set (rdkit, MDAnalysis, …), so give it a minute; later runs are cached.
Just want the lightweight cheminformatics subset?
uvx --from 'novomcp-lite[mcp]' novomcp-lite starts a stdio MCP server in seconds — RDKit properties/profiling + public-API search, no backend. The full engine imports the same code, so the two never drift. See novomcp-lite.
pip install¶
Into a virtual environment you control:
novomcp is the console command — it boots the MCP + REST server.
Prebuilt Docker image (GHCR)¶
No Python, no build — pull the published image and run it:
The engine listens on port 8018 (MCP + REST); /health is the readiness check. The image is rebuilt on every push to main (:latest, plus :sha-<short> to pin an exact build) and on each release tag (:vX.Y.Z, with the moving :X.Y and :X). It's public — no login to pull. Pass configuration with -e, e.g. -e NOVOMCP_QM_URL=... to wire a compute service.
From source¶
For hacking on the engine itself:
git clone https://github.com/NovoMCP/novomcp.git
cd novomcp/orchestrator
python3.11 -m venv .venv && source .venv/bin/activate
python -m pip install --upgrade pip
pip install -e .
python main_https.py # or: novomcp
The engine boots with:
- Auth: none required (
LocalAuthGate, every request resolves to alocaluser with unlimited tier) - Metering: none (
NoopMeter, no usage accounting) - Audit: local file (
FileAuditSink, appends JSON-lines to~/.novo/audit.jsonl)
If you accidentally create the venv with Python 3.9, python main_https.py fails fast with an actionable message telling you which install command to run.
Everything at once — Docker Compose (engine + dashboard)¶
If you have Docker, one command builds and runs both the engine and the web dashboard, wired to each other on a private network:
- Engine →
http://localhost:8018 - Dashboard →
http://localhost:3000
docker compose up starts both and streams both logs; Ctrl-C (or docker compose down) stops both. No start-order to worry about. Compose is also the single-VM cloud recipe — the same file scales from your laptop to one box in the cloud.
First requests¶
In another shell:
Health check¶
Expected: {"status": "healthy", ...}.
List tools¶
LocalAuthGate accepts any bearer token — x works fine. Returns the 18 always-available tools; the other 51 in the 69-tool catalog appear as you wire their backing services (see tool-availability.md).
Get a molecule profile (aspirin)¶
curl -s -X POST http://localhost:8018/mcp/tools/get_molecule_profile \
-H 'Authorization: Bearer x' \
-H 'Content-Type: application/json' \
-d '{"arguments": {"smiles": "CC(=O)Oc1ccccc1C(=O)O"}}' \
| python3 -m json.tool
Returns properties (MW, logP, TPSA, QED, Lipinski) computed on-the-fly via RDKit. admet_available will be false because the ADMET service (addie-models) isn't wired locally — that's expected. See Deploying services to enable ADMET.
MCP JSON-RPC handshake¶
curl -s -X POST http://localhost:8018/mcp/ \
-H 'Authorization: Bearer x' \
-H 'Content-Type: application/json' \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {"protocolVersion": "2024-11-05", "capabilities": {}}
}' \
| python3 -m json.tool
Returns server info, capabilities, and the funnel-id instructions blurb. This is the endpoint MCP-compatible clients (Claude Desktop, Cursor, Zed) connect to.
Audit sink¶
Every tool call is logged as a JSON-lines row: ts, event, and a payload with the tool, its run (funnel_id), success, duration, and:
arguments: what was run, enough to repeat the call. Keys that look like secrets (API keys, tokens, passwords) are stored as<redacted>; a very large value, such as a long SMILES list, keeps only its length and a sample.outcome: a short result. Forcheck_compliance, the verdict and its flags; for property and ADMET tools, the headline numbers.
The file is the whole record: copy it to keep or share a history. In the dashboard, Activity lists every call, filters by tool, result or run, opens a call to copy it and run it again, and downloads the history as a CSV for a spreadsheet or as the raw log for scripts. Its Runs tab shows each run as a timeline: the calls it made, plus the stages a discovery funnel saved with save_funnel_stage (molecules in and out, what was filtered and why, and the human decisions). Those stages are kept in the same file, as funnel_stage events. For analysis, load the file directly, for example pandas.read_json("~/.novo/audit.jsonl", lines=True).
The web dashboard¶
The engine is headless — a REST/MCP backend. The web dashboard (Next.js) is the visual surface: molecule profiles, live engine/service status, and the config screens for LLM keys, compliance, observability, and data connectors.
The engine and the dashboard are two separate processes. The dashboard's server-side routes proxy to the engine at NOVOMCP_ENGINE_URL (default http://localhost:8018); the browser only ever talks to the dashboard.
Start order doesn't matter
The dashboard degrades gracefully when the engine is down — every panel shows what's missing and offers a Recheck button — so you can start either first and connect them in any order.
Brings up the engine and the dashboard together (see above). Open http://localhost:3000.
Two terminals. In the first, run the engine (Install and run). In the second:
Open http://localhost:3000. Set NOVOMCP_ENGINE_URL if your engine runs somewhere other than http://localhost:8018.
Once connected, the dashboard shows Tools available: 18 of 69 — the 18 that work with nothing wired, out of the full catalog. That count climbs as you deploy services; the dashboard lists which env var unlocks each capability. See Tool availability for the full map.
What runs where¶
| Where it runs | How you connect it | |
|---|---|---|
| Engine, RDKit properties, the 18 default tools | Your machine | Nothing to do |
| Compute services: ADMET, docking, MD, QM, NN potentials, properties, structure, molecule index | Your hardware, or a DGX Spark (Novo Station, which runs all of them plus the dashboard) | Connections → Self-hosted compute: the service's URL |
| Compliance screening (closed source) | The paid API, api.novomcp.com | Connections → NovoMCP compliance: https://api.novomcp.com/v1/compliance and your API key; each check is billed to that key's account |
| LLM, data connectors, observability | The provider you choose | Connections, under each category |
Connections saves to ~/.novo/credentials.env, which the engine loads at startup: restart the engine after saving. Each tile's status is live — connected means the service answered, unreachable that it didn't, and for compliance key rejected or needs API key point at the key. With docker compose up, the engine and the dashboard share that file through the novomcp-audit volume.
The dashboard checks services from where it runs, so service URLs must be reachable from there. In Docker, http://addie-models:8025-style names work inside the compose network; for services on another machine, such as a Spark on your network, use that machine's address.
What next¶
- Configure an LLM provider — enable intent recognition, orchestration planning, and semantic tool search
- Deploy compute services — wire up ADMET, docking, MD, structure prediction, QM
- Connect an MCP client — point Claude Desktop / Cursor / Zed at
http://localhost:8018/mcp/ - Read the engineering stories — how the engine was designed and where it went wrong the first time