Skip to content

Releases: musharna/phylokit-mcp

v0.4.0

Choose a tag to compare

@musharna musharna released this 02 Aug 05:43
8436698

Minor, deliberately not a patch.

phylokit_mcp.server no longer exposes a module-level mcp. Anything importing it breaks, so this is a public API change even though no tool signature moved — build_server() replaces it. Importing the module no longer constructs a server, and each call returns a fresh one.

Two tests pin that property and were seen to fail against three deliberate mutants (a reintroduced singleton, a cached factory, and a factory registering no tools). Verified from an installed wheel: 5 tools, console script intact.

Also carries the community-health tier (CONTRIBUTING.md, SECURITY.md, issue forms, PR template, .editorconfig, .mcp.json, glama.json, CodeQL, Dependabot) and server.json validation against the registry's published schema.

v0.3.0 — protein alignments

Choose a tag to compare

@musharna musharna released this 01 Aug 23:19
434b766

Protein alignments. infer_tree and select_substitution_model accept sequence_type="protein". The molecule type is declared, never inferred: an alignment of only A/C/G/T is a valid protein alignment too, and guessing wrong returns a tree, a likelihood and bootstrap support that are all wrong and none complaining.

Topology recovery checked against a known simulated tree, with built-in negative controls — which immediately caught a blinded RF comparator that the primary "we recovered it" assertion passed happily, since RF = 0 reads as perfect recovery whether you compared the right trees or nothing at all.

Audit fixes. A signal-free alignment now gets a diagnosis instead of piqtree's IQ-TREE output is malformed — an upstream parsing error that read as a broken server and invited pointless retries. Branch lengths pinned at the optimiser's ceiling (10 subs/site) are flagged as a floor rather than returned as a measurement. Trees carry their engine version and branch_length_units.

Full detail in CHANGELOG.md.

v0.2.2 — Zenodo archival, licence ID corrected

Choose a tag to compare

@musharna musharna released this 31 Jul 04:52
e94f48b

Archived to Zenodo. This release exists so the tag carries the citation metadata a DOI is minted from.

Why a release was required

The Zenodo↔GitHub integration mints its DOI from the tag's tarball. The previous tag predated .zenodo.json and CITATION.cff entirely — both were added after it was cut — so re-publishing the existing release could never have archived correct metadata, and Zenodo returns 409 on it regardless, having created a deposit on the first attempt that never became public.

Correction — these notes originally claimed a licence bug that does not exist

This release was first described as fixing a defect in which SPDX casing in .zenodo.json "silently dropped the licence from the published record". That was wrong, and the claim is retracted here rather than quietly removed.

Zenodo normalises the licence identifier on ingest. The sibling ldraw-mcp archived with "MIT" still in place and its record reads license: mit-license. Every record carries its licence correctly; the licence was never dropped.

The apparent evidence was two measurement errors, both the same mistake — probing a proxy instead of the artifact:

  1. Querying the licence vocabulary endpoint (MIT → 404, mit → 200) and treating that as what the ingest accepts. It is not.
  2. Reading the RDM-era field names (rights, subjects, creators[].person_or_org) against an API endpoint that returns the legacy shape (metadata.license, metadata.keywords, creators[].orcid). Every field reported as absent was present throughout.

The lowercase identifier is kept because it is the canonical spelling — it simply fixed nothing.

Verified before publishing

Version agreement across pyproject.toml, server.json (three fields) and CITATION.cff; cffconvert --validate against the CFF 1.2.0 schema; mcp-publisher validate; ruff check and format; registry metadata tests; twine check; the mcp-name marker confirmed inside the built wheel's METADATA; and both wheel and sdist confirmed present on the /simple/ index after upload.

No functional change — tools, guards and dependency pins are untouched.

v0.2.1 — published to the official MCP registry

Choose a tag to compare

@musharna musharna released this 31 Jul 02:22
51a13b5

Published to the official MCP registry via server.json and a GitHub Actions OIDC workflow, so this server is discoverable from MCP clients and directories rather than only from PyPI.

Why this needed a release

The registry proves PyPI ownership by finding an mcp-name: <server-name> marker in the package README as published to PyPI — and PyPI captures long_description at release time. A marker sitting on the default branch verifies nothing, so registering required a version that carries it.

Guarding it

tests/test_registry_metadata.py asserts what would otherwise only fail during a release, after the version is spent:

  • server.json states the version in three places, with nothing else making them agree with pyproject.toml
  • the README marker must match the name server.json declares — that exact string is what the registry greps for
  • the declared transport is stdio, which is what this server implements

All three were confirmed red against mutants before being kept.

Running the real mcp-publisher validate also caught a constraint no schema read surfaced: description is capped at 100 characters. That would otherwise have failed the publish after the version was already on PyPI and unreclaimable, so the workflow now validates before authenticating.

No functional change.

v0.2.0 — mcp 2.x

Choose a tag to compare

@musharna musharna released this 31 Jul 00:52
3757667

Migration to the mcp 2.x SDK. No change to what the tools do.

mcp.server.fastmcp no longer exists in 2.0.0 — but FastMCP was renamed, not removed. It is now mcp.server.mcpserver.MCPServer: the same class, the same decorator, the same annotations and structured_output kwargs. ToolError moved to mcp.server.mcpserver.exceptions; mcp.types.ToolAnnotations did not move.

The dependency moves to mcp>=2,<3 rather than widening to <3. This package imports mcp.server.mcpserver, which does not exist in 1.x, so a range spanning both majors could resolve to a version that cannot import the server. The old 1.28.1 floor was a security floor rather than a feature one, and every 2.x release is above it.

mcp.types field names went camelCase → snake_case (inputSchemainput_schema), which touched a test assertion rather than the server.

__version__ is read from installed metadata instead of restated

It was a literal beside a pyproject.toml version, with nothing enforcing agreement. They happened to agree here — but plantcv-mcp shipped 0.2.0 reporting "0.1.0" from exactly that arrangement, so this is a latent form of a defect that has already shipped elsewhere.

There was no version test at all. tests/test_version.py now compares the reported version against what pyproject.toml declares — deliberately not against importlib.metadata, which is what __version__ now reads from, since asserting those agree would compare a value to itself. Confirmed to fail against a reintroduced literal before being kept.

Full changelog: https://github.com/musharna/phylokit-mcp/blob/main/CHANGELOG.md

v0.1.0 — a topology without support is not a result

Choose a tag to compare

@musharna musharna released this 30 Jul 15:03
22bd67e

First release. Phylogenetic inference over MCP, driving IQ-TREE 2 through
piqtree.

pip install phylokit-mcp

The rule

infer_tree always runs a bootstrap and always returns per-clade support. There
is no flag to skip it, because a maximum-likelihood tree looks identical whether
or not the data support it.

Measured on alignments simulated from a known 7-taxon tree, so the right answer
is not in doubt:

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 — nothing about the topology distinguishes them. The support values do, 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 attach
    these, so the standard format drops them silently. Support is computed in-server
    (Felsenstein 1985) because piqtree 0.8.3 runs bootstrap_replicates but does not
    expose the values — which turned out to be the reason this is visible at all.
  • Model runners-up with ΔAIC. On a 400-site alignment simulated under JC,
    the AIC winner is F81 — a model the data were not generated under — with
    several models inside the conventional ±2 margin. A winner without its margin is
    a claim the numbers do not support.
  • Length versus evidencen_parsimony_informative alongside n_sites.

Reproducibility, stated rather than claimed

Across fresh processes the same request reproduces byte-identically. Within one
long-lived process it does not: passing the same rand_seed does not fully reset
IQ-TREE's internal state. Measured drift over six repeated 50-replicate calls was
0.02 on one of four clades — a single replicate flipping, inside the
bootstrap's own sampling error. Every response reports this, because an MCP server
is long-lived by design and that is exactly the condition which exposes it.

Licence

GPL-2.0-only, and the "only" is load-bearing: piqtree declares GPL-2.0-only,
which is incompatible with GPL-3.0, so the distributed combination cannot be GPL-3.


5 tools · 62 tests against real IQ-TREE with no mocked engine · 10 mutation checks
· a real-process JSON-RPC handshake test. Attached artifacts are byte-identical to
those on PyPI (sha256 verified).