#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ MCP server pro pojišťovny (insurance) — používá oficiální MCP SDK (FastMCP) Spustit: python mcp_insurance.py Dotazy na VZP B2B portál pomocí klientského certifikátu (mTLS). Vychází z knihovny Knihovny/vzpb2b_client.py a skriptů v Insurance/. Nástroje: - registrovani_lekari(rodne_cislo, k_datu) — kdo jsou registrující lékaři pacienta (praktik 001, gynekolog 002, stomatolog 014, ...) ke dni k_datu. - stav_pojisteni(rodne_cislo, k_datu, prijmeni) — jestli je pacient ke dni k_datu platně pojištěný a u které pojišťovny. """ import re import sys import traceback from pathlib import Path from datetime import date from typing import Optional from mcp.server.fastmcp import FastMCP PROJECT_ROOT = Path(__file__).resolve().parent sys.path.insert(0, str(PROJECT_ROOT)) from Knihovny.vzpb2b_client import VZPB2BClient # Všechny logy MUSÍ jít na stderr — stdout je rezervován pro JSON-RPC def log(msg: str): print(msg, file=sys.stderr, flush=True) # ── Certifikát ────────────────────────────────────────────────────────────── PFX_PATH = PROJECT_ROOT / "Insurance" / "Certificates" / "picka.pfx" PFX_PASS = "Vlado7309208104+" if not PFX_PATH.exists(): log(f"Chyba: certifikát nenalezen: {PFX_PATH}") sys.exit(1) try: vzp = VZPB2BClient("prod", str(PFX_PATH), PFX_PASS) log("VZP B2B klient inicializován (prod)") except Exception as e: log(f"Chyba inicializace VZP B2B klienta: {e}") sys.exit(1) def _norm_rc(rodne_cislo: str) -> str: """Rodné číslo bez lomítka a nečíselných znaků.""" return re.sub(r"\D", "", rodne_cislo or "") # MCP server mcp = FastMCP("insurance") @mcp.tool() def registrovani_lekari(rodne_cislo: str, k_datu: Optional[str] = None) -> dict: """Zjisti registrující lékaře pacienta u VZP ke dni k_datu. Dotáže se VZP B2B služby RegistracePojistencePZSB2B a vrátí, kdo je registrující praktický lékař (odbornost 001), gynekolog (002), stomatolog (014) a případně další, pokud je VZP eviduje. Args: rodne_cislo: Rodné číslo pacienta (lomítko nevadí). k_datu: Datum ve formátu YYYY-MM-DD. Pokud chybí, použije se dnešek. Returns: dict s rodným číslem, datem dotazu, počtem nalezených registrací a seznamem lékařů (každý: kód a název odbornosti, IČZ, IČP, jméno lékaře, název ZZZ, pojišťovna, data registrace/zahájení/ukončení). """ try: rc = _norm_rc(rodne_cislo) if not rc: return {"error": "Neplatné rodné číslo."} if k_datu: k_datu = k_datu.strip() else: k_datu = date.today().isoformat() xml = vzp.registrace_lekare(rc=rc, k_datu=k_datu, odbornosti=None) zaznamy = vzp.parse_registrace_lekare(xml) return { "rodne_cislo": rc, "k_datu": k_datu, "pocet": len(zaznamy), "lekari": zaznamy, } except Exception: log(f"registrovani_lekari chyba: {traceback.format_exc()}") raise # Význam pole "stav" v odpovědi stavPojisteniB2B _STAV_POPIS = { "1": "pojištěn u uvedené pojišťovny", "4": "cizinec (smluvní/EU pojištění) — považováno za pojištěného", } _STAV_POJISTEN = ("1", "4") # tyto kódy znamenají platné pojištění @mcp.tool() def stav_pojisteni(rodne_cislo: str, k_datu: Optional[str] = None, prijmeni: Optional[str] = None) -> dict: """Zjisti, jestli je pacient ke dni k_datu platně pojištěný a u které pojišťovny. Dotáže se VZP B2B služby stavPojisteniB2B. VZP centrálně eviduje pojištěnce všech pojišťoven, takže odpověď vrátí i pojišťovnu jinou než VZP. Args: rodne_cislo: Rodné číslo pacienta (lomítko nevadí). k_datu: Datum ve formátu YYYY-MM-DD. Pokud chybí, použije se dnešek. prijmeni: Příjmení (volitelné) — VZP umožňuje křížovou kontrolu se jménem. Returns: dict s rodným číslem, datem dotazu, příznakem pojisteny (True/False), kódem a popisem stavu, kódem zpracování požadavku a údaji o pojišťovně (kód, název, kód pojistného vztahu). """ try: rc = _norm_rc(rodne_cislo) if not rc: return {"error": "Neplatné rodné číslo."} k_datu = k_datu.strip() if k_datu else date.today().isoformat() prijmeni = prijmeni.strip() if prijmeni else None xml = vzp.stav_pojisteni(rc=rc, k_datu=k_datu, prijmeni=prijmeni) parsed = vzp.parse_stav_pojisteni(xml) stav = parsed.get("stav") pojisteny = stav in _STAV_POJISTEN return { "rodne_cislo": rc, "k_datu": k_datu, "pojisteny": pojisteny, "stav": stav, "stav_popis": _STAV_POPIS.get(stav, "nepojištěn u této pojišťovny / nenalezen"), "stav_vyrizeni": parsed.get("stavVyrizeni"), "kod_pojistovny": parsed.get("kodPojistovny"), "nazev_pojistovny": parsed.get("nazevPojistovny"), "pojisteni_kod": parsed.get("pojisteniKod"), } except Exception: log(f"stav_pojisteni chyba: {traceback.format_exc()}") raise if __name__ == "__main__": log("MCP Insurance server spuštěn (FastMCP)") mcp.run()