Metadata-Version: 2.3
Name: just-prs-mcp
Version: 0.3.1
Summary: An MCP server wrapping just-prs — PGS Catalog search and polygenic risk score computation
Keywords: mcp,fastmcp,prs,pgs-catalog,polygenic-risk-score,genomics
Author: Anton Kulaga, Newton Winter
Author-email: Anton Kulaga <anton.kulaga@uni-rostock.de>, Newton Winter <isoutthere@gmail.com>
License: MIT License
         
         Copyright (c) 2026 Newton Winter
         
         Permission is hereby granted, free of charge, to any person obtaining a copy
         of this software and associated documentation files (the "Software"), to deal
         in the Software without restriction, including without limitation the rights
         to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
         copies of the Software, and to permit persons to whom the Software is
         furnished to do so, subject to the following conditions:
         
         The above copyright notice and this permission notice shall be included in all
         copies or substantial portions of the Software.
         
         THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
         IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
         FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
         AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
         LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
         OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
         SOFTWARE.
Requires-Dist: fastmcp[tasks]>=3.4.2
Requires-Dist: pydantic>=2
Requires-Dist: pydantic-settings>=2
Requires-Dist: typer>=0.16
Requires-Dist: anyio>=4
Requires-Dist: just-prs==0.7.6
Requires-Dist: smithery>=0.4.2
Requires-Dist: pgenlib ; sys_platform != 'win32' and extra == 'reference'
Maintainer: Anton Kulaga, Newton Winter
Maintainer-email: Anton Kulaga <anton.kulaga@uni-rostock.de>, Newton Winter <isoutthere@gmail.com>
Requires-Python: >=3.13
Provides-Extra: reference
Description-Content-Type: text/markdown

# just-prs-mcp: Conversational and Programmatic Polygenic Risk Scores

[![PyPI version](https://badge.fury.io/py/just-prs-mcp.svg)](https://pypi.org/project/just-prs-mcp/)
[![Python 3.13+](https://img.shields.io/badge/python-3.13+-blue.svg)](https://www.python.org/downloads/)
[![Claude Plugin](https://img.shields.io/badge/Claude-Plugin-d97706?logo=anthropic&logoColor=white)](#claude-plugin)
[![MCP](https://img.shields.io/badge/MCP-Claude%20%7C%20Cursor%20%7C%20Codex-blueviolet.svg)](#connect-claude-cursor-or-another-mcp-client)
[![BioContextAI Registry](https://img.shields.io/badge/BioContextAI-Registry-0b7285.svg)](https://biocontext.ai/registry/dna-seq/just-prs-mcp)
[![Core library](https://img.shields.io/badge/core-just--prs-2ea44f.svg)](https://github.com/dna-seq/just-prs)
[![Research use only](https://img.shields.io/badge/use-research%20only-orange.svg)](#research-use-only)
[![Not medical advice](https://img.shields.io/badge/medical-not%20advice-red.svg)](#research-use-only)

`just-prs-mcp` brings the
**[just-prs](https://github.com/dna-seq/just-prs) bioinformatics toolbox** into
Claude, Cursor, Codex, and any other
[Model Context Protocol](https://modelcontextprotocol.io/) client. It exposes
typed tools for searching **5,000+ published polygenic scoring models**,
normalizing VCF and consumer-array data, computing PRS, comparing results with
reference populations, estimating absolute risk, and assessing result quality.

It is useful whether you are:

- a **bioinformatician** adding reproducible PRS operations to an MCP-enabled
  research environment;
- a **researcher or analyst** who wants to inspect models and evidence through a
  conversational interface;
- a **Claude or Cursor user** who wants to analyze a local genome without
  writing a pipeline; or
- a **developer** embedding PRS tools in an agent, application, or internal
  system.

The server runs locally by default, needs no API key, and does not upload your
genome. The underlying scoring, catalog, and reference-panel logic remains in
`just-prs`; this repository provides the MCP tools, Claude plugin, packaging,
prompts, and guided interpretation workflows.

The server is published in the
[BioContextAI Registry](https://biocontext.ai/registry/dna-seq/just-prs-mcp), a
community registry for agentic biomedical systems, where researchers can
discover its scientific scope, supported features, and registry metadata.

## See the evidence, not just a score

![Trait-level PRS report showing model percentiles, match rates, quality, and a consensus reference curve](images/intelligence.jpg)

Instead of hiding uncertainty behind one number, the server gives clients the
evidence needed to judge a result: PGS IDs, variant-match rates, model quality,
reference population, percentiles, absolute-risk context, and agreement or
conflict across models for the same trait.

## How it fits into your workflow

```mermaid
flowchart LR
    U["Researcher, bioinformatician,<br/>or genome owner"]
    C["Claude · Cursor · Codex<br/>or another MCP client"]
    M["just-prs-mcp<br/>typed tools + prompts + skill"]
    J["just-prs<br/>scoring and catalog engine"]
    V["Local VCF / array"]
    P["PGS Catalog metadata<br/>and reference distributions"]

    U --> C --> M --> J
    V --> J
    P --> J
    J --> M --> C
```

| Use case | Recommended interface |
|---|---|
| Ask questions and receive an evidence-aware interpretation | [Claude plugin](#claude-plugin) |
| Add structured PRS tools to Claude, Cursor, Codex, or an internal system | [MCP server](#connect-claude-cursor-or-another-mcp-client) |
| Build scripts, notebooks, pipelines, or a browser UI directly | [just-prs](https://github.com/dna-seq/just-prs) |
| Evaluate quickly without personal genomic data | [Public test genomes](#test-genomes-quick-play) |

## What can you do with it?

```text
"Search the PGS Catalog for type 2 diabetes models and explain which are best supported."

"Normalize this local VCF, compute PRS for coronary artery disease, and report
the match rate, percentile, reference population, and absolute-risk context."

"Run every suitable model for this trait and show where the models agree or conflict."

"Compare the same trait across Anton's and Livia's public genomes."

"List the available reference panels and score these PGS IDs in a batch."
```

The client chooses the tools and preserves the provenance of the result. You can
also call every tool directly from your own MCP application.

## Connect Claude, Cursor, or another MCP client

Local use requires [uv](https://docs.astral.sh/uv/). `uvx` creates an isolated
environment for the published package, so there is no repository clone or
project-level installation step.

### Claude Code

Add the MCP server:

```bash
claude mcp add just-prs -- uvx just-prs-mcp@latest stdio
claude mcp list
```

Use a pinned version in reproducible research environments:

```bash
claude mcp add just-prs -- uvx just-prs-mcp@0.2.0 stdio
```

For the MCP server plus the bundled trait-interpretation skill, use the
[Claude plugin](#claude-plugin).

### Cursor

Add to `.cursor/mcp.json` (project) or your user MCP config
([Cursor MCP docs](https://cursor.com/docs/mcp)):

```json
{
  "mcpServers": {
    "just-prs": {
      "command": "uvx",
      "args": ["just-prs-mcp@latest", "stdio"],
      "env": { "PRS_MCP_MODE": "essentials" }
    }
  }
}
```

### Codex

In `~/.codex/config.toml`:

```toml
[mcp_servers.just-prs]
command = "uvx"
args = ["just-prs-mcp@latest", "stdio"]
```

### Other MCP clients and internal systems

Use the same local stdio process:

```json
{
  "command": "uvx",
  "args": ["just-prs-mcp@latest", "stdio"],
  "env": {
    "PRS_MCP_MODE": "essentials"
  }
}
```

The server is built with [FastMCP](https://gofastmcp.com) and returns structured
Pydantic outputs, so it can be used interactively or orchestrated by your own
MCP client.

### Claude plugin

The repository is also packaged as a **Claude Code / Cowork plugin**.
[`.claude-plugin/plugin.json`](./.claude-plugin/plugin.json) registers the
`prs-trait-interpretation` skill, while [`.mcp.json`](./.mcp.json) launches the
MCP server through `uvx`. The skill guides Claude through model selection,
quality filtering, percentile interpretation, model agreement, and research-use
caveats rather than treating the first available score as definitive.

Use it directly from a checkout:

```bash
claude plugin validate .
claude --plugin-dir .
```

Or build the minimal ZIP for manual upload in Claude Desktop / Cowork:

```bash
uv run pack plugin
# dist/just-prs-claude-plugin-<version>.zip
```

The ZIP contains only the plugin manifest, MCP configuration, skill, README, and
license. See the official
[Claude plugin documentation](https://code.claude.com/docs/en/plugins).

### Claude Desktop (`.mcpb` extension)

If you only need the MCP tools, package a Claude Desktop extension:

```bash
uv run pack mcpb
# dist/just-prs-mcp-<version>.mcpb
```

Double-click the `.mcpb` or drag it into Claude Desktop. The extension still
runs the server locally. Metadata and configurable options are declared in
[`manifest.json`](./manifest.json).

### Version pinning tip

`uvx` caches the first version it resolves for a bare package name. Prefer
`just-prs-mcp@latest` or `just-prs-mcp@<version>` — avoid the bare name.

Use `--mode extended` or `PRS_MCP_MODE=extended` for bulk downloads, HuggingFace
upload, prevalence priors, multi-method absolute risk, and reference-panel
scoring.

### From a clone (development)

The plugin's `.mcp.json` launches the pinned PyPI release. To run the working
tree while developing:

```toml
[mcp_servers.just-prs]
command = "uv"
args = ["run", "just-prs-mcp", "stdio"]
```

## What is a PRS?

Many traits and common diseases — type 2 diabetes, coronary artery disease,
height, longevity — are **polygenic**: influenced by thousands of small genetic
effects. A Polygenic Risk Score adds those effects and places the result
relative to a reference population. It is not a diagnosis; it can visualize
inherited predisposition and, where evidence allows, translate a percentile into
an absolute-risk estimate.

## What is MCP?

The [Model Context Protocol](https://modelcontextprotocol.io/) lets AI assistants
and applications call external tools through a shared protocol. Here, MCP turns
the `just-prs` Python API into discoverable, typed operations with structured
inputs and outputs. A chat client can use them conversationally; a
bioinformatics platform can orchestrate the same operations programmatically.

## Contents

- [See the evidence, not just a score](#see-the-evidence-not-just-a-score)
- [How it fits into your workflow](#how-it-fits-into-your-workflow)
- [What can you do with it?](#what-can-you-do-with-it)
- [Connect Claude, Cursor, or another MCP client](#connect-claude-cursor-or-another-mcp-client)
- [What is a PRS?](#what-is-a-prs)
- [What is MCP?](#what-is-mcp)
- [Quickstart (developers)](#quickstart-developers)
- [Test genomes (quick play)](#test-genomes-quick-play)
- [Tools](#tools)
- [Prompts and resources](#prompts-and-resources)
- [Typical MCP workflow](#typical-mcp-workflow)
- [Modes](#modes)
- [Configuration](#configuration)
- [Methodology](#methodology)
- [Research use only](#research-use-only)
- [Privacy](#privacy)
- [Deployment](#deployment)
- [Project layout](#project-layout)
- [License](#license)

## Quickstart (developers)

```bash
uv sync                                    # deps (incl. dev)
uv sync --extra reference                  # + pgenlib (Linux/WSL)

uv run just-prs-mcp stdio                  # stdio for MCP clients
uv run just-prs-mcp stdio --mode extended  # full tool surface
uv run just-prs-mcp http                   # HTTP (default :3011)
uv run fastmcp dev fastmcp.json            # MCP Inspector

uv run pytest
uv run ruff check .
uv run pyright
```

The server **boots with no environment configured** — every setting is optional.

## Test genomes (quick play)

Two public WGS datasets from
[just-dna-lite](https://github.com/dna-seq/just-dna-lite) are built in:

| Sample | Zenodo | VCF | Size | License | Parameter |
|--------|--------|-----|------|---------|-----------|
| Anton Kulaga | [18370498](https://zenodo.org/records/18370498) | `antonkulaga.vcf` | ~482 MB | CC0 | `sample="anton"` |
| Livia Zaharia | [19487816](https://zenodo.org/records/19487816) | `SIMHIFQTILQ.hard-filtered.vcf.gz` | ~349 MB | CC-BY-4.0 | `sample="livia"` |

```
"Download Anton's sample genome, normalize it, and compute the PRS for type 2 diabetes."
```

Typical tool chain: `download_sample_genome` → (auto-normalize) →
`compute_prs_by_trait` → `percentile` → `absolute_risk` → optional
`plot_trait_panel`.

## Tools

### Essentials (always available)

| Tool | Description |
|------|-------------|
| `search_scores` | Search the PGS Catalog by free text |
| `score_info` | Cleaned metadata for one PGS ID |
| `best_performance` | Best evaluation metrics (OR / HR / AUROC / C-index) |
| `search_traits` | REST trait search with synonym retry |
| `trait_info` | Trait by EFO / MONDO ID + associated PGS IDs |
| `list_genomes` | Inventory of downloaded and normalized genomes in the cache |
| `download_sample_genome` | Fetch a public sample WGS VCF from Zenodo (background task; auto-normalizes by default) |
| `normalize_vcf` | VCF → genotype Parquet (background task) |
| `compute_prs` | Score one VCF against one PGS model |
| `compute_prs_batch` | Score one VCF against many PGS models (background task) |
| `compute_prs_by_trait` | Score models for a trait; auto-save result (background task) |
| `percentile` | Population percentile (reference panel / theoretical / AUROC fallback) |
| `absolute_risk` | Absolute disease risk from a PRS z-score + prevalence |
| `assess_quality` | Quality label + interpretation (pure logic, no I/O) |
| `compare_genomes` | Cross-genome comparison from saved by-trait results |
| `plot_trait_panel` | Plotly figure (JSON / optional HTML) from a saved trait report |

### Extended (opt-in via `--mode extended`)

| Tool | Description |
|------|-------------|
| `normalize_array` | 23andMe / AncestryDNA → Parquet (background task) |
| `download_scoring_file` | One harmonized scoring file from EBI FTP |
| `list_pgs_ids` | All PGS IDs on EBI FTP |
| `download_all_metadata` | All metadata sheets as Parquet (background task) |
| `bulk_download_scores` | Many/all scoring files (background task) |
| `prevalence_info` | Population prevalence priors for a score or trait |
| `absolute_risk_bundle` | Multi-method absolute-risk estimation |
| `push_catalog_to_hf` | Upload cleaned catalog to HuggingFace (needs token) |
| `download_reference_panel` | Fetch 1000G / HGDP+1kGP panel (background task) |
| `reference_score` / `reference_score_batch` | Score against a reference panel (needs `pgenlib`) |
| `pgen_read_pvar` / `pgen_read_psam` / `pgen_score` | PLINK2 binary ops (needs `pgenlib`) |

> **File paths:** computation tools take local paths on the **server**
> filesystem. Over stdio that is your machine. Reference / pgen tools need
> `uv sync --extra reference` (Linux/WSL).

## Prompts and resources

| Prompt | Description |
|--------|-------------|
| `compute_prs_for_trait` | Step-by-step: search → normalize → score → interpret |
| `interpret_prs_for_trait` | End-to-end trait read with quality shortlist and consensus |
| `interpret_prs_result` | Interpret a single PRS result |
| `interpret_trait_results` | Interpret combined results across models for one trait |

Resource: `resource://prs/panels` — reference panels, genome builds, and active
cache directory.

## Typical MCP workflow

```
1. search_traits("venous thromboembolism")     → trait ID (e.g. EFO_0001645)
2. download_sample_genome(sample="anton")       → VCF (+ normalized Parquet)
3. compute_prs_by_trait(trait_id, genotypes_path) → score models, auto-save JSON
4. percentile(prs_score, pgs_id)                → percentile + z-score
5. absolute_risk(pgs_id, z_score)               → lifetime probability + risk ratio
6. assess_quality(...)                          → quality label
7. plot_trait_panel(result_path)                → optional chart for the client
```

For cross-genome comparison, repeat scoring per genome, then:

```
8. compare_genomes(result_paths=[...])          → ranked comparison
```

`compute_prs_by_trait` returns `result_path`; pass those paths to
`compare_genomes` or `plot_trait_panel`.

## Modes

`PRS_MCP_MODE` (env) or `--mode` (CLI), default `essentials`:

| Mode | What's registered |
|------|-------------------|
| `essentials` | Catalog + core compute/analyze + comparison. Smaller tool list for clients. |
| `extended` | Batch downloads, HF upload, prevalence, multi-method risk, reference/pgen. |

## Configuration

All settings are optional. See [`.env.example`](./.env.example) and
[`settings.py`](src/just_prs_mcp/settings.py).

| Variable | Description |
|----------|-------------|
| `PRS_MCP_MODE` | `essentials` (default) or `extended` |
| `PRS_MCP_CACHE_DIR` | Cache for catalog data, scoring files, panels, results |
| `PRS_MCP_DEFAULT_GENOME_BUILD` | Default genome build (`GRCh38`) |
| `PRS_MCP_DEFAULT_PANEL` | Default reference panel (`1000g`) |
| `PRS_MCP_DUCKDB_MEMORY_LIMIT` | DuckDB memory for batch scoring (e.g. `8GB`) |
| `PRS_MCP_HF_TOKEN` | HuggingFace token for `push_catalog_to_hf` (also `HF_TOKEN`) |
| `PRS_MCP_TRANSPORT` | `stdio` / `http` / `sse` |
| `PRS_MCP_HOST` / `PRS_MCP_PORT` | Bind address for HTTP/SSE (default `0.0.0.0:3011`) |
| `PRS_MCP_LOG_LEVEL` | Logging level (`info` by default) |

## Methodology

### Percentile estimation

Percentiles use the **1000 Genomes Project phase 3** panel (2,504 individuals;
AFR, AMR, EAS, EUR, SAS) on GRCh38 harmonized scoring files. PRS is
`Σ(effect_weight × dosage)` for matched variants; the user sample is placed on
the same distribution.

### Quality scoring

Synthetic quality score (0–100) from four tiers:
- **T1a**: AUROC / C-index (strongest)
- **T1b**: Beta only (0.95×)
- **T2**: OR / HR only (0.90×; probit transform)
- **T3**: No performance metric (0.6× floor)

Also factors cohort size, coverage, and harmonized-score penalty. Labels: High
(≥70), Normal (≥50), Moderate (≥30), Low (<30).

### Absolute risk

For disease traits, `absolute_risk` converts a z-score into lifetime probability
and risk ratio vs population average. `risk_ratio` 1.0 = average; >1 elevated;
<1 reduced. If prevalence data is unavailable, the tool reports that explicitly.

### Interpreting results

Built-in instructions guide agents to:
- Present PRS as predisposition, not a trait measurement
- Call `absolute_risk` after `percentile` for disease traits
- Respect trait directionality
- Flag ancestry mismatches, low coverage, and model disagreement
- Cite PGS IDs with links to the [PGS Catalog](https://www.pgscatalog.org/)

See the
[just-prs interpretation guide](https://github.com/dna-seq/just-prs#research-use-only-interpreting-prs-results).

## Research use only

PRS results are for **research and educational purposes only** and do not
constitute medical advice.

- PRS models are statistical proxies, not causal readouts.
- Catalog listing does not mean clinical readiness.
- Environment, lifestyle, age, sex, and biomarkers often matter as much as or
  more than common-variant signal.
- Low match rates (common with consumer arrays) mean a noisier, less informative
  score.
- Ancestry matters: accuracy often drops outside the training population.

A high PRS is not a diagnosis; a low PRS is not a guarantee.

## Privacy

Genomic computation is designed to stay local:

- Over **stdio** / Claude Desktop / the Claude plugin, tools read paths on **your**
  machine. VCFs are not uploaded to a third-party API by this server.
- There is deliberately no client-to-server VCF upload or remote-fetch tool.
- Optional HuggingFace upload (`push_catalog_to_hf`, extended mode) sends
  **catalog metadata**, not personal genotype files, and only when you invoke it
  with a token.

## Deployment

- **Docker**: `docker build -t just-prs-mcp . && docker run -p 3011:3011 just-prs-mcp`
- **Smithery** (GitHub connect): repo is ready — `smithery.yaml` (`runtime: python`)
  + `[tool.smithery]` pointing at `just_prs_mcp.server:start_mcp_smithery`.
  1. Push this repo to GitHub.
  2. Follow the current [Smithery publishing guide](https://smithery.ai/docs/build/publish).
  3. Optional: set `PRS_MCP_MODE=extended` (or other `PRS_MCP_*`) in the Smithery
     project env if you want the full tool surface on the hosted instance.
  Local smoke-test of the same entrypoint:
  `uv run smithery dev` / `uv run playground` / `uv run start`.
- **Declarative**: `fastmcp.json` for `fastmcp run` / `fastmcp dev`

## Project layout

```
src/just_prs_mcp/
  server.py          build_server(), CLI, graceful shutdown, Smithery entrypoint
  settings.py        pydantic-settings (PRS_MCP_*), safe defaults
  client.py          shared PRSCatalog / REST-client construction + adapters
  models.py          Pydantic tool I/O models (+ reused just-prs models)
  plugin_package.py  minimal Claude plugin ZIP builder
  logging_setup.py   stdlib logging → stderr
  tools/
    catalog.py         essentials — PGS Catalog search and lookup
    compute.py         essentials — normalize, compute, analyze, compare
    extended.py        extended — batch downloads, HF upload, prevalence, multi-risk
    reference.py       extended — reference-panel / pgen scoring (pgenlib)
tests/               in-memory client tests (wiring + logic, no network)
```

## License

MIT — see [LICENSE](./LICENSE).
