doctolib-mcp

doctolib-mcp

MCP server for Doctolib that enables searching practitioners, checking availability, and booking or canceling appointments across Doctolib Germany, France, and Italy.

Category
访问服务器

README

doctolib-mcp

I injured myself the other day, and now we have a Doctolib MCP server.

MCP tools over Doctolib's internal JSON API. Search practitioners, read their visit motives, and find the earliest bookable slot across an entire specialty in one call. With a logged-in session it also reads your own appointment calendar, and behind an explicit confirmation step it can book and cancel.

Works against doctolib.de, doctolib.fr and doctolib.it through the country argument. The specialty slug list is German only, but free-text keywords work everywhere.

Why this exists

Doctolib's official API is partner-only, restricted to medical software vendors and hospital information systems. There is no self-serve tier to sign up for. The only thing on offer elsewhere was an Apify scraper actor, which returns directory data with no availability and no booking, at roughly $30 per 1000 records.

What does exist is the JSON that Doctolib's own patient frontend talks to, and it turns out to be enough for the whole job.

The tool worth having is find_earliest_appointment. The website makes you open practitioners one at a time, which is precisely the part that stops working when what you want is the first free appointment instead of one particular doctor. This searches, fans out over every candidate's availability in parallel, and returns them sorted by soonest slot.

Tools

Tool What it does Writes?
find_earliest_appointment search plus parallel availability fan-out, sorted by soonest slot no
search_doctors practitioners only, no availability check no
get_practitioner every visit motive, agenda and place for one profile no
get_availabilities open slots for one motive/agenda/practice triple no
list_specialties common German specialty slugs no
booking_url the URL a human opens to book in a browser no
my_appointments your own confirmed, pending and past appointments no, but authenticated
session_status whether the stored login is still live no
abort_booking_draft release a lingering temporary slot hold yes, but harmless
book_appointment books a real appointment. See docs/safety.md yes, binding
cancel_appointment cancels a real appointment. See docs/safety.md yes, binding

Search results already carry visitMotiveId, agendaIds and practiceId, so a search chains straight into availabilities without a per-practitioner round trip. The full reverse-engineered endpoint contract lives in docs/endpoints.md.

Install

cd ~/dev/doctolib-mcp
uv venv
uv pip install "mcp>=2.0.0" "httpx>=0.27" "typer>=0.12"

The project sets package = false under [tool.uv], so it is never installed into the venv. Everything runs with PYTHONPATH=src, which is why the MCP registration below sets that environment variable. There is no installed entry point to fall back on.

For the authenticated tools, add Playwright:

uv pip install "playwright>=1.44"
./.venv/Scripts/python.exe -m playwright install chromium

Registered in ~/.claude.json as:

"doctolib": {
  "command": "/absolute/path/to/doctolib-mcp/.venv/Scripts/python.exe",
  "args": ["-m", "doctolib_mcp.mcp_server"],
  "env": { "PYTHONPATH": "/absolute/path/to/doctolib-mcp/src" }
}

pi registers the same server lazily, with a directTools list that deliberately omits book_appointment and cancel_appointment so the two binding writes stay behind the proxy instead of sitting one token away in the default tool list.

Logging in

Both binding writes and my_appointments need a real logged-in session. Chrome wraps its own cookie store in App-Bound Encryption (v20 / APPB keys), so there is no reliable way to lift a session cookie out of it offline, and the session cookie is httpOnly so JavaScript cannot read it either. Rather than fight that, this server owns a separate browser profile: a persistent Playwright user-data-dir under .session/<country>/ that you log into once.

PYTHONPATH=src ./.venv/Scripts/python.exe -m doctolib_mcp.cli login

A real browser window opens. Sign in, complete any 2FA, and the command returns once the session goes live. The profile survives restarts, so this is a one-time step until Doctolib actually expires the session. session_status reports when that has happened.

You do not have to run that by hand

When an authenticated tool finds no live session, it opens the login window itself and retries the call once the login exits successfully. So my_appointments against an expired session shows you a browser, waits while you sign in, and then answers, instead of returning an error that tells you to go and run a command.

Three details govern how that behaves:

It runs the login as a subprocess and treats exit code 0 as the completion signal, which doctolib login gives only after it has probed the session and found it live. That also sidesteps a hard constraint. Chromium takes an exclusive lock on the profile directory, so a login cannot start while another context is open on it, and every caller closes its own context before the login is spawned.

It is opt-out through DOCTOLIB_AUTO_LOGIN=0. A blocking browser window is right at a terminal and wrong in an unattended worker, so anything running without a human in front of it should set that and get the immediate failure instead.

It backs off and serialises. A dismissed window is not reopened for 90 seconds, and concurrent callers produce one window between them rather than one each.

Public reads deliberately do not trigger any of this. They work anonymously, so a plain search never pops a browser at you. Only the tools that genuinely cannot proceed without authentication will.

If your MCP client's own request timeout is shorter than the login takes, the tool call will fail while the window is still open. Finish signing in anyway. The session persists, and the next call picks it up.

.session/ is gitignored and holds a live login. It must never be committed, copied into a scratch directory, or handed to another agent.

The public reads use the session too

Once you are logged in, the read tools stop querying anonymously and go through the session. Without it they miss whatever the backend personalises for a known patient: existing-patient ("Bestandspatient") visit motives, slots that some practices only show to logged-in known patients, the correct insurance sector, and personalised ranking. Every read result carries "authenticated": true or false so it is always clear which view produced the answer.

With no live session the read tools fall back to the anonymous public view silently. They remain fully functional, just not personalised.

Mechanically the reads take the fast path. The session cookie is read out of the login profile, cached in-process for five minutes so a fan-out like find_earliest_appointment costs one browser launch instead of twenty, and replayed over httpx. Only the write path insists on running inside the real browser context.

Writes

book_appointment commits a real clinical slot at a real practice under a real name. If it succeeds, a clinic has put you in their calendar and a human being expects you to physically show up. cancel_appointment is the inverse and releases that slot irreversibly.

Three things guard them, and it is worth being precise about which is which:

  1. confirm=False is the default, and on that path book_appointment performs no network call at all. It returns the payload that would be sent so an agent can read the appointment back to you in plain language and get an explicit yes. cancel_appointment with confirm=False performs one read, fetching the valid cancellation reasons, and issues no DELETE.
  2. A live login session is required. There is no cookie parameter on the booking client, because the funnel is session-bound and needs real CSRF tokens and draft state rather than a replayed cookie. Without a session the call fails before it can commit anything.
  3. Neither tool is in pi's directTools, so on that box they stay behind the proxy.

One thing that does not protect you: DoctolibBookingClient takes no cookie parameter and reads no DOCTOLIB_COOKIE, so leaving that environment variable unset guards nothing. The confirmation step and the login requirement are the whole list. docs/safety.md records where that particular misconception came from.

The booking funnel is four stateful steps sharing a server-side draft, and it has never been executed end to end, because that cannot be tested without making a real booking. Cancelling has been executed once, on 05.08.2026, as an explicitly authorised real cancellation. Both contracts, the failure modes, and the rules an agent should follow are written up in docs/safety.md.

CLI

Every tool is reachable without an MCP client, so a misbehaving tool reproduces in one shell command:

PYTHONPATH=src ./.venv/Scripts/python.exe -m doctolib_mcp.cli earliest handchirurg --city Berlin --within-days 21

Available commands: earliest, search, practitioner, slots, login, status, appointments, abort-draft, cancel. The cancel command is a preview unless you pass --confirm.

Tests

uv pip install "pytest>=8.0"
PYTHONPATH=src ./.venv/Scripts/python.exe -m pytest tests -q

111 tests, about six seconds, no network. The suite runs entirely offline because a conftest.py fixture blocks real socket connections, blocks Playwright from launching, and repoints the session directory at a temporary path, so a test that reaches for the network or for the real login fails loudly instead of quietly succeeding. For a repository that can cancel real medical appointments, a test run that can touch the live account is itself the hazard.

What the tests are there to catch, in descending order of what it costs when they go red:

  • The confirmation step never writes. A regression that makes confirm=False commit something is the worst failure this codebase has available to it.
  • _pick_patient chooses the right person. On an account with family members, the wrong branch books a clinical slot for the wrong human.
  • _build_confirm_payload invents nothing. Every field submitted to a medical intake form has to have come from the server's own prefill.
  • The booking funnel calls its four steps in the documented order, at the documented paths. This is as close to end-to-end as is reachable without making a real booking, which makes it the most useful thing the suite does.
  • Failures after step one tear the draft down, so a broken run never leaves a slot held.
  • Answers that would be silently wrong rather than loudly broken: the Nominatim bounding-box transposition, the multi-chunk availability walk, and the speciality field that arrives as a string in one payload and a dict in another.

session.login() and session.status() are deliberately untested. They drive a visible interactive browser, and mocking Playwright deeply enough to cover them would only test the mock. The commit hop itself is also untested, for the obvious reason.

Etiquette

Requests are throttled per host with jitter, and the availability fan-out is capped at four workers. Keep it that way. This is a personal-use tool hitting a healthcare provider's undocumented endpoints, and the polite failure mode is being slow rather than being blocked.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选