{
  "markdown": "# codecalc — universal code & logic calculator for AI models\n\n<!-- mcp-name: io.github.The-40-Thieves/codecalc -->\n\n**codecalc is an offline, self-hosted MCP server that gives an AI agent a\ncalculator, a code runner, and a logic checker — so it gets a *correct* answer\ninstead of a guessed one.** It runs code in **31 languages**, does exact\nsymbolic math, solves SMT/logic problems, and measures complexity, all exposed\nas **52 MCP tools**.\n\n**Fastest path:** `uvx 'codecalc[full]' setup --write` registers codecalc with your MCP client automatically. New to MCP, or want more detail first? See [QUICKSTART.md](QUICKSTART.md), or the Install section below.\n\nThree things nobody else offers together cleanly:\n\n- **Offline-core** — ships no model, no API key, no gateway, no telemetry.\n  The core opens no sockets; network access is opt-in and only where a\n  specific tool's job needs it (the Piston provider, `install_package`, the\n  runtime-update tools, executed code unless `no_net`, and a one-time\n  in-process grammar download on first `analyze_complexity` — full breakdown\n  in the network-boundary table below).\n- **Safe execution of untrusted code** — an opt-in *strict* isolation\n  boundary (gVisor+Docker on Linux, AppContainer on Windows) layered above\n  the default rlimit sandbox, fail-closed and attested.\n- **Verification tools** — `verify_translation` proves a port to another\n  language behaves identically, `verify_optimization` proves an optimization\n  preserved behavior, and `z3_check` proves or refutes logic with an SMT\n  solver.\n\n## When to use codecalc\n\nUse it when you want a free, local, private, hardened code-runner and\nverifier that an MCP agent can call directly — no vendor account, no cloud\nspend, nothing leaving the machine except where a tool's job explicitly\nrequires it.\n\nReach for something else when you want managed cloud scale instead of\nself-hosting (a hosted sandbox like E2B or Modal), or when you're not\nself-hosting at all and the model vendor's built-in code interpreter already\ncovers what you need.\n\n## codecalc vs. the alternatives\n\ncodecalc is not a general cloud sandbox and not a vendor code interpreter. It\noverlaps with several things and beats them in only one narrow place — forcing a\nmodel to *measure* a claim instead of asserting it. Where that isn't what you\nneed, one of these is the better tool, and this table says so plainly.\n\n| You want… | Better fit | Why |\n|---|---|---|\n| To just run some Python/JS quickly, zero setup | Your model vendor's built-in interpreter | Already there, already sandboxed, nothing to install |\n| Heavy or multi-tenant workloads, managed scale | A cloud sandbox (E2B, Modal, Daytona) | Per-tenant Firecracker/gVisor isolation codecalc does not claim by default |\n| Pure arithmetic or symbolic math, nothing else | A small calculator or SymPy MCP | Lower token cost; none of the 31-language runtime machinery |\n| **A model to stop _guessing_ numbers, equivalence, and speedups — locally, privately, with graded evidence** | **codecalc** | Exact rationals, `verify_translation`/`verify_optimization`, and `unenforced`/grade honesty — offline, no account |\n\n**Do not reach for codecalc if** you need multi-tenant or network-exposed\nisolation (its threat model is explicitly single-operator, local, stdio), if\nzero-setup convenience matters more than measurement, or if a hosted interpreter\nalready covers your case. It earns its keep only when the *correctness of the\nclaim* — not just \"it ran\" — is the point.\n\n## Install\n\n### Quickstart with `codecalc setup`\n\nThe fastest path to a working MCP connection, without reading the rest of\nthis section:\n\n```bash\nuvx 'codecalc[full]' setup            # prints what it would do — nothing on disk changes\nuvx 'codecalc[full]' setup --write    # applies it: merges your client's config, copies the skill\n```\n\nIt detects which MCP client is installed (Claude Desktop, Claude Code,\nCursor, VS Code, Zed — pass `--client=NAME` if none or several are found),\nreuses `codecalc doctor`'s own backend/extras/grammar-cache checks, prints the\nexact config block in that client's own JSON shape with absolute paths\nalready filled in, runs two real canaries (`execute_code`, `evaluate_expression`)\nto prove the connection would work, and ends in one verdict: `ready` /\n`degraded` / `not-ready`. `--write` is the only mode that changes anything —\nit MERGES the `codecalc` entry into your existing client config (every other\nserver stays exactly as it was) and backs up the original to\n`<path>.codecalc-bak` first. `codecalc --help` lists every subcommand.\n\n> [!NOTE]\n> Published as **`codecalc` 0.5.0** on PyPI (`pip install codecalc`) and the\n> **`codecalc-exec` 0.5.0** executor on crates.io\n> ([#91](https://github.com/The-40-Thieves/codecalc/issues/91)). Every release\n> artifact carries a keyless sigstore **build-provenance attestation** — verify\n> one with `gh attestation verify <file> --repo The-40-Thieves/codecalc`; PyPI\n> wheels additionally carry PEP 740 attestations.\n\n**The published install** (simplest — no build step, and what most people want):\n\n```bash\nuvx 'codecalc[full]'          # run it directly, no environment to manage\n# or\npip install 'codecalc[full]'  # into your own virtualenv\n```\n\n**From source**, if you would rather build the executor yourself:\n\n```bash\ngit clone https://github.com/The-40-Thieves/codecalc\ncd codecalc\nuv sync --all-extras                 # or: pip install -e '.[full]'\ncargo build --release --manifest-path executor/Cargo.toml\nmkdir -p bin                         # bin/ is gitignored, so a fresh clone has none\ncp executor/target/release/codecalc-exec bin/\nuv run codecalc doctor               # verify: backend should read `rust`\n```\n\nWithout the `cargo build`, everything still runs on the pure-Python fallback —\n`doctor` will say so, and the network table below says what that costs.\n\n**Why `[full]`.** The base install is the MCP surface and the sandbox executor:\n31 language runtimes, sessions, packages, ~32 MB. The symbolic half — sympy and\nz3 — is 88.6 MB measured, and a caller who only runs code should not download an\nSMT solver to do it. So it is an extra:\n\n| install | size | what you get |\n|---|---|---|\n| `codecalc` | ~32 MB | execute_code, sessions, packages, complexity-free tools |\n| `codecalc[symbolic]` | +83 MB | evaluate_expression, solve, limits, truth tables, z3, units |\n| `codecalc[parsing]` | +5 MB installed, **+89 MB fetched on first use** | analyze_complexity via tree-sitter |\n| `codecalc[full]` | ~120 MB | everything |\n\nNothing fails silently: a tool whose extra is missing returns\n`{\"ok\": false, \"error\": \"sympy is not installed. It ships in the 'symbolic'\nextra: pip install 'codecalc[symbolic]' ...\"}`, and `codecalc doctor` lists\nwhich extras are present before you make a call.\n\n### Editions\n\nFour names for the capability sets above, plus the two that live outside\n`pyproject.toml` entirely — a Docker image and an opt-in isolation boundary.\n**The invariant that makes \"edition\" a meaningful word here:** in the edition\nthat lists a tool, that tool is functional — never listed-but-missing-its-extra.\nA tool an edition doesn't have returns the `dependency_missing` contract error\nnaming the extra that provides it (see above), not a silent failure or a\ntool that appears to exist and doesn't work.\n\n| Edition | Install | What you get |\n|---|---|---|\n| **Full** | `uvx 'codecalc[full]'` / `pip install 'codecalc[full]'` | The recommended local product: the native (Rust) executor, symbolic tools (`evaluate_expression`, `solve_linear`, `z3_check`, …), and parsing (`analyze_complexity`). Everything this README documents actually runs. |\n| **Core** | `uvx codecalc` / `pip install codecalc` | Execution + non-symbolic tools only — the base install in the table above. Every symbolic/parsing tool is still *listed* by `tools/list` (MCP doesn't support per-install schemas), but calling one returns `dependency_missing` naming the extra, before any other work happens. |\n| **Docker** | `docker build -f docker/mcp-server.Dockerfile .` | The MCP server itself, packaged to run as an ordinary container. Core-shaped **by default**: ships `python3`/`node`/`ruby`/`php`/`perl`/`gawk`/`lua`/`c`/`cpp`/`jq`/`sqlite3` and the default rlimit sandbox — symbolic/parsing are absent by design (no `[full]` in the base image; see the Dockerfile's own comment for why, including an arm64 z3-solver wheel gap). `--build-arg CODECALC_EXTRA=full` adds them. This image cannot nest the Strict Host boundary below inside itself (no privileged docker-in-docker), and `codecalc doctor` inside it says so rather than claiming a boundary it doesn't have. |\n| **Strict Host** | opt-in; `CODECALC_STRICT_URL` (client) or the gVisor+Docker host itself (server) — see [`docs/deployment/README.md`](docs/deployment/README.md) | Not an install, a *boundary*: the gVisor `runsc` sandbox on Linux, or AppContainer hardening on Windows, layered above whichever install above is already running. Fails closed — no digest pinned, no fallback to unenforced local execution. |\n\n`codecalc doctor` reports which of these you're actually running (backend,\nextras present, `strict_runtime` prerequisites) — read it before assuming a\ncapability rather than after a tool call surprises you.\n\n`.github/workflows/release.yml` publishes a platform-tagged wheel per target\n(Linux x86_64/aarch64 musl, macOS x86_64/aarch64, Windows x86_64), each\ncarrying the matching `codecalc-exec` binary and — where the platform has\none — its `--no-net` shim, so `executor.backend() == \"rust\"` on install\nwithout a manual build step. No wheel for your platform, or installed from\nsource instead? Everything still runs; see the network table above for what\nfalls back and to `unenforced` in that case.\n\nPoint an MCP client at the installed command. **The key differs by client** —\n`mcpServers` for most, `servers` for VS Code, `context_servers` for Zed — so\nthese are given separately rather than as one snippet to adapt:\n\n**Claude Desktop** — `~/Library/Application Support/Claude/claude_desktop_config.json`\n(macOS), `%APPDATA%\\Claude\\claude_desktop_config.json` (Windows) · **Cursor**\n(`.cursor/mcp.json`) and **Claude Code** (`.mcp.json`) use the same shape:\n\n```json\n{ \"mcpServers\": { \"codecalc\": { \"command\": \"uvx\", \"args\": [\"codecalc[full]\"] } } }\n```\n\n**VS Code** — `.vscode/mcp.json`, top-level key is `servers`:\n\n```json\n{ \"servers\": { \"codecalc\": { \"command\": \"uvx\", \"args\": [\"codecalc[full]\"] } } }\n```\n\n**Zed** — `~/.config/zed/settings.json`, key is `context_servers`:\n\n```json\n{ \"context_servers\": { \"codecalc\": { \"command\": \"uvx\", \"args\": [\"codecalc[full]\"], \"env\": {} } } }\n```\n\n**Windows paths need doubled backslashes** in JSON. If you installed into a venv\nrather than using `uvx`, point at the interpreter directly:\n\n```json\n{ \"mcpServers\": { \"codecalc\": {\n    \"command\": \"C:\\\\path\\\\to\\\\venv\\\\Scripts\\\\python.exe\",\n    \"args\": [\"-m\", \"codecalc\"] } } }\n```\n\nRun `codecalc doctor` to print a config block with the absolute paths of *your*\ninstall already filled in.\n\n**Install the skill too.** The tools cannot help a model that never reaches for\nthem — a model confident about `0.1 + 0.2` does not feel uncertain, it feels\nfinished. `codecalc/SKILL.md` ships inside the package and says when calling is\nmandatory (any non-integer, any comparison you will state, anything past 2^53,\nany number stated as a claim), when it is noise (`2 + 3 + 4` needs no tool), and\nhow results must be reported — `passed: true` means \"equivalent on N inputs\",\nnever \"verified\". `codecalc doctor` prints its path; copy it into your client's\nskills directory. `check_claims.py` gates it, so it cannot name a tool that does\nnot exist or a field no tool returns.\n\nNot sure what your install actually resolved? Ask it, rather than finding out\nfrom a tool call later:\n\n```bash\ncodecalc doctor          # or: python -m codecalc doctor\n```\n\n**This is the install verification step.** It exits `0` when the install can\nexecute — a writable workspace and a resolved backend — and `1` when it cannot,\nso it works unchanged in a Dockerfile, a provisioning script or a CI job. A\nmissing optional extra or an uninstalled Haskell does **not** fail it: those are\nfacts about the host, not a broken install, and a check that goes red for them\nis one people learn to ignore.\n\nIt prints the execution backend and the binary behind it, whether installs are\nconfined, the status of every one of the 31 runtimes, whether the workspace is\nwritable, and a client config block with absolute paths filled in. All of that\nis otherwise discoverable only by making a tool call and reading `backend`,\n`unenforced`, or a failure.\n\n```bash\ncodecalc doctor --json   # the same report, for scripts\ncodecalc doctor --deep   # actually RUN each runtime, and read its version\n```\n\n`--json` emits the report and nothing else, against a published schema\n([`docs/contract/doctor-v1.schema.json`](docs/contract/doctor-v1.schema.json))\ncarrying the same `contract_version` and the same policy as a tool result.\n\nEach runtime reports one of four states, and the difference between two of them\nis which measurement was actually taken:\n\n| state | means |\n|---|---|\n| `supported` | codecalc knows the language; nothing for it resolves here |\n| `installed` | its command resolves and is executable — **not run** |\n| `unhealthy` | resolves but cannot run, or was run and failed |\n| `available` | actually executed here and answered — `--deep` only |\n\n`status_basis` says which pass produced them. Without `--deep` nothing is ever\nreported `available`, because nothing was executed, and claiming otherwise for a\nbinary that was merely found on `PATH` would be a stronger measurement than was\ntaken.\n\nBuilding the Rust core yourself, or running from a checkout? See \"Build the\nRust core\" and \"Run the server\" below.\n\n### Use it from an MCP client\n\nThe shortest version of the config above — this registers codecalc as a\nstdio MCP server. The console entry point is `codecalc`, so `uvx codecalc`\nlaunches it directly:\n\n```json\n{\n  \"mcpServers\": {\n    \"codecalc\": { \"command\": \"uvx\", \"args\": [\"codecalc\"] }\n  }\n}\n```\n\nInstalled with `pip install codecalc` instead? Point at the resolved\ncommand with no args:\n\n```json\n{\n  \"mcpServers\": {\n    \"codecalc\": { \"command\": \"codecalc\" }\n  }\n}\n```\n\n## Network boundary\n\n**CodeCalc's core opens no sockets.** No model gateway or telemetry is built\nin. `tests/test_offline.py` asserts this for the top-level core modules. The\nopt-in Piston provider is the deliberate exception: its wire client lives under\n`codecalc/provider_adapters/` and is registered only when\n`CODECALC_PISTON_URL` is configured.\n\nThat is a claim about the **package**, not about every tool call, and the\ndifference is worth stating rather than leaving a reader to discover:\n\n| layer | reaches the network? |\n|---|---|\n| CodeCalc core | **No HTTP client, model gateway, or telemetry.** One dependency exception: `analyze_complexity` may download a grammar on first use (see below) |\n| configured Piston provider | **Yes, explicitly.** Calls only the operator-supplied `CODECALC_PISTON_URL`; credentials stay in its authorization header and are redacted from results |\n| `install_package` | **Yes, by design.** It runs uv / npm / gem / cargo, which fetch from their registries. Installer hooks also run *outside* the sandbox — see [SECURITY.md](SECURITY.md) |\n| `runtimes_status`, `update_runtimes` | **Yes.** They shell out to mise / rustup / swiftly / npm, which check remote versions |\n| code you execute | **Yes, unless `no_net=True`** — and that guarantee needs the native executor (seccomp-bpf where the Linux kernel supports it, a symbol shim otherwise; see the guarantee table below), so the pure-Python fallback reports it in `unenforced` instead of applying it. Set `CODECALC_REQUIRE_NATIVE=1` to turn \"fallback in use\" into a startup failure instead of a result you have to notice by reading `unenforced` |\n\nThese distinctions are stated precisely on purpose: a guarantee described more\nbroadly than it is enforced is exactly the failure mode this project works to\navoid, so \"offline-core\" is scoped to what the structural test can actually\nsupport rather than claimed as a blanket \"no network calls\".\n\n**The grammar download, stated plainly, because it is the one that is easy to\nmiss.** The other three paths above go through a CHILD PROCESS, which is what\n`tests/test_offline.py` says it cannot see. This one does not:\n`tree-sitter-language-pack` ships a ~5 MB extension and fetches each grammar on\nfirst use, **in-process**, into a local cache — 28 grammars, 89 MB, about 15\nseconds on a cold cache. So the first `analyze_complexity` call for a given\nlanguage opens a socket from inside the server.\n\nIt is verified (the pack checks a signature and raises on a checksum mismatch),\nit is cached, and it never happens again for that language. So the offline-core\nclaim is scoped to steady state: this first-use grammar fetch is the one\nin-process exception, which is why it is called out here rather than glossed\nover.\n\n**For an offline or egress-restricted install**, warm the cache first — it is one\ncommand, and afterwards nothing here reaches the network. If you installed\ncodecalc (`pip install`/`uvx`, not a source checkout), `scripts/` did not come\nwith it, so use the shipped console script instead:\n\n```bash\ncodecalc-prefetch-grammars                    # installed: fetch all 28 grammars\ncodecalc-prefetch-grammars --print-cache-dir  # installed: the directory to copy\n```\n\nBuilding from source? The script still works and calls the same code:\n\n```bash\npython scripts/prefetch_grammars.py                    # fetch all 28 grammars\npython scripts/prefetch_grammars.py --print-cache-dir  # the directory to copy\n```\n\n`codecalc doctor` reports whether that cache is populated, so this is\ndiscoverable before it matters rather than after a tool call degrades.\n\n## Architecture (language-per-strength)\n\n| Layer | Language | Why |\n|---|---|---|\n| Executor core (`executor/`) | **Rust** | Sandbox + rlimits + process-group kill + JSON CLI. No `eval()` anywhere near user input; memory-safe host; single static binary |\n| Logic layer (`codecalc/logic.py`) | **Python** | sympy (symbolic math, equation solving) and z3 (SMT) have no Rust equivalents |\n| MCP server (`codecalc/server.py`) | **Python** | the official `mcp` SDK (2.0) generates tool schemas from type hints; protocol **2026-07-28** |\n\nPython orchestrates; Rust executes; sympy/z3 reason. Each layer does what it's\nbest at. The Rust binary is preferred automatically; a pure-Python executor is\nthe fallback if the binary is missing.\n\n## Older-computer support\n\n- **No modern instruction-set requirements** — rustc targets a generic CPU by\n  default and nothing overrides it. (`executor/.cargo/config.toml` explains why\n  `-C target-cpu=generic` is deliberately NOT written there: it would be a\n  no-op that reads like a guarantee.)\n- **Static musl builds** run on any Linux regardless of glibc version:\n  `bin/codecalc-exec-x86_64-musl`, `bin/codecalc-exec-aarch64-musl` (~430K each;\n  the exact size moves with every toolchain bump, so it is not pinned here)\n- Size-optimized profile (`opt-level=\"z\"`, LTO, panic=abort, stripped) —\n  **measured, not assumed**: against an otherwise identical `opt-level=3` build,\n  `z` came out 1.02 ± 0.26 times faster on the executor's own path (i.e. no\n  detectable difference) while being 16% smaller. The executor spends its time\n  in syscalls, not arithmetic, so there was nothing for a higher optimisation\n  level to speed up.\n- **Lazy sympy/z3 imports.** Both are imported on first use, so a session that\n  only executes code never pays for them. This claimed \"~40ms, not ~600ms\" for a\n  long time while being wrong in both directions: the server took **1.9s** to\n  start, and sympy was not actually lazy — `units.py` imported it at module\n  scope and `server.py` imports `units`, so every start paid 437ms for it.\n  Deferring that took spawn-to-first-response from **1888ms to 1243ms**\n  (measured, median of 7). The remaining ~870ms is the `mcp` SDK's own import,\n  which is not ours to remove.\n- **The fork-bomb measurement is taken once, and only when it is needed.**\n  Sizing `RLIMIT_NPROC` means reading `/proc/<pid>/status` for every process on\n  the machine. That walk used to run during argument parsing and again for every\n  step: a C compile-and-run opened 1767 status files on a 590-process box to\n  answer one question three times, and `--lang notalanguage` paid the full cost\n  to produce a one-line error. Measured lazily and cached, an error costs 1.1ms\n  instead of 13.3ms and a compiled run 78ms instead of 104ms.\n- `list_languages` probes runtime availability and reports which languages\n  actually work on the machine (graceful degradation on minimal installs)\n\n## Build the Rust core\n\n```bash\ncd executor\ncargo build --release                          # native\ncargo zigbuild --release --target x86_64-unknown-linux-musl   # static x86_64 (uses zig)\ncargo zigbuild --release --target aarch64-unknown-linux-musl  # static arm64\n# Copy the executable AND its --no-net shim together. build.rs rebuilds the\n# shim whenever blocknet.c changes, but the executor looks for it beside the\n# BINARY, so installing only the binary leaves the previous shim in place — and\n# a stale shim silently enforces the old policy while every \"is it there?\"\n# check still passes. Copy both or neither.\nmkdir -p ../bin                                # bin/ is gitignored, so a fresh clone has none\ncp target/release/codecalc-exec target/release/blocknet.so ../bin/\n```\n\nRequires: Rust 1.97+, a C compiler for the `--no-net` shim (the build warns and\ncarries on without one; on macOS, or a Linux kernel without seccomp support,\n`--no-net` then reports itself in `unenforced` rather than pretending — a\nLinux kernel with seccomp support enforces it in-kernel either way), and\n[cargo-zigbuild](https://github.com/rust-cross/cargo-zigbuild)\nfor the static cross-builds (zig is used as the linker; no x86_64 GCC needed).\n\n## MCP tools (52) + MCP resources\n\nEvery session file is also exposed as an MCP resource:\n`codecalc://session/<session_id>/files/<path>` — images render inline for the\nmodel, text returns as text, other files download.\n\n**Exact arithmetic & programmer-mode**: exact rationals, threshold checks, bit\nanalysis, binary64 introspection.\n\n| Tool | Description |\n|---|---|\n| `calc_exact` | EXACT arithmetic: `0.1+0.2 == 0.3` is True; arbitrary-precision ints, bitwise ops inline, whitelisted math funcs, pi/e/tau |\n| `compare_threshold` | Exact threshold verdict with shortfall: `('1/25', '>', '0.05')` → False, shortfall 1/100 |\n| `percentage` | Exact share and percentage of PART/TOTAL (rationals accepted) |\n| `calc_stats` | mean, median, sample stdev, **CV** (CV > 0.2 = noise swamps the effect) |\n| `percentiles` | p50/p90/p95/p99 by nearest-rank AND interpolation; warns n<100 |\n| `collision_probability` | Birthday-bound hash collision: 1e5 items/32 bits ≈ 0.69, 1e6/64 ≈ 2.7e-8 |\n| `data_sizes` | Byte sizes both ways: KiB/MiB (binary) AND KB/MB (decimal) |\n| `human_duration` | Humanised duration + per-day/per-30d rates |\n| `epoch_time` | Epoch s/ms/µs/ns → ISO 8601 UTC, implausible readings suppressed |\n| `base_repr` | hex/oct/bin + two's complement at WIDTH + signed-overflow detection |\n| `radix_convert` | Any base 2..36, fractions included, non-termination flagged (`0.1` base 2) |\n| `float_repr` | What binary64 actually stores: exact value, raw bits, ULP, neighbours, representable-or-not |\n| `int_widths` | Which i8..i64/u8..u64 hold N + wrapped values; 2^53 JS/JSON caveat |\n| `bit_analysis` | popcount, bit length, trailing zeros, next pow2, alignment padding |\n| `bitop` | Programmer mode: and/or/xor/nand/nor/xnor/not/shl/shr/sar/rol/ror at 8/16/32/64, unsigned+signed+hex+oct+bin; shr vs sar distinction; shift-overflow flagged |\n| `algebraic_equiv` | Are `(a*b)/c` and `a*(b/c)` identical? refactor verification (with float/truncation caveat) |\n| `solve_expression` | Solve roots/crossovers: `x**2 - 4 = 0`, `2*x + 1 = 7` |\n| `limit_expression` | Asymptotic limits: `n*log(n)/n**2` → 0 (settles complexity arguments) |\n| `simplify_expression` | Simplified + factored + expanded forms |\n\n**Core tools**\n\n| Tool | Description |\n|---|---|\n| `list_languages` | 31 languages with extension, compile flag, runtime availability |\n| `list_execution_providers` | Execution-provider identity, interface version, host class, and machine-readable capabilities |\n| `execute_code` | Run code in any language → stdout/stderr/exit_code/**verdict** (OK/TLE/MLE/OLE/RTE)/cpu_ms/peak_memory_kb; per-call limits (`max_memory_mb`, `max_output_kb`, `max_cpu`), `no_net`, `compact`. With a session and no explicit `max_output_kb`, oversized output **spills** into the session workspace (`stdout_spill`/`stderr_spill`) instead of just truncating |\n| `execute_code_stream` | Provider-selected execution using the same canonical limits as `execute_code`, with progress + partial output when the provider supports streaming |\n| `run_submit` | Submit code for **background execution**; returns a `run_id` immediately instead of holding the call open |\n| `run_inspect` | Poll a background run: status while running, the full `execute_code` result shape once terminal |\n| `run_cancel` | Cancel a background run; idempotent on an already-terminal run, honest about providers that cannot cancel mid-flight |\n| `session_start` | Persistent session; python3/node get a stateful REPL worker (variables/imports persist across calls), other languages a workspace dir |\n| `session_stop` / `session_list` | Session lifecycle |\n| `session_files` / `session_read_file` / `session_write_file` | Workspace file tools, jailed to the session dir; listings support `page_size`/`cursor`, and reads return images inline (`as_image`) |\n| `session_run` | **Multi-file programs**: execute an entry file that imports other session files (helper.py, data/...) in the workspace |\n| `session_artifacts` | List files created by executed code (results, images, CSVs) |\n| `install_package` | Install packages (uv pip/npm/gem/go/cargo...) into a session or shared cache |\n| `verify_translation` | **Prove a port is equivalent**: you write the translation, the executor runs both versions on the same inputs and reports match / diverged / inconclusive per input. A pass is graded `cross_checked` (see [Grade vocabulary](#grade-vocabulary)) |\n| `verify_optimization` | **Prove an optimisation**: you write the candidate, the executor confirms it still agrees with the original AND times both — accepted only if equivalent and measurably faster. Accepted is graded `cross_checked` |\n| `extract_function` | Pull a named function + its dependency closure (imports, referenced helpers) into a standalone program and run it (ast-exact for python3, best-effort elsewhere) |\n| `compare_edge_cases` | Run the same logic in N languages on edge-case inputs (empty, zero, negative, float precision) and flag behavioral divergence |\n| `convert_units` | Dimensional unit conversion via sympy: length, mass, time, speed, energy, power, force, pressure, temperature (°C/°F/K), volume, area, data, frequency |\n| `physical_constants` | 22 physical constants with values (c, h, N_A, k_B, G, g, m_e, R, ...) |\n| `list_units` | All 140+ unit aliases for convert_units |\n| `evaluate_expression` | Symbolic math: `integrate(x**2, x)`, `sqrt(144) + 2**10` |\n| `truth_table` | Boolean algebra: `a and b or not c`, `p xor q`, `a implies b` |\n| `z3_check` | SMT-LIB2 satisfiability + model. An `unsat` verdict is graded `solver_proven`; `sat` is graded `ungraded` (decided, but not proof-shaped — see [Grade vocabulary](#grade-vocabulary)) |\n| `solve_linear` | Systems of equations: `x + y = 10; x - y = 2` |\n| `matrix` | Structured matrix ops: det/inverse/eigenvalues/transpose/rank/trace on a `rows` array — never a caller string through sympify, so `evaluate_expression`'s `[`/`]` RCE screen never applies. Each entry screened individually |\n| `analyze_complexity` | Static Big-O estimate from code structure, parsed with **tree-sitter** (every supported language). Reports `analysis: tree-sitter\\|regex-fallback` so you can tell a parse from a guess |\n| `benchmark` | Empirical Big-O: runs code at increasing N, fits growth curve |\n| `compare_execution` | Same code across N languages side-by-side |\n| `runtimes_status` | **Non-mutating** update check: current vs latest for every language runtime, which package manager owns it, and the command that would run |\n| `update_runtimes` | Update runtimes. **Dry-run by default** (`apply=False` returns the commands); `apply=True` executes them |\n\n## Grade vocabulary\n\n`verify_translation`, `verify_optimization` and `z3_check` return `grade` +\n`grade_basis` (+ `grade_rules_version`) on top of their own result. The grade\nnames how strong the evidence for a success actually is; it is derived from\nevidence those tools already emit, in `codecalc/grades.py` — the verifiers\nnever assign their own grade.\n\n| Grade | Means | Emitted by |\n|---|---|---|\n| `cross_checked` | Two independently authored programs were both actually run and their outputs agreed. `grade_basis` names the runtime(s) that did the checking. | `verify_translation` (source vs. port), `verify_optimization` (original vs. candidate) |\n| `solver_proven` | Z3 returned `unsat` within its timeout — a machine-checked refutation, not a heuristic. `grade_basis` names the engine version and the timeout bound. **Not** `sat`: see below. | `z3_check` |\n| `executed` | Reserved: the claimed computation ran and produced the reported result, with no independent second opinion. Not currently emitted by any tool above — every one of them also clears the `cross_checked`/`solver_proven` bar. | — |\n| `ungraded` | Explicit non-grade for a mismatch, an inconclusive comparison, a rejected optimisation candidate, a measurement failure, a Z3 `unknown` verdict, and — deliberately — a Z3 `sat` verdict. A real value on `grade`, never an absent key. **Never** a softened stand-in for one of the three grades above. | any of the above, on a non-success |\n\n`z3_check`'s `sat` verdicts are graded `ungraded`, not `solver_proven`, even\nthough `sat` is just as decisive a verdict as `unsat`. The ticket's motivating\npattern is proving a property P by asserting not-P and checking `unsat`; a\ncaller running that pattern who gets `sat` back has learned P is FALSE, and\n`solver_proven` on that result would let a reader who skims `grade` without\n`result` mistake a counterexample for a proof. `sat`'s `grade_basis` says so\nexplicitly: satisfiability was decided, but `solver_proven` is reserved for\n`unsat` so a counterexample can never wear a proof grade. Widening `sat` back\ninto `solver_proven` later is additive; narrowing it after callers depend on\nthe wider behaviour would not be, so this ships narrow now. Full reasoning:\n`codecalc/grades.py`'s module docstring.\n\n`algebraic_equiv` is deliberately NOT graded: it compares two expressions via\n`sympy.simplify(a - b) == 0`, a CAS transformation rather than a decision\nprocedure with a checkable certificate, and it is one simplifier's opinion\nrather than two independent implementations agreeing. None of the three\ngrades describes that evidence honestly.\n\n## Runtime self-update\n\nEvery language is mapped to its package manager, and codecalc can update its own\nruntimes:\n\n| Manager | Languages | Update command |\n|---|---|---|\n| mise | python3, node, bun, deno, ruby, go, erlang, elixir, gleam, zig, java, kotlin, sqlite, duckdb, gradle | `mise up` |\n| rustup | rust (stable/nightly toolchains) | `rustup update` |\n| swiftly | swift | `swiftly update` |\n| apt | c, c++, fortran, csharp, php, perl, lua, tcl, r, jq, bash, zsh | `apt-get install --only-upgrade` (language packages only) |\n| npm | typescript/tsc | `npm update -g` |\n| uv | mojo | `uv tool upgrade mojo` |\n| nix | haskell (on-demand) | nothing persistent |\n\n`runtimes_status` is always safe. `update_runtimes` refuses to mutate unless\n`apply=True` is passed explicitly — and it only touches the package manager\nthat owns each language (never the Rust sandbox, which has no update powers).\n\nOne of those managers is elevated: apt updates system packages, so its command\nstarts with `sudo`. `apply=True` is an argument a connected model controls, so\nthat branch takes a second key the model does not have — the host must set\n`CODECALC_ALLOW_RUNTIME_APPLY=1`. Without it the apt command is reported as\nskipped with `ok: false` and the variable named, while the unprivileged managers\nstill run. `sudo -n` already fails closed where a password is required; this\ncovers the passwordless-sudo rule common on developer machines and CI images,\nwhich is exactly where `-n` does not stop it.\n\n## Run the server\n\n```bash\ncd /path/to/codecalc && .venv/bin/python -m codecalc.server\n# stdio transport — register with any MCP client\n\n# The identical tool/resource registry over stateless Streamable HTTP:\n.venv/bin/python -m codecalc.server serve-http --host 127.0.0.1 --port 8000\n```\n\nStreamable HTTP binds to loopback by default and has no CodeCalc authentication\nlayer. Do not bind it to an untrusted network without an authenticating reverse\nproxy and the stronger process/container isolation described in `SECURITY.md`.\n\nPoint an MCP client at it:\n\n```json\n{ \"mcpServers\": { \"codecalc\": { \"command\": \"/path/to/codecalc/.venv/bin/python\",\n                                \"args\": [\"-m\", \"codecalc.server\"],\n                                \"env\": {\n                                  \"PYTHONPATH\": \"/path/to/codecalc\",\n                                  \"CODECALC_RUNTIME_PATH\": \"/path/to/mise/shims:/usr/local/bin:/usr/bin:/bin\"\n                                } } } }\n```\n\n## MCP protocol\n\nProtocol revision **2026-07-28**, on the official `mcp` SDK 2.0. Not fastmcp:\nfastmcp 3.x pins `mcp>=1.24,<2.0` and so cannot reach this revision at all.\n\nVerifying that is less obvious than it looks. `mcp.types.LATEST_PROTOCOL_VERSION`\nreads `2026-07-28` regardless of what a given connection negotiated, and the\n*same server* answers on either protocol depending only on how you connect:\n\n| client | negotiated | cache hints |\n|---|---|---|\n| `ClientSession.initialize()` | `2025-11-25` | dropped |\n| `Client(..., mode=\"auto\")` | **`2026-07-28`** | applied |\n\nSo `tests/test_mcp_protocol.py` asserts the negotiated value from a real\nconnection. The legacy path still works — backward compatibility is a feature —\nit just must not be mistaken for the new protocol.\n\nWorth noting for anyone reading the spec's headline change: 2026-07-28 removes\nprotocol-level sessions, and directs servers needing cross-call state to use\n\"explicit, server-minted handles passed as ordinary tool arguments\". That is\nexactly what codecalc's `session_id` already is.\n\n## The result contract\n\nEvery result carries `contract_version`, currently **1.3.0**. The published\nschema is [`docs/contract/result-v1.schema.json`](docs/contract/result-v1.schema.json)\nand the policy behind it — what MAJOR/MINOR/PATCH may change, the twelve-month\ndeprecation window, worked success/failure/timeout examples, and the migration\npath from unversioned servers — is in\n[`docs/contract/README.md`](docs/contract/README.md).\n\nFor in-process Python use, the supported protocol-neutral service boundary—and\nthe session/storage internals that are deliberately not public—is documented in\n[`docs/embedding.md`](docs/embedding.md).\n\nTwo things a caller should know before reading anything else:\n\n- **`ok` means \"ran and exited 0\".** A program that behaves exactly as intended\n  and exits 3 comes back `ok: false`, `exit_code: 3`, `verdict: \"RTE\"`. To tell\n  a failed *program* from a failed *request*, read `verdict` — a request that\n  never reached a runtime has no `verdict` at all, and has a `code` instead.\n- **`code` is the branch target, not `error`.** Eight stable values; the prose\n  in `error` is free to improve and is not a contract. An unrecognised `code`\n  must be treated as `internal` — that is what lets a `1.x` client survive a\n  `2.0.0` server, though adding a code is still a MAJOR change, because the\n  published enum is closed and a strict validator rejects the result first.\n- **Truncation reports a size, not just a flag.** `output_truncated` says output\n  was cut; `stdout_bytes` / `stderr_bytes` say by how much — the bytes the\n  program actually produced, before the cap. A 200 000-character `print` under\n  `max_output_kb=1` returns 1 039 bytes of `stdout` and `stdout_bytes: 200001`,\n  so a caller can size a retry instead of guessing. `null` there means not\n  measured (nothing ran); a program that printed nothing reports `0`.\n\nThe schema is JSON Schema 2020-12 — the dialect MCP 2026-07-28 defaults tool\n`outputSchema` to — so a client can validate our results with it directly.\n`scripts/check_contract.py` regenerates it from `codecalc/contract.py` and fails\non a diff, and separately re-derives both backends' verdict vocabularies from\n`main.rs` and `executor.py`: `check_parity.py` compares the two backends' *key\nsets* and is structurally blind to a new verdict *value*, which would leave the\npublished enum short and make a strictly validating client reject a good result.\n\n## Configuration\n\nAll optional. codecalc runs with none of these set.\n\n| Variable | Default | What it does |\n|---|---|---|\n| `CODECALC_RUNTIME_PATH` | the server's own `PATH`, else `/usr/local/bin:/usr/bin:/bin` | The `PATH` executed code resolves runtimes on. **Set this when an MCP client spawns the server**: clients often launch with a stripped environment, so an inherited `PATH` can miss a toolchain manager's shims entirely and most languages silently become unavailable. `list_languages` reports what actually resolved. |\n| `CODECALC_EXEC_BIN` | `bin/codecalc-exec` (arch-matched) | Override the sandbox binary. Without one, codecalc falls back to a pure-Python executor — `list_languages` and `execute_code` still work, but the Rust path is the production one. |\n| `CODECALC_REQUIRE_NATIVE` | *(unset)* | Fail-closed: refuse to start if no usable `codecalc-exec` binary was found (checked at import, so this is also a server-start check), instead of silently answering every call on the weaker Python fallback. Raises naming `CODECALC_REQUIRE_NATIVE` and the paths that were checked. |\n| `CODECALC_EXECUTION_PROVIDER` | `local` | Default execution-provider ID. Explicit `execute_code(provider=...)` selection still wins. Setting this to an unregistered provider fails explicitly; it never falls back. |\n| `CODECALC_PISTON_URL` | *(unset)* | Register the non-local open-source Piston v2 provider at this absolute HTTP(S) base URL. No public service is contacted by default. |\n| `CODECALC_PISTON_AUTHORIZATION` | *(unset)* | Exact value for Piston's `Authorization` header. It is scoped to the Piston transport and redacted from normalized results, descriptors, health, and receipts. |\n| `CODECALC_STRICT_URL` | *(unset)* | Activate the current OS's `<host>-strict` provider as an authenticated client of the Linux strict execution service. Without it, strict selection fails closed. The adapter verifies the remote enforcement handshake before sending source. |\n| `CODECALC_STRICT_AUTHORIZATION` | *(unset)* | Exact value for the strict service's `Authorization` header. It is never published in descriptors, doctor output, errors, or receipts. |\n| `CODECALC_RUN_STATE_DIR` | `~/.codecalc/runs` | Durable metadata-only journal backing `run_submit`/`run_inspect`/`run_cancel`, for every provider (not only managed strict runs). Source, stdin, output, and credentials are never written there. On restart, recorded orphan runs are cancelled and cleaned through their owning provider where it supports that; where it does not (the built-in `local` provider), there is nothing to signal and the record is simply marked recovered. |\n| `CODECALC_MAX_ACTIVE_RUNS` | `64` | Admission cap for `run_submit`: how many runs may be running/cancelling at once before further submissions are refused with a `resource_exhausted` error. Bounds the in-memory run table and its thread pool against an unbounded burst or a caller that never inspects/cancels what it starts. An empty, non-numeric or non-positive value falls back to `64` with a message on stderr — a set-but-empty variable is a shell and compose-file commonplace, and it used to abort the server's import. |\n| `CODECALC_ALLOW_RUNTIME_APPLY` | *(unset)* | Permit `update_runtimes(apply=True)` to run the **elevated** update commands (apt, via `sudo`). Unset, they are skipped with `ok: false` naming this variable, and the unprivileged managers still run. Deliberately an environment variable rather than a tool argument: `apply` is something a connected model can flip, and this is not. Accepts `1`/`true`/`yes`/`on`; an empty value is not consent. |\n| `CODECALC_SESSION_ROOT` | `~/.codecalc/sessions` | Where session workspaces live. **Keep this codecalc-private.** `codecalc cleanup --write --include-unmarked` removes plain, session-shaped subdirectories under it on a heuristic (name shape + age) that is a loose filter, not a strong one — never point it at a directory anything else writes into. |\n| `CODECALC_CLEANUP_ABANDONED_AGE_HOURS` | `24` | How old (and untouched) a marker-less, session-shaped directory must be before `codecalc cleanup --include-unmarked` will consider it abandoned. Only consulted with `--include-unmarked`; the default `cleanup` invocation never reads it. |\n| `CODECALC_PACKAGE_ALLOWLIST` | *(unset)* | Deny-by-default allowlist for `install_package`. Unset, any syntactically valid package name may be installed (today's behaviour). Set, only listed packages install — anything else is refused before any subprocess or network work, with the stable `permission_denied` code. Comma-separated; each entry is `<language>:<name>` (scoped to one ecosystem) or a bare `<name>` (every ecosystem). Matches the bare name, ignoring `[extras]` and `==version` pins. |\n| `CODECALC_SESSION_IDLE_TTL_SECONDS` | *(unset)* | Idle-expiry for stateful (python3/node) session workers: a session untouched for longer than this is reaped — worker killed via the same teardown `session_stop` uses — on its next access. Unset, a session worker lives until `session_stop` or server exit, same as before this existed. A subsequent call on an expired session gets `ok: false` with the stable `worker_failure` code, never a silent respawn. |\n| `CODECALC_SESSION_DISK_QUOTA_MB` | `512` | Per-session ceiling on total workspace disk. `session_write_file` and oversized-output spilling refuse BEFORE writing (`resource_exhausted`, no partial file); code run via `execute_code(session_id=...)`/`session_run` is checked before it starts and, since its own writes cannot be pre-checked, again after — an over-quota run still returns its result, now with `disk_quota_exceeded` plus usage/limit, and the session's next write/run is refused until usage (re-measured fresh each time) drops back under the line. |\n| `CODECALC_TOTAL_DISK_QUOTA_MB` | `8192` | Global ceiling on disk summed across every session workspace on this host — closes the gap where staying under the per-session quota by opening many sessions would otherwise be unbounded. Same enforcement points and `resource_exhausted` contract as `CODECALC_SESSION_DISK_QUOTA_MB`. |\n| `CODECALC_MAX_ARTIFACT_BYTES` | `16777216` (16 MiB) | Per-write size ceiling for anything a session write path creates — independent of the total quotas above, so one runaway file cannot hide under a generous session/global total. A WRITE-time cap; distinct from `RESOURCE_MAX_BYTES` (4 MiB), which caps what a *read* may serve back. |\n| `CODECALC_MAX_ARTIFACT_COUNT` | `500` | Per-session ceiling on the number of artifact files — catches a session writing one byte at a time into thousands of tiny files, a shape no byte-sized cap alone bounds. Only a write that creates a NEW file is checked; overwriting an existing one always succeeds regardless of the count. |\n| `CODECALC_MIN_HOST_FREE_MB` | `256` | Refuse a session write when the HOST's free disk space drops below this — protects the host even when every quota above is generous, since a shared host can be driven low by something that is not a codecalc session at all. Measured with `shutil.disk_usage`, which works identically on Windows, unlike `statvfs`. |\n| `CODECALC_CAPABILITY_POLICY` | *(unset)* | Capability broker. Unset, no brokering — a job's capabilities run as requested (today's behaviour); the execution receipt still discloses them under `provider.capabilities` with `brokered: false`. Set, comma-separated directives narrow them: `deny-network` forces `no_net` on a job that did not request network (enforced where the provider can, disclosed as `effective` where it cannot); `allow-network` explicitly grants network to a job that requested it; `strict` rejects a job whose denial the provider cannot enforce. The broker never approves a capability the request did not ask for — an escalation is refused with `permission_denied` / `capability_not_requested`, before any side effect. |\n| `CODECALC_AUDIT_LOG` | `~/.codecalc/audit/audit.log` | Append-only JSON-lines audit stream for broker decisions and security-relevant side effects (denied capability, refused install, cleanup). Each event carries a source-safe timestamp, the run/session id, the decision and reason, and never the executed source or a credential. Set to a path to relocate it; set empty to disable. Best effort — a write failure never fails a run. |\n| `CODECALC_PROCESS_HEADROOM` | `512` | Fork-bomb guard. `RLIMIT_NPROC` is a **uid-wide task budget**, not a per-sandbox one — the kernel compares it against every thread your user owns, machine-wide. So codecalc measures the ambient count per execution and sets the limit to *ambient + headroom*: a bomb can add at most this many tasks, while a runtime wanting a few threads always has room however busy the box is. |\n| `CODECALC_MAX_PROCESSES` | *(unset)* | Escape hatch: pin `RLIMIT_NPROC` to an absolute value and skip the measurement. |\n\nThe strict service runs on Linux x86_64 or ARM64 with Docker Engine, cgroup v2,\nand an explicitly registered gVisor `runsc` runtime. Its executor image must be\npinned by `@sha256:` digest on the execution path. That image is published to\nGHCR (`ghcr.io/the-40-thieves/codecalc-exec`, multi-arch amd64+arm64) by the\n`publish-executor-image` workflow, which an operator dispatches\n(`workflow_dispatch`); the workflow commits the immutable digest into\n`docker/executor-image.lock`, and `published_strict_image()` resolves it as the\nproduction default. Until that first dispatch no digest is pinned and the\nexecution path **fails closed** — it never falls back to the mutable local\ndiagnostic tag (`codecalc-exec:strict`), which `doctor` and the conformance\nsuite keep using. The default `systrap` platform works without KVM, so the same\nauthenticated service can be used from Linux, macOS, and Windows; strict clients\nnever fall back to native local execution.\n\nProvisioning and running any of the three strict backends in production —\nthe gVisor+Docker host, Windows AppContainer hardening, and the macOS/Windows\nremote-client configuration — is covered in\n[`docs/deployment/README.md`](docs/deployment/README.md), separate from the\nprovider interface itself in\n[`docs/contract/provider-v1.md`](docs/contract/provider-v1.md).\n\nBoth backends resolve `CODECALC_RUNTIME_PATH` identically, and\n`scripts/check_parity.py` fails CI if the Rust and Python copies of that\ncontract ever drift — including if a machine-specific home directory finds its\nway back into the default.\n\n## Tool-definition token cost\n\ncodecalc's `tools/list` returns 52 definitions. Measured with `o200k_base` as a\nproxy, that is roughly 9,200 tokens of descriptions and input schemas, and every\nclient pays it before the first user message.\n\ncodecalc does not hide its tools behind a discovery facade, and that is\ndeliberate: the tool surface is where per-operation approval prompts, audit\nnames and typed schemas live, and collapsing 52 tools into one dispatcher makes\n`install_package` and `percentage` look like the same permission to a client\nthat approves by tool name. The cost is real, but the client is the better place\nto solve it, because the client can defer definitions **without** giving up the\nschemas or the per-tool boundary.\n\nIf you are paying too much for codecalc's definitions:\n\n- **Claude Code** enables MCP tool search automatically once a server's tool\n  descriptions exceed roughly 10k tokens. codecalc sits under that threshold, so\n  it is not deferred by default. Set `ENABLE_TOOL_SEARCH=true` to force it on.\n- **Claude API, via the MCP connector**, takes `defer_loading` once on the\n  toolset's `default_config`, or per tool in `configs`. Deferred definitions stay\n  out of the system-prompt prefix, prompt caching is preserved, and a matching\n  tool is expanded into its full definition when the model searches for it.\n- **Any client** can filter which of the 52 tools it exposes to the model.\n  Nothing here requires codecalc to change.\n\nA server-side facade remains under consideration for clients with no such\nmechanism (`docs/design/2026-08-10-tool-facade.md`), and is not implemented.\n\nTrimming a description to cut this cost is exactly the change\n`scripts/tool_select_eval.py` exists to gate: an offline, labeled eval of\nwhether a deterministic lexical (BM25) selector still picks the right tool\nfor a plain-language ask, scored against the live `tools/list` text.\nMeasured v1 baseline (196 hand-labeled prompts, none containing their own\ntarget tool's name — see the script's own docstring): **60.71%** top-1 /\n75.51% top-3 accuracy on the `full` surface (62.75% / 63.0% top-1 on `dev` /\n`core` respectively). It is a lexical proxy, not a model — see the script's\nmodule docstring for exactly what a green run does and does not prove.\n\nThe checked-in baseline PINS the exact labeled corpus by content hash\n(`prompt_set_sha256`); a `--baseline` compare against a corpus that no\nlonger hashes to it fails with a distinct \"corpus changed\" error rather than\nsilently scoring a smaller, easier prompt set against the old numbers. And\nbecause a tool can be top-1-wrong against `full`'s 51 distractors (zero\nheadroom to lose) while still having real headroom against `core`'s much\nsmaller distractor set, both the regression compare and the ablation\nself-check (replacing real descriptions with a generic stub, one tool at a\ntime, across every candidate tool — no sampling) run separately against all\nthree of `full`/`dev`/`core`, wired into CI via\n`tests/test_tool_select_eval.py` so the gate is proven live, on every\nsurface, on every run — not just at the PR that added it.\n\n## Reducing the tool surface\n\nFor an operator who would rather not configure every client, codecalc also has\na first-party knob: `CODECALC_TOOLS` registers only a chosen slice of the\n52-tool surface, so a client that never enables tool search still pays for a\nsmaller `tools/list`.\n\n**This is not the facade** the section above declines to build. Every tool a\ngroup activates keeps its own name, its own typed input schema and its own\nper-tool approval prompt — a group that is not active simply never registers\nits tools with the MCP SDK at all, so they are absent from `tools/list` *and*\nrejected by `tools/call`, not merely hidden behind a dispatcher a client could\nstill invoke by guessing the name.\n\nEvery tool belongs to exactly one group:\n\n| Group | Tools |\n|---|---|\n| `calculator` (25) | `calc_exact`, `compare_threshold`, `percentage`, `calc_stats`, `percentiles`, `collision_probability`, `data_sizes`, `human_duration`, `epoch_time`, `base_repr`, `radix_convert`, `float_repr`, `int_widths`, `bit_analysis`, `bitop`, `solve_expression`, `limit_expression`, `simplify_expression`, `convert_units`, `physical_constants`, `list_units`, `evaluate_expression`, `truth_table`, `solve_linear`, `matrix` |\n| `verification` (5) | `verify_translation`, `verify_optimization`, `algebraic_equiv`, `compare_edge_cases`, `z3_check` |\n| `execution` (6) | `list_languages`, `list_execution_providers`, `execute_code`, `execute_code_stream`, `compare_execution`, `runtimes_status` |\n| `sessions` (11) | `session_start`, `session_stop`, `session_list`, `session_files`, `session_write_file`, `session_read_file`, `session_run`, `session_artifacts`, `run_submit`, `run_inspect`, `run_cancel` |\n| `analysis` (3) | `analyze_complexity`, `benchmark`, `extract_function` |\n| `admin` (2) | `install_package`, `update_runtimes` |\n\n`CODECALC_TOOLS` takes a comma-separated list of group names, preset names, or\nboth:\n\n| Preset | Expands to |\n|---|---|\n| `core` | `calculator` |\n| `dev` | `calculator`, `execution`, `verification`, `analysis` |\n| `full` | every group (the default) |\n\n```bash\nCODECALC_TOOLS=calculator            # just the calculator (25 tools)\nCODECALC_TOOLS=core                  # same thing, by preset name\nCODECALC_TOOLS=calculator,execution  # two groups, unioned\nCODECALC_TOOLS=dev                   # a coding-assistant slice (39 tools)\n```\n\nUnset or empty registers every group — 52 tools, same as today —\nso nothing changes for an operator who does not set this. An unknown group or\npreset name is a loud startup failure naming the bad value and every known\ngroup/preset, never a silent fallback to \"everything\" or \"nothing\": either\ndirection would turn a typo into a footgun nobody notices until it matters.\n`codecalc doctor` prints the active groups, the full group→tools mapping, and\nhow many tools this process actually registered, whatever `CODECALC_TOOLS` is\nset to.\n\nClient-side deferred loading (the section above) and this env var compose\ncleanly: point a client with no deferred-loading mechanism at a\n`CODECALC_TOOLS`-restricted process, or use both — a smaller declared surface\nstill benefits from being deferred.\n\n## Test\n\nEach file is a standalone script that prints one `PASS`/`FAIL` line per\nassertion and exits non-zero if any failed — no test runner, no plugins.\n\n```bash\ncd /path/to/codecalc\n\n# everything. `|| break` used to be `|| break` alone, which stopped at the\n# first failure AND left the loop exiting 0 — a red suite reported success to\n# anything wrapping this command. This form runs them all and carries the\n# failure out.\nfail=0\nfor f in tests/test_*.py; do PYTHONPATH=. .venv/bin/python \"$f\" || { echo \"FAILED: $f\"; fail=1; }; done\nfor f in scripts/*.py;    do PYTHONPATH=. .venv/bin/python \"$f\" || { echo \"FAILED: $f\"; fail=1; }; done\n[ \"$fail\" -eq 0 ]   # the exit status of the whole run\n\n# or individually\nPYTHONPATH=. .venv/bin/python tests/test_smoke.py           # every language, via the Rust executor\nPYTHONPATH=. .venv/bin/python tests/test_mcp_all.py         # every tool over MCP stdio, answers checked\nPYTHONPATH=. .venv/bin/python tests/test_executor_sweep.py  # sandbox regressions\n```\n\n57 test files and 15 CI-invoked scripts, **2184 assertions**. \"CI-invoked\"\nmeans referenced by path (`scripts/<name>.py`) from a job in\n`.github/workflows/*.yml` — `scripts/check_claims.py` derives the count that\nway and gates it, so a script wired into a workflow without this sentence\nchanging, or this sentence bumped without a workflow change, fails the build.\nNothing in the suite\nneeds the internet, so none of it is ever skipped for lack of a network.\n\nIt **can** skip for lack of a *capability*, and that is correct rather than a\nregression: a machine without a symlink privilege, without a given language\nruntime, or without a built native executor cannot exercise the cases that\nneed them. The suite reports three distinct outcomes — the property holds, the\nproperty is broken, and this machine cannot exercise it — and every skip names\nits real cause. A nonzero skip count on Windows or in fallback mode is the\nhealthy result; what would be wrong is a skip reading as a pass.\n\nThis paragraph previously claimed **zero skips** unconditionally. That became\nfalse the moment the suite learned to distinguish the third outcome, and\nnothing gated it: `check_claims.py` gates the counts below, not the prose\naround them. The counts are gated by\n`scripts/check_claims.py`: they were written by hand once and were stale within\nthree pull requests, which is exactly the failure the rest of that script\nexists to prevent. Four of the files are regression suites named after the\nsweep that produced them — `test_bug_sweep`, `test_executor_sweep`,\n`test_python_sweep`, `test_network_modules` — and each one's docstring states\nthe defect it locks out and how it was reproduced, because a regression test\nwhose reason has been forgotten is the first one deleted.\n\nTwo rules the suite holds itself to, learned from breaking both:\n\n- **Assert the value, not the shape.** Three of these files once had no\n  assertions at all: they called tools, printed the output and exited 0. They\n  caught a crash and never a wrong answer — a `runtimes_status` total replaced\n  with `-999` passed, printing `total = -999`.\n- **Don't pin what varies.** `benchmark` and `compare_execution` rank by\n  measured time, so their winner moves under load; their structure is asserted\n  and their timing is not. `runtimes_status` is checked against itself — the\n  summary must agree with the data it summarises — so it holds on any machine\n  rather than describing this one.\n\n## Platform support\n\nLinux, macOS and Windows. The three do not offer the same primitives, and the\nexecutor reports which ones it could **not** apply in an `unenforced` array on\nevery result rather than letting a caller assume they all held.\n\nThe native table below describes the `local` provider and is **not a hostile-code\nsecurity boundary**. On macOS, `<host>-strict` instead uses the explicitly\nconfigured Linux strict service: the macOS binary performs provider selection,\nattestation, supervision, and result validation, while untrusted code executes\ninside the remote cgroup/namespace/seccomp/Landlock boundary. A missing or\nincomplete service fails before source leaves the Mac and never falls back to\nnative execution.\n\nSymbolic evaluation carries the same idea. Every symbolic tool runs SymPy in\na forked child under CPU and memory ceilings with a wall clock the parent\nenforces, so an expression nobody anticipated is still bounded — SymPy's own\nmaintainers abandoned their attempt at a `safe=` flag as \"security theater\", so\nthe screen in `safe_expr.py` buys time and the child buys the bound. Where\nthere is no `fork`, the result reports `expression_bound_not_enforced_without_fork`\nrather than implying a guarantee.\n\nA second field, `output_error`, covers the other way a result can be wrong:\nabsent means `stdout`/`stderr` are what the program produced, present means at\nleast one of them is **not**, and names which stream and the OS error. That\ndistinction did not exist until [#80](https://github.com/The-40-Thieves/codecalc/issues/80)\n— an output file that could not be read came back as a program that printed\nnothing, on a run reported as successful. `ok` now accounts for it on both\nbackends.\n\n| Guarantee | Linux | macOS | Windows |\n|---|---|---|---|\n| Wall-clock timeout | yes | yes | yes |\n| Kill the whole process tree | `killpg` + `PDEATHSIG` | `killpg` | `TerminateJobObject` |\n| Fork-bomb guard | `RLIMIT_NPROC` (uid-wide) | `RLIMIT_NPROC` (uid-wide) | Job `ActiveProcessLimit`, **reported unverified**⁵ |\n| Memory ceiling | `RLIMIT_AS` | reported unenforced¹ | Job `ProcessMemoryLimit` |\n| CPU-time ceiling | `RLIMIT_CPU` | `RLIMIT_CPU` | Job `PerProcessUserTimeLimit`⁴ |\n| Open-file ceiling | `RLIMIT_NOFILE` | `RLIMIT_NOFILE` | reported unenforced |\n| Output cap | yes | yes | yes (on read) |\n| `no_net` | seccomp-bpf filter⁶ (falls back to `LD_PRELOAD` shim²) | `DYLD_INSERT_LIBRARIES`²˒³ | reported unenforced |\n| Stateful sessions | yes | yes | yes |\n\n¹ Darwin accepts `setrlimit(RLIMIT_AS)` but does not enforce address space the\nway Linux does, so setting it would buy an illusion.\n² Dynamically-linked programs only — a sta",
  "bytes": 60000,
  "sha": "c83f477536b82bfb46aa222079597629a822043daae9860dab36433cf2ac572a",
  "repo_slug": "the-40-thieves/codecalc",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_the_40_thieves_codecalc_1d04832a/readme"
}