Skip to main content

MCP server exposing dummy Indonesian hospital doctor-scheduling tools

Project description

mcp-jadwal-dokter

An MCP (Model Context Protocol) server that exposes dummy hospital doctor-scheduling tools: search doctors, read a schedule, check open slots, and make a booking. It is built to be driven by an LLM agent, and it ships as a pip package that runs over stdio with one command.

The server is deterministic: data plus tools, no model and no API key. That is the point. An MCP server is the tool layer; the model lives in the client. A separate Streamlit demo (in demo/) lets an LLM drive these tools in an agentic chat, for example to find a pediatrician free on Thursday afternoon and book the slot.

This is a portfolio project (project 11 of a larger set). PLAN.md is the full build plan and design record.

Architecture

flowchart LR
    User -->|chat| Streamlit
    Streamlit -->|asyncio.run per turn| Agent[Agent loop]
    Agent <-->|prompt plus tools / tool calls| LLM[LLM provider<br/>Anthropic or OpenRouter]
    Agent <-->|stdio MCP| Server[jadwal-dokter MCP server]
    Server --> Tools[search_doctors / get_schedule<br/>check_slot / create_booking]
    Tools --> Seed[(doctors.json, read-only)]
    Tools --> DB[(SQLite bookings)]

Everything from the MCP server rightward is the published package and needs no key. The LLM and the Streamlit client are the demo, and only they read an API key. Each user turn opens one stdio session to the server, runs the tool-calling loop, and closes it.

Tools

Eight tools, plus resources and a prompt:

Tool What it does
get_today() The server's date and weekday, so an agent resolves relative phrases ("next Thursday") against the server clock.
search_doctors(specialization?, name?, day?) Find doctors by specialization, name substring, and/or practice day. Filters are optional and combine with AND.
get_schedule(doctor_id) A doctor's weekly practice blocks and any leave periods.
check_slot(doctor_id, date) Available 30-minute slots for a doctor on an ISO date. A day off, a leave date, or a full day return an empty list with an explanatory note, not an error.
create_booking(doctor_id, date, time, patient_name) Create a booking. Validates the doctor, practice day, leave, slot, availability, and rejects past dates, with a specific, actionable error when it cannot book.
cancel_booking(booking_id) Cancel a booking; the slot frees again, history is kept as cancelled.
reschedule_booking(booking_id, new_date, new_time) Move a confirmed booking to another slot, keeping its id; the original is untouched on failure.
list_bookings(doctor_id?, date?, patient_name?, status?) Filtered list of bookings, including cancelled ones.

Resources: jadwal://roster, jadwal://specializations, jadwal://info. Prompt: book_appointment. Tool descriptions and error messages are available in Indonesian (default) or English via JADWAL_LANG.

All data is fictional. The bundled example is a made-up hospital ("Rapha Hospital"): 32 doctors across 12 specializations, with varied weekly schedules and deliberate edge cases (a doctor on leave, a fully booked day, a specialization with a single doctor). Bookings persist in SQLite, so a booked slot shows as taken on the next check. There is no connection to any real hospital, patient, or record.

Bring your own roster

Point JADWAL_DATA at a JSON or YAML file (same schema as the bundled example) and the server schedules against your doctors instead:

JADWAL_DATA=/path/to/my-roster.yaml mcp-jadwal-dokter

Copy jadwal_dokter/seed/doctors.example.yaml as a starting point. Each doctor has an id, name, specialization, weekly_blocks (day in lowercase Indonesian senin..minggu, start/end as HH:MM; slots are 30 minutes), and optional leave_periods (inclusive ISO date ranges). A bare list or a {doctors: [...]} wrapper both work. YAML needs the [yaml] extra (pip install "mcp-jadwal-dokter[yaml]").

Requirements

Python 3.10 or newer.

Install and run the server

During development, from this directory:

python -m venv .venv
.venv/Scripts/python -m pip install -e .    # on macOS/Linux: .venv/bin/python

Run the server over stdio:

mcp-jadwal-dokter

The server speaks JSON-RPC on stdio and logs to stderr, so it is meant to be launched by an MCP client rather than used directly in a terminal.

Once published (see PLAN.md, milestone M3), it will run without a manual install:

uvx mcp-jadwal-dokter

Use from Claude Desktop

Add the server to claude_desktop_config.json:

{
  "mcpServers": {
    "jadwal-dokter": {
      "command": "uvx",
      "args": ["mcp-jadwal-dokter"]
    }
  }
}

For a local editable checkout instead of a published package, point the command at the venv's mcp-jadwal-dokter (or python -m jadwal_dokter.server) and set JADWAL_DB if you want the bookings file in a specific place.

Run the demo (agent chat)

The demo is an optional extra with its own dependencies and an API key:

.venv/Scripts/python -m pip install -e ".[demo]"
copy .env.example .env          # then set your key, see below
.venv/Scripts/python -m streamlit run demo/streamlit_app.py

Set one key in .env: OPENROUTER_API_KEY (with JADWAL_PROVIDER=openai) or ANTHROPIC_API_KEY (with JADWAL_PROVIDER=anthropic). Then click "Jalankan skenario contoh" and watch the agent chain search_doctors -> check_slot -> create_booking, with every call shown in the tool-call log.

A captured end-to-end run of that scenario is in SAMPLE_RUN.md.

Full stack (Vue console + chat)

Beyond the Streamlit demo there is a richer web app: a FastAPI backend (backend/) that holds the key and drives the MCP server, and a Vue frontend (frontend/) with an agent Chat and an MCP Console (roster, schedules, bookings, a tool-invoke panel, metrics, and a tool-call history). See docs/design-system.md and frontend/README.md.

Run the three processes (the MCP server over HTTP, the backend, and the Vite dev server):

# 1. MCP server in HTTP mode
JADWAL_TRANSPORT=http JADWAL_API_KEY=dev-key mcp-jadwal-dokter

# 2. backend (points at the MCP server; holds the provider key from .env)
JADWAL_MCP_URL=http://127.0.0.1:8000/mcp JADWAL_API_KEY=dev-key \
  .venv/Scripts/python -m backend.app

# 3. frontend dev server (proxies /api to the backend)
cd frontend && npm install && npm run dev   # http://localhost:5173

In production, npm run build emits frontend/dist, which the backend serves from the same origin, so it is one server. When hosting, set JADWAL_BACKEND_KEY and JADWAL_BACKEND_RATE_LIMIT to gate /api, and JADWAL_CORS_ORIGINS for your domain. Note: the backend has no per-user auth; it is a single-tenant demo (see docs/decisions/).

Note on free models: OpenRouter :free models are rate limited per upstream and can return 429. The app handles this with retries and a clean error message. For a smooth run, add a small OpenRouter credit, or use an Anthropic key.

Tool design decisions

The tools are the product here, so the design choices are deliberate:

  1. Verb-noun names, schema from types. search_doctors, get_schedule, check_slot, create_booking. Typed parameters become the JSON input schema and docstrings become the descriptions, so the model can pick and fill a tool from the schema alone.
  2. Error messages written for the model. A failed booking says what is wrong and what to do next ("slot 14:00 sudah dibooking; slot tersedia: 15:00, 15:30"), not "400". The agent recovers instead of giving up.
  3. Compact structured returns. Each tool returns a small typed object, not a prose paragraph. Fewer tokens, and the model reads fields reliably.
  4. Unavailable is an answer, not an error. A day off, a leave date, or a full day come back from check_slot as an empty slot list with a note, so the agent can reason and try another day. Only genuinely bad input (unknown id, malformed date) raises.
  5. Dummy but persistent bookings. SQLite so a booking then shows the slot taken, with some slots pre-seeded full. State without a real backend.
  6. The LLM is in the client, not the server. The server is data plus tools, so it installs, runs, and tests with no key. That is what an MCP server is, and it is what makes the package broadly useful.

Configuration

Variable Purpose Default
JADWAL_DATA Path to a custom roster (JSON/YAML). bundled example
JADWAL_DB Path to the SQLite bookings file. bookings.db
JADWAL_LANG Tool/error language: id or en. id
JADWAL_TZ IANA timezone for dates and get_today. Asia/Jakarta
JADWAL_TODAY Pin "today" (ISO date) for reproducible runs. real clock
JADWAL_TRANSPORT stdio or http (Streamable HTTP at /mcp). stdio
JADWAL_HTTP_HOST / JADWAL_HTTP_PORT Bind address for HTTP mode. 127.0.0.1 / 8000
JADWAL_API_KEY HTTP mode: require this key (Bearer/X-API-Key). none (no auth)
JADWAL_RATE_LIMIT HTTP mode: max requests/min per client IP. 0 (off)
JADWAL_PROVIDER Demo only: openai or anthropic. openai
JADWAL_MODEL Demo only: override the model id. a free OpenRouter model

The server core needs no API key. The JADWAL_* provider variables and the API keys in .env.example are read only by the demo.

Development

.venv/Scripts/python -m pip install -e ".[dev]"
.venv/Scripts/python -m pytest

The suite is deterministic and needs no model or key: the server tools are pure, and the agent-loop tests drive the real MCP server with a stub provider. Each test runs against an isolated temporary bookings database.

There is also a rerunnable, model-driven eval (python -m eval.run) that runs scenarios through the real agent and checks final state. It needs a working model and reports free-tier 429s as skips.

Status

  • Server core and tools: done, tested.
  • Streamlit agent demo: done, tested; full scenario verified end to end.
  • PyPI publish, CI, and a recorded demo: planned (see PLAN.md).

Project details


Download files

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

Source Distribution

mcp_jadwal_dokter-0.2.0.tar.gz (47.8 kB view details)

Uploaded Source

Built Distribution

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

mcp_jadwal_dokter-0.2.0-py3-none-any.whl (33.9 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for mcp_jadwal_dokter-0.2.0.tar.gz
Algorithm Hash digest
SHA256 c48ef9155b2d3c0a290844add364b6ea4f38899538a5c84d76428ab7821fb05f
MD5 a8172fde86cbe031615d71051a08b008
BLAKE2b-256 e08d066001daa9a5849e0188323e40cf64b10a52aaa5bb88c9d70f27853dd752

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for mcp_jadwal_dokter-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 cc0b934bb2280b3d0d9aec12e5100587904a28b57b48851fd7ed5c09798b22e6
MD5 19ce0f6cf3724af617b6068c66853710
BLAKE2b-256 06dd6e62a9c870630c0ea9499a13281bfe7de87bc6be4792740d88907cdd8662

See more details on using hashes here.

Supported by

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