{
  "markdown": "# Euclid-MCP\n\n[![Euclid-MCP MCP server](https://glama.ai/mcp/servers/meob/Euclid-MCP/badges/score.svg)](https://glama.ai/mcp/servers/meob/Euclid-MCP)\n[![PyPI version](https://img.shields.io/pypi/v/euclid-mcp?color=blue)](https://pypi.org/project/euclid-mcp/)\n[![Python versions](https://img.shields.io/pypi/pyversions/euclid-mcp)](https://pypi.org/project/euclid-mcp/)\n[![License](https://img.shields.io/github/license/meob/Euclid-MCP?cacheSeconds=86400)](LICENSE)\n[![CI](https://img.shields.io/github/actions/workflow/status/meob/Euclid-MCP/ci.yml?branch=main&label=CI)](https://github.com/meob/Euclid-MCP/actions/workflows/ci.yml)\n[![Coverage](https://img.shields.io/codecov/c/github/meob/Euclid-MCP)](https://codecov.io/gh/meob/Euclid-MCP)\n\n**MCP server for logical reasoning** — turns facts into formal proofs.\n\n<!-- mcp-name: io.github.meob/euclid-mcp -->\n\nEuclid-MCP is a hybrid cognitive architecture: a lightweight LLM describes the world in facts, and a deterministic engine performs the actual deduction. The LLM never needs to reason — it only needs to describe.\n\nWith Euclid-MCP, an 8B model can solve reasoning tasks that stump even 400B+ cloud models — because the engine handles deduction deterministically. Every answer comes with a proof tree, so you can trace *why* a conclusion holds, not just *what* it is. Use it to enforce RBAC policies, audit cloud compliance, validate loan eligibility rules, or reason over any domain where answers must be explainable and verifiable.\n\n<p align=\"center\">\n  <img src=\"https://raw.githubusercontent.com/meob/Euclid-MCP/main/website/assets/demo/diagnose.gif\"\n       alt=\"euclid-cli session: diagnose why bob cannot deploy, then enable it with a what-if role grant\"\n       width=\"720\">\n</p>\n\nEuclid-MCP is written in Python and uses **Euclid-IR**, a human-readable intermediate language designed for both AI agents and humans. It uses **SWI-Prolog** as its primary inference engine — and, where SWI-Prolog is not available (e.g. minimal containers), a pure-Python **native engine** that interprets Euclid-IR directly (see [`docs/NATIVE_ENGINE.md`](docs/NATIVE_ENGINE.md)).\nIt can be consumed in multiple ways: via **MCP** by AI agents (OpenCode, Claude, Cursor), via **HTTP** by tools and automation platforms (n8n, Zapier, Make), and via **Python API** for direct integration. Euclid-IR rules can also be used to **augment RAG** pipelines with deterministic policy enforcement.\n\n\n## How it works\n\n```\n┌──────────────┐     ┌──────────────────┐     ┌──────────────┐     ┌─────────────────┐\n│  LLM/Agent   │────▶│  Euclid-MCP      │────▶│  Translator  │────▶│  SWI-Prolog     │\n│  (MCP Client)│◀────│  (MCPServer)     │◀────│  + Meta-IP   │◀────│  (persistent)   │\n└──────────────┘     └──────────────────┘     └──────────────┘     └─────────────────┘\n```\n\n1. Receive facts, rules, and a query in a simple intermediate language\n2. Translate into Prolog with a meta-interpreter for proof tree capture\n3. Execute via a persistent SWI-Prolog engine process (JSON-lines protocol on stdin/stdout; the workspace is reloaded per call, no process spawn overhead)\n4. Return solutions + proof trees as structured JSON\n\nAdditional tools (`explain`, `diagnose`, `what_if`, `check_kb`) extend this core flow with natural-language explanations, analysis, scenario testing, and validation.\n\nLLMs describe. Euclid MCP proves.  \n\n\n### Knowledge Base\n\nFor small knowledge bases, facts and rules can be provided with each request.\n\nA knowledge base can be loaded at server startup and reused across\ncalls, so agents only pass the session-specific facts for the current query.\nThis minimizes token usage, improves performance, and allows small LLMs to reason over large rule sets without reconstructing the entire knowledge base for every request.\n\n\n## Intermediate Language\n\nEven if currently Euclid-MCP uses a Prolog Engine, no Prolog syntax is required.  \n**Euclid-IR** (Intermediate Representation) is a declarative intermediate representation for logical inference.\nVariables use `$name`, implication is `IF`, conjunction is `AND`.\n\n**Text format:**\n```\nhuman(socrates)\nmortal($x) IF human($x)\n\n? mortal($who)\n```\n\n**YAML format:**\n```yaml\nfacts:\n  - parent(tom, bob)\n  - parent(bob, ann)\n  - parent(tom, liz)\nrules:\n  - ancestor($x, $y) IF parent($x, $y)\n  - ancestor($x, $y) IF parent($x, $z) AND ancestor($z, $y)\n\nquery: ancestor(tom, $who)\n```\n\nFull language reference: [`docs/EUCLID_IR.md`](docs/EUCLID_IR.md)\n\n\n### Euclid-IR Syntax Reference\n\n| Element | Syntax | Example |\n|---------|--------|---------|\n| Facts | `predicate(args)` | `parent(tom, bob)` |\n| Variables | `$name` (lowercase) | `$who`, `$x`, `$count` |\n| Implication | `IF` | `mortal($x) IF human($x)` |\n| Conjunction | `AND` | `p($x) AND q($x)` |\n| Negation | `NOT` | `NOT active($user)` |\n| Boolean literals | `true` / `false` in rule bodies | `merchant($m) IF false` |\n| Query | `? predicate` | `? ancestor(tom, $who)` |\n| String literals | `\"...\"` or `'...'` | `\"alice@example.com\"` |\n| Multi-line rules | Body on next line | `rule($x) IF\\n    body($x)` |\n\n### Arithmetic Comparisons\n\nRules support arithmetic comparisons that are evaluated during deduction:\n\n```\n# Stale access: users who haven't logged in for 90+ days\nstale_access($user) IF\n    user($user) AND last_login_days($user, $days) AND $days > 90\n\n# Excessive permissions: more than 15 direct permissions\nexcessive_permissions($user, $count) IF\n    user($user) AND permission_count($user, $count) AND $count > 15\n\n# Clearance check: user clearance >= resource classification\ncan_access($user, $resource) IF\n    user($user) AND resource($resource, _, _, _, _, $cls) AND\n    classification($cls, $cls_level, _) AND\n    user_clearance($user, $user_level) AND $user_level >= $cls_level\n```\n\n**Supported operators:** `>`, `>=`, `<`, `<=`, `==`, `is`, `!=`\n\n### Multi-line Rules\n\nRules can span multiple lines for readability:\n\n```\ncan_deploy($user, $env) IF\n    user($user) AND\n    has_role($user, $role) AND\n    deploy_requires_level($env, $min) AND\n    deploy_role_level($role, $level) AND\n    $level >= $min AND\n    user_has_permission($user, deploy_code)\n```\n\n### Conjunctions in Queries\n\nQueries can combine multiple predicates:\n\n```\n? can_access_resource($who, $res) AND resource($res, _, _, _, _, secret)\n```\n\nThis returns solutions where both conditions are satisfied simultaneously.\n\n## Why External Inference?\n\nThe external inference gives several advantages:\n- deterministic\n- explainable\n- verifiable\n- inexpensive\n- replaceable backend\n\nIn the current implementation Euclid-MCP uses Prolog.  \nProlog is a 50-year-old battle-tested logic engine. Using it as a \"deduction coprocessor\" lets small LLMs perform complex multi-step reasoning without needing larger, more expensive models. The intermediate language strips away Prolog's syntax quirks while keeping its logical core.\n\nA specific [benchmark](benchmarks/docs/02-rbac-at-scale.md) demonstrate the difference: with 1 000+ facts, LLMs alone score 2/5 while Euclid-MCP scores 5/5 — and runs 7× faster while outputting 14× fewer tokens.\n\n\n## Tools\n\nEuclid-MCP exposes **8 tools**, each with a specific purpose:\n\n| Tool | Purpose |\n|------|---------|\n| `reason` | Main deduction — get solutions + proof trees |\n| `explain` | Readable, natural-language reasoning steps |\n| `diagnose` | Understand why a query succeeds or fails |\n| `what_if` | Test modifications before applying them |\n| `check_kb` | Validate KB consistency before reasoning |\n| `register_kb` | Register a named KB under a `kb_id` |\n| `unregister_kb` | Remove a named KB from the registry |\n| `list_kbs` | List registered named KBs (metadata) |\n\n### `reason`\n\nMain tool for verifiable deterministic reasoning.\n\n| Parameter | Type | Default | Description |\n|-----------|------|---------|-------------|\n| `knowledge` | `string?` | — | Facts & rules in text or YAML format |\n| `kb_id` | `string?` | — | Reference a KB registered via `register_kb` |\n| `delta_knowledge` | `string?` | — | Session-specific facts appended to the `kb_id` base |\n| `query` | `string?` | — | Override query (optional) |\n| `max_solutions` | `int` | `5` | Max solutions to return |\n| `max_depth` | `int` | `30` | Max proof tree depth |\n\n**Returns** `ReasonResult` with `solutions[]` — each containing variable bindings and a proof tree.\n\n### `explain`\n\nDeterministic proof-tree → natural-language reasoning steps. No LLM involved: it\nwalks the proof tree of each solution and renders every step in plain language,\nciting the rule ID (`# RULE: <id>`) when a rule has one. Use it to turn a proof\ninto an auditable, human-readable explanation.\n\n| Parameter | Type | Default | Description |\n|-----------|------|---------|-------------|\n| `knowledge` | `string?` | — | Facts & rules in text or YAML format |\n| `kb_id` | `string?` | — | Reference a KB registered via `register_kb` |\n| `delta_knowledge` | `string?` | — | Session-specific facts appended to the `kb_id` base |\n| `query` | `string?` | — | Override query (optional) |\n| `max_solutions` | `int` | `5` | Max solutions to return |\n| `max_depth` | `int` | `30` | Max proof tree depth |\n\n**Returns** `ExplanationResult` with `explanations[]` — each containing variable\nbindings, an ordered list of natural-language `steps`, and language-independent\n`structured_steps` (typed `kind`/`goal`/`rule_id`/`body`, ready for localized\nrendering in a UI).\n\n### `diagnose`\n\nQuery analysis — understand why a query succeeds or fails.\n\n| Parameter | Type | Default | Description |\n|-----------|------|---------|-------------|\n| `knowledge` | `string?` | — | Facts & rules in text or YAML format |\n| `kb_id` | `string?` | — | Reference a KB registered via `register_kb` |\n| `delta_knowledge` | `string?` | — | Session-specific facts appended to the `kb_id` base |\n| `query` | `string` | — | Query to diagnose |\n| `mode` | `string` | `why` | One of: `why`, `why_not`, `what_needs` |\n| `max_solutions` | `int` | `5` | Max solutions to return |\n| `max_depth` | `int` | `30` | Max proof tree depth |\n\n**Modes:**\n- `why` — explain why a query holds (or that it doesn't)\n- `why_not` — explain why a query fails (missing facts/rules)\n- `what_needs` — suggest what would make a false query true\n\n**Returns** `DiagnosisResult` with `holds`, `findings[]`, `conclusion`, and optionally `proof`.\n\n### `what_if`\n\nScenario analysis — apply modifications to a knowledge base and compare results.\n\n| Parameter | Type | Default | Description |\n|-----------|------|---------|-------------|\n| `base_knowledge` | `string?` | — | Base facts & rules |\n| `kb_id` | `string?` | — | Reference a KB registered via `register_kb` |\n| `delta_knowledge` | `string?` | — | Session-specific facts appended to the `kb_id` base |\n| `modifications` | `string` | — | `+ fact(...)` to add, `- fact(...)` to remove |\n| `query` | `string` | — | Query to evaluate |\n| `max_solutions` | `int` | `5` | Max solutions to return |\n| `max_depth` | `int` | `30` | Max proof tree depth |\n\n**Returns** `WhatIfResult` with `before_count`, `after_count`, `delta`, `solutions_before`, `solutions_after`, `conclusion`.\n\n### `check_kb`\n\nKnowledge base validator — check for consistency before running deduction.\n\n| Parameter | Type | Default | Description |\n|-----------|------|---------|-------------|\n| `knowledge` | `string?` | — | Facts & rules in text or YAML format |\n| `kb_id` | `string?` | — | Reference a KB registered via `register_kb` |\n| `delta_knowledge` | `string?` | — | Session-specific facts appended to the `kb_id` base |\n\n**Returns** `KBCheckResult` with `valid`, `errors[]`, `warnings[]`, `facts_count`, `rules_count`, `predicates_count`, and `predicates[]` — the predicate inventory (name → arities, facts, rules counts) that doubles as the contract for LLM extraction.\n\n#### KB identity in results\n\nEvery tool result — `ReasonResult`, `ExplanationResult`, `DiagnosisResult`,\n`WhatIfResult`, and `KBCheckResult` — carries two identity fields:\n\n| Field | Value |\n|-------|-------|\n| `content_hash` | sha256 of the **KB text payload** (the exact source that was reasoned over) |\n| `version` | the `@version` directive of the KB, or `null` when absent |\n\nThe fields are present on **every** return path, including error branches, so a\nresult can always be pinned to the exact KB it was computed from: anyone with\nthe `.euclid` text and Euclid-MCP can recompute the hash and verify it. This is\nthe foundation for KB versioning, signatures, and audit trails built on top of\nthe engine.\n\n```json\n{\n  \"query\": \"mortal($who)\",\n  \"solutions\": [...],\n  \"elapsed_ms\": 12.4,\n  \"content_hash\": \"a3f9c1e4b82d55f0…\",\n  \"version\": \"1.0\"\n}\n```\n\n\n#### KB Preload\n\nA knowledge base can be loaded **once at server startup** and reused across\ncalls, so agents only pass the session-specific facts for the current query.\n\nPreload a KB by file path, via the `EUCLID_KB_PATH` environment variable or a\n`--kb-path` CLI flag:\n\n```bash\n# Environment variable\nEUCLID_KB_PATH=/path/to/policies.euclid python3 -m euclid_mcp\n\n# CLI flag (MCP stdio, console script, and HTTP API)\npython3 -m euclid_mcp --kb-path /path/to/policies.euclid\npython3 integrations/euclid_api.py --kb-path /path/to/policies.euclid --port 8080\n```\n\nBehavior:\n\n- The file is **validated with `check_kb` at startup** and the server fails fast\n  with a clear message if the file is missing, unreadable, oversized, or invalid.\n- `knowledge`/`base_knowledge` on `reason`, `explain`, `diagnose`, `what_if`, and\n  `check_kb` become **optional**: an explicit value always wins, an empty value\n  falls back to the preloaded KB. With neither, tools return a clear\n  \"No knowledge provided\" error.\n- A **markdown digest** of the preloaded KB (fact/rule/predicate counts, predicate\n  inventory, rules with their IDs) is appended to the server instructions, so\n  agents can see what the KB covers without extra tool calls.\n\nBackward compatible: passing `knowledge` explicitly behaves exactly as before.\n\n\n#### Named KBs (`kb_id` + `delta_knowledge`)\n\nA KB can also be **registered once under a `kb_id`** and then referenced on\nevery call without resending the text — the in-memory registry is per\nserver instance, so replicas re-register their KBs on startup (matching the\nscale-out model of the HTTP API). Up to 32 KBs per instance; `register_kb`\noverwrites an existing `kb_id` (update semantics for idempotency).\n\n```python\n# Register once — validated with check_kb first\nregister_kb(kb_id=\"rbac-policy\", knowledge=\"has_role(alice, admin) ...\\n? $role ...\")\n\n# Reference it on every call\nresult = reason(kb_id=\"rbac-policy\", query=\"can_deploy($user, prod)\")\n\n# Session-specific facts on top of the registered base (no re-registration):\nresult = reason(\n    kb_id=\"rbac-policy\",\n    delta_knowledge=\"has_role(alice, dev)\\nhas_env(dev, staging)\",\n    query=\"can_deploy($user, staging)\",\n)\n```\n\n- `register_kb(kb_id, knowledge)` — validates the `kb_id` (allowlist\n  `[a-z0-9_-]{1,64}`) and the KB (`check_kb`), then stores it. Returns the\n  record: `registered`, `kb_id`, `content_hash`, `version`, `facts`, `rules`,\n  `predicates`. Unknown ids are rejected; a full registry returns an error.\n- `unregister_kb(kb_id)` — removes the KB; returns `removed: true/false`.\n- `list_kbs()` — lists registered KBs (metadata only, no source text).\n\n**Resolution precedence** on `reason`, `explain`, `diagnose`, `what_if`,\n`check_kb`: explicit `knowledge`/`base_knowledge` wins → else `kb_id`\n(unknown id → `Unknown kb_id: <id>`; `delta_knowledge` is concatenated to the\nregistered source) → else the `EUCLID_KB_PATH` preload → else a clear\n\"No knowledge provided\" error. `delta_knowledge` without a `kb_id` is an\nerror. `content_hash`/`version` on a `kb_id` result are computed from the\neffective source (base + delta), so a result can always be pinned to the\nexact text reasoned over.\n\nThe HTTP API exposes the same flow as `POST /register-kb`,\n`POST /unregister-kb`, and `POST /list-kbs`.\n\n\n## Installation\n\n### pip\n\n```bash\n# Prerequisite: Python ≥ 3.10\n\n# SWI-Prolog (for better performances)\nbrew install swi-prolog\n\n# Install\npip install euclid-mcp\n```\n\n### From source\n\n```bash\ngit clone https://github.com/meob/Euclid-MCP\ncd Euclid-MCP\npython3 -m venv .venv && source .venv/bin/activate\npip install -e .\n```\n\n### Docker\n\nNo local SWI-Prolog installation needed — the image bundles everything.\n\n```bash\n# Build\ndocker build -t euclid-mcp .\n\n# MCP stdio mode (for local MCP clients)\ndocker compose run --rm euclid-mcp\n\n# HTTP API mode (for n8n, Zapier, remote access)\ndocker compose up euclid-api\n# API available at http://localhost:8080\n```\n\nSee [Docker in Integrations](#docker) for full details.\n\n## Usage\n\n### Via MCP (OpenCode, Claude, etc.)\n\n```json\n{\n  \"mcpServers\": {\n    \"euclid-mcp\": {\n      \"command\": \"python3\",\n      \"args\": [\"-m\", \"euclid_mcp\"],\n      \"cwd\": \"/path/to/euclid-mcp\"\n    }\n  }\n}\n```\n\n### Via Python\n\n```python\nfrom euclid_mcp.server import reason, explain, diagnose, what_if, check_kb\n\n# Reasoning\nresult = reason(knowledge=\"\"\"\n    human(socrates)\n    mortal($x) IF human($x)\n    ? mortal($who)\n\"\"\")\nfor sol in result.solutions:\n    print(sol.substitutions, sol.proof.type)\n\n# Explanation — readable reasoning steps (cites rule IDs when present)\nexpl = explain(\n    knowledge=\"human(socrates)\\nmortal($x) IF human($x)  # RULE: BIO-001\",\n    query=\"mortal($who)\"\n)\nfor e in expl.explanations:\n    print(e.substitutions, e.steps)\n    print(e.structured_steps)  # typed, language-independent steps\n\n# Diagnosis — why does a query fail?\ndiag = diagnose(\n    knowledge=\"human(socrates)\\nmortal($x) IF human($x)\",\n    query=\"mortal(plato)\",\n    mode=\"why_not\"\n)\nprint(diag.conclusion)\n\n# What-if — how does adding a fact change results?\nscenario = what_if(\n    base_knowledge=\"human(socrates)\\nmortal($x) IF human($x)\",\n    modifications=\"+ human(plato)\",\n    query=\"mortal($who)\"\n)\nprint(f\"Before: {scenario.before_count}, After: {scenario.after_count}\")\n\n# KB validation\ncheck = check_kb(knowledge=\"human(socrates)\\nmortal($x) IF human($x)\")\nprint(f\"Valid: {check.valid}, Errors: {check.errors}\")\n```\n\n### Via CLI\n\nThe `euclid-cli` command wraps the five reasoning tools (`reason`, `explain`,\n`diagnose`, `what_if`, `check_kb`) for the terminal. It reads\nthe KB from a `.euclid` file (`-f`), inline (`--knowledge`), or falls back to\n`EUCLID_KB_PATH`/preload, and selects the backend with `--backend`\n(`auto` | `prolog` | `native`). Queries come from `--query` or from the `?`\nlines inside the KB file.\n\nRun with **no subcommand** to open an interactive **Euclid-IR REPL** — type\nfacts, rules and `? query` lines directly, like you would in `swipl` or\n`psql`. The session knowledge base accumulates across queries.\n\n```bash\n$ euclid-cli\nEuclid-MCP REPL — type facts and rules in Euclid-IR, then `? query`.\nCommands: :help  :check  :kb  :load  :explain  :diagnose  :what-if  :reset  :quit\n\neuclid > human(socrates)\neuclid > mortal($x) IF human($x)\neuclid > ? mortal($who)\nQuery: mortal($who)\nSolution 1:\n  who: socrates\nmortal(socrates)  [rule]\n  human(socrates)  [fact]\n\neuclid > :what-if + human(plato)\nSolutions: 1 -> 2 (delta: more)\neuclid > :quit\n```\n\nREPL meta-commands: `:check`, `:kb`, `:load <file>`, `:explain [query]`,\n`:diagnose <query> [why|why_not|what_needs]`, `:what-if <mods>`, `:reset`,\n`:quit`. Multi-line rules continue after `IF`/`AND` (prompt becomes `... >`).\nPiped input runs the same loop as a batch script without prompts:\n\n```bash\nprintf 'human(socrates)\\nmortal($x) IF human($x)\\n? mortal($who)\\n' | euclid-cli\n```\n\n```bash\n# Validate a knowledge base\neuclid-cli check -f policies.euclid\n\n# Run a deduction (query taken from the ? line in the file)\neuclid-cli reason -f policies.euclid\n\n# Explicit query + limits\neuclid-cli reason -f policies.euclid --query \"can_deploy($user, prod)\" \\\n    --max-solutions 10 --max-depth 40\n\n# Inline KB (no file)\neuclid-cli reason --knowledge \"human(socrates)\nmortal(\\$x) IF human(\\$x)\n? mortal(\\$who)\"\n\n# Readable reasoning steps\neuclid-cli explain -f policies.euclid\n\n# Why does a query fail?\neuclid-cli diagnose -f policies.euclid --query \"can_deploy(bob, prod)\" \\\n    --mode why_not\n\n# What-if: how does adding a fact change the answer?\neuclid-cli what-if -f policies.euclid \\\n    --modifications \"+ has_role(bob, deployer)\" --query \"can_deploy(bob, prod)\"\n\n# Force the pure-Python native engine (no SWI-Prolog)\neuclid-cli --backend native reason -f policies.euclid\n\n# Machine-readable output\neuclid-cli reason -f policies.euclid --json\n```\n\nExit codes: `0` on success, `1` when the tool reports an error (including an\ninvalid KB from `check`), `2` on usage errors.\n\nFull CLI reference (all flags, backends, JSON output): [`docs/CLI.md`](docs/CLI.md)\n\n### Example output\n\n```json\n{\n  \"query\": \"ancestor(tom, $who)\",\n  \"solutions\": [\n    {\n      \"substitutions\": {\"who\": \"bob\"},\n      \"proof\": {\n        \"type\": \"rule\",\n        \"goal\": \"ancestor(tom, bob)\",\n        \"body\": \"parent(tom, bob)\",\n        \"rule_id\": \"GEN-1\",\n        \"subproof\": {\"type\": \"fact\", \"goal\": \"parent(tom, bob)\"}\n      }\n    },\n    {\n      \"substitutions\": {\"who\": \"ann\"},\n      \"proof\": {\n        \"type\": \"rule\",\n        \"goal\": \"ancestor(tom, ann)\",\n        \"body\": \"parent(tom, bob), ancestor(bob, ann)\",\n        \"rule_id\": \"GEN-2\",\n        \"subproof\": {\n          \"type\": \"and\",\n          \"left\": {\"type\": \"fact\", \"goal\": \"parent(tom, bob)\"},\n          \"right\": {\n            \"type\": \"rule\",\n            \"goal\": \"ancestor(bob, ann)\",\n            \"body\": \"parent(bob, ann)\",\n            \"rule_id\": \"GEN-1\",\n            \"subproof\": {\"type\": \"fact\", \"goal\": \"parent(bob, ann)\"}\n          }\n        }\n      }\n    }\n  ]\n}\n```\n\nRules can carry an audit-trail ID via a trailing `# RULE: <id>` comment; the ID\nis surfaced as `rule_id` on the `rule` nodes of the proof tree, so a decision\ncan be cited (\"this derives from rule GEN-2\").\n\n#### Diagnose output\n\n```json\n{\n  \"query\": \"mortal(plato)\",\n  \"mode\": \"why_not\",\n  \"holds\": false,\n  \"findings\": [\n    {\n      \"type\": \"satisfied\",\n      \"predicate\": \"human\",\n      \"detail\": \"Facts exist for 'human' (1 facts)\"\n    }\n  ],\n  \"conclusion\": \"The query fails. Check rule conditions.\"\n}\n```\n\n#### What-if output\n\n```json\n{\n  \"query\": \"mortal($who)\",\n  \"modifications\": \"+ human(plato)\",\n  \"before_count\": 1,\n  \"after_count\": 2,\n  \"delta\": \"more\",\n  \"solutions_before\": [{\"substitutions\": {\"who\": \"socrates\"}}],\n  \"solutions_after\": [\n    {\"substitutions\": {\"who\": \"plato\"}},\n    {\"substitutions\": {\"who\": \"socrates\"}}\n  ],\n  \"conclusion\": \"Solutions increased: 1 -> 2.\"\n}\n```\n\n#### Explain output\n\n```json\n{\n  \"query\": \"mortal($who)\",\n  \"explanations\": [\n    {\n      \"substitutions\": {\"who\": \"socrates\"},\n      \"steps\": [\n        \"mortal(socrates) is derived by rule BIO-001 from: human(socrates).\",\n        \"human(socrates) is asserted as a fact in the knowledge base.\"\n      ]\n    }\n  ]\n}\n```\n\n\n## Use cases\n\n- **Small LLM reasoning**: Offload deduction from LLMs (3-8B) to a deterministic engine\n- **Explainable decisions**: Every answer comes with a proof tree which allows explanation, reasoning trace, and justification\n- **Business rules**: Validate logic chains (permissions, workflows, compliance)\n- **Dependency analysis**: Circular dependency detection, topological ordering\n- **Education**: Interactive logic tutoring with visible proof chains (see [`docs/DIDACTIC.md`](docs/DIDACTIC.md), a step-by-step teaching guide built around the `euclid-cli` REPL)\n- **Knowledge preload**: Complex business rules can be loaded in Euclid instead of using a vector database\n- **Query diagnosis**: Understand why queries fail and what facts/rules are missing\n- **Scenario analysis**: Test \"what-if\" modifications before applying them to production\n- **KB validation**: Check knowledge bases for consistency before reasoning\n\n\n### Real-world examples\n\nThere are several examples provided as samples: Genealogy, RBAC, Classification, Loan Eligibility,\nCluedo Detective, IT Security & Compliance, LLM vs Euclid-MCP, ...\nMost interesting ones are the **IT Security & Compliance** (with\nCIS, AWS, IAM Standards enforcement, Company Policies implementation, hundreds of Data Facts)\nand side-by-side **LLM vs Euclid-MCP**.\n\nExamples full description: [`docs/EXAMPLES.md`](docs/EXAMPLES.md)\n\n\n## Integrations\n\n### OpenCode\n\nEuclid-MCP includes a pre-configured agent in `.opencode.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"euclid-mcp\": {\n      \"command\": \"python3\",\n      \"args\": [\"-m\", \"euclid_mcp\"],\n      \"cwd\": \".\"\n    }\n  },\n  \"agents\": {\n    \"reasoning-engine\": {\n      \"description\": \"Deterministic logic engine\",\n      \"instructions\": \"Write facts in Euclid IR, use the reason tool...\",\n      \"mcpServers\": [\"euclid-mcp\"]\n    }\n  }\n}\n```\n\n### n8n / Zapier / Make\n\nRun the HTTP API:\n\n```bash\npython3 integrations/euclid_api.py --port 8080\n```\n\n| Endpoint | Method | Purpose |\n|----------|--------|---------|\n| `/reason` | POST | Deduction with proof trees |\n| `/explain` | POST | Natural-language reasoning steps |\n| `/diagnose` | POST | Query failure analysis |\n| `/what-if` | POST | Scenario testing |\n| `/check-kb` | POST | KB validation |\n| `/register-kb` | POST | Register a named KB (`kb_id`) |\n| `/unregister-kb` | POST | Remove a named KB |\n| `/list-kbs` | POST | List registered named KBs |\n| `/health` | GET | Health check (deep: pings the engine; 503 only when wedged) |\n| `/metrics` | GET | Prometheus metrics (open, read-only, never KB content) |\n\n```bash\n# Reasoning\ncurl -X POST http://localhost:8080/reason \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"knowledge\": \"human(socrates)\\nmortal($x) IF human($x)\\n? mortal($who)\"}'\n\n# Explanation\ncurl -X POST http://localhost:8080/explain \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"knowledge\": \"human(socrates)\\nmortal($x) IF human($x)\\n? mortal($who)\"}'\n\n# Diagnosis\ncurl -X POST http://localhost:8080/diagnose \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"knowledge\": \"human(socrates)\\nmortal($x) IF human($x)\", \"query\": \"mortal(plato)\", \"mode\": \"why_not\"}'\n\n# What-if\ncurl -X POST http://localhost:8080/what-if \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"base_knowledge\": \"human(socrates)\\nmortal($x) IF human($x)\", \"modifications\": \"+ human(plato)\", \"query\": \"mortal($who)\"}'\n\n# KB validation\ncurl -X POST http://localhost:8080/check-kb \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"knowledge\": \"human(socrates)\\nmortal($x) IF human($x)\"}'\n\n# Register a named KB once, then reference it by kb_id\ncurl -X POST http://localhost:8080/register-kb \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"kb_id\": \"rbac\", \"knowledge\": \"human(socrates)\\nmortal($x) IF human($x)\"}'\n\ncurl -X POST http://localhost:8080/reason \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"kb_id\": \"rbac\", \"delta_knowledge\": \"human(plato)\", \"query\": \"mortal($who)\"}'\n\ncurl -X POST http://localhost:8080/list-kbs \\\n  -H \"Content-Type: application/json\" \\\n  -d '{}'\n\ncurl -X POST http://localhost:8080/unregister-kb \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"kb_id\": \"rbac\"}'\n```\n\n### Docker\n\nThe Docker image bundles SWI-Prolog + Python, so no local prerequisites are needed.\nBase image: [`swipl:stable`](https://hub.docker.com/_/swipl) (Debian Bookworm).\n\n**Two modes via docker-compose:**\n\n```bash\n# MCP stdio — pipe to a local MCP client\ndocker compose run --rm euclid-mcp\n\n# HTTP API — expose REST endpoints on port 8080\ndocker compose up euclid-api\n```\n\n**Standalone usage:**\n\n```bash\n# Build\ndocker build -t euclid-mcp .\n\n# Run HTTP API\ndocker run --rm -p 8080:8080 euclid-mcp \\\n  python3 integrations/euclid_api.py --port 8080\n\n# Run MCP stdio (interactive)\ndocker run --rm -i euclid-mcp\n\n# Quick test — reason directly from CLI\ndocker run --rm euclid-mcp python3 -c \"\nfrom euclid_mcp.server import reason\nr = reason(knowledge='human(socrates)\\nmortal(\\$x) IF human(\\$x)\\n? mortal(\\$who)')\nprint(r.solutions[0].substitutions)\n\"\n```\n\n**Docker image size:** ~370 MB (SWI-Prolog + Python 3.11 + dependencies).\n\n**Native-only (slim):** a smaller image with the pure-Python Euclid-IR engine\nand no SWI-Prolog (`EUCLID_BACKEND=native`). Best for containers with limited\nspace or as the default for small knowledge bases.\n\n```bash\n# Build\ndocker build -f Dockerfile.native -t euclid-mcp-native .\n\n# Run MCP stdio (interactive)\ndocker compose run --rm euclid-mcp-native\n\n# Run HTTP API\ndocker run --rm -p 8080:8080 euclid-mcp-native \\\n  python3 integrations/euclid_api.py --port 8080\n\n# Quick test — reason directly from CLI\ndocker run --rm euclid-mcp-native python3 -c \"\nfrom euclid_mcp.server import reason\nr = reason(knowledge='human(socrates)\\nmortal(\\$x) IF human(\\$x)\\n? mortal(\\$who)')\nprint(r.solutions[0].substitutions)\n\"\n```\n\nBase image: [`python:3.12-slim`](https://hub.docker.com/_/python).\n\n### CLI pipeline\n\n```bash\necho '{\"knowledge\": \"red(apple)\\\\n? red($x)\"}' | python3 integrations/euclid_cli.py\n```\n\nSee `integrations/README.md` for full details.\n\n\n## Scalability\n\nEuclid-MCP engine is **persistent**: a single long-lived SWI-Prolog\nprocess per server instance, reloaded per request over a JSON-lines pipe\ninstead of booting Prolog for every call. \nA single instance handles one request at a time.\nRequests stay **stateless**: \neach one brings its own knowledge base (or uses the preloaded one), \nso instances share nothing.\n\nThis makes Euclid-MCP horizontally scalable:\n\n- **HTTP API** — run any number of instances behind a load balancer (nginx, a\n  Kubernetes Service, …). No session affinity needed: any instance can serve any\n  request.\n- **MCP stdio** — each MCP client spawns its own instance by design, giving\n  natural isolation and parallelism across clients.\n- **Resource footprint** — one `swipl` process per instance (~tens of MB)\n  instead of one short-lived process per request, so a single instance serves\n  many requests cheaply.\n\nReference production architecture — load balancing, resource limits, security\nhardening, and monitoring for a replica battery behind HAProxy:\n[`docs/PRODUCTION.md`](docs/PRODUCTION.md).\n\n\n## Development\n\nRequirements: Python ≥ 3.10, SWI-Prolog.\n\n```bash\n# Install in editable mode with dev dependencies\npython3 -m venv .venv && source .venv/bin/activate\npip install -e \".[dev]\"\n\n# Lint\nruff check .\n\n# Type check\nmypy euclid_mcp integrations\n\n# Tests with coverage\npytest --cov=euclid_mcp --cov=integrations\n```\n\nThe CI workflow ([`.github/workflows/ci.yml`](.github/workflows/ci.yml)) runs these\nsame checks on push and pull request, across Python 3.10–3.14.\n\n### Logging & tracing\n\nEvery tool call is logged with its name, elapsed time, and outcome. Enable\nstructured logs by setting `EUCLID_LOG_LEVEL` (one of `DEBUG`, `INFO`,\n`WARNING`, `ERROR`, `CRITICAL`) — e.g. `EUCLID_LOG_LEVEL=INFO`. Without the\nvariable, only warnings and errors are emitted.\n\nThe HTTP API also supports request tracing: send an `X-Request-Id` header and\nit is echoed back on the response and included in the access logs.\n\n### Monitoring & metrics\n\nThe HTTP API exposes Prometheus metrics on `GET /metrics` (open, read-only,\nnever carries KB content): per-tool call/error counters and latency\nhistograms, engine requests/restarts/timeouts, HTTP traffic, solutions\nreturned, auth failures and process uptime — always on, zero dependencies\n(`euclid_mcp/metrics.py`). `GET /health` is a deep check that pings the\nengine and reports its workspace stats (503 only when a wedged engine exists).\n\n```bash\ncurl -s localhost:8080/metrics\n```\n\nFor a full stack (Prometheus + Grafana + cAdvisor, dashboard and alert rules\nincluded): `monitoring/README.md`.\n\n\n## What is Prolog?\n\n[**Prolog**](https://en.wikipedia.org/wiki/Prolog) (from *PROgrammation en\nLOGique*) is a declarative logic programming\nlanguage: instead of telling the machine *how* to compute an answer, you state\n**facts** and **rules** and let it find *what* follows from them, using\nunification and backtracking. Born in the early 1970s, it remains one of the\nmost battle-tested tools for symbolic reasoning.\n\n![SWI-Prolog](docs/swipl.png)\n\nEuclid-MCP uses **[SWI-Prolog](https://www.swi-prolog.org/)** as its inference\nengine. SWI-Prolog is a mature\n**open-source** implementation — continuously developed and freely available\nsince 1987 — widely used in industry, academia, and research. You write\nyour rules in Euclid-IR; the translator compiles them to Prolog, and SWI-Prolog\nperforms the deduction and produces the proof trees that make every Euclid-MCP\nanswer verifiable.\n\n\n## How is Euclid?\n\n**Euclid** was an ancient Greek mathematician. Living and teaching in Alexandria, he built the foundations of geometry and number theory using rigorous logical proofs.\n\n**Euclid-MCP** is not:\n- an LLM\n- a knowledge base\n- a vector database\n- an agent framework\n- a planner\n\n**Euclid-MCP** is a deterministic inference engine that can be used by any of them.  \nEuclid-MCP allows deterministic and explainable replies from small LLMs on Edge hardware too.\n\n\n## License\n\nApache 2.0\n",
  "bytes": 32846,
  "sha": "b3f406eebbeae9f93aaa301c478ba657633fb82af0d2218427d88c2ae83e6265",
  "repo_slug": "meob/euclid-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_meob_euclid_mcp_3a44b8b3/readme"
}