Skip to main content

MCP server

A read-only MCP server over the wiki. Point any MCP client at it to let an agent query the insurance knowledge base.

Install & run

The server is keyless and read-only; the dataset is the repo itself. From a clone:

python3 -m venv .venv && . .venv/bin/activate
pip install -r mcp/requirements.txt
python mcp/insurance_wiki_mcp.py

Or install it as a console script (pip install . from the repo root, or from anywhere with pip install git+https://github.com/sluyasu/OpenInsurance.git). When the script does not run from inside a clone, point it at one:

INSURANCE_WIKI_REPO=/path/to/OpenInsurance insurance-wiki-mcp

Register with a client

Claude Code one-liner (from the repo root):

claude mcp add insurance-wiki -- "$(pwd)/.venv/bin/python" "$(pwd)/mcp/insurance_wiki_mcp.py"

Or in any MCP client config (e.g. a project .mcp.json):

{
  "mcpServers": {
    "insurance-wiki": {
      "command": "/absolute/path/to/OpenInsurance/.venv/bin/python",
      "args": ["/absolute/path/to/OpenInsurance/mcp/insurance_wiki_mcp.py"]
    }
  }
}

Tools

Tool Purpose
list_countries() Countries covered: products, insurers, branches covered vs taxonomy vs written overviews.
list_branches(country) Branches for a country, with mandatory flag and product counts.
search(query, country, type, branch, insurer, limit) Search pages by title/content, filterable; announces when results were truncated.
get_page(path) Full text of one knowledge page.
get_product(country, insurer_slug, product_name, document_type?, edition?) A product's full structured data (coverages, exclusions, ...) + source_url.
get_coverage(country, insurer_slug, product_name, topic, document_type?, edition?) Compact answer to one question: only the coverages/exclusions relevant to topic, with limits, conditions, quotes and pages.
verify_claim(country, insurer_slug, product_name, claim) Verbatim excerpts that share the claim's terms, so the calling agent can fact-check its own draft. Retrieval, not judgment.
compare_products(country, product_names, on, insurer_slugs?) Compare products on coverages / exclusions / deductibles.
find_overlap(country, product_names, on, insurer_slugs?) Flag candidate duplicate cover when combining 2+ products (e.g. home + family liability).
get_branch_overview(country, branch) The hand-authored branch overview.

One commercial product usually maps to several documents (its general conditions and its IPID share the name, and several editions can coexist). Tools that take a product name resolve deterministically - general conditions over summaries, non-superseded, newest edition, then reference order as the final tie-break - and every response identifies the chosen document (document_type, edition_date, reference, superseded): the single-product tools list the alternatives under other_documents, the multi-product tools carry the identifiers inside each entry. Use document_type= / edition= on get_product / get_coverage to pick a specific one.

What keeps the answering LLM honest

The LLM consuming these tools is the layer that can hallucinate, so the response shape works against that:

  • Grounding contract + citation line. Every response carrying product facts starts with a short contract ("state only what's here, never invent dates/articles/figures, cite source_url, no advice or ranking"); single-product responses add a compact CITATION line carrying the real product, insurer, document type, edition date (or "not stated"), reference and source_url. The model copies real values instead of reconstructing them.
  • Ambiguity is refused, not guessed. A name matching several distinct products ("Assurance Auto" exists at several insurers) returns the candidates instead of silently picking one. An exact name wins over products that merely contain it, and the response lists the similar products it skipped.
  • Ties are flagged. When two documents of the same type carry the same edition date, the response says the choice fell back to reference order and points at the alternative.
  • Gaps and caps are explicit. Superseded documents say so in the citation, empty topic matches say "not proof of absence", truncated search results announce the real total.
  • Self-verification. A chatbot can (and should) re-check its own draft: extract each factual assertion, call verify_claim with it, and drop or soften anything whose evidence does not literally say it. The recommended system-prompt rules for any chatbot built on this server:
    1. State a fact (date, article, amount, deductible, formula) only if it appears in a tool response; otherwise say "not stated in the document".
    2. Never pick an insurer for the user: if none is named, ask or list neutrally.
    3. No advice verbs (recommend, choose, drop, best): information, not advice.
    4. End with the source_url the tools returned.

Measured latency

Warm server, in-process, median of 50 runs per tool (Apple silicon, Belgium dataset, 162 documents; reproduce with .venv/bin/python tests/bench_tools.py):

Tool median p95
list_countries 0.09 ms 0.10 ms
list_branches 0.04 ms 0.04 ms
search (title hit) 3.0 ms 3.2 ms
search (full-text body scan) 3.1 ms 3.2 ms
get_page 0.04 ms 0.04 ms
get_product 0.4 ms 0.4 ms
get_coverage 2.2 ms 2.3 ms
verify_claim 2.8 ms 2.9 ms
compare_products 0.9 ms 0.9 ms
find_overlap 0.9 ms 0.9 ms
get_branch_overview 0.01 ms 0.01 ms

The server reads every file once (index, extractions, page bodies, country metadata, category keywords) and serves from memory, so no file is re-read per query. The first call after startup pays the cache fill (tens of ms). In a real chatbot, the user-perceived time is dominated by the LLM's own inference (seconds), not by these tools: keep the server process warm (any MCP client does) and the tool side is imperceptible. Full-text search scans the corpus in memory (~3 ms for one country); an inverted index only becomes worth it with several countries.

Security & testing

Every product/coverage response carries the source_url and a no-advice disclaimer. The server never writes, and get_page only reads text files in the knowledge folders (wiki/, data/, _meta/, sources/, schema/, root *.md) - never dotfiles, code or git internals, even through path tricks. Without a dataset (installed via pip/uvx but no clone configured) the server refuses to start with instructions, instead of serving empty results.

The test suite (tests/, run in CI) covers the get_page confinement, deterministic document selection, accent/apostrophe-insensitive matching, ambiguity refusals, honest caps and a smoke test of every tool:

.venv/bin/pip install pytest && .venv/bin/python -m pytest tests/ -q

Overlap detection - and how to build an analysis tool like it

find_overlap answers a real question: "If I take policy A and policy B, which covers am I paying for twice?" (the classic case: a home policy and a family-liability policy both cover civil liability). It's also a worked example of how to add your own analysis tool to this server. Read it before writing one.

The design principle: the MCP tool stays a deterministic data provider

The tool does not call an LLM and does not give advice. It shapes the data so the calling agent can reason. Semantic judgment ("is this really the same cover?") is the agent's job; the tool's job is to surface strong, deterministic candidates with their sources. That keeps the server keyless, reproducible, and honest.

How find_overlap is built (4 pieces)

  1. A controlled vocabulary - schema/coverage_categories.json maps insurance concepts (responsabilite_civile, assistance, protection_juridique, vol, incendie, …) to keyword patterns (accent-insensitive, fr/nl). This is data, committed and versioned - extend it without touching code.
  2. A categorizer - _categorize(text) normalizes a coverage's name + description (lowercase, strip accents) and returns every category whose keywords appear. Pure string matching, no model.
  3. Grouping - each product's coverages are tagged; a candidate overlap is any category that appears in 2 or more of the products. Groups are returned with the offending items per product, sorted by how many products share them.
  4. Honesty - the response carries the no-advice disclaimer plus a note: candidates are heuristic (a shared category), must be confirmed against the actual descriptions, and absence of a category is not proof of no overlap (the taxonomy isn't exhaustive). The tool has good recall and modest precision by design - it is a lead generator for an agent, not a verdict.

Worked example

find_overlap("be", ["Police habitation pour le propriétaire", "La Police familiale"])
→ candidate_overlaps: Responsabilité civile (both), Dommages / responsabilité animaux,
  Incendie et périls connexes, Vol / vandalisme, Relogement / frais supplémentaires, …

The top hit is the true one: home cover (RC immeuble / locataire) and family cover (RC vie privée) both carry civil liability - a real potential duplication for the policyholder to check.

How to extend it

  • Improve coverage without code: add categories or keywords to coverage_categories.json, bump its version.
  • Higher precision (the deterministic upgrade): instead of keyword-matching at query time, tag one primary coverage_category per coverage during extraction/build (a normalization pass), then overlap becomes an exact set intersection with no keyword noise. The same coverage_categories.json is the seed for that pass.

How to add a brand-new tool

  1. Write a function in mcp/insurance_wiki_mcp.py decorated with @mcp.tool(). The docstring is the tool's description the agent sees - state what it does and its limits.
  2. Read from data/<cc>/extracted/*.json (structured product data) and/or wiki/ (Markdown) via the existing _extracted() / _read_index() / _safe_repo_path() helpers. Never write.
  3. Return a string (JSON or Markdown). For anything about coverages, prepend DISCLAIMER and include source_url.
  4. Restart the server; the new tool is exposed automatically. No registration step.

Publishing to the MCP registry (maintainers)

The server is packaged by the root pyproject.toml (console script insurance-wiki-mcp, PyPI name openinsurance-wiki-mcp). To list it on the official MCP registry (which the public directories like PulseMCP ingest automatically):

  1. Publish the package to PyPI: python -m build && twine upload dist/*.
  2. Install the publisher CLI and generate the manifest: mcp-publisher init (namespace io.github.sluyasu/*, package openinsurance-wiki-mcp, transport stdio, document the INSURANCE_WIKI_REPO environment variable).
  3. mcp-publisher login github then mcp-publisher publish.

See https://github.com/modelcontextprotocol/registry for the current publishing guide.

mcp-name: io.github.sluyasu/openinsurance-wiki

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

openinsurance_wiki_mcp-0.2.0.tar.gz (36.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

openinsurance_wiki_mcp-0.2.0-py3-none-any.whl (22.4 kB view details)

Uploaded Python 3

File details

Details for the file openinsurance_wiki_mcp-0.2.0.tar.gz.

File metadata

  • Download URL: openinsurance_wiki_mcp-0.2.0.tar.gz
  • Upload date:
  • Size: 36.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.5

File hashes

Hashes for openinsurance_wiki_mcp-0.2.0.tar.gz
Algorithm Hash digest
SHA256 cab1f5ec2d470ad49f2a3488760fe75f9849e7de07582affe6dfc47ce83176bc
MD5 937761ec766824d7e19576d49a7b3284
BLAKE2b-256 d59467edabf94ccafa243146669f84815f84ca90ec4d5cb01c734e41cf8cb3fd

See more details on using hashes here.

File details

Details for the file openinsurance_wiki_mcp-0.2.0-py3-none-any.whl.

File metadata

File hashes

Hashes for openinsurance_wiki_mcp-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 7e2bba52f082fbc6cf0ca4ea51ff992a224989e77c41bde5f7c4c22a736298f0
MD5 9a949b9f528031208dcc81bb69cccb1e
BLAKE2b-256 f2c297a8012ceaf4aaa8007756ac99ac81d449ed918e8c1ea61e8e7e01f9b854

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 files

0.1.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page