Metadata-Version: 2.5
Name: phylokit-mcp
Version: 0.8.0
Summary: MCP server for phylogenetic inference — never returns a topology without its support
Project-URL: Homepage, https://github.com/musharna/phylokit-mcp
Project-URL: Repository, https://github.com/musharna/phylokit-mcp
Project-URL: Issues, https://github.com/musharna/phylokit-mcp/issues
Project-URL: Changelog, https://github.com/musharna/phylokit-mcp/blob/main/CHANGELOG.md
Author: Jaret Arnold
License-Expression: GPL-2.0-only
License-File: LICENSE
License-File: NOTICE
Keywords: bioinformatics,bootstrap,iqtree,maximum-likelihood,mcp,model-context-protocol,phylogenetics
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
Requires-Python: >=3.12
Requires-Dist: mcp<3,>=2
Requires-Dist: numpy>=1.26
Requires-Dist: piqtree<0.9,>=0.8
Description-Content-Type: text/markdown

# phylokit-mcp

phylokit-mcp is an MCP server that lets AI agents build maximum-likelihood
trees with IQ-TREE 3 and align sequences with MAFFT.

[![CI](https://img.shields.io/github/actions/workflow/status/musharna/phylokit-mcp/ci.yml?branch=main&label=CI)](https://github.com/musharna/phylokit-mcp/actions/workflows/ci.yml)
[![PyPI](https://img.shields.io/pypi/v/phylokit-mcp)](https://pypi.org/project/phylokit-mcp/)
[![Python](https://img.shields.io/pypi/pyversions/phylokit-mcp)](https://pypi.org/project/phylokit-mcp/)
[![License](https://img.shields.io/pypi/l/phylokit-mcp)](https://github.com/musharna/phylokit-mcp/blob/main/LICENSE)
[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.21713870.svg)](https://doi.org/10.5281/zenodo.21713870)
[![Glama](https://glama.ai/mcp/servers/musharna/phylokit-mcp/badges/score.svg)](https://glama.ai/mcp/servers/musharna/phylokit-mcp)

<!-- mcp-name: io.github.musharna/phylokit-mcp -->

**A topology without support is not a result.** `infer_tree` always runs a
bootstrap and returns per-clade support. There is no flag to skip it.

## Why the rule

A maximum-likelihood tree looks the same whether or not the data support it.
Measured on alignments simulated from a _known_ 7-taxon tree (JC, 100
replicates, seed 1):

| Sites | Informative sites | Recovered the true tree? | Lowest clade support |
| ----: | ----------------: | ------------------------ | -------------------: |
|   300 |                51 | yes, exactly             |                 1.00 |
|    60 |                11 | **no, RF 2**             |                 0.57 |

At 60 sites the tree contains a clade (`C,D,G`) that does not exist and omits
one that does (`E,F,G`). Both runs return a fully resolved Newick string of the
same shape. Only the support values tell them apart, and the false clade is the
_lowest-supported_ one in the tree.

## What it reports that a Newick string cannot

- **Conflicting clades**: groupings the data support at ≥0.70 that are _absent_
  from the reported tree. A support-annotated Newick string has nowhere to put
  them.
- **`fraction_resolved`**: the share of clades clearing 0.70. The headline
  number, before any single grouping is repeated as fact.
- **Model runners-up with ΔAIC**, not just a winner. On the 300-site alignment
  above, simulated under **JC**, the AIC winner is **not JC** (TPM2u and TPM2
  tie), and several models, JC among them at ΔAIC 0.7, sit inside the
  conventional ±2 margin (`seed=1`, piqtree 0.8.3).
- **Length versus evidence**: `n_parsimony_informative` alongside `n_sites`. A
  10,000-site alignment of near-identical sequences supports nothing.

## Tools

| Tool                        | What it does                                                                       |
| --------------------------- | ---------------------------------------------------------------------------------- |
| `infer_tree`                | ML tree **plus** bootstrap support (nonparametric or UFBoot2), per clade.          |
| `select_substitution_model` | Ranks 100+ models with ΔAIC/AICc/BIC, and says when the criteria disagree.         |
| `compare_trees`             | Robinson–Foulds distance and the clades that differ. Compares splits, not strings. |
| `simulate_alignment`        | Generates sequences along a tree you specify: the positive control.                |
| `align_sequences`           | Aligns unaligned FASTA with MAFFT, inline or to a new file (`out_path`).           |
| `map_positions`             | Maps a residue to its alignment column, and that column to every other sequence.   |
| `capabilities`              | piqtree and IQ-TREE versions, 215 substitution models, MAFFT version, limits.      |

Tools that take FASTA or Newick also take a file path (`fasta_path`, or
`trees_path` for two trees), read by the server; a relative path resolves
against the server's working directory.

The tests run real IQ-TREE and real MAFFT (no mocked engine), with 23 mutation
checks and a real-process JSON-RPC handshake test.

## Install

```bash
pip install phylokit-mcp
```

Requires **Python 3.12+**. IQ-TREE 3 comes through
[piqtree](https://github.com/cogent3/piqtree) 0.8, which builds it into its
wheel. `align_sequences` also needs the
[MAFFT](https://mafft.cbrc.jp/alignment/software/) binary on `PATH`, as does
`map_positions` on unaligned input; the other tools work without it. Details: [docs/INSTALL.md](https://github.com/musharna/phylokit-mcp/blob/main/docs/INSTALL.md).

## Configure your MCP client

```json
{
  "mcpServers": {
    "phylokit": {
      "command": "uvx",
      "args": ["phylokit-mcp"]
    }
  }
}
```

If `uvx` picks a Python older than 3.12, use
`"args": ["--python", "3.12", "phylokit-mcp"]`.

## What the benchmark shows

Claude with the server was compared with Claude writing Python in an environment we set up with
piqtree, cogent3 and MAFFT, on 38 phylogenetics questions, 3 runs each. A tie
means the server matches that result without you building the environment.
Questions right in every run, server against Python: Claude Haiku 5.5 34 against
37, Sonnet 5.5 37 against 35, Opus 5.5 36 against 35 (on the commit that takes
file paths). None is separated (pre-registered; Holm-adjusted McNemar p = 0.75 on
both Haiku and Sonnet). The server cost about the same as Python on Haiku and
more on Sonnet (1.23×) and Opus (1.26×).

<p align="center">
  <img src="https://raw.githubusercontent.com/musharna/phylokit-mcp/main/benchmarks/agent_qa/agent_qa.png" alt="Bar chart of 114 answers per setup (38 questions, 3 runs each). Claude alone: 114 declined. Read only: 76 right, 24 wrong, 12 declined, 2 invalid. Python, MAFFT and web: 113 right, 1 invalid. phylokit-mcp: 110 right, 4 wrong." width="780">
</p>

The chart is the first Opus 5.5 run, on a release that took the data as text.
Counting answers, the server got 110 of 114 right and Python 113; counting
questions, 36 of 38 were right in every run against 37 (sign test p = 1). Per
question the server took 6.0 times as long (95% CI 4.4 to 8.3) and cost 14.4
times as much (9.9 to 20.2), each a geometric mean of per-question ratios. Most
of that cost was the model pasting the data file into tool calls; taking file
paths (pre-registered re-run, 2026-10-18) brought it down to 1.26 times (1.04 to
1.55).

We wrote the questions and ran the benchmark ourselves; an outside re-run is
welcome. The benchmark does not score support values; method, controls and other
limitations are in [benchmarks/agent_qa](https://github.com/musharna/phylokit-mcp/blob/main/benchmarks/agent_qa/README.md);
the Haiku and Sonnet runs:
[2026-10-20 re-run](https://github.com/musharna/phylokit-mcp/blob/main/benchmarks/agent_qa/README.md#haiku-and-sonnet-re-run-2026-10-20).

## Reproducibility

Not bit-exact, in any setting: IQ-TREE reads the wall clock during its search,
so the same request and seed can differ in the trailing digits of branch
lengths and log-likelihood, and a support value can move by a replicate
flipping. The server reports `deterministic_across_processes: false`. Compare
trees with `compare_trees` and numbers with a tolerance, never by string
equality. Measurements: [docs/REPRODUCIBILITY.md](https://github.com/musharna/phylokit-mcp/blob/main/docs/REPRODUCIBILITY.md).

## Limitations

- **Nucleotide and protein, no codon models.** Pass `sequence_type="protein"`
  and a protein model (`LG`, `WAG`, …). The molecule type is **declared, never
  sniffed**: an A/C/G/T-only alignment is also valid protein (Ala/Cys/Gly/Thr),
  and a wrong guess returns a wrong tree, likelihood and support with no
  warning.
- **Two bootstraps, no aLRT or approximate Bayes.** The default is the
  nonparametric bootstrap (Felsenstein 1985), computed here, clades supported
  at 0.70; it is the only one that reports well-supported clades missing from
  the ML tree. `support_method="ufboot"` is IQ-TREE's UFBoot2 (Hoang et al.
  2018), clades supported at 0.95.
- **Nonparametric cost is linear in replicates**, each a full ML search: on 89
  taxa x 435 sites one replicate took 25-93 s, and 1000 UFBoot2 replicates 83 s.
  Capped at 200 taxa, 1000 nonparametric and 10000 UFBoot2 replicates. Every
  tool sends a progress notification every 15 s, so a client idle timeout does
  not abort a long call.
- **Alignment is MAFFT `--auto`, single-threaded, and nothing else**: no
  strategy choice, profile alignment or trimming; at most 200 sequences of
  100,000 residues and 600 s. Gapped input is refused, not degapped. The tree
  tools refuse ragged input rather than align it.
- **Unrooted trees.** No rooting, dating or ancestral reconstruction.

## License

**GPL-2.0-only**, because piqtree is `GPL-2.0-only`, which is _incompatible_
with GPL-3.0. cogent3 is BSD.

If you publish results from this server, cite IQ-TREE 3, piqtree and (when it
made the alignment) MAFFT, not this wrapper. The references are in
[CITATION.cff](https://github.com/musharna/phylokit-mcp/blob/main/CITATION.cff),
and licensing and affiliation are in
[NOTICE](https://github.com/musharna/phylokit-mcp/blob/main/NOTICE).
