doctolib-mcp
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@doctolib-mcpFind the earliest available appointment with a dermatologist in Paris."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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.
Related MCP server: Health ID Service MCP Server
Tools
Tool | What it does | Writes? |
| search plus parallel availability fan-out, sorted by soonest slot | no |
| practitioners only, no availability check | no |
| every visit motive, agenda and place for one profile | no |
| open slots for one motive/agenda/practice triple | no |
| common German specialty slugs | no |
| the URL a human opens to book in a browser | no |
| your own confirmed, pending and past appointments | no, but authenticated |
| whether the stored login is still live | no |
| release a lingering temporary slot hold | yes, but harmless |
| books a real appointment. See docs/safety.md | yes, binding |
| 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 chromiumRegistered 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 loginA 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:
confirm=Falseis the default, and on that pathbook_appointmentperforms 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_appointmentwithconfirm=Falseperforms one read, fetching the valid cancellation reasons, and issues noDELETE.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.
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 21Available 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 -q111 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=Falsecommit something is the worst failure this codebase has available to it._pick_patientchooses the right person. On an account with family members, the wrong branch books a clinical slot for the wrong human._build_confirm_payloadinvents 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
specialityfield 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.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- Alicense-qualityDmaintenanceAn MCP server that provides healthcare tools for interacting with FHIR data and medical resources on EMRs like Cerner and EpicLast updated1MIT
- Flicense-qualityDmaintenanceAn MCP server for accessing India's National Digital Health Mission (NDHM) Health ID services, enabling health information management through the NDHM API.Last updated
- Alicense-qualityDmaintenanceAn MCP server that brings AI-powered search and conversation to your FHIR clinical documents.Last updated1MIT
- FlicenseBqualityCmaintenanceMCP server for headless care coordination, providing tools for case-aware provider search powered by ai-carematch.Last updated4
Related MCP Connectors
MCP server for medicare-coverage
Hosted MCP server exposing US hospital procedure cost data to AI assistants
MCP server for AgentDocs (agentdocs.eu): read, search, write, comment on & share Markdown docs.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/esinecan/doctolib-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server