phylokit-mcp
# 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.
[](https://github.com/musharna/phylokit-mcp/actions/workflows/ci.yml)
[](https://pypi.org/project/phylokit-mcp/)
[](https://pypi.org/project/phylokit-mcp/)
[](https://github.com/musharna/phylokit-mcp/blob/main/LICENSE)
[](https://doi.org/10.5281/zenodo.21713870)
[](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 about 0.05 (a
replicate flipping) for the nonparametric bootstrap, and by no fixed bound for
UFBoot2, whose replicates IQ-TREE draws inside the search (one clade moved 0.16
across six calls on one machine). The server reports
`deterministic_across_processes: false` and the drift bound for the method
used, `null` for UFBoot2. 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).
TDQS
Scored across 7 tools
Each tool targets a distinct pipeline stage: alignment, model selection, tree inference, tree comparison, position mapping, simulation, and capability introspection. The only potential overlap (map_positions aligning internally) is explicitly framed as a convenience, and infer_tree vs select_substitution_model produce clearly different outputs.
Six of seven tools follow a clean verb_noun pattern (infer_tree, align_sequences, map_positions, compare_trees, select_substitution_model, simulate_alignment). The lone noun 'capabilities' deviates but is a conventional meta-tool name and remains readable.
Seven tools is well-scoped for a phylogenetic pipeline, with no redundant or filler tools. Every tool corresponds to a necessary step in the workflow.
The surface covers the full lifecycle: align -> select model -> infer with support -> compare trees -> simulate a positive control -> map positions, plus a capabilities probe. out_path handling and internal alignment in map_positions close the usual I/O gaps.