HyphAeon from Claude
The same analyses as a Model Context Protocol server. Locally it runs on your machine with the
vendored model, reads files by path, and sends nothing anywhere; remotely it is a claude.ai
connector on this site's origin. One tool, hyphaeon_analyze, does what dropping a
file on the front page does and returns the report; the per-pillar tools are there for a
single question.
Local, over stdio
For Claude Code, one line:
claude mcp add hyphaeon -- npx @veg/hyphaeon-mcp Or start the server yourself and point any MCP client at its stdio:
npx @veg/hyphaeon-mcp Current release: @veg/hyphaeon-mcp 0.4.0 — read from the package at build, with the 12 tools listed below.
The server runs the ONNX graphs under Node with onnxruntime-node; the model files
ship with the package and the hash of each graph is verified before it scores. Over stdio, alignment, tree and the other file inputs accept a file:// URL, so a transcript never carries the sequences. Results write provenance.surface: "mcp-stdio".
HYPHAEON_MCP_THREADS- onnxruntime intra-op threads (default 1; 4 to 8 on a laptop).
HYPHAEON_MODELS_DIR- a directory with
manifest.jsonand the graphs, to use a different build of the model.
Nothing else is needed on the host: no Python, no HyPhy. Every pillar, phenotype included,
runs in this process, and a tree is optional — without one, or without branch lengths,
the run takes pairwise TN93 distances (the reference's --use-tn93).
Remote, as a claude.ai connector
Add a custom connector with this site's /mcp endpoint. Authorization is OAuth
2.1 with dynamic client registration, PKCE, and an out-of-band redirect for headless clients,
auto-approved the way the Datamonkey connector is, so the ceremony completes without an
account.
https://<host>/mcp The remote server is the same code under the same caps as the Node server: your sequences
are sent to it, it says so in the tool description, keeps nothing in logs, expires each job
after 7 days, and refuses file:// inputs. Results write provenance.surface: "mcp-http". The endpoint ships with the Node server; the
host is the deployment decision the plan records as D1.
Tools
Every analysis tool runs in-process: the JavaScript port runs inside the MCP
process and the result matches the reference at the published parity classes. That has been
true of all eight since Phase 3 — hyphaeon_phenotype included, which was the last
tool to answer through a Python subprocess — so nothing here is marked "bridged" and no tool
result carries provenance.surface: "python-reference". The names below are the
ones @veg/hyphaeon-mcp 0.4.0 registers.
| Tool | Returns |
|---|---|
hyphaeon_analyze | The whole report: diagnostics, sites, gene, epistasis and sectors, attribution, filter, DMS (progressive, capped), phenotype when a trait is given, provenance and timings, in the schema the web report reads. The same options as the report's "Re-run with": model_variant, max_species, reference_sequence, call_mode, seed, permutations, dms; plus phenotype / phenotype_file for the trait, and use_tn93 (a tree is optional). |
hyphaeon_validate | Diagnostics with the same warning codes as the browser, the run mode (inline or job) and a cost estimate. |
hyphaeon_meme | Per-site LRT, p, q, invariable flag and the report's rank columns; --filter and --attribute as options. |
hyphaeon_busted | p_ACAT, p_Simes, omnibus LRT, selection energy, significant-site counts, and the neural head's fields. |
hyphaeon_epistasis | Co-selection edges with CESI and q, sectors with coherence and p_perm, optional per-sector DMS, GraphML. |
hyphaeon_dms | 19-substitution scan per site with intrinsic plasticity and the ΔLRT map. |
hyphaeon_phenotype | Trait association per site, PARS signature, trait sectors, permulation p. The trait is a preset, a foreground list or pattern, or an inline trait table (phenotype_file); permulations need a tree with branch lengths. |
hyphaeon_evaluate | Concordance of a meme CSV with a HyPhy MEME JSON: correlations, ROC-AUC, PPV, FPR, confusion matrices. |
| Job control | |
job_status | Phase, progress and warnings of a queued run. |
get_results | A completed job's result, shaped with fields, top or summary_only. |
cancel_job | Cancels a queued or running job. |
list_models | The weights manifest and the engine's status. |
Every analysis tool accepts fields, top and summary_only to shape its result, and answers inside the call when the
alignment is at most 12,000 codons and the work term sites × taxa² is at most 2.5 × 10⁹
(× 19 for DMS); above that, or with run_async, it returns a job id for job_status and get_results. Errors are {error, kind, hint} with kind either input (your alignment, tree or options) or server (the model files or the
runtime).
Resources: hyphaeon://models, hyphaeon://methods/requirements, hyphaeon://caveats (the same numbers as the methods page), hyphaeon://examples/{name}, hyphaeon://gallery. Prompts: one interpretation guide per pillar, which is
how the closing turn of the transcript below knows what to say.
Example transcript
Recorded 2026-09-05 against @veg/hyphaeon-mcp 0.4.0 over stdio, on Smc6 (20 primates × 1,097 codons, bundled example). Tool results are abridged to what a reader needs; the numbers are the server's.
- You
Is there episodic selection in Smc6? The alignment is examples/Smc6.fasta and the tree examples/Smc6.nwk.
hyphaeon_validate· 98 ms{ "alignment": "file:///path/to/examples/Smc6.fasta", "tree": "file:///path/to/examples/Smc6.nwk" }Result
{ "ok": true, "warnings": [ { "code": "NON_ACGT_FRACTION", "severity": "info", "message": "0.4% of characters are not A/C/G/T (249 gaps, 2 ambiguity or other codes)." }, { "code": "SHALLOW_TREE", "severity": "info", "message": "Median patristic distance 0.0238 substitutions/site is shallow (< 0.05); the viral variant was trained on this regime.", "data": { "suggestVariant": "viral" } }, { "code": "COST_ESTIMATE", "severity": "info", "message": "1097 codons x 20 taxa: about < 1 s of model time on a laptop CPU (reference path)." }, { "code": "RUN_MODE", "severity": "info", "message": "hyphaeon_meme runs in-process (ONNX Runtime under Node) and answers inside the tool call." } ], "summary": { "format": "fasta", "sequence_count": 20, "taxa_matched": 20, "unique_haplotypes": 20, "codons": 1097, "tree_source": "user", "match_tier": "exact", "tree_free": null, "branch_lengths": { "branches": 37, "missing": 0, "zeros": 2 }, "mode": "sync" } }hyphaeon_meme· 344 ms{ "alignment": "file:///path/to/examples/Smc6.fasta", "tree": "file:///path/to/examples/Smc6.nwk", "summary_only": true }Result
{ "analysis": "meme", "summary": { "taxa_count": 20, "codon_count": 1097, "runtime_sec": 0.153, "invariable_sites": 1000, "variable_sites": 97, "significant_p05": 1, "significant_p10": 5, "significant_q05": 0, "significant_q10": 0, "top_sites": [ { "site": 697, "hyphaeon_lrt": 6.913, "p_value": 0.0141, "q_value": 0.667, "call": "Top 2%", "percentile": 100 }, { "site": 930, "hyphaeon_lrt": 4.068, "p_value": 0.0611, "q_value": 0.667, "call": "Top 2%", "percentile": 99.0 }, { "site": 628, "hyphaeon_lrt": 3.735, "p_value": 0.0726, "q_value": 0.667, "call": "Top 5%", "percentile": 97.9 }, { "site": 365, "hyphaeon_lrt": 3.405, "p_value": 0.0863, "q_value": 0.667, "call": "Top 5%", "percentile": 96.9 }, { "site": 279, "hyphaeon_lrt": 3.292, "p_value": 0.0916, "q_value": 0.667, "call": "Top 5%", "percentile": 95.9 } ] }, "collections": { "sites": 1097 }, "provenance": { "surface": "mcp-stdio", "engine": "in-process", "model_version": "v1", "model_variant": "general", "artifact_sha256": "aa10e8e0f059436bd8a6f59e1fb3f0137e845533b9c65009230c7de6b91ef22c", "artifact_verified": true, "is_surrogate": true, "surrogate_for": "MEME", "seed": 42, "elapsed_sec": 0.351, "preprocessing": { "taxa_used": 20, "duplicates_collapsed": 0, "pd_subsampled": false, "tree_source": "user", "tree_provided": "user", "tree_free": null, "display_tree_source": "user", "distance_rescaled": false }, "reference_command": ["hyphaeon", "meme", "-a", "Smc6.fasta", "-t", "Smc6.nwk", "--cpu", "-o", "<out.json>"] } }hyphaeon_busted· 247 ms{ "alignment": "file:///path/to/examples/Smc6.fasta", "tree": "file:///path/to/examples/Smc6.nwk", "summary_only": true }Result
{ "analysis": "busted", "summary": { "gene": "Smc6", "taxa": 20, "sites": 1097, "p_value_acat": 0.1180, "p_value_simes": 1.0, "omnibus_lrt": 3.299, "total_selection_energy": 84.82, "sig_sites_p05": 5, "sig_sites_p10": 14, "selection_probability": 0.533, "predicted_gene_lrt": 0.330, "synonymous_rate_variation": 0.607, "rate_distributions": { "omega_1": 0.1, "proportion_1": 0.101, "omega_2": 1, "proportion_2": 0.738, "omega_3": 1.164, "proportion_3": 0.161 }, "positive_selection_detected": true }, "provenance": { "surface": "mcp-stdio", "surrogate_for": "BUSTED", "seed": 42, "elapsed_sec": 0.224, "busted_head_sha256": "2ad554e02b5580b813a915d8fbd8a4fa925c7679977adb596388fdc8d4c1c116", "busted_head_verified": true, "neural_head": { "deterministic_upstream": false } } }- You
Do the top sites track the great apes specifically, rather than the primates in general?
hyphaeon_phenotype· 1037 ms{ "alignment": "file:///path/to/examples/Smc6.fasta", "tree": "file:///path/to/examples/Smc6.nwk", "foreground": "hg18,homSap_293T,panTro4,panPan,ponAbe2,nomLeu3", "permulations": 100, "seed": 42, "summary_only": true }Result
{ "analysis": "phenotype", "summary": { "phenotype_meta": { "mode": "discrete", "foreground_count": 6, "background_count": 14, "description": "User-specified foreground patterns: ['hg18', 'homSap_293T', 'panTro4', 'panPan', 'ponAbe2', 'nomLeu3']" }, "taxa_count": 20, "codon_count": 1097, "sites": 97, "spectral_energy": 0.5557, "norm_spectral_ratio": 0.3251, "max_assoc": 0.99999824, "p_evd_length_adjusted": 0.00846, "compact_pars_signature": "[ R704H - Q567P - D930E - K763E - A697A - N913S - M685T - ... ]", "permulations_count": 100, "gene_p_value_perm": 0.485, "significant_sites_count": 0, "trait_sectors": 0, "coselection_pairs": 0, "top_sites": [ { "site": 704, "ref_aa": "R", "derived_aa": "H", "hyphaeon_lrt": 1.861, "association_rho": 1.000, "score": 1.364, "foreground_freq_pct": 100, "background_freq_pct": 0 }, { "site": 567, "ref_aa": "Q", "derived_aa": "P", "hyphaeon_lrt": 1.536, "association_rho": 1.000, "score": 1.240, "foreground_freq_pct": 100, "background_freq_pct": 0 }, { "site": 930, "ref_aa": "D", "derived_aa": "E", "hyphaeon_lrt": 4.068, "association_rho": 0.596, "score": 1.202, "foreground_freq_pct": 100, "background_freq_pct": 0 }, { "site": 763, "ref_aa": "K", "derived_aa": "E", "hyphaeon_lrt": 2.091, "association_rho": 0.816, "score": 1.181, "foreground_freq_pct": 66.7, "background_freq_pct": 0 }, { "site": 697, "ref_aa": "A", "derived_aa": "A", "hyphaeon_lrt": 6.913, "association_rho": 0.434, "score": 1.141, "foreground_freq_pct": 66.7, "background_freq_pct": 35.7 } ] }, "provenance": { "surface": "mcp-stdio", "engine": "in-process", "is_surrogate": true, "surrogate_for": "no HyPhy counterpart (attention-based trait association)", "seed": 42, "elapsed_sec": 1.011, "options": { "foreground": "hg18,homSap_293T,panTro4,panPan,ponAbe2,nomLeu3", "permulations": 100, "seed": 42 }, "preprocessing": { "taxa_used": 20, "tree_source": "user", "tree_free": null }, "reference_command": ["hyphaeon", "phenotype", "-a", "Smc6.fasta", "-t", "Smc6.nwk", "-fg", "hg18,homSap_293T,panTro4,panPan,ponAbe2,nomLeu3", "--permulations", "100", "--seed", "42", "--cpu", "-o", "<out.json>"] } }- Claude
Smc6 has 1,097 codons, 97 of them variable across the 20 primates. One site reaches nominal p ≤ 0.05, codon 697 (LRT 6.9, p 0.014), with codons 930, 628, 365 and 279 next in rank; no site survives false-discovery control (every q is 0.67), so at the gene level the combination tests are neutral (p_ACAT 0.118, p_Simes 1.0, omnibus LRT 3.3). The "positive selection detected" flag is true only because the neural head's selection probability is 0.53, and that field is not reproducible upstream, so I would not lean on it. On the great-ape question: several sites do separate the six great apes cleanly (704 R→H and 567 Q→P are present in all six and absent from the other fourteen, ρ ≈ 1.0), but with a Brownian-motion permulation null over the tree the gene-level p is 0.49 — that pattern is what phylogenetic autocorrelation alone produces for a clade-shaped trait on this tree, and no site survives with a permulation p. Two things to keep in mind throughout: these are surrogate scores for MEME (rank is reliable, the LRT scale is compressed), and the tree is shallow (median patristic distance 0.024), the regime the viral variant was trained on, so a re-run with model_variant "viral" is worth comparing. If codon 697 matters to you, confirm it with MEME on Datamonkey.
Reproducing a report
Every report page and every MCP result end with the same two things: the install line above, and the tool call that reproduces the run. The convention is fixed so a snippet can be pasted into Claude Code as it stands:
- Files are
file:///path/to/<name>placeholders carrying the file names the run had, never the sequences. - Option names are the CLI's flags in snake_case (
model_variant,max_species,filter_p_thresh,n_permutations); a browser run's options are mapped to them one to one, and the presentation-only ones are dropped. - A run with no usable tree is
use_tn93: truein the snippet, not a tree path: under D22 the app takes pairwise TN93 distances into the MDS, which is the reference's own--use-tn93. - The reference command that reproduces the same run with the Python package is in
provenance.reference_commandof every result.
claude mcp add hyphaeon -- npx @veg/hyphaeon-mcp
{
"tool": "hyphaeon_analyze",
"arguments": {
"alignment": "file:///path/to/Smc6.fasta",
"tree": "file:///path/to/Smc6.nwk",
"model_variant": "general",
"seed": 42
}
}