nhs-intelligence-mcp
nhs-intelligence-mcp
Ein MCP-Server, der Tools für NHS-Wartezeiten und Trust-Intelligenz bereitstellt, damit ein agentischer Client (Claude Code, Claude Desktop oder ein beliebiger MCP-kompatibler Host) offene Fragen zur Leistung von NHS-Providern auf der Grundlage echter öffentlicher Daten beantworten kann.
Der Server stellt ausschließlich Daten- und Analysetools bereit; er ruft niemals ein LLM auf. Das agentische Denken findet im verbindenden Client statt, der diese Tools miteinander verkettet. Dadurch bleiben die Zahlen deterministisch und der gesamte Server offline unit-testbar.
Status
Meilenstein 3. Fünf Tools über drei Datenquellen: RTT-Trend, My Planned Care aktueller Stand (Suche und Ranking), CQC-Qualitätsbewertungen und ein kombiniertes trust_profile, das alle drei verbindet. Jedes ist Ende-zu-Ende mit einer Offline-Testsuite abgedeckt (57 Tests).
Hinweis zur Trust-Identität
Die drei Quellen benennen Trusts unterschiedlich: RTT verwendet einen numerischen Provider-Code (z. B. RGT), My Planned Care verwendet den Trust-Namen (z. B. Guy's and St Thomas'), CQC verwendet eine eigene Provider-ID (z. B. 1-101681210). Die Einzelquellen-Tools verwenden die Identität, die ihre Quelle veröffentlicht. trust_profile führt die drei anhand einer kuratierten Mapping-Datei zusammen; ein Trust, der in der Zuordnung fehlt, gibt found=false zurück. Bewertungsdaten sind ergänzend: Wenn der CQC-Feed nicht erreichbar ist, entfernt das Profil diesen einen Abschnitt und gibt weiterhin die Warte- und Trenddaten zurück.
Related MCP server: NHS MCP Server
Design
src/nhs_intel/
domain/ # immutable value objects (WaitTimePoint, TrustRating, ...)
sources/ # adapters behind Protocols (CSV feeds, CQC HTTP, identity map)
analysis/ # pure trend, ranking and profile-join logic
server.py # thin FastMCP layer: validate -> pure handler -> serialiseDer reine Kern (domain, analysis) hat keine Ein-/Ausgabe und keine MCP-Importe, sodass er in CI ohne Netzwerk und ohne Live-NHS-Feed läuft. Jede Quelle liegt hinter einem Protocol, einschließlich des CQC-HTTP-Clients, sodass Tests In-Memory-Fakes injizieren und die Suite vollständig offline ist. Dies ist die Ports-and-Adapter-Grenze aus nhs-webscraper, angewendet auf Datenfeeds.
Datenquellen
Quelle | Häufigkeit | Rolle | Zugriff |
NHS England RTT | Monatlich | Trend-Grundlage | CSV-Cache |
My Planned Care (via nhs-webscraper) | Wöchentlich | Ebene für den aktuellen Stand | CSV |
CQC-Bewertungen | Täglich | Trust-Qualitätskontext | Öffentliche API, ohne Schlüssel |
Tools
Tool | Eingabe | Rückgabe |
| provider_code, specialty | Start-/End-Wartezeiten, Delta, prozentuale Änderung, Richtung, monatliche Serie (RTT) |
| provider (Name), specialty | aktuellste Wartezeit für einen Trust (My Planned Care) |
| specialty, region?, limit? | Trusts sortiert nach aktueller Wartezeit, längste zuerst (My Planned Care) |
| cqc_provider_id | CQC-Gesamtbewertung und Bewertungen je Domäne |
| identifier, specialty, by_name? | kombinierte aktuelle Wartezeit, Trend und Bewertung für einen Trust |
Die Trendrichtung wird durch eine relative Totzone (Standard: 5 %) bestimmt, sodass eine kleine Schwankung bei einer langen Wartezeit als flat gelesen wird und nicht als Scheintrend: dieselbe Entscheidung zur Rauschunterdrückung wie im MRR-prediction-Projekt. Das Ranking listet Trusts, die eine Fachrichtung veröffentlichen, aber keinen Wert angeben, nach den bereits sortierten Trusts auf, sodass Abdeckungslücken sichtbar bleiben und sich von einer Wartezeit von null unterscheiden.
Ausführen
Abhängigkeiten werden mit uv verwaltet und in uv.lock festgeschrieben; eine einfache requirements.txt wird zusätzlich exportiert, für alle, die kein uv verwenden.
# uv resolves and creates .venv automatically from uv.lock:
uv sync --extra dev
# Point the server at an ingested RTT CSV cache (provider_code,specialty,weeks,as_of):
export NHS_INTEL_RTT_CSV=tests/fixtures/rtt_sample.csv
# Optional: My Planned Care scraper output for the current-state tools.
export NHS_INTEL_PLANNED_CARE_CSV=tests/fixtures/planned_care_sample.csv
# Optional: cross-source identity mapping for trust_profile.
export NHS_INTEL_IDENTITY_CSV=tests/fixtures/identity_sample.csv
# Optional: a partnerCode string sent with CQC requests to identify your caller.
export NHS_INTEL_CQC_PARTNER_CODE=my-org
# Run the MCP server over stdio:
uv run nhs-intel-mcpOhne uv: pip install -r requirements.txt ausführen und dann nhs-intel-mcp direkt starten, wobei dieselben Umgebungsvariablen gesetzt sind.
Nur NHS_INTEL_RTT_CSV ist erforderlich; jedes Tool meldet deutlich, wenn seine Quelle nicht konfiguriert ist, und der CQC-Feed degradiert bei Nichterreichbarkeit zu einem fehlenden Bewertungsabschnitt. .mcp.json registriert den Server für Claude Code über uv run, sodass die projekteigene Umgebung verwendet wird, unabhängig davon, welche in der aufrufenden Shell aktiv ist.
Entwicklung
uv sync --extra dev
uv run pytest # offline, no network or keys required
uv run ruff check .Available Tools
5 toolsget_trust_ratingA
Look up a trust's current CQC quality rating by its CQC provider id (e.g. '1-101681210'). Returns the overall rating (e.g. 'Good', 'Requires improvement') and any per-domain ratings (safe, effective, caring, responsive, well-led). Returns found=false if the provider is unknown or not yet rated; do not infer a rating in that case.
| Name | Required | Description | Default |
|---|---|---|---|
| cqc_provider_id | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the behavioral disclosure burden. It discloses the return shape (overall plus per-domain ratings), the not-found behavior (found=false), and an important instruction not to infer a rating. This is strong for a read-only lookup tool, though it does not explicitly mention safety or data freshness.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, each earning its place: purpose and input, return content, and the critical not-found behavior. Information is front-loaded and no filler is present.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter lookup tool with an output schema, the description is complete. It covers input, expected outputs, the not-found case, and the correct behavior when no rating exists. Nothing essential for an agent to select and call the tool correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0%, so the description must compensate. It explains that cqc_provider_id is the CQC provider id and supplies a realistic example ('1-101681210'), adding meaning beyond the bare schema field. It does not elaborate on id source or formatting rules, but the example and context are sufficient for correct invocation.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly identifies the action (look up), the resource (a trust's current CQC quality rating), and the lookup key (CQC provider id), with a concrete example. It also distinguishes itself from sibling tools focused on wait times and trust profiles by explicitly focusing on CQC quality ratings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description states the task and input clearly enough that an agent knows when to invoke it: whenever a trust's CQC rating is needed. It does not explicitly name sibling alternatives or exclusion conditions, but the focused wording and the contrast with wait-time-related siblings imply the appropriate use case.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
lookup_wait_timeA
Look up the latest reported waiting time for one NHS trust and specialty from My Planned Care (updated weekly). Waits are in whole weeks. Provide the trust name as published (e.g. 'Guy's and St Thomas'') and the specialty (e.g. 'Cardiology'). Returns found=false if the trust/specialty is not held; do not invent a figure. Note the trust is identified by name here, unlike wait_time_trend which uses an RTT provider code.
| Name | Required | Description | Default |
|---|---|---|---|
| provider | Yes | ||
| specialty | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of behavioral disclosure. It states that waits are in whole weeks, data is updated weekly, found=false is returned when the trust/specialty is not held, and that the agent must not invent a figure. These are material behaviors beyond the schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Every sentence adds necessary information: purpose and source, units, parameter formatting, not-found behavior, and the sibling-tool distinction. The description is front-loaded with the core purpose and contains no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-parameter lookup tool with an output schema, this description is remarkably complete. It covers source, freshness, units, input formatting, missing-data behavior, and the key alternative tool, leaving no significant gap for an agent to resolve.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate. It does so thoroughly by explaining that provider should be 'the trust name as published' with an example, and specialty should be like 'Cardiology' — also clarifying the name-based identifier, which is essential for correct use.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb and resource: 'Look up the latest reported waiting time for one NHS trust and specialty from My Planned Care.' It clearly distinguishes this tool from wait_time_trend by noting the trust is identified by name here rather than by RTT provider code.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly contrasts this tool with wait_time_trend, stating that lookup_wait_time uses the trust name while wait_time_trend uses an RTT provider code. This gives the agent a concrete basis for choosing between the two alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
rank_trusts_by_waitA
Rank NHS trusts by their latest waiting time for a specialty (longest first), using My Planned Care weekly data. Provide the specialty; optionally restrict to a region and cap the number of trusts returned. Trusts that publish the specialty but no wait figure are listed after the ranked ones, so coverage gaps are visible rather than hidden.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| region | No | ||
| specialty | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description must carry behavioral disclosure, and it does: it reveals the ordering, the optional filters, and the important edge-case behavior that trusts without figures are listed after ranked ones. It does not mention read-only status or availability constraints, but for a data-query tool the stated behavior is substantively transparent.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences contain the core purpose, data source, ordering, optional parameters, and an edge-case behavior with zero filler. The statement is front-loaded with the action and resource.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a ranking tool with an output schema, the description is largely complete: it explains the data source, ordering, filters, and gap behavior. Minor details like the unit of waiting time or whether limit applies to gap-listed trusts are not stated, but an agent can invoke the tool correctly from this description.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema has 0% parameter coverage, so the description must compensate, and it maps all three parameters: specialty is the required filter, region restricts geography, and limit caps the number of returned trusts. It does not enumerate valid specialty/region values, but it conveys the role of each parameter beyond the bare schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific action ('Rank NHS trusts by their latest waiting time for a specialty'), specifies ordering ('longest first'), and identifies the data source. This clearly differentiates it from siblings like wait_time_trend or lookup_wait_time, which are point/trend lookups rather than cross-trust rankings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description states the use case clearly—comparing trusts across a specialty and optionally filtering by region or count. It does not explicitly name sibling alternatives or say when not to use it, but the ranking purpose and data source give enough context for an agent to select it over lookup/trend tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
trust_profileA
Build a combined profile for one NHS trust and specialty, joining its current wait (My Planned Care), waiting-time trend (RTT), and CQC quality rating. Identify the trust by RTT provider code (e.g. 'RGT') or trust name (e.g. 'Guy's and St Thomas''); set by_name=true when passing a name. Each section is null when that source holds no data. If the trust is not in the identity mapping, returns found=false.
| Name | Required | Description | Default |
|---|---|---|---|
| by_name | No | ||
| specialty | Yes | ||
| identifier | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the behavioral disclosure burden and does disclose key edge cases: sections are null when a source has no data and found=false when the trust isn't in the identity mapping. It does not mention any side effects, auth, or rate limits, but for a read/composite tool these may be less critical; output schema can cover return details.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, no filler: purpose first, then identity parameters, then fallback/null behavior. Every sentence adds a distinct piece of knowledge an agent needs.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The core behavior, identity resolution, nullable sections, and not-found case are all covered, and an output schema exists for return shapes. The main remaining gap is valid specialty values, but the description is otherwise sufficient for correct tool selection and invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0%, so the description must compensate. It explains identifier as an RTT provider code or trust name and defines by_name=true for names, but specialty is only implied by context with no accepted values, format, or examples, leaving one required parameter underspecified.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific composite verb+resource: builds a combined profile for one NHS trust and specialty, joining three named sources. This clearly differentiates it from siblings like wait_time_trend and get_trust_rating, which presumably cover a single source.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description establishes clear context for when to use it — when current wait, RTT trend, and CQC rating are needed together. It also gives conditional guidance for identifier mode via by_name, but doesn't explicitly name sibling alternatives for single-source queries, so no exclusions are stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
wait_time_trendA
Summarise how the waiting time for a given NHS trust and specialty has moved over time, using NHS England RTT monthly data. Waits are in weeks. Provide the provider code (e.g. 'RGT') and specialty (e.g. 'Cardiology'). Returns the start and end values, the change in weeks, the percentage change, a direction (improving | worsening | flat), and the full monthly series. If no data is held for that trust/specialty, returns found=false; do not invent a trend in that case.
| Name | Required | Description | Default |
|---|---|---|---|
| specialty | Yes | ||
| provider_code | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full behavioral disclosure burden and does an excellent job. It discloses the output return fields (start/end values, change, percentage, direction, full monthly series), the units (weeks), and the found=false edge case, explicitly instructing not to invent a trend when data is absent.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact yet information-dense: purpose, units, input guidance, output summary, and edge-case handling are all included in three sentences. Every sentence earns its place and no filler exists.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's modest complexity, an output schema, and no annotations, the description is complete. It covers the data source, input semantics, units, expected return values, and the missing-data behavior, so an agent has everything needed to invoke it correctly and interpret results appropriately.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate for both parameters. It does by explaining that provider_code is an NHS trust code with an example ('RGT') and specialty is a clinical specialty with an example ('Cardiology'), giving an agent enough semantic grounding to call the tool correctly.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states a specific verb ('Summarise') and a specific resource ('waiting time for a given NHS trust and specialty... moved over time'), which distinguishes it from sibling tools like lookup_wait_time or rank_trusts_by_wait. It also previews concrete outputs, leaving no ambiguity about what the tool does.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description clearly implies when to call this tool: when a trend summary over time is needed, rather than a point lookup or ranking. It gives concrete input requirements (provider code, specialty) and an explicit no-data behavior, but it does not explicitly name alternative tools or state when not to use it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
5 tool updates
v0.1.0- First observed
get_trust_rating - First observed
lookup_wait_time - First observed
rank_trusts_by_wait - First observed
trust_profile - First observed
wait_time_trend
TDQS
Scored across 5 tools
Each tool targets a clearly distinct query: single-trust trend, single-trust latest wait, cross-trust ranking, quality rating, and combined profile. The descriptions explicitly call out different data sources and identity keys, so an agent should not misselect.
Three tools use a verb-first pattern (lookup_, rank_, get_), but wait_time_trend and trust_profile are noun phrases, so the set lacks a single consistent convention. The names are still readable and specific, but the pattern is mixed.
Five tools is a well-scoped size for an NHS intelligence read-only server. Each tool covers a distinct query need without redundancy or bloat.
The surface covers current waiting times, historical trends, cross-trust ranking, quality ratings, and an integrated profile joining those sources. Because trust_profile accepts either an RTT code or a trust name, it also bridges the identifier gap, so there are no dead-end lookup paths.
Maintenance
Related MCP Connectors
Access CQC care ratings, NHS health services, and food hygiene data across the UK
Hosted MCP server exposing US hospital procedure cost data to AI assistants
NHS organisations, schools, care quality ratings, food hygiene and policing for any UK area.
Hosted MCP server for Cliniko — patients, appointments, availability, and invoices for AI agents.
Related MCP Servers
- AlicenseAqualityBmaintenanceEnables querying of current A\&E waiting times, specialist outpatient waiting times, and general outpatient clinic quotas in Hong Kong using natural language through MCP.320 PyPI4MIT
- FlicenseAqualityDmaintenanceEnables AI assistants to query NHS public health datasets, including prescribing data, dataset exploration, and organisation lookup via the NHSBSA Open Data Portal.9-
- AlicenseNot gradedqualityBmaintenanceMCP server for the Semble practice-management API, enabling AI agents to search for patients, contacts, and users, as well as retrieve patient relationships via read-only tools.MIT
- AlicenseAqualityBmaintenanceEnables querying the Polish National Health Fund (NFZ) public API for medical waiting lists and service dictionary, allowing AI to compare wait times and find providers.3MIT