RAG-powered MCP (Model Context Protocol) servers for the LASER disease modeling framework. Provides two servers that allow Claude and other MCP-compatible tools to search LASER documentation and generate model code.
Three MCP servers are provided:
| Server | Container | Default Port | Purpose |
|---|---|---|---|
jenner-mcp |
jenner-mcp |
9765 | General LASER core documentation |
jenner-measles-mcp |
jenner-measles-mcp |
9766 | Measles-specific documentation |
jenner-generic-mcp |
jenner-generic-mcp |
9767 | laser-generic modeling documentation |
Server toolsets differ by target:
-
jenner-mcpsearch(query)— semantic search over the indexed LASER core documentationgenerate_code(prompt)— generateslaser-corePython code using retrieved context + GPT
-
jenner-measles-mcpsearch(query)— semantic search over the indexed measles documentationgenerate_code(prompt)— generateslaser-measlesPython code using retrieved context + GPTversion_info()— returns server version, model, and vectorstore metadata
-
jenner-generic-mcpsearch(query)— semantic search over the indexed laser-generic documentationgenerate_code(prompt)— generateslaser-genericPython code (importslaser.generic) using retrieved context + GPTversion_info()— returns server version, model, and vectorstore metadata
- Python 3.11
- Docker (with daemon running)
- An OpenAI API key (for embeddings and code generation)
- Documentation sources (all checked into this repo):
laser.pdf(core),laser-measles.pdf(measles), andlaser-generic.md(laser-generic, header**laser-generic version: X.Y.Z**)
Create a .env file in the project root:
OPENAI_API_KEY=sk-...
Each server indexes its own documentation source, all checked into this repo:
| Server | Source | Format |
|---|---|---|
jenner-mcp (core) |
laser.pdf |
PDF (version from cover Release X.Y.Z) |
jenner-measles-mcp |
laser-measles.pdf |
PDF (version from cover Release X.Y.Z) |
jenner-generic-mcp |
laser-generic.md |
Markdown (version from header **laser-generic version: X.Y.Z**) |
laser.pdf and laser-measles.pdf are static PDFs updated manually.
laser-generic.md is generated by the build-combined-doc.yml workflow in the
laser-generic repo and lands here via an auto-PR — see
How the RAG Pipelines Work for the full flow.
The ingest step runs entirely in Docker — no local virtual environment needed.
make ingest-core # laser.pdf -> vectorstore/
make ingest-measles # laser-measles.pdf -> vectorstore_measles/
make ingest-generic # laser-generic.md -> vectorstore_generic/
make ingest-all # all three
# Override a source path if it lives elsewhere:
make ingest-measles MEASLES_PDF=/path/to/laser-measles.pdf
make ingest-generic GENERIC_MD=/path/to/laser-generic.mdmake run-all # start all three servers as Docker daemons
# or individually:
make run-core
make run-measles
make run-genericclaude mcp add jenner-mcp --transport http "http://localhost:9765/mcp"
claude mcp add jenner-measles-mcp --transport http "http://localhost:9766/mcp"
claude mcp add jenner-generic-mcp --transport http "http://localhost:9767/mcp"After registration, restart Claude Code. The following tools will be available:
mcp__jenner-mcp__search/mcp__jenner-mcp__generate_codemcp__jenner-measles-mcp__search/mcp__jenner-measles-mcp__generate_codemcp__jenner-generic-mcp__search/mcp__jenner-generic-mcp__generate_code
Note: If ports 9765/9766/9767 are unavailable, override with
PORT_CORE,PORT_MEASLES, andPORT_GENERIC(e.g.make run-core PORT_CORE=8080) and update the URLs above accordingly.
The setup above runs the MCP servers locally in Docker (the alpha tier — where you iterate). The same servers are also deployed in AKS at beta (staging) and prod (stable) tiers, which you can point any MCP client at directly without running anything on your own machine.
| Tier | Core URL (jenner-mcp) |
Measles URL (jenner-measles-mcp) |
When to use |
|---|---|---|---|
| prod | http://172.193.178.78/mcp |
https://jenner-laser-ingress.westus2.cloudapp.azure.com/mcp (TLS) |
Stable. Measles has a public HTTPS ingress with a Let's Encrypt cert — use that one. |
| beta | http://48.192.48.32/mcp |
http://4.155.180.84/mcp |
Staging. New images land here first; use to validate changes before they go to prod. |
| legacy | http://20.72.235.86/mcp |
http://48.192.9.70/mcp |
Older deployments kept around for backward compat. New clients should not use these. |
| alpha | http://localhost:9765/mcp |
http://localhost:9766/mcp |
Whatever you're iterating on locally via the setup above. |
Naming convention: the bare names jenner-mcp and jenner-measles-mcp are used by register-local (the alpha tier — the canonical name every tool/prompt suite references). Beta and prod use explicit -beta and -prod suffixes so all three tiers can coexist without colliding in your MCP client config. If you'd rather have prod be your default jenner-mcp, register it by hand with the bare name (last example below) instead of running make register-prod.
# Prod (recommended for general use, registered with -prod suffix)
claude mcp add jenner-mcp-prod --transport http "http://172.193.178.78/mcp"
claude mcp add jenner-measles-mcp-prod --transport http "https://jenner-laser-ingress.westus2.cloudapp.azure.com/mcp"
# Beta (for testing changes before they ship to prod)
claude mcp add jenner-mcp-beta --transport http "http://48.192.48.32/mcp"
claude mcp add jenner-measles-mcp-beta --transport http "http://4.155.180.84/mcp"
# Prod under the bare name (if you don't want a local server and want prod to be the default)
claude mcp add jenner-mcp --transport http "http://172.193.178.78/mcp"
claude mcp add jenner-measles-mcp --transport http "https://jenner-laser-ingress.westus2.cloudapp.azure.com/mcp"Or use the convenience make targets — they run the claude mcp add commands for you with the current URLs baked in:
make register-local # register local Docker servers as jenner-mcp / jenner-measles-mcp (no suffix)
make register-beta # register AKS beta servers as jenner-mcp-beta / jenner-measles-mcp-beta
make register-prod # register AKS prod servers as jenner-mcp-prod / jenner-measles-mcp-prodTLS coverage: Only the measles prod ingress (
jenner-laser-ingress.westus2.cloudapp.azure.com) has a public TLS certificate. All other endpoints are plain HTTP — fine on a trusted network, not appropriate for public use.Health check:
curl -sS -o /dev/null -w "%{http_code}\n" "<mcp-url>"— a healthy MCP endpoint returns HTTP406(Not Acceptable, because curl doesn't send the MCP client's content negotiation headers).200would be wrong; connection-refused or404means the service is down or the URL is wrong.IPs / hostnames are pinned in this README for discoverability. They're verified against
kubectl get svc -n laser-aion the laser-ai AKS cluster. If a row goes stale, the AKS topology is the source of truth — re-run the kubectl query and update this table.
make status # show running containers
make logs-core # tail logs for jenner-mcp
make logs-measles # tail logs for jenner-measles-mcp
make stop-all # stop and remove all containersRun make help for the full reference.
laser-mcp/
├── main.py # jenner-mcp server (core LASER)
├── main_measles.py # jenner-measles-mcp server (measles)
├── main_generic.py # jenner-generic-mcp server (laser-generic)
├── ingest.py # vectorstore ingestion script
├── Dockerfile # core server image
├── Dockerfile.measles # measles server image
├── Dockerfile.generic # laser-generic server image
├── Dockerfile.ingest # ingest-only image
├── docker-compose.yml # service definitions
├── Makefile # all build/run/stop targets
├── requirements.txt # Python dependencies
├── .env # API keys (not committed)
├── vectorstore/ # FAISS index for core LASER (generated)
├── vectorstore_measles/ # FAISS index for measles (generated)
├── vectorstore_generic/ # FAISS index for laser-generic (generated)
├── laser.pdf # LASER core documentation
├── laser-measles.pdf # measles documentation
├── laser-generic.md # laser-generic documentation (combined markdown)
└── tests/ # prompt test suites and validators
When a documentation source changes, replace the source file in this repo and
re-ingest. The run-* targets rebuild the Docker images, so the updated
vectorstore is picked up automatically.
# Core: replace laser.pdf, then
make ingest-core && make run-core
# Measles: replace laser-measles.pdf, then
make ingest-measles && make run-measles
# laser-generic: replace laser-generic.md, then
make ingest-generic && make run-genericThe docs version is extracted automatically during ingest — from the PDF cover
(Release X.Y.Z) for core/measles, and from the markdown header
(**laser-generic version: X.Y.Z**) for laser-generic — and recorded in
<vectorstore>/ingest_metadata.json, surfaced by each server's version_info.
Each server indexes its own documentation source. The flow is the same for all three: source file → Docker ingest → FAISS vectorstore → server image.
laser.pdf is a static PDF checked into this repo. Update it by replacing the file.
laser-core/
(PDF export of LASER core documentation)
│
laser-mcp/
laser.pdf ──────────────────────────────→ make ingest-core
│
vectorstore/ (FAISS)
│
jenner-mcp (Docker)
The docs version is extracted from the PDF cover (Release X.Y.Z) during ingest.
laser-measles.pdf is a static PDF checked into this repo. Update it by replacing the file.
laser-measles/
(PDF export of laser-measles documentation)
│
laser-mcp/
laser-measles.pdf ──────────────────────→ make ingest-measles
│
vectorstore_measles/ (FAISS)
│
jenner-measles-mcp (Docker)
The docs version is extracted from the PDF cover (Release X.Y.Z) during ingest.
laser-generic.md is a generated artifact produced by the
build-combined-doc.yml GitHub Actions workflow in the laser-generic repo.
The workflow runs make docs-jenner, which:
- Executes every
docs/**/*.ipynbnotebook vianbconvert(intodist/executed_nbs/). - Builds the MkDocs HTML site from
docs/*.mdpages, the API reference (mkdocstrings oversrc/**/*.pydocstrings), and the executed notebooks. - Flattens the rendered site + executed notebooks into a single combined
markdown via
docs/concat_mkdocs.py. - Opens an auto-PR in this repo that copies the result to
laser-generic.md(branchauto/sync-laser-generic-doc).
laser-generic/
docs/**/*.ipynb ─→ nbconvert --execute ─→ dist/executed_nbs/
│
docs/*.md ──────────────────────────────────┤
src/**/*.py │
(docstrings) ─→ mkdocs build │
│ │
dist/site/ (HTML) │
│ │
└──────→ concat_mkdocs.py ← (executed nbs)
│
dist/combined_mkdocs.md ← auto-PR handoff
│
laser-mcp/ │
laser-generic.md ←──────────────────────┘
│
make ingest-generic
│
vectorstore_generic/ (FAISS)
│
jenner-generic-mcp (Docker)
The docs version comes from the **laser-generic version: X.Y.Z** header in the
combined markdown, which reflects laser.generic.__version__ at CI build time.
To refresh after a laser-generic docs or code change:
- Trigger
build-combined-doc.ymlin the laser-generic repo (workflow dispatch). - Merge the auto-PR into this repo.
make ingest-generic && make run-generic