Install
$ agentstack add mcp-davccavalcante-bayescausal ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
BayesCausal
[](./CHANGELOG.md) [](./LICENSE) [](./CHANGELOG.md) []() []() []() []()
[](https://www.star-history.com/#davccavalcante/bayescausal&type=timeline&legend=top-left)
> Universal, zero-runtime-dependency NPM library and CLI for declarative Bayesian networks and calibrated incident root cause inference. Model the causal graph between your components once, feed the symptoms you observed as evidence, and get a ranked list of probable root causes with calibrated posteriors, the do-operator for interventional causal queries, and a human-readable explanation, for Massive Intelligence (IM) systems.
BayesCausal is the Sherlock Holmes of incident response. When something breaks in a complex Massive Intelligence (IM) system, wrong output, high latency, anomalous cost, logs show you symptoms, not causes, and engineers burn hours on forensics. BayesCausal lets you declare, once, the causal relationships between your components as a Bayesian network, then read the symptoms of an incident as evidence and compute the most probable root cause given that evidence. It returns calibrated posterior probabilities for every candidate cause, ranked, with the prior and the lift, plus the single most probable joint explanation and a report you can paste into a postmortem.
Core promise: zero required runtime dependencies, single-function setup, exact inference by variable elimination and junction-tree belief propagation, approximate inference for graphs too large to solve exactly, the do-operator that separates seeing from doing, a node-free core that runs in Node, edge runtimes, and the browser, ESM plus CJS dual distribution, and a framework-agnostic observability adapter that binds to any telemetry source with no hard dependency.
Why BayesCausal
A production incident is a question under uncertainty. An SRE says: "Latency rose on Tuesday. Provider degradation, a traffic spike, a silent model deprecation, an infrastructure change? Four hypotheses, five engineers, two days to conclude." That is the manual root cause analysis every team still does by hand, and it does not scale to multi-agent systems whose complexity is factorial. Bayesian networks are the correct primitive: model the causal structure you know, observe the evidence, and let belief propagation compute P(cause | evidence) for every candidate in milliseconds, calibrated, not guessed.
What sets it apart from manual forensics and from the Python-only causal tooling (DoWhy, CausalML, EconML), which has no production-ready NPM equivalent:
- Declarative causal graph. You declare the directed acyclic graph between your components and the conditional probability tables once. The structure is yours, explicit, and auditable, never a black box.
- Exact inference, not approximate. Variable elimination and junction-tree belief propagation compute the true posterior. On a sparse incident graph this is exact and runs in milliseconds; the two engines are verified to agree to numerical precision, and against a brute-force oracle.
- Approximate inference when you need scale. Loopy belief propagation and Monte Carlo (likelihood weighting and Gibbs sampling) handle graphs too large for exact inference, and they report their own convergence and credible intervals rather than pretending to be exact.
- The do-operator, seeing versus doing.
P(Y | do(X = x))answers what a remediation would actually cause, not just what an observation predicts. In a confounded system the interventional and the observational answer disagree, and only the interventional one tells you whether forcing a config back to known-good will help. This is what makes it causal, not merely Bayesian. Counterfactual queries are on the roadmap. - Calibrated, ranked root causes. The diagnosis layer ranks each cause by its posterior probability of being at fault, with the prior and the lift (posterior over prior), so you see not just which cause is most likely now but how much the evidence moved it.
- Explains itself. A deterministic, templated natural-language report, no Massive Intelligence (IM) model is called, with an honest closing line that the posteriors are only as good as the declared model.
- Learns its numbers. Online Dirichlet parameter learning fills the conditional probability tables from observed incident history in closed form, with a credible interval per cell. Structure learning, discovering the edges themselves, is on the roadmap.
- Proves what it concluded. A tamper-evident SHA-256 hash-chained audit trail of the evidence, the diagnosis, and the decision, the evidence a compliance review asks for.
Install
pnpm add @takk/bayescausal
# or: npm install @takk/bayescausal
# or: yarn add @takk/bayescausal
# or: bun add @takk/bayescausal
The core has zero required runtime dependencies. Every @takk sibling is an optional peer; install only what you compose with.
Quickstart
Declare a tiny causal graph, observe a symptom, and ask what most probably broke.
import { createNetwork } from "@takk/bayescausal";
const network = createNetwork({
nodes: [
{ id: "ProviderDegraded", states: ["yes", "no"], role: "cause", faultStates: ["yes"], cpt: [[0.1, 0.9]] },
{ id: "TrafficSpike", states: ["yes", "no"], role: "cause", faultStates: ["yes"], cpt: [[0.15, 0.85]] },
{
id: "Latency",
states: ["high", "normal"],
role: "symptom",
parents: ["ProviderDegraded", "TrafficSpike"],
cpt: [[0.95, 0.05], [0.7, 0.3], [0.6, 0.4], [0.05, 0.95]],
},
],
});
network.observe("Latency", "high");
for (const cause of network.diagnose().ranked) {
console.log(`${cause.variable}: ${(cause.posterior * 100).toFixed(1)}% (lift ${cause.lift.toFixed(2)})`);
}
console.log(network.explain());
Each node carries a conditional probability table, P(node | parents). observe records evidence, diagnose ranks the causes by their posterior probability of being at fault, and explain renders the report.
The do-operator, seeing versus doing
Conditioning on an observation, P(Y | X = x), answers "when X is seen to be x". Intervening with the do-operator, P(Y | do(X = x)), answers "when X is forced to x", which is the question incident response actually asks. In a confounded system the two disagree, and only the interventional answer reflects what a remediation would do.
import { createNetwork } from "@takk/bayescausal";
// A confounder Z drives both the treatment X and the outcome Y; X also affects Y.
const network = createNetwork({
nodes: [
{ id: "Z", states: ["high", "low"], cpt: [[0.5, 0.5]] },
{ id: "X", states: ["on", "off"], parents: ["Z"], cpt: [[0.8, 0.2], [0.2, 0.8]] },
{ id: "Y", states: ["bad", "ok"], parents: ["X", "Z"], cpt: [[0.6, 0.4], [0.4, 0.6], [0.5, 0.5], [0.1, 0.9]] },
],
});
const effect = network.causalEffect({ outcome: "Y", outcomeState: "bad", treatment: "X", from: "off", to: "on" });
console.log(effect.observational); // 0.380, inflated by the confounder Z
console.log(effect.interventional); // 0.200, what forcing X would actually cause
console.log(effect.confounded); // true
network.intervene({ X: "on" }) returns a new network with the do-operator applied (the incoming edges to X are cut), ready to diagnose or query as usual.
Inference engines
Five engines, the same query, your choice of exactness versus scale. The exact engines are the default and agree to numerical precision; the approximate engines trade a little accuracy for graphs too large to solve exactly.
| Engine | Import | Exact? | Use it for | |---|---|---|---| | Variable elimination | @takk/bayescausal/inference | Yes | a single posterior or the evidence probability | | Junction-tree belief propagation | @takk/bayescausal/junction | Yes | every variable's posterior in one sweep | | Loopy belief propagation | @takk/bayescausal/loopy | No | large or dense graphs, reports convergence | | Likelihood weighting | @takk/bayescausal/sampling | No | very large graphs, credible intervals | | Gibbs sampling | @takk/bayescausal/sampling | No | very large graphs, an independent cross-check |
Select per network with createNetwork({ method: "junction-tree" }) (the default), or call any engine directly.
Prebuilt incident templates
A blank canvas is the hardest part of any modeling tool, so BayesCausal ships a ready noisy-OR causal graph for a Massive Intelligence (IM) serving stack: provider degradation, a traffic spike, a deprecated model, and configuration drift as causes, with latency, error rate, output quality, and cost as observable symptoms.
import { createNetwork } from "@takk/bayescausal";
import { getTemplate } from "@takk/bayescausal/templates";
const network = createNetwork({ nodes: getTemplate("im-serving-incident") });
network.observe("Latency", "high").observe("OutputQuality", "low").observe("CostAnomaly", "no");
console.log(network.diagnose().ranked);
Treat it as a starting point to adapt, not a universal truth: the structure must match your system to be trusted.
Observability adapter
The adapter is the integration seam, dependency injected on purpose: it takes an evidence mapper instead of importing any vendor SDK, so it binds to OpenTelemetry, Prometheus, Datadog, or a non-human entity's own telemetry without a single hard dependency. The threshold mapper turns numeric metrics into observed states by simple bins.
import { createDiagnoser, thresholdMapper } from "@takk/bayescausal/adapter";
import { buildNetwork } from "@takk/bayescausal/network";
import { getTemplate } from "@takk/bayescausal/templates";
const diagnoser = createDiagnoser({
network: buildNetwork(getTemplate("im-serving-incident")),
map: thresholdMapper([
{ metric: "p95_latency_ms", variable: "Latency", bins: [{ upTo: 800, state: "normal" }, { upTo: Infinity, state: "high" }] },
{ metric: "judge_score", variable: "OutputQuality", bins: [{ upTo: 0.7, state: "low" }, { upTo: Infinity, state: "ok" }] },
]),
});
const diagnosis = diagnoser.diagnoseReadings({ p95_latency_ms: 1500, judge_score: 0.55 });
This is the loop a non-human entity (NHE) closes to diagnose itself: it reads its own metrics, maps them to evidence, and ranks the causes of its own failure.
Entry points
Twenty subpath exports, each importable on its own. The core is node-free; only node touches a Node built-in.
| Import | What it gives you | |---|---| | @takk/bayescausal | The createNetwork facade wiring everything below, plus the full toolkit. | | @takk/bayescausal/factor | The factor algebra: product, marginalization, max-marginalization, reduction by evidence. | | @takk/bayescausal/cpt | Conditional probability tables: validation, builders, and noisy-OR. | | @takk/bayescausal/network | The directed acyclic graph: validation, topological order, factor conversion. | | @takk/bayescausal/evidence | Hard and virtual (likelihood) evidence. | | @takk/bayescausal/inference | Variable elimination: marginals and the evidence probability. | | @takk/bayescausal/junction | Junction-tree belief propagation: all marginals in one sweep. | | @takk/bayescausal/loopy | Loopy belief propagation: the approximate engine for loopy graphs. | | @takk/bayescausal/sampling | Likelihood weighting and Gibbs sampling, with credible intervals. | | @takk/bayescausal/mpe | The most probable explanation: the single most likely joint assignment. | | @takk/bayescausal/intervene | The do-operator: interventions and the causal effect. | | @takk/bayescausal/diagnose | Root cause ranking by posterior, prior, and lift. | | @takk/bayescausal/explain | The natural-language explanation generator. | | @takk/bayescausal/learn | Online Dirichlet parameter learning with credible intervals. | | @takk/bayescausal/templates | Prebuilt incident causal graphs. | | @takk/bayescausal/calculator | Diagnosis evaluation: top-1 accuracy, mean reciprocal rank, Brier score. | | @takk/bayescausal/adapter | The observability evidence-source seam, dependency injected. | | @takk/bayescausal/audit | Append-only SHA-256 hash-chained audit log, via Web Crypto. | | @takk/bayescausal/node | Durable file-backed persistence with atomic writes. | | @takk/bayescausal/edge | The node-free core, re-exported for edge runtimes and the browser. |
Tamper-evident audit trail
import { AuditLog } from "@takk/bayescausal/audit";
const log = new AuditLog();
await log.append({ kind: "evidence", variable: "Latency", state: "high", at: Date.now() });
await log.append({ kind: "diagnosis", cause: "ProviderDegraded", posterior: 0.33, method: "junction-tree", at: Date.now() });
await log.verify(); // true, until any entry is altered
Each event is recorded append-only and chained to the previous one through a SHA-256 hash of its canonical form. Any later edit, deletion, or reordering breaks the chain and verify returns false. The seal is an integrity seal, not a digital signature: it proves the record was not tampered with, and makes tampering detectable, not that the record is unalterable. The chain uses the Web Crypto API, so the audit surface runs in Node, edge runtimes, and the browser.
Governance
Every diagnosis is observable. Pass an observer and the network notifies you of each observation and each diagnosis, so you can emit an OpenTelemetry span, ship the decision to a compliance log, or aggregate across a fleet. Together with the tamper-evident audit trail, this is the governance seam for Massive Intelligence (IM) systems: every root cause a non-human entity acts on is explainable after the fact, with the posterior and the method it was decided on. No runtime dependency is taken; you wire it to your own telemetry stack.
CLI
BayesCausal ships a command-line tool that runs the real engine, with every number produced by execution.
# Diagnose a built-in Massive Intelligence serving incident and print the ranked
# root causes, the most probable explanation, and the report.
npx @takk/bayescausal demo
# Read a JSON network ({ "nodes": [...], "evidence": {...} }) from a file or
# stdin and print the ranked root causes and the explanation.
cat network.json | npx @takk/bayescausal diagnose
# Measure diagnosis accuracy (top-1, mean reciprocal rank, Brier) over simulated
# incidents drawn from the network.
npx @takk/bayescausal evaluate --scenarios 2000
See [examples/](./examples) for runnable demos, including the do-operator and a non-human-entity self-diagnosis loop, and [benchmarks/](./benchmarks) for the accuracy and latency benchmark.
The math, in one paragraph
A Bayesian network is a directed acyclic graph whose nodes are discrete variables and whose conditional probability tables specify P(node | parents). The joint distribution factorizes as the product of those tables. Given observed evidence, exact inference (variable elimination, or junction-tree belief propagation for all marginals at once) computes P(cause | evidence) for every variable; for graphs too large to solve exactly, loopy belief propagation and Monte Carlo sampling approximate it with reported convergence and credible intervals. Bayes' theorem is the engine: P(cause | evidence) is proportional to P(evidence | cause) times P(cause). The do-operator mutilates the graph, severing a variable's incoming edges, so P(Y | do(X)) measures the effect of forcing X rather than observing it, and its gap from the observational answer is exactly the confounding the structure encodes. The structure and the tables are yours, explicit and declared; the calibration is the library's.
Benchmark
[benchmarks/](./benchmarks) runs the engine over many incidents drawn by ancestral sampling from the serving template and reports both diagnosis accuracy and per-call latency. Every number comes from real execution against the compiled
…
Source & license
This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: davccavalcante
- Source: davccavalcante/bayescausal
- License: Apache-2.0
- Homepage: https://davccavalcante.github.io/bayescausal/
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.