# OpenKP Website: [openkp.org](https://openkp.org) A patient-directed MCP server that bridges Claude and Kaiser Permanente Northern California's patient portal. Inspired by [Open Record](https://github.com/Fan-Pier-Labs/openrecord) by Ryan Hughes / Fan Pier Labs. Open Record targets vanilla Epic MyChart, which Kaiser is not. OpenKP implements the same idea (let Claude drive your medical record through MCP) against Kaiser's Ping-fronted portal, with independently written code. ## Status **Phase 2 (read-only) closed. Phase 3 (writes) in progress.** As of 2026-06-05: - **27 MCP tools** registered: 3 housekeeping, 22 reads, 2 writes (mail-order refill, non-urgent message). - **591 tests** passing on macOS. Windows runs the same suite with 4 platform-specific failures that don't affect any user-facing tool — see [`docs/install/windows.md`](../docs/install/windows.md). - **NorCal region only** — see "Regional support" below. Live-verified end-to-end against real Kaiser data: profile, messages, lab results (incl. PDF download), medications, problems, allergies, appointments (upcoming + past), visit notes, AVS, care team and recent providers, implanted devices, access logs, upcoming orders, refill *preview*, send-message *preview*, refill order tracking. Commit paths for `request_refill` and `send_message` are unit-tested but not yet exercised live; see the "Write tools — preview vs commit" section. The repo is now public at [github.com/hugooc/OpenKP](https://github.com/hugooc/OpenKP). The historical pre-launch checklist lives in `docs/release-checklist.md` at the workspace root. ## Regional support **OpenKP is only tested against Kaiser's Northern California (NorCal) region.** Kaiser operates 8 regions: NorCal, SoCal, Hawaii, Northwest, Colorado, Georgia, Mid-Atlantic, and Washington. They share a common portal front door at `healthy.kaiserpermanente.org`, but region codes, pharmacy endpoints, and data shapes differ. If you're a KP member outside NorCal, expect breakage on at least the medication and refill tools. The architecture leaves room for per-region adapters (see `DESIGN.md` §12), but none exist yet. Issues and HAR captures from other regions are welcome. ## Why this exists Kaiser's FHIR patient API is read-only and exposes only what federal interoperability rules require (a baseline called USCDI — the United States Core Data for Interoperability). That's a policy floor, not the technical ceiling. Your record contains more than what the API surfaces, and you have every right to act on it (refill meds, message your team, view labs) through any interface you choose, including an AI agent. OpenKP is that interface. This is a **personal research tool**. It logs in with your own credentials, on your own machine, and nothing leaves your laptop except requests to Kaiser. OpenKP is also a working example of **critical AI health literacy** — the practice of using AI you direct, on data you own, to make your own care legible to you. The framing comes from ["Critical AI Health Literacy as Liberation Technology"](https://nam.edu/perspectives/critical-ai-health-literacy-as-liberation-technology-a-new-skill-for-patient-empowerment) (NAM Perspectives) and [aipatients.org](https://aipatients.org). ## How it works ``` Claude Desktop │ (MCP over stdio) ▼ openkp.mcp_server (FastMCP) │ ▼ scrapers/.py (per-endpoint logic + pydantic models) │ ▼ scrapers/request.py (authenticated HTTP, retry on session expiry) │ ▼ scrapers/session.py (cookie persistence + httpx keepalive probe) ↑ scrapers/auth.py (Playwright + Ping OAuth, interactive on first login) │ ▼ healthy.kaiserpermanente.org Epic / MyChart endpoints behind Ping apims.kaiserpermanente.org pharmacy BFF microservices ``` Authentication uses a persistent Chromium profile driven by Playwright. **First login is interactive** — Chromium pops up, you enter your KP username and password, complete MFA. Subsequent sessions are silent as long as Kaiser's device-trust cookie holds (typically a few weeks). Once authenticated, cookies are handed to an `httpx` client for fast JSON calls. ## Install You need: - macOS (tested) or Windows (tested, see [`docs/install/windows.md`](../docs/install/windows.md) for platform-specific steps). Linux is untested but should be close to macOS. - Python 3.11 or newer. - A Kaiser Permanente NorCal member account. ### 1. Bootstrap the venv (one-time, ~2 minutes) From the workspace root (`~/OpenKP`): ```bash bash scripts/setup-dev.sh ``` That creates `openkp/.venv`, installs OpenKP in editable mode with dev extras, downloads the Playwright Chromium binary, and runs the test suite. Idempotent — re-run it any time after a dependency bump. ### 2. Configure credentials (one-time) ```bash cd openkp cp .env.example .env ``` Edit `.env` and set `KP_USERNAME` to your kp.org login. Leave `KP_PASSWORD` empty. Then store the password in your macOS Keychain (more secure than the `.env` file): ```bash .venv/bin/python -c 'import keyring; keyring.set_password("openkp", input("KP username: "), input("KP password: "))' ``` If you accept the `keyring` prompt for keychain access, you're done. The password is now retrievable by OpenKP's process and nothing else. ### 3. Verify the install (one-time, ~30 seconds) From `~/OpenKP/openkp`: ```bash .venv/bin/pytest -q ``` You should see `591 passed` on macOS. On Windows you'll see 4 failures in `_strftime` / file-permission tests — those are platform-specific and harmless. See [`docs/install/windows.md`](../docs/install/windows.md). If anything else fails, stop and investigate. ### 4. First authenticated run (one-time, ~2 minutes) The first MCP call that needs a session will trigger an interactive Chromium login. Run the server in stdio mode and call `session_check`: ```bash .venv/bin/openkp ``` That blocks waiting for stdio input. From a separate terminal, you can pipe an MCP `tools/call` request, but the easier path is to skip ahead to step 5 and let Claude Desktop drive the first login. ### 5. Connect Claude Desktop Open `~/Library/Application Support/Claude/claude_desktop_config.json` and merge this into your `mcpServers` block (creating the file if it doesn't exist): ```json { "mcpServers": { "openkp": { "command": "/Users//OpenKP/openkp/.venv/bin/openkp" } } } ``` Replace `` with your actual macOS username. The path must be absolute. Fully quit Claude Desktop (Cmd+Q, not just close-window) and relaunch. In a new chat, ask: > Use openkp's session_check tool. The first time, a Chromium window pops up showing the kp.org login. Sign in (including MFA). When kp.org's home page loads, OpenKP captures the session and the window closes automatically. Subsequent calls are silent. ## First things to try Once `session_check` returns `status: alive`, try these prompts in Claude Desktop. Lead with the questions that show what OpenKP makes possible — surface insight your patient portal isn't built to give you: - **"Read every visit note from the last two years. Find every time I raised a concern, asked a question, or pushed back. How was it documented?"** — chains `list_past_visits` → `read_visit_notes` across the full history. Surfaces clinician framing language patients rarely see. - **"Compare what each of my providers has written about my condition over the past three years. Where do they agree, where do they diverge?"** — same chain, organized by provider rather than chronologically. - **"Which lab values have drifted in directions worth asking about?"** — `list_lab_results` with longitudinal pattern reading. - **"Which third-party apps accessed my Kaiser record, what did they read, and when?"** — `list_access_log` over the portal or third-party access-log surfaces. - **"What tests or procedures has Kaiser ordered that I still need to complete, and what are the prep instructions?"** — `list_upcoming_orders` → `read_upcoming_order_instructions`. - **"Are there diagnoses on my problem list I don't recognize or wasn't told about?"** — `list_problems` plus your own memory. Lighter / transactional: - **"What's my next appointment?"** — exercises `list_appointments`. - **"How many appointments did I have last year, split by virtual vs in-person?"** — `list_past_visits` with `until_iso`. - **"Show me my active medications."** — `list_medications`. - **"Summarize the unread messages in my Kaiser inbox."** — `list_messages` + `read_message`. For write operations: - **"Preview a refill for one of my mailable prescriptions."** — uses `request_refill` with the default `confirm=False`. Returns a preview with no Kaiser side effect. Add `confirm=True` only after you've reviewed it. ## Tool inventory ### Housekeeping | Tool | Description | | --- | --- | | `ping` | Smoke test. Returns `pong`. | | `whoami` | Returns the configured KP username and data dir. Never returns the password. | | `session_check` | Verifies end-to-end auth. Triggers the interactive Chromium login on first call. | ### Reads | Tool | Description | | --- | --- | | `get_profile` | Demographics, contact info, insurance, PCP, emergency contacts, healthcare agents. | | `list_messages` | Message-center thread list. Supports `folder`, `search`, `before_iso`, and `deep_search` for archival pagination. | | `read_message` | One thread in full, all messages, HTML-stripped to plain text. | | `download_message_attachment` | Saves a message attachment (PDF/JPG/etc.) to `~/.openkp/downloads/`. | | `list_lab_results` | Lab, imaging, and other test results, newest-first. | | `read_lab_result` | One result with components, values, reference ranges, abnormal flags, narrative. | | `download_lab_result_pdf` | Saves the kp.org-generated PDF to disk. Surfaces `generation_in_progress` for lazy-built PDFs. | | `list_medications` | Active and recent prescriptions with dose, prescriber, refills, copay, mailable / auto-refill flags. | | `list_problems` | Active diagnoses (name + date noted). Kaiser intentionally hides ICD codes from patients. | | `list_allergies` | Allergy list. Handles "no known allergies" as a first-class state. | | `list_appointments` | Upcoming + in-progress visits. | | `list_past_visits` | Past visits, paginated. Supports `max_pages`, `page_size` (default 50, cap 78), and `until_iso`. | | `read_visit_notes` | Clinical notes (provider chart notes, progress notes) + rendered After Visit Summary. | | `download_visit_avs_pdf` | Saves the canonical AVS PDF to `~/.openkp/downloads/`. | | `list_care_team` | Care team and recent providers — PCP, specialists, recently-seen clinicians, with per-provider capability flags. | | `list_implants` | Implanted and explanted devices (pacemakers, ICDs, leads, IOLs, ortho hardware) with manufacturer, model, serial, UDI, body area, and implant/explant procedure details. | | `list_access_log` | Portal and third-party access-log entries with bounded pagination and explicit incomplete-pagination warnings. | | `list_upcoming_orders` | Pending tests/procedures a doctor has ordered but that have not yet become results. | | `read_upcoming_order_instructions` | Full patient-facing prep/instruction HTML and plain text for one pending order. | | `track_refill_order` | Read-side companion to `request_refill`. Surfaces order status, shipping, and tracking. | | `list_message_recipients` | Providers and pools you can message. | | `list_message_topics` | "Reason for Message" catalog (5 topics). | ### Writes Write tools follow a **two-call confirm pattern**: the first call (with `confirm=False`, the default) returns a preview without touching Kaiser. The second call (with `confirm=True`) commits. See "Write tools — preview vs commit" below. | Tool | Description | | --- | --- | | `request_refill` | Mail-order prescription refill. v1 supports mail only; local pickup is deferred. | | `send_message` | Non-urgent medical-advice message to a provider or care team pool. v1 starts new conversations only. | ## Write tools — preview vs commit Every write tool ships with a hard guard: the default mode is preview-only. You must pass `confirm=True` explicitly to actually commit a change to your Kaiser account. Both the preview and the commit are recorded in `~/.openkp/audit.log` (JSONL format, gitignored, lives outside the repo). The audit log records: - **Intent** — what the tool was asked to do (recipient name, medication, etc.). Subject and body of messages are NOT logged. - **Result** — Kaiser's response on success. - **Error** — failure mode and reason on the way out. If you ever want to reconstruct what OpenKP did on your behalf, `tail ~/.openkp/audit.log` is the source of truth. ## Project layout ``` openkp/ ├── pyproject.toml ← deps, entry point, build config ├── .env.example ← template for credentials ├── README.md ← this file ├── LICENSE ← PolyForm Noncommercial 1.0.0 ├── src/openkp/ │ ├── __init__.py │ ├── config.py ← credential loader (env + keychain) │ ├── mcp_server.py ← FastMCP server, all tools registered here │ └── scrapers/ │ ├── auth.py ← Ping/OAuth login via Playwright │ ├── session.py ← cookie persistence + keepalive │ ├── request.py ← authenticated HTTP wrapper │ ├── csrf.py ← shared anti-forgery token fetch │ ├── profile.py ← demographics + PCP + emergency contacts │ ├── messages.py ← message center read + send │ ├── labs.py ← lab results + PDF download │ ├── medications.py ← prescription list (pharmacy BFF) │ ├── problems.py ← active health issues │ ├── allergies.py ← allergy list │ ├── access_logs.py ← portal and third-party access logs │ ├── upcoming_orders.py ← pending tests/procedures and instructions │ ├── appointments.py ← upcoming + past visits │ ├── visit_notes.py ← clinical notes + AVS │ ├── care_team.py ← care team + recent providers │ ├── implants.py ← implanted + explanted devices │ ├── emergency_contacts.py ← emergency contacts + DPOAHC agents │ └── refill.py ← mail-order refill commit + tracking ├── scripts/ │ └── recon_*.py ← per-endpoint reconnaissance scripts └── tests/ └── test_*.py ← 591 tests, mock httpx via _patch_http ``` ## Privacy - Credentials live in your OS keychain or in a gitignored `.env` file. The repo enforces this via `.gitignore`. - Browser profile and session cookies live in `~/.openkp`. Nothing is uploaded. - PHI passes only between Kaiser, OpenKP on your Mac, and Claude Desktop on your Mac. No OpenKP-owned server exists. - HAR captures and recon dumps with PHI live in `docs/research/captures/` (gitignored, never committed). - Recent session journals (`session-*.md`) live outside the repo entirely, in `~/Desktop/OpenKP Documentation/recon/`. ## Legal - Kaiser's terms of service almost certainly prohibit automated access. Personal use on your own account is a gray zone. This is a research tool. - Open Record's license is source-available and prohibits commercial redistribution. OpenKP is licensed under [PolyForm Noncommercial 1.0.0](https://polyformproject.org/licenses/noncommercial/1.0.0/) and does not use Open Record's code, only its public architecture as inspiration. See ADR-007 for the relicense rationale. ## Credits - Ryan Hughes / Fan Pier Labs for [Open Record](https://github.com/Fan-Pier-Labs/openrecord), which demonstrated the idea. - Brendan Keeler for ["The Scrapers At MyChart's Gate"](https://healthapiguy.substack.com/p/the-scrapers-at-mycharts-gate).