{
  "markdown": "# rigor\n\n<!-- mcp-name: io.github.mrnh/rigor-mcp -->\n\nVerified statistical inference for AI agents.\n\nLLMs are decent at reciting statistics but bad at *doing* it reliably —\na t-statistic or a required sample size is a number recalled from\ntraining data, not computed and checked. `rigor` is the alternative:\nclassical hypothesis testing (parametric and non-parametric),\ncorrelation and regression, effect sizes, power/sample-size\ncalculation, and multiple-comparisons correction, computed from scratch\nand returned as a cited, assumption-checked answer -- plus a decision\nhelper for picking the right tool and a batch tool for running/\ncorrecting many comparisons at once, since \"which test do I even use\"\nand \"I forgot to correct for multiple comparisons\" are their own common\nfailure modes, distinct from getting a single formula wrong.\n\n**A concrete case where this matters.** The one sample-size number\neveryone half-remembers is Cohen (1988)'s own worked example: d=0.5,\nalpha=.05, power=.80 -> n≈64 per group. It's in every textbook and\nslide deck, so it's also what gets pattern-matched to when a\n*similar*-looking question comes up. Ask instead for d=0.46, power=.85\n-- a modest, realistic revision, not a trick:\n\n```sh\n$ rigor power ttest-2samp --effect-size 0.46 --power 0.85\nRequired n per group = 84.86 (round up: 85)\n```\n\n85, not \"about 64\" -- a third more participants to recruit than the\nhalf-remembered number suggests, from a question that *looks* like the\nfamous one. The formula itself isn't hard (`power.py` runs the same\nbisection search either direction, in a few lines); the failure mode\nis that recalling a nearby-looking answer feels indistinguishable from\ncomputing the right one, right up until the number's wrong.\n\nBuilt as an MCP server: a scan of the current MCP ecosystem (Context7\nfor coding docs, several physics/engineering/chemistry/geo servers,\neven Bentley's STAAD integration) found statistics/experimental design\nas one of the few common agent needs nobody had covered yet.\n\nThe statistics themselves (`rigor/distributions.py`, `inference.py`,\n`nonparametric.py`, `correlation.py`, `regression.py`,\n`effect_size.py`, `power.py`, `corrections.py`, plus the decision/batch\nhelpers in `advisor.py` and `batch.py`) are pure standard library, no\ndependencies. The package as a whole does depend on the official `mcp`\nSDK, since the MCP server is a first-class part of what it ships, not\nan add-on -- see [Install](#install).\n\n## Install\n\n```sh\npip install rigor-mcp\n```\n\n(the PyPI distribution is `rigor-mcp` since plain `rigor` was already\ntaken by an unrelated package; the importable package and the CLI\ncommand are both still just `rigor`.) This gets you both console\ncommands, `rigor` (CLI) and `rigor-mcp` (MCP server) -- deliberately\none install, no extras to get right, since `uvx rigor-mcp` (how most\nMCP clients would actually invoke this) has no way to request an\nextra.\n\n## What's in it\n\n- **`rigor/distributions.py`** — t, chi-squared, and F distributions\n  built from scratch on stdlib (regularized incomplete gamma/beta),\n  verified against exact closed-form identities (t(1) = Cauchy,\n  chi2(2) = scaled exponential, t² = F(1, df)) rather than trusted\n  transcription.\n- **`rigor/inference.py`** — one-/two-sample and paired t-tests,\n  one-/two-proportion z-tests, chi-squared goodness-of-fit and\n  independence, Fisher's exact test (2x2, exact via the hypergeometric\n  distribution — the small-sample alternative chi_square_independence's\n  own low-expected-count warning points to), one-way ANOVA, and\n  Levene's (Brown-Forsythe) test for equal variances. Each returns a\n  `TestResult`: statistic, degrees of freedom, two-tailed p-value, a\n  confidence interval, a citation, and assumption warnings (e.g. small-n\n  normality reliance, low expected cell counts).\n- **`rigor/nonparametric.py`** — Mann-Whitney U, Wilcoxon signed-rank,\n  and Kruskal-Wallis: the non-parametric alternative to\n  two_sample_t_test/paired_t_test/one_way_anova respectively, for when\n  a parametric test's own assumption warnings make its result suspect.\n  Rank-based, with tie correction; also returns `TestResult`.\n- **`rigor/correlation.py`** — Pearson (linear) and Spearman\n  (monotonic, via ranks) correlation, each returned as a `TestResult`\n  (H0: no association) with a confidence interval via the Fisher\n  z-transform.\n- **`rigor/regression.py`** — simple (single-predictor) ordinary least\n  squares regression: slope, intercept, R², and a significance test +\n  CI for the slope.\n- **`rigor/effect_size.py`** — Cohen's d, Hedges' g, Cohen's h, Cramér's\n  V, eta²/omega² (for one_way_anova), and rank-biserial correlation\n  (for mann_whitney_u).\n- **`rigor/power.py`** — power and required sample size for the\n  one-/two-sample t-test and two-proportion z-test (the one-sample\n  formula covers paired_t_test too, since a paired t-test is a\n  one-sample t-test on the differences). The two directions (given n,\n  find power; given power, find n) are exact numerical inverses of each\n  other by construction (bisection on the same underlying power\n  function), and sanity-checked against the Cohen (1988)\n  d=0.5/α=.05/power=.80 textbook reference case (n≈64).\n- **`rigor/corrections.py`** — Bonferroni and Benjamini-Hochberg (FDR)\n  multiple-comparisons correction.\n- **`rigor/advisor.py`** — `recommend_test`: a decision helper, not a\n  statistic. Answer a few characteristics of the data/question\n  (continuous/proportion/categorical/ordinal, how many groups, paired,\n  small-or-skewed, association-not-difference) and get back which tool\n  to call, what to call instead if this test's assumptions look shaky,\n  and what to run alongside it -- compiling the cross-references every\n  other module's docstrings already carry into one callable answer, so\n  an agent doesn't need to have already read all of them to find the\n  relevant one.\n- **`rigor/batch.py`** — `pairwise_group_comparisons`: runs every\n  pairwise comparison across 2+ groups (`two_sample_t_test` or\n  `mann_whitney_u`, your choice) and applies Bonferroni/BH correction\n  to the whole batch in one call, instead of the agent orchestrating\n  k*(k-1)/2 separate calls plus a correction call by hand and risking\n  forgetting the correction step. The natural follow-up\n  `one_way_anova`/`kruskal_wallis` already recommend in their own\n  docstrings once a result comes back significant.\n- **`rigor/cli.py`** — a CLI over all of the above (`rigor.py` at the\n  repo root is a thin shim so `python3 rigor.py ...` also works from a\n  plain checkout, without installing anything).\n- **`rigor/mcp_server.py`** — an MCP tool wrapper exposing all 32\n  operations to any MCP client (Claude Code, Claude Desktop, etc.).\n  Smoke-tested end-to-end over stdio against a real client — tool\n  discovery plus representative calls checked against known reference\n  values, including the full round-trip still landing the Cohen (1988)\n  case at n=63 and Fisher's original \"lady tasting tea\" case at\n  p≈0.4857.\n\n## Usage\n\nCLI, once installed:\n\n```sh\nrigor ttest one-sample --data 5.1,4.9,5.3,5.0,4.8,5.2 --mu0 5.0\nrigor corr pearson --x 1,2,3,4,5 --y 2,4,5,4,5\nrigor regress --x 1,2,3,4,5 --y 3,5,7,9,11\nrigor nonparam mann-whitney --a 1,2,3 --b 4,5,6\nrigor power ttest-2samp --effect-size 0.5 --power 0.8\nrigor recommend --outcome-type continuous --n-groups 3   # which test fits?\nrigor posthoc --groups \"1,2,3|4,5,6|7,8,9\" --labels A,B,C  # pairwise + correction\nrigor --help   # full list of subcommands (ttest, ztest, chi2, fisher, anova,\n                # levene, nonparam, corr, regress, effect-size, power, correct,\n                # recommend, posthoc)\n```\n\nor straight from a checkout without installing anything:\n\n```sh\npython3 rigor.py ttest one-sample --data 5.1,4.9,5.3,5.0,4.8,5.2 --mu0 5.0\n```\n\nMCP server, over stdio (the transport local clients like Claude Code\nexpect):\n\n```sh\npip install rigor-mcp\nrigor-mcp\n```\n\nor from a checkout: `pip install mcp && python3 -m rigor.mcp_server`.\n\nRegister it with Claude Code:\n\n```sh\nclaude mcp add rigor -- rigor-mcp\n```\n\n(or, from a checkout: `claude mcp add rigor -- python3 -m rigor.mcp_server`,\nrun from this repo's root or with an absolute module path). For\ninteractive poking with the MCP Inspector, run it as a script rather\nthan the installed command — which means the package root has to be\nput on the path by hand, since the Inspector imports the file directly:\n\n```sh\npip install \"mcp[cli]\"\nPYTHONPATH=. mcp dev rigor/mcp_server.py\n```\n\n## A transport-level edge case, handled\n\n`cohens_d` correctly returns `+inf`/`-inf` for zero-variance samples\n(per its own documented contract), but non-finite floats serialize to\nJSON `null` over MCP's structured content — which used to fail the\ntool's own number-typed output schema and crash the call. The MCP\n`cohens_d` tool now returns `{\"value\": float | null, \"warnings\": [...]}`\ninstead of a bare float, so that case is reported explicitly (null\nvalue, a warning naming the direction) rather than blowing up. That\nfix is specific to tools with a *bare-scalar* output schema — every\ntool that returns a dict (all the `TestResult`-based ones, plus\n`simple_linear_regression`) has been confirmed over real stdio to pass\na non-finite field straight through as JSON's non-standard `Infinity`,\nsince a generic dict return doesn't get a strict per-field number\nschema. Of the bare-float tools, `cohens_d` is the only one that can\nactually produce a non-finite value.\n\n## Tests\n\n```sh\npython3 -m unittest discover -s tests -v\n```\n\n153 tests: 140 exercise the statistics/decision logic directly; 12\nspawn `mcp_server.py` as a real MCP client would and check results over\nthe wire (skipped automatically if `mcp` isn't installed); 1 checks\nthat server.json's version hasn't drifted from pyproject.toml's (the\ntwo aren't otherwise linked -- see test_release_metadata.py).\n\n## License\n\nMIT — see [LICENSE](LICENSE).\n\n[![rigor MCP server](https://glama.ai/mcp/servers/mrnh/rigor/badges/card.svg)](https://glama.ai/mcp/servers/mrnh/rigor)\n",
  "bytes": 10017,
  "sha": "9c17f57d7cd798ce86ab5b1443c493fb59e2bfeb16a3fa25c5ec7c9db7d00d2a",
  "repo_slug": "mrnh/rigor",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_mrnh_rigor_mcp_fe1236be/readme"
}