ruledsl
Python binding and desktop workbench for RuleDSL — a deterministic, embedded business-rule engine with a stable C ABI. Same input, same bytecode, same decision — on every run and every platform.
This package is pure Python (ctypes, zero dependencies). The engine library
itself (ruledsl_capi.dll / libruledsl_capi.so) is not inside — download an
SDK bundle from GitHub Releases
and point the binding at it.
The workbench — authoring & replay companion
pip install ruledsl
ruledsl-workbench --dll path/to/ruledsl_capi.dll
A zero-dependency Tkinter desktop tool for the two human ends of the rule pipeline (it is not a server component — servers embed the C ABI directly):
- Authoring: edit a ruleset (or
File > Open Rules…), run it against sample inputs, read the decision, its output fields, the engine's own evaluation trace (why this decision), and a canonical decision hash. Run 100× asserts every run produced the identical hash — the determinism contract, live. - Replay:
File > Open Bytecode (Replay)…loads a compiled.axbcexactly as your server ran it (no recompilation; the editor is disabled), andFile > Load Inputs from JSON…restores an incident's inputs. Same bytecode, same input → the same decision and trace, on your desk. - Deliberately no
.axbcexport: production artifacts come fromruledslc, which stamps the authenticity manifest.
The binding
from ruledsl import RuleDSL
with RuleDSL("path/to/ruledsl_capi.dll") as engine: # .so on Linux
bytecode = engine.compile('rule r1 { when amount > 100; then decline; }')
decision = engine.evaluate(bytecode, {"amount": 250.0})
print(decision.action, decision.rule_name) # DECLINE r1
trace = []
engine.evaluate(bytecode, {"amount": 250.0}, on_trace=trace.append)
print("\n".join(trace)) # why this decision
Deterministic by contract: the engine never reads the system clock (time-based
rules require an explicit now_utc_ms), never touches the network, and returns
stable, append-only error codes.
The MCP server — optional [mcp] extra (EXPERIMENTAL)
pip install "ruledsl[mcp]"
ruledsl-mcp --rules path/to/rules --decision-log decisions.jsonl --engine-lib path/to/libruledsl_capi.so
An MCP (Model Context Protocol) stdio server that lets an AI agent invoke
deterministic rule evaluation — never produce the decision itself. Exactly
three tools (list_rules, evaluate_case, engine_info), a hash-verified
rule-library manifest, a mandatory explicit now_utc_ms (the server never
reads a clock), and a canonical JSONL decision log with a replayable
decision_hash per decision. Requires Python 3.10+; setup and Claude Desktop
configuration:
MCP quickstart.
Changelog
- 1.2.0 — MCP contract stabilization. The experimental MCP surface gets a
strict, published contract: per-rule input schemas, typed errors carried as
isError, matching value fidelity in the Python and C# bindings, and CI that holds all of it. This breaks callers on purpose — see the migration notes below. The engine, the language, and the decision payload are unchanged. - 1.1.1 — self-contained pip path for MCP: the example rule library ships
inside the wheel (
ruledsl-mcp --print-example-rules), the smoke client is a module (python -m ruledsl_mcp.smoke, no checkout needed), a clear Python 3.10+ runtime message, andserverInfo.versionreports the package. - 1.1.0 — MCP server migrated into the package as the optional
[mcp]extra +ruledsl-mcpconsole command (EXPERIMENTAL). Base package unchanged and still zero-dependency. - 1.0.3 — workbench: Cases batch runner (JSON/JSONL) + humane explicit clock (ISO-8601 ⇄ epoch-ms echo, Now (UTC) button).
- 1.0.2 — first public release: binding (
on_trace, cached buffers) + workbench (authoring, replay, JSON inputs).
Migrating to 1.2.0
Everything below is a deliberate break. The MCP surface is EXPERIMENTAL and had no compatibility promise; keeping the old paths alive would have meant shipping two contracts, one of which quietly produced wrong answers.
| What changed | What to do |
|---|---|
Rule libraries must declare manifest_version: 2, and every rule must carry an input_schema. A v1 manifest is refused at startup. |
Add "manifest_version": 2 and an input_schema per rule. Call list_rules — it returns each rule's schema. |
Failed tool calls now return CallToolResult(isError=true) with a structured {error_domain, error_code, error_name, message, field} object. They used to look like successes. |
Branch on isError / error_code instead of parsing text. |
now_utc_ms must be a JSON integer, at or after the epoch. A numeric string, a fractional value and a negative value are all refused; an integral float normalizes to the same integer. |
Send an integer. |
| Integers outside ±(2^53−1) are refused instead of being silently rounded. This is the one break that reaches non-MCP binding users. | Pass identifiers (account numbers, ledger ids) as strings. They were never safe as numbers — a float64 cannot hold them, so the engine was already evaluating a different value than you sent. |
| Fields the rule's schema does not declare are refused, not ignored. Strings containing NUL, text with no UTF-8 form, and non-scalar values are refused. | Match the declared schema exactly. |
The mcp SDK 1.x / FastMCP path is gone; the extra requires mcp>=2.0,<3. |
pip install -U "ruledsl[mcp]". |
If you supply now_utc_ms both as an argument and as a now_utc_ms field, that
is now an error rather than the argument silently winning.
Version & license
Package 1.x targets engine 1.0.x (ABI 1); the minor version moves when the
package itself gains capability (e.g. 1.1.0 added the [mcp] extra).
Licensed under the PolyForm Free Trial License 1.0.0 — evaluation use;
commercial production use requires a separate agreement (see the
repository).
Metadata
Release files for ruledsl 1.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| ruledsl-1.2.0.tar.gz | 60.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ruledsl-1.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 124.0 kB
Release files / ruledsl-1.2.0.tar.gz
| Download URL | ruledsl-1.2.0.tar.gz |
|---|---|
| Size | 60.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6af3c15896a7dd2789b4df162ed4ac222aa90d5aa0d4bdd16bafc46acde91730
|
|
BLAKE2b-256 checksum How to use checksums |
51f5e9dd940c2bbc8e9ff434c50039aa65c3c9422be68133d961c4f1963b922f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 3, 2026.
Transparency logRelease files / ruledsl-1.2.0-py3-none-any.whl
| Download URL | ruledsl-1.2.0-py3-none-any.whl |
|---|---|
| Size | 63.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
458bc6250fc973369ce68a3b2e90305bf34c88f2e9763001668f5d8eedbc8393
|
|
BLAKE2b-256 checksum How to use checksums |
d781c7fb1d33c8e9c16e528a8dcb643868eae8530f4b6dfbc596f91e6d0da21e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 3, 2026.
Transparency log