{
  "markdown": "<!-- mcp-name: io.github.rikarazome/prolog-reasoner -->\n# prolog-reasoner\n\n[![PyPI version](https://img.shields.io/pypi/v/prolog-reasoner.svg)](https://pypi.org/project/prolog-reasoner/)\n[![Python versions](https://img.shields.io/pypi/pyversions/prolog-reasoner.svg)](https://pypi.org/project/prolog-reasoner/)\n[![CI](https://github.com/rikarazome/prolog-reasoner/actions/workflows/test.yml/badge.svg)](https://github.com/rikarazome/prolog-reasoner/actions/workflows/test.yml)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nSWI-Prolog as a \"logic calculator\" for LLMs — available as an MCP server and a Python library. Eliminate the black box from LLM logical reasoning.\n\nLLMs excel at natural language but struggle with formal logic. Prolog excels at logical reasoning but can't process natural language. **prolog-reasoner** bridges this gap by exposing SWI-Prolog execution to LLMs. \n\n## Does it help?\n\nOn the built-in 30-problem logic benchmark:\n\n| Pipeline | Accuracy |\n|----------|----------|\n| LLM-only (`claude-sonnet-4-6`) | 22/30 (73.3%) |\n| **LLM + prolog-reasoner** | **27/30 (90.0%)** |\n\nThe gap concentrates in constraint satisfaction and multi-step reasoning — the combinatorial territory LLMs are weak on and Prolog is strong on. [Full breakdown below.](#benchmark)\n\n## Why it works\n\nLLMs pattern-match; Prolog actually searches and solves. When the LLM writes its problem down as Prolog, two things happen at once:\n\n- Prolog handles the combinatorial work LLMs are weak on — constraint satisfaction, multi-step inference, exhaustive search.\n- The reasoning exists as code you can read, re-run, and debug. When it goes wrong, you see the exact Prolog that failed and why.\n\n## Two ways to use it\n\n- **MCP server** — Claude (or any MCP client) calls it as a logic solver during conversation. **Rule bases** let the LLM save stable domain rules once and reference them by name per call.\n- **Python library** — full NL→Prolog pipeline with self-correction. Requires OpenAI or Anthropic.\n\n## Features\n\n- **MCP tools**: `execute_prolog` for arbitrary SWI-Prolog execution, plus `list_rule_bases` / `get_rule_base` / `save_rule_base` / `delete_rule_base` for reusable named rule bases (v14)\n- **Rule bases**: save stable Prolog rules once (e.g. chess move rules, legal axioms) and reference them by name from `execute_prolog` so the LLM only writes the situation-specific facts per call\n- **Transparent intermediate representation**: the Prolog code is the audit trail — inspect, modify, or verify before execution\n- **CLP(FD) support**: constraint logic programming for scheduling and optimization\n- **Negation-as-failure, recursion, all standard SWI-Prolog features**\n- **Library mode**: NL→Prolog translation with self-correction loop (OpenAI / Anthropic)\n\n## Requirements\n\n- Python ≥ 3.10\n- [SWI-Prolog](https://www.swi-prolog.org/download/stable) installed and on PATH (≥ 9.0)\n- API key for OpenAI or Anthropic — **only for library mode**, not for the MCP server\n\n## Installation\n\n```bash\n# MCP server only (no LLM dependencies)\npip install prolog-reasoner\n\n# Library with OpenAI\npip install prolog-reasoner[openai]\n\n# Library with Anthropic\npip install prolog-reasoner[anthropic]\n\n# Both providers\npip install prolog-reasoner[all]\n```\n\n## MCP Server Setup\n\nThe MCP server exposes five tools — `execute_prolog` runs Prolog code written by the connected LLM, and four rule-base tools manage named, reusable Prolog modules. It does **not** call any external LLM API, so no API key is required.\n\n### Claude Desktop / Claude Code\n\n```json\n{\n  \"mcpServers\": {\n    \"prolog-reasoner\": {\n      \"command\": \"uvx\",\n      \"args\": [\"prolog-reasoner\"]\n    }\n  }\n}\n```\n\nOr, if `prolog-reasoner` is installed directly:\n\n```json\n{\n  \"mcpServers\": {\n    \"prolog-reasoner\": {\n      \"command\": \"prolog-reasoner\"\n    }\n  }\n}\n```\n\n### Docker (SWI-Prolog bundled)\n\nUse Docker if you don't want to install SWI-Prolog locally:\n\n```bash\ndocker build -f docker/Dockerfile -t prolog-reasoner .\n```\n\n```json\n{\n  \"mcpServers\": {\n    \"prolog-reasoner\": {\n      \"command\": \"docker\",\n      \"args\": [\"run\", \"-i\", \"--rm\", \"prolog-reasoner\"]\n    }\n  }\n}\n```\n\n### Tool reference\n\n**`execute_prolog(prolog_code, query, rule_bases=None, max_results=100, trace=False)`**\n- `prolog_code` — Prolog facts and rules (string)\n- `query` — Prolog query to run, e.g. `\"mortal(X)\"` (string)\n- `rule_bases` — optional list of saved rule base names to prepend to `prolog_code` (in order). Use this to reuse stable domain rules across calls without re-sending them\n- `max_results` — cap the number of solutions returned (default 100)\n- `trace` — when `True`, attach a structured proof tree per solution to `metadata.proof_trace`. Opt-in sub-feature; has performance overhead and does not support CLP(FD), higher-order predicates, or assert/retract.\n\nReturns a JSON object with `success`, `output`, `query`, `error`, and `metadata`.\n\nOn success, `metadata` includes `execution_time_ms`, `result_count`, `truncated`, and `rule_bases_used`. When rule bases were requested, `rule_base_load_ms` is also attached (disk I/O timing). On failure, `metadata` also includes `error_category` (one of `syntax_error`, `undefined_predicate`, `unbound_variable`, `type_error`, `domain_error`, `evaluation_error`, `permission_error`, `timeout`, `trace_mechanism_error`, `unknown`) and `error_explanation` — a natural-language hint for the connected LLM (or human) to decide how to fix the Prolog code.\n\n**Rule base tools** — manage named, reusable Prolog modules under `PROLOG_REASONER_RULES_DIR` (defaults to `~/.prolog-reasoner/rules/`). Names are restricted to `[a-z0-9_-]`, length 1–64.\n\n- **`save_rule_base(name, content)`** — write or overwrite a rule base. Content is syntax-validated (parse-only) before the write; failures surface as `RULEBASE_003`. Returns `{\"success\": true, \"name\": ..., \"created\": bool}` where `created` is `true` on first write, `false` on overwrite. Files over `max_rule_size` are rejected with `RULEBASE_005`.\n- **`list_rule_bases()`** — return all saved rule bases with `name`, `description`, and `tags`. Metadata is extracted from leading `% description:` / `% tags:` comments in each file.\n- **`get_rule_base(name)`** — return the raw Prolog source of a saved rule base.\n- **`delete_rule_base(name)`** — remove a saved rule base.\n\nFor name/size/existence errors, the tools return `{\"success\": false, \"error\": \"...\", \"error_code\": \"RULEBASE_001\"|\"RULEBASE_002\"|\"RULEBASE_003\"|\"RULEBASE_005\"}` rather than raising. I/O failures (`RULEBASE_004`) are propagated as infrastructure errors.\n\n**Rule base conventions** — start each rule base file with leading comments that double as `list_rule_bases` metadata:\n\n```prolog\n% description: Chess piece movement rules\n% tags: chess, games\n\npiece_move(knight, (X1,Y1), (X2,Y2)) :- ...\n```\n\nThen reference from `execute_prolog`:\n\n```json\n{\n  \"rule_bases\": [\"chess_moves\"],\n  \"prolog_code\": \"position(knight, (4,4)).\",\n  \"query\": \"piece_move(knight, (4,4), Target)\"\n}\n```\n\nRule bases also serve as the foundation for domain-specialized forks: ship a curated set (legal axioms, game rules, tax scenarios, etc.) bundled via `BUNDLED_RULES_DIR` as a ready-to-use reasoning package.\n\n## Library Usage\n\nThe library exposes `PrologExecutor` (Prolog-only, no LLM) and `PrologReasoner` (NL→Prolog pipeline, needs an LLM API key).\n\n### Execute Prolog directly (no LLM)\n\n```python\nimport asyncio\nfrom prolog_reasoner.config import Settings\nfrom prolog_reasoner.executor import PrologExecutor\n\nasync def main():\n    settings = Settings()  # no API key needed\n    executor = PrologExecutor(settings)\n    result = await executor.execute(\n        prolog_code=\"human(socrates). mortal(X) :- human(X).\",\n        query=\"mortal(X)\",\n    )\n    print(result.output)  # mortal(socrates)\n\nasyncio.run(main())\n```\n\n### Full NL→Prolog pipeline (requires LLM API key)\n\n```python\nimport asyncio\nfrom prolog_reasoner import PrologReasoner, TranslationRequest, ExecutionRequest\nfrom prolog_reasoner.config import Settings\nfrom prolog_reasoner.executor import PrologExecutor\nfrom prolog_reasoner.translator import PrologTranslator\nfrom prolog_reasoner.llm_client import LLMClient\n\nasync def main():\n    settings = Settings(llm_api_key=\"sk-...\")  # from env or explicit\n    llm = LLMClient(\n        provider=settings.llm_provider,\n        api_key=settings.llm_api_key,\n        model=settings.llm_model,\n        timeout_seconds=settings.llm_timeout_seconds,\n    )\n    reasoner = PrologReasoner(\n        translator=PrologTranslator(llm, settings),\n        executor=PrologExecutor(settings),\n    )\n    translation = await reasoner.translate(\n        TranslationRequest(query=\"Socrates is human. All humans are mortal. Is Socrates mortal?\")\n    )\n    print(translation.prolog_code)\n    result = await reasoner.execute(\n        ExecutionRequest(prolog_code=translation.prolog_code, query=translation.suggested_query)\n    )\n    print(result.output)\n\nasyncio.run(main())\n```\n\n## Configuration\n\nAll settings via environment variables (prefix `PROLOG_REASONER_`):\n\n| Variable | Default | Required for |\n|----------|---------|--------------|\n| `LLM_PROVIDER` | `openai` | library (`openai` or `anthropic`) |\n| `LLM_API_KEY` | `\"\"` | library only — leave unset for MCP |\n| `LLM_MODEL` | `gpt-5.4-mini` | library |\n| `LLM_TEMPERATURE` | `0.0` | library |\n| `LLM_TIMEOUT_SECONDS` | `30.0` | library |\n| `SWIPL_PATH` | `swipl` | both |\n| `EXECUTION_TIMEOUT_SECONDS` | `10.0` | both |\n| `RULES_DIR` | `~/.prolog-reasoner/rules` | both (where user-saved rule bases live) |\n| `BUNDLED_RULES_DIR` | unset | both (optional — synced into `RULES_DIR` on first startup for shipping default rules with a fork) |\n| `MAX_RULE_SIZE` | `1048576` (1 MiB) | both (per-file save cap; `save_rule_base` rejects larger content with `RULEBASE_005`) |\n| `MAX_RULE_PROMPT_BYTES` | `65536` (64 KiB) | library only (total budget for the \"Available rule bases\" prompt section; truncated with a marker when exceeded) |\n| `LOG_LEVEL` | `INFO` | both |\n\n## Benchmark\n\n`benchmarks/` contains 30 logic problems across 5 categories (deduction, transitive, constraint, contradiction, multi-step) to compare LLM-only reasoning vs LLM+Prolog reasoning. The benchmark exercises the **library** path (translator + executor), since it requires the NL→Prolog step.\n\n### Results\n\nMeasured on `anthropic/claude-sonnet-4-6`, single run over 30 problems:\n\n| Pipeline | Accuracy | Avg latency |\n|----------|----------|-------------|\n| LLM-only | 22/30 (73.3%) | 1.7s |\n| **LLM + Prolog** | **27/30 (90.0%)** | 3.8s |\n\nPer-category breakdown:\n\n| Category | LLM-only | LLM + Prolog |\n|----------|----------|--------------|\n| deduction | 6/6 | 6/6 |\n| transitive | 6/6 | 5/6 |\n| constraint | 3/7 | **6/7** |\n| contradiction | 4/4 | 3/4 |\n| multi-step | 3/7 | **7/7** |\n\nThe gap is concentrated in **constraint** (SEND+MORE, 6-queens, knapsack, K4 coloring, Einstein-lite) and **multi-step** (Nim game theory, 3-person knights-and-knaves, TSP-4, zebra puzzle) — exactly the combinatorial/search-heavy territory where symbolic solvers outperform pattern completion. On purely deductive or transitive questions the LLM is already strong and Prolog adds latency without accuracy gains.\n\nAll 3 LLM+Prolog failures were Prolog execution errors from malformed LLM-generated code (missing predicate definitions, unbound CLP(FD) variables) rather than reasoning errors — addressable via prompt tuning. Notably, every failure is inspectable: you can see the exact Prolog that failed and why, rather than a wrong natural-language answer with no explanation.\n\n### Running it yourself\n\n```bash\ndocker run --rm -e PROLOG_REASONER_LLM_API_KEY=sk-... \\\n    prolog-reasoner-dev python benchmarks/run_benchmark.py\n```\n\nResults are saved to `benchmarks/results.json`.\n\n## Comparison with other Prolog MCPs\n\nSeveral Prolog MCP servers exist, each with different design choices. **prolog-reasoner** is intentionally stateless and spot-use — Prolog is a calculator you call when logic matters, not the backbone of your agent's memory.\n\n| | prolog-reasoner | Stateful Prolog MCPs |\n|---|---|---|\n| Prolog's role | Per-call reasoning tool | Project-wide knowledge base |\n| State | Stateless execution (each call independent); optional named **rule bases** for reusable static rules, no inter-call session memory | Persistent sessions / layered KBs |\n| Reproducibility | Same input (incl. same rule bases) → same output, always | Depends on accumulated state |\n| Integration effort | Use where logic matters, skip where it doesn't | Architectural commitment |\n| A/B testable vs LLM-only | Yes (each call is a controlled experiment) | Structurally not comparable |\n\nThis is also why accuracy benchmarks are published here and not elsewhere: statelessness is what makes a side-by-side comparison possible.\n\nIf you need persistent agent memory, hallucination-safeguarded fact storage, or a full neuro-symbolic substrate, other projects may fit better:\n\n- [adamrybinski/prolog-mcp](https://github.com/adamrybinski/prolog-mcp) — Trealla WASM with save/load sessions\n- [umuro/prolog-mcp](https://github.com/umuro/prolog-mcp) — layered KB with file-backed persistence\n- [vpursuit/model-context-lab](https://github.com/vpursuit/model-context-lab) — SWI-Prolog with security sandboxing\n- [dr3d/prolog-reasoning](https://github.com/dr3d/prolog-reasoning) — neuro-symbolic memory with write-path safety\n\nWe're the spot-use option.\n\n## Development\n\n```bash\n# Build dev image\ndocker build -f docker/Dockerfile -t prolog-reasoner-dev .\n\n# Run tests (no API key needed — LLM calls are mocked)\ndocker run --rm prolog-reasoner-dev\n\n# With coverage\ndocker run --rm prolog-reasoner-dev pytest tests/ -v --cov=prolog_reasoner\n\n# Or via docker compose\ndocker compose -f docker/docker-compose.yml run --rm test\n```\n\n## License\n\nMIT\n",
  "bytes": 13872,
  "sha": "fe93a1dacd0ae9a723105c61c987d345ad550e95921d073f4721a7a78f8ed437",
  "repo_slug": "rikarazome/prolog-reasoner",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_rikarazome_prolog_reasoner_d795f671/readme"
}