Skip to main content
Glama
mcp-chainladder

mcp-chainladder

by datalattice

mcp-chainladder

PyPI Python License

Actuarial chain-ladder reserving for Claude. A Model Context Protocol server that hands Claude the tools to compute IBNR, project ultimates, run Mack (1993) stochastic error estimates, check Mack (1994) model assumptions, and parse loss triangles from CSV — all from a natural- language conversation.

Calculation only — not actuarial advice. This is a mechanical chain-ladder calculator. Selection of data, factors, tails, and exclusions, and the interpretation of stochastic error measures, require professional actuarial judgement.


What it does

Eight tools, exposed over MCP. Claude picks the right one when you ask a question; you don't have to call them by name.

Tool

When Claude reaches for it

compute_chain_ladder

"What's the IBNR on this triangle?" — the workhorse

project_triangle

"What does the full projected triangle look like?"

mack_stochastic

"What's the uncertainty on the total reserve?"

mack_diagnostics

"Are there outliers or trend issues?"

parse_csv_triangle

"Run the chain ladder on this CSV file"

to_incremental

"Show me the incremental development pattern"

to_cumulative

"Cumulate these incremental values"

sample_triangle

"Show me a working example" — quick demo

All numerical conventions match the Mack 1993 and Mack 1994 papers; results are stable to ~1e-12 against hand calculation on the textbook 10×10 triangle.


Related MCP server: ModelRisk MCP

Install

pipx install mcp-chainladder
# or, to run on demand without installing:
uvx mcp-chainladder

Python 3.10 or newer; no other runtime dependencies beyond the official MCP SDK.

Hook into Claude Desktop

Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):

{
  "mcpServers": {
    "chainladder": {
      "command": "uvx",
      "args": ["mcp-chainladder"]
    }
  }
}

Quit and reopen Claude Desktop. The triangle tools appear under the 🔌 icon in any conversation.

For Cursor / Cline / Continue / Zed — same mcpServers block, each app has its own config file location (their docs walk you through it).


Examples

Run the chain ladder on a CSV

Me: I have a paid-loss triangle at ~/Desktop/motor_2026Q1.csv.
    Run the chain ladder and tell me the IBNR.

Claude: [calls parse_csv_triangle, then compute_chain_ladder]
        Loaded a 10×10 cumulative triangle. Using volume-weighted
        factors:
          • Total paid to date: 49,458,000 USD
          • Total ultimate:     65,883,400 USD
          • Total IBNR:         16,425,400 USD
        Selected factors trend from 1.879 at the front of the
        triangle down to 1.002 at the tail.

Check whether you should worry about assumptions

Me: Same triangle — any reason not to trust the projection?

Claude: [calls mack_diagnostics]
        Three flags worth a look:
        • Inflation trend: slope 0.053, p ≈ 1e-12 — STRONG evidence
          link ratios are drifting upward across accident years.
          This violates Mack's homogeneity assumption; consider
          weighting recent years more.
        • Calendar-year: Z = 0.0, p = 1.0 — no diagonal effect.
        • Independence: Z = -0.35, p = 0.73 — no obvious dependence
          across development columns.
        • Outliers: 0 cells exceed |2σ|.

        The inflation finding is the one to act on.

Exclude an outlier and re-run

Me: Drop the link ratio at AY 3, dev period 2→3. What changes?

Claude: [calls compute_chain_ladder with excluded=[[2, 1]]]
        Excluding (AY 3, 2→3):
          • Volume factor at 2→3 moves from 1.331 to 1.328
          • Total IBNR moves from 16,425 to 16,389 (−36)
          • Mack SE total moves from ±354.6 to ±348.2
        Net: small enough to be a "robustness check passes" rather
        than a finding.

Tool reference

Each tool returns a JSON object (or a 2-D list, in the case of project_triangle/to_incremental/to_cumulative). Claude reads the descriptions and types directly from the server — you don't need to memorise the shapes — but here's the cheat sheet.

compute_chain_ladder(triangle, selected_factors?, tail?, excluded?)

End-to-end chain ladder.

Field returned

Meaning

volume_factors[j]

All-year volume-weighted age-to-age factor for transition j→j+1

simple_factors[j]

Unweighted average of individual link ratios

selected_factors[j]

The factor set actually used to project (defaults to volume)

individual_factors[i][j]

Per-row link ratio C[i,j+1] / C[i,j]; null where the pair is unobserved

cdf[j]

Cumulative dev factor to ultimate; cdf[-1] == tail

latest_diagonal[i]

Most recent observed value per AY

ultimates[i]

Projected ultimate per AY

ibnr[i]

Ultimate − Latest per AY

total_*

Sums of the three above

n_acc, n_dev

Triangle dimensions

mack_stochastic(triangle, selected_factors, excluded?)

Mack (1993) distribution-free standard errors. Returns σ̂²_j per dev period (tail-rule backfilled when only one observation), SE & CV per row, and SE_total / CV_total including cross-row covariance per eq. 5.15.

mack_diagnostics(triangle, selected_factors, excluded?, outlier_threshold?)

Returns standardised residuals, outliers (|r| > threshold, default 2.0), the calendar-year sign test, Spearman independence test across adjacent dev columns, and the inflation slope of mean log-link-ratio against accident-year index.

p-value bands to translate to plain English:

p < 0.005 → strong evidence
p < 0.05  → significant
p < 0.10  → borderline
p ≥ 0.10  → no evidence

parse_csv_triangle(path)

Reads a CSV from disk, treats blank / NA / NaN / N/A / − cells as unobserved, strips embedded thousand-separator commas, and skips header / metadata rows. Returns the triangle + dimensions + the absolute path read (useful for the assistant to confirm what it loaded).

project_triangle(triangle, selected_factors)

Fills the lower-right of the triangle with chain-ladder projections. Returns a fully-rectangular list[list[float]] (no nulls). NaN where a row has no observation to project forward from.

to_incremental(cumulative) / to_cumulative(incremental)

Two conversions. Unobserved cells stay unobserved; the inverse on observed cells is exact.

sample_triangle()

Returns the textbook 10×10 cumulative paid triangle. Use as a self-check: pass it to compute_chain_ladder and you should get Paid 49,458 / Ultimate 65,883 / IBNR 16,425.


Triangle format

[
    # AY 1 — fully developed
    [1000, 1855, 2423, 2988, 3335, 3483, 3552, 3603, 3624, 3631],
    # AY 2 — observed through dev 9
    [1113, 2103, 2774, 3422, 3844, 4010, 4090, 4148, 4172, None],
    # …
    # AY 10 — only the first observation
    [2640, None, None, None, None, None, None, None, None, None]
]
  • Outer index = accident year, oldest first

  • Inner index = development period, 0 = first age

  • Use None (or JSON null) for unobserved cells

  • All rows must be the same length — pad with trailing null


Testing

pipx install --editable mcp-chainladder
pytest -q

Tests pin every public tool against the textbook triangle's well-known parity values to ~1e-9.

Pro tier

<< UNDER REVIEW - COMING SOON >>

The free tier covers all 8 tools listed above. Pro unlocks additional methods + bulk workflows, gated by a local license file at ~/.chainladder/license (or wherever $CHAINLADDER_LICENSE_FILE points).

Pro licenses are currently for internal and testing purposes only — not open to the public. No purchase channel is available at this time. The Pro tools are listed below for reference and will continue to return pro_license_required for external users.

Pro tool

What it does

pro_license_status

Inspect current license state (free to call)

interpret_diagnostics

Mack tests with verdict labels + plain-English summaries + recommended actions

sensitivity_analysis

Drop each link ratio one-at-a-time and rank by IBNR impact

tail_extrapolation

Fit exponential + inverse-power tail models, recommend best fit

bornhuetter_ferguson

BF reserving method with side-by-side CL comparison

compare_methods

Run CL + BF in one call, report deltas + largest divergence

generate_pdf_report (coming v1.2)

Full 5-page actuarial PDF — cover / triangle / factors / results / 3D loss surface

batch_csv_processing (coming v1.2)

Fold the chain ladder over a directory of CSV triangles

cape_cod, mack_bf (coming v1.3)

Additional reserving methods, all returning side-by-side comparisons

License file format

{
  "product":  "mcp-chainladder-pro",
  "owner":    "alice@example.com",
  "expires":  null,
  "key":      "CL-PRO-1A2B3C4D",
  "signature": "…"
}

Drop it at ~/.chainladder/license (the file's directory must exist; the server doesn't create it). Pro tools immediately respond as unlocked the next time Claude calls them.

When the license is missing or expired, every Pro tool returns {"error": "pro_license_required", "status": {...}} instead of computing — Claude reads the status and points you to the upgrade URL. The free-tier tools always work regardless of license state.

License

MIT. See LICENSE.

Available Tools

14 tools
bornhuetter_fergusonA

Bornhuetter-Ferguson (1972) reserving method. Pro tier.

Combines the chain-ladder development pattern with an externally- provided a-priori ultimate per accident year, giving a result that is far less sensitive to noisy late development than pure chain ladder. The canonical "second method" for benchmarking reserves — when chain-ladder and BF agree, you can publish with confidence; when they diverge, the divergence is the finding.

Args: triangle: Cumulative loss triangle, same shape as compute_chain_ladder. a_priori_ultimates: Expected ultimate per accident-year row, usually derived from premium × expected loss ratio or a plan figure. Must have length n_acc. selected_factors: Override factor set; defaults to volume- weighted (same default as chain ladder). tail: Multiplicative tail factor. Default 1.0. excluded: Outlier exclusions, same shape as in compute_chain_ladder.

Returns either: - On success: {a_priori_ultimates, used_up_proportion, bf_ultimates, bf_ibnr, cl_ultimates, cl_ibnr, total_bf_ultimate, total_bf_ibnr, total_cl_ultimate, total_cl_ibnr} — note the chain-ladder values are returned alongside so the user can see the two methods side-by-side. - On license failure: {error, status} with status giving the upgrade URL and reason. Free-tier callers get this and can still see the API shape; no compute happens.

Inputs: triangle (required), a_priori_ultimates (required), selected_factors, tail, +1 more. Returns: object

compare_methodsA

Run chain ladder + Bornhuetter-Ferguson on the same triangle and return a side-by-side comparison. Pro tier.

The canonical "second opinion" — when both methods agree, the reserve is defensible; when they diverge, the divergence is the finding. Reports total deltas plus the single AY where the two methods disagree most.

Args: triangle: As in compute_chain_ladder. a_priori_ultimates: One expected ultimate per AY for the BF method. selected_factors, tail, excluded: As in compute_chain_ladder.

Returns either: - On success: {chain_ladder, bornhuetter_ferguson, diffs_by_ay, total_diff_ultimate, total_diff_ibnr, largest_divergence}. - On license failure: {error, status}.

Inputs: triangle (required), a_priori_ultimates (required), selected_factors, tail, +1 more. Returns: object

compute_chain_ladderA

Run a full chain-ladder reserving calculation on a cumulative loss triangle. This is the workhorse tool — call it whenever the user asks "what's the IBNR?", "what does the chain ladder say?", or hands you a triangle and asks for projections.

Args: triangle: Cumulative loss triangle as a list of lists. Outer index is the accident-year row (oldest first). Inner index is the development period (0 = first age). Cells are numbers or null; null means unobserved (typical lower-right corner of a real triangle). All rows should be the same length — pad with trailing nulls if needed. selected_factors: Optional list of user-chosen age-to-age factors, one per development-period transition (length = n_dev - 1). When omitted, the volume-weighted factors derived from the triangle are used as the selected set. tail: Multiplicative tail factor applied beyond the last development period. Default 1.0 (no tail). excluded: List of [row_i, dev_j] pairs identifying individual link ratios to drop from the volume and simple averages. Use this for outlier handling — typically after consulting mack_diagnostics to find suspect cells.

Returns: A dictionary containing: - volume_factors: list[float] — volume-weighted age-to-age - simple_factors: list[float] — simple average of link ratios - selected_factors: list[float] — the set actually used - individual_factors: list[list[float | None]] — per-row link ratios C[i, j+1] / C[i, j]; null where the pair is unobserved - cdf: list[float] — cumulative dev factors to ultimate; last element is tail - latest_diagonal: list[float] — most recent observed value per accident row - ultimates: list[float] — projected ultimate per accident row - ibnr: list[float] — Ultimate − Latest, per accident row - total_latest, total_ultimate, total_ibnr: float scalars - n_acc, n_dev: int — triangle dimensions for convenience

Inputs: triangle (required), selected_factors, tail, excluded. Returns: object

interpret_diagnosticsA

Run Mack assumption diagnostics + label each result with a plain- English verdict and a recommended action. Pro tier.

Free-tier mack_diagnostics returns raw Z-scores and p-values; this Pro variant adds:

• verdict band ("strong evidence" / "significant" / "borderline" / "no evidence") for each test, using the standard p-value cutoffs • a one-paragraph summary written so Claude can read it back to the user without further interpretation • a specific recommended action for each finding ("try BF", "weight recent years only", "investigate cells X, Y, Z", …) • an overall verdict pulling the three tests + outlier scan together into a single sentence

Use this when the user asks "is the chain ladder OK?", "should I publish this?", or "what does the model tell me about itself?".

Args: triangle: As in compute_chain_ladder. selected_factors: Length-(n_dev - 1) factor set used for the point estimate. excluded: Outlier exclusions to honour. outlier_threshold: Absolute residual threshold for flagging cells (default 2.0).

Returns either: - On success: {calendar_year, independence, inflation, outliers, overall} — each test object carries stat, p_value, verdict, summary, recommendation. The outliers object also reports count, severity (clean / mild / moderate / severe), and a list of flagged cells. - On license failure: {error: "pro_license_required", status: {...}}.

Inputs: triangle (required), selected_factors (required), excluded, outlier_threshold. Returns: object

mack_diagnosticsA

Mack (1994) assumption diagnostics — three statistical tests plus standardised-residual outlier detection. Use when the user asks "does the chain ladder look OK?", "are there any outliers?", or "should I be worried about [calendar-year / inflation / dependence] effects?".

Args: triangle: As in compute_chain_ladder. selected_factors: Length-(n_dev - 1) factor set. excluded: Outlier exclusions to honour. outlier_threshold: Absolute standardised-residual threshold for flagging an outlier. Default 2.0 (the Tk app's default).

Returns: - standardised_residuals: list[list[float | null]] — per-cell residual r[i,j] = (C[i,j+1] − f_j·C[i,j]) / (σ_j·√C[i,j]); null where the cell is excluded or the pair is unobserved - outliers: list of {row: int, dev: int, residual: float} — cells with |residual| > outlier_threshold - calendar_year: {z: float, p_two_sided: float} — Tarbell sign test for calendar-year effects (large |z| ⇒ suspect) - independence: {z: float, p_two_sided: float} — Spearman rank-correlation between adjacent development columns (large |z| ⇒ link ratios are not independent) - inflation: {slope: float, p_value: float} — OLS slope of mean(ln link-ratio) on accident-year index (non-zero ⇒ accident-year trend in link ratios)

Verdict guidance for translating p-values to plain English: p < 0.005 → "strong evidence"; p < 0.05 → "significant"; p < 0.10 → "borderline"; p ≥ 0.10 → "no evidence".

Inputs: triangle (required), selected_factors (required), excluded, outlier_threshold. Returns: object

mack_stochasticA

Mack (1993) stochastic chain-ladder error estimation. Returns distribution-free standard errors and coefficients of variation per accident row plus the totals — the canonical sensitivity check for a deterministic chain-ladder result.

Use this when the user asks about uncertainty, reserve risk, or confidence intervals. Pair it with compute_chain_ladder (use the same selected_factors for consistency).

Args: triangle: As in compute_chain_ladder. selected_factors: The factors used for the point-estimate projection. Length = n_dev - 1. excluded: Outlier exclusions to honour, same shape as in compute_chain_ladder.

Returns: - sigma2: list[float] — σ̂_j² per development period; backfilled via Mack's tail rule when only one observation is available - se_per_row: list[float] — standard error of each ultimate - cv_per_row: list[float] — coefficient of variation (SE / Ultimate) per accident row - se_total: float — SE of the sum of all ultimates (includes cross-row covariance per Mack eq. 5.15) - cv_total: float — CV of the total ultimate

Inputs: triangle (required), selected_factors (required), excluded. Returns: object

parse_csv_triangleA

Parse a CSV file from disk into a cumulative loss triangle.

Reads the file, treats empty cells and the tokens "NA", "N/A", "NaN", "-" as unobserved, strips embedded commas (thousand separators), and drops any leading row whose first cell is non-numeric but whose remaining cells are mostly numeric (e.g. a "Dev 1, Dev 2, …" header). Auto-pads jagged rows to a rectangle with nulls.

Use this when the user gives you a CSV file path and wants to run the chain ladder on it.

Args: path: Absolute or ~-relative path to the CSV file. Must be readable by the server process.

Returns: - triangle: list[list[float | null]] — parsed cells, ready to pass to compute_chain_ladder - n_acc: int — number of accident-year rows - n_dev: int — number of development periods (max row length) - source: str — absolute path actually read

Inputs: path (required). Returns: object

project_triangleA

Fill the lower-right (unobserved) cells of a cumulative triangle using the supplied age-to-age factors.

Args: triangle: As in compute_chain_ladder. selected_factors: One factor per development-period transition. Length must be n_dev - 1.

Returns a dict with: - triangle: 2-D list of floats, same shape as the input, with unobserved cells filled in by chain-ladder projection. Rows with no observation contain nan for the projected cells. - disclaimer: standard actuarial-use disclaimer.

Inputs: triangle (required), selected_factors (required). Returns: object

pro_license_statusA

Inspect the current Pro-tier license state. Returns whether Pro tools are unlocked, the registered owner, an expiry date if any, and a human-readable message that Claude can relay to the user. Free to call regardless of license state.

Returns: - active: bool — whether the license is currently valid - owner: str | null — email associated with the license - expires: int | null — unix-epoch seconds, null = perpetual - message: str — plain-English status (great for Claude to read back to the user) - upgrade_url: str — where to buy / renew

Inputs: no named parameters. Returns: object

sample_triangleA

Return the classic textbook 10×10 cumulative-paid triangle (Friedland-style). Useful for demos, examples, and verifying the server is working — feed it to compute_chain_ladder to get the well-known parity values (Paid 49,458 / Ultimate 65,883 / IBNR 16,425 / Mack SE ±354.61).

Returns: - triangle: the 10×10 list with the lower-right as null - n_acc: 10 - n_dev: 10 - expected_totals: well-known parity values for cross-checking

Inputs: no named parameters. Returns: object

sensitivity_analysisA

Drop each observable link ratio one at a time, rerun the chain ladder, and rank the link ratios by their impact on total IBNR. Pro tier.

The fastest way to find the few observations that are actually driving the projection. Use after mack_diagnostics flags outliers — these are the cells to investigate first.

Args: triangle: As in compute_chain_ladder. selected_factors: Override factor set; defaults to volume- weighted. tail: Multiplicative tail factor. excluded: Existing exclusions; the analysis honours these and tests one additional cell at a time. top_n: Cap on the number of "most influential" cells returned. Default 10. Set higher for larger triangles.

Returns either: - On success: {baseline_ibnr, n_tested, top_influential[], summary} — each top_influential entry has row, dev, ratio, ibnr_with_excluded, ibnr_delta, ibnr_delta_pct. - On license failure: {error, status}.

Inputs: triangle (required), selected_factors, tail, excluded, +1 more. Returns: object

tail_extrapolationA

Fit parametric tail models to the late development factors and extrapolate forward. Pro tier.

Fits two candidate models — exponential decay (ln(f_j - 1) = a + b·j) and inverse-power (ln(f_j - 1) = a + b·ln(j+1)) — picks the better R², and returns the implied tail factor for plugging back into compute_chain_ladder(tail=…).

Use this when the triangle obviously hasn't reached ultimate by the last observed development period — i.e., the last selected factor is still meaningfully above 1.

Args: selected_factors: The factor set you want to extrapolate from. n_extra: How many extra development periods to project. Default 6 (covers most P&C lines).

Returns either: - On success: {fits[], recommended, summary} — each fits entry has model, parameters, r_squared, extrapolated[], tail_factor. - On license failure: {error, status}.

Inputs: selected_factors (required), n_extra. Returns: object

to_cumulativeA

Convert an incremental triangle to cumulative (running sum per row). Unobserved cells propagate as null. Inverse of to_incremental on observed cells.

Args: incremental: Incremental triangle.

Returns dict with triangle (cumulative, same shape) and the standard disclaimer.

Inputs: incremental (required). Returns: object

to_incrementalA

Convert a cumulative triangle to incremental (per-period) values.

inc[i, 0] = cum[i, 0] inc[i, j>0] = cum[i, j] - cum[i, j-1] (when both observed)

Unobserved cells stay unobserved.

Args: cumulative: Cumulative triangle, possibly with unobserved cells.

Returns dict with triangle (incremental, same shape) and the standard disclaimer.

Inputs: cumulative (required). Returns: object

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A4.4/5.0

    Scored across 14 tools

    Disambiguation5/5

    Each tool has a clearly distinct purpose: core calculation (compute_chain_ladder), alternative method (bornhuetter_ferguson), comparison, diagnostics (raw and interpreted), stochastic error, sensitivity, tail extrapolation, data I/O, conversions, license status. No overlap.

    Naming Consistency5/5

    All tool names follow a consistent verb_noun pattern with snake_case, e.g., compute_chain_ladder, mack_diagnostics, parse_csv_triangle. The style is uniform and predictable across all 14 tools.

    Tool Count5/5

    14 tools is well-scoped for an actuarial reserving server, covering core methods, diagnostics, error estimation, sensitivity, tail extrapolation, data I/O, and conversions. No extraneous tools, and each serves a necessary function.

    Completeness5/5

    The tool set covers the full typical reserving workflow: data input (parse_csv_triangle, sample_triangle), core deterministic calculation, alternative method, comparison, diagnostics, stochastic error, sensitivity analysis, tail extrapolation, and format conversions. No obvious gaps.

    Maintenance

    ActivityInactive
    ResponsivenessOutdated data

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      A
      quality
      D
      maintenance
      Connect Claude to your Power BI semantic models. Browse workspaces, tables, and measures, run DAX queries, and get results — with large datasets automatically saved to local CSV files to protect the LLM context window. Includes a query history log for cross-session reuse and auditability.
      13
      37 PyPI
      14
      MIT
    • A
      license
      Not graded
      quality
      C
      maintenance
      Lets Claude run validated, auditable HEOR analyses (cohort construction, outcome computation, overlap-weighted comparison) as deterministic tools on synthetic healthcare data.
      MIT
    • A
      license
      Not graded
      quality
      D
      maintenance
      Provides quantitative analytics and statistical inference for financial data, including Monte Carlo DCF valuation, risk metrics, and trend regression diagnostics, enabling natural language-driven financial analysis via Claude Desktop.
      MIT