@cyanheads/uk-police-crime-mcp-server
Click on "Deploy 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., "@@cyanheads/uk-police-crime-mcp-servershow me the latest crimes reported near 51.5074, -0.1278"
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.
Public Hosted Server: https://uk-police-crime.caseyjhand.com/mcp
Overview
Monthly street-level crime, police outcomes, and stop and search records from data.police.uk, plus the neighbourhood policing teams behind them. Covers the 43 territorial forces of England and Wales, the Police Service of Northern Ireland, and British Transport Police, whose station records are the only coverage of Scotland. Search by point, polygon, map point, neighbourhood, or whole force; follow a crime's outcome history; find who polices a place. Runs as a stdio process, a local Streamable HTTP server, or the public hosted endpoint above.
Tools
Tool | Description |
| Decode force ids, crime categories, a force's neighbourhood ids, and which months and forces are published |
| Search the street-level crimes recorded in one month, or a range of up to 12, inside an area, with counts by category and latest outcome |
| List the police outcomes recorded in one month inside an area, for crimes from that month or earlier |
| Fetch the full outcome history of up to 25 crimes by |
| Search stop and search records for one month, or a range of up to 12, inside an area or across a whole force, with breakdowns and filters |
| Find the force and neighbourhood policing team for a point, or look one up by id |
Related MCP server: open-meteo-mcp-server
Capability reference
ukcrime_list_reference tool
topic:forces,categories,availability, orneighbourhoods(needsforce);name_containsfilters forces and neighbourhoods by name, andmonthnarrowsavailabilityto one rowavailabilityreturnslatest_month,earliest_month, and which forces published stop and search each month; withforce, the months that force did and did not publishforcesincludes British Transport Police (btp) and each force's known coveragegaps; typed errors:force_required,unknown_force,invalid_filter
ukcrime_search_crimes tool
area:point(lat,lng; a 1-mile radius),polygon(3–2,500 vertices),location(location_id),neighbourhood(force+neighbourhood_id), orforce_unplaced(force: the crimes it could not place); optionalmonth(default: latest published),month_from(a range of up to 12 months ending atmonth), andcategory(default: every category)Returns
total,by_category,by_outcome, up to 10top_locations, and a page ofcrimeswith thepersistent_idthatukcrime_get_crime_outcomestakes;limitdefaults to 25 (max 200),next_offsetcontinuesWith
month_from, the totals, breakdowns andtop_locationscover the range,by_monthgives each month's total, and rows page across it, oldest month firstFailures carry a typed
reason, such asmonth_not_published,unknown_category, orarea_too_large; a range that cannot be fetched whole in one call failsrange_incomplete(call again: fetched months stay cached) orrange_too_large
ukcrime_search_outcomes tool
Same areas as
ukcrime_search_crimesexceptforce_unplaced;monthis when the outcome was recorded, andcategorycounts only outcomes for crimes in that categoryReturns
total,unfiltered_total,by_outcome(code, name, count),by_crime_month, and a page ofoutcomes, each with its crime;limitdefaults to 20 (max 200)Police outcomes only: court results are not published, and the result says when a force publishes no outcomes
ukcrime_get_crime_outcomes tool
persistent_ids: 1–25 64-character ids fromukcrime_search_crimesorukcrime_search_outcomes; anti-social behaviour records carry noneEach crime comes back with every outcome, oldest first;
history_available: falsemarks a crime with no published historyUnknown ids land in
not_foundand failed lookups infailed, so one bad id never fails the batch
ukcrime_search_stops tool
area:point,polygon,location,neighbourhood, orforce(every stop the force published, placed or not); optionalmonthandmonth_from, as onukcrime_search_crimes; up to 8filtersontype,self_defined_ethnicity,officer_defined_ethnicity,outcome,object_of_search,legislation,age_range,gender,outcome_linked_to_object_of_search, orremoval_of_more_than_outer_clothing(the last two take'true','false'or'(not recorded)'; a JSON boolean also works)Returns
total,unfiltered_total,unplaced,force_published(whether every force found for the area — the one it names, or those located at a point, a polygon's centre and outermost vertices, or a location's map point — published stop and search that month, or every month of a range), a breakdown per filterable field, and a page ofstops;limitdefaults to 15 (max 200)With
month_from,by_monthgives each month's total and its ownforce_published, so a month a force skipped reads as a gap, not a drop; the notice lists the months each force skippedFilters narrow breakdowns and page together, in every month of a range: filter one field and read another's breakdown for a cross-tab
ukcrime_find_neighbourhood tool
Look up by
lat+lngor byforce+neighbourhood_id;includepickspriorities,team,events(the default three) andboundaryReturns the force, the team's description, contact channels and stations, current priorities, every upcoming event the force published (soonest first), and the boundary as a polygon string the search tools accept
Team entries carry only rank and name; biographies and personal contact details are not returned. A point outside coverage returns
found: falsewithguidance
Features
Built on @cyanheads/mcp-ts-core: stdio and Streamable HTTP transports, pluggable auth (none / jwt / oauth), swappable storage (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), structured logging with optional OpenTelemetry tracing.
data.police.uk-specific:
Every location is an anonymised map point covering at least eight addresses, never the place a crime or stop happened; outputs label it that way
Validates locally what the upstream would silently widen: unknown categories, months outside the window, forces matching no id or name (
Leicestershire Policeworks as well asleicestershire), and area fields the chosen area does not useOmitting
monthsearches the latest published month and echoes it; per-force coverage gaps are dated and named in each result'snoticeA 12-month trend of crimes or stop and search is one call for most areas:
month_fromfetches the months two at a time and caches each for 15 minutes, so paging and a repeat send no area request; a large area that runs short of time failsrange_incomplete, and the repeat fetches only the months still missingRequests are paced process-wide under data.police.uk's rate limit, with retries and in-process caches
Agent-friendly output:
Every response carries the Open Government Licence attribution; all tools except
ukcrime_list_referenceadd adata_noteon what the records can and cannot sayTotals and breakdowns cover every matched record;
limit/offset/next_offsetpage the rowsTyped failure reasons with a recovery hint, and a
noticethat separates "no data published" from "nothing happened"
Known limitations
Only the latest 36 months are served; longer trends need the bulk archive at data.police.uk, which this server does not read
A range covers at most 12 months, and
ukcrime_search_outcomestakes one month at a time: an outcomes month not recently asked for can take data.police.uk 10–53 s to answerGreater Manchester Police publishes no crime, outcome or stop and search data; searches there return only other forces' or British Transport Police records
Northern Ireland has no outcomes and no stop and search, and every crime other than anti-social behaviour carries the placeholder outcome
Under investigationCourt outcomes from June 2019 onward are not published
data.police.uk can refuse an area holding more than about 10,000 records, whatever the category, and the point radius is fixed at one mile; dense city centres need polygons smaller than a borough
The API reports which forces published stop and search each month, but not which supplied crime data, so a force that skipped a month shows as a thin or empty area; a low or zero count can mean missing data
Many stops carry a blank outcome, reported as
(not recorded); stopdatetimeis UTC while months follow UK local time
Getting started
Public Hosted Instance
A public instance is available at https://uk-police-crime.caseyjhand.com/mcp — no installation required. Point any MCP client at it via Streamable HTTP:
{
"mcpServers": {
"uk-police-crime-mcp-server": {
"type": "streamable-http",
"url": "https://uk-police-crime.caseyjhand.com/mcp"
}
}
}Self-Hosted / Local
Add the following to your MCP client configuration file.
{
"mcpServers": {
"uk-police-crime-mcp-server": {
"type": "stdio",
"command": "bunx",
"args": ["@cyanheads/uk-police-crime-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}Or with npx (no Bun required):
{
"mcpServers": {
"uk-police-crime-mcp-server": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@cyanheads/uk-police-crime-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}Or with Docker:
{
"mcpServers": {
"uk-police-crime-mcp-server": {
"type": "stdio",
"command": "docker",
"args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT_TYPE=stdio", "ghcr.io/cyanheads/uk-police-crime-mcp-server:latest"]
}
}
}For Streamable HTTP, set the transport and start the server:
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
# Server listens at http://localhost:3010/mcpPrerequisites
Bun v1.4.0 or higher (or Node.js v24+).
No API key: data.police.uk is open and keyless.
Installation
Clone the repository:
git clone https://github.com/cyanheads/uk-police-crime-mcp-server.gitNavigate into the directory:
cd uk-police-crime-mcp-serverInstall dependencies:
bun installConfiguration
The server reads no environment variables of its own. It paces requests process-wide under data.police.uk's limit of 15 per second (burst 30); calls queue for their turn. When data.police.uk rate-limits the server, a Retry-After that leaves the call time for another attempt is waited out there; a longer one, or a rate limit that outlasts the retries, fails the call as rate_limited, naming the wait. A busy queue in this server fails as pacer_shed, an outage as upstream_unavailable, and a call that runs out of time as retry_deadline_exceeded; all four are retryable, and the hint names the wait when one is known.
Variable | Description | Default |
| Transport: |
|
| HTTP server port. |
|
| HTTP session mode: |
|
| Authentication: |
|
| Log level ( |
|
| Directory for log files (Node.js only). |
|
| Storage backend: |
|
| Enable OpenTelemetry. |
|
See .env.example for the common framework overrides.
Running the server
Local development
Build and run the production version:
# One-time build bun run rebuild # Run the built server bun run start:http # or bun run start:stdioRun checks and tests:
bun run devcheck # Lints, formats, type-checks, and more bun run test # Runs the test suite
Project structure
Directory | Purpose |
| Tool definitions ( |
| data.police.uk client: request pacer, retries, caches, upstream schemas, record normalization, dated coverage-gaps table. |
| Unit and integration tests. |
Development guide
See CLAUDE.md for development guidelines and architectural rules. The short version:
Handlers throw, framework catches — no
try/catchin tool logicUse
ctx.logfor logging,ctx.statefor storageRegister new tools in
allToolDefinitions(src/mcp-server/tools/definitions/index.ts), whichsrc/index.tspasses tocreateApp()Wrap external API calls: validate raw → normalize to domain type → return output schema; never fabricate missing fields
Contributing
Issues are welcome. Run checks and tests before submitting:
bun run devcheck
bun run testLicense
This project is licensed under the Apache 2.0 License. See the LICENSE file for details.
Data: Contains public sector information licensed under the Open Government Licence v3.0. Source: data.police.uk. Every tool response carries this attribution; storing, caching and redistributing the data are permitted with it. This project is independent of the Home Office, data.police.uk and the police forces.
This server cannot be deployed
Maintenance
Related MCP Connectors
Access street-level crime reports, stop and search data, and outcomes across UK police forces
UK Police MCP — wraps the UK Police Data API (free, no auth)
UK administrative boundary data from OS Boundary Line: name search, point lookup, GeoJSON geometries
Search UK planning applications, entities, datasets, and conservation areas
Related MCP Servers
- AlicenseNot gradedqualityAmaintenanceExposes the FBI Crime Data Explorer API — crime estimates, agency offense rates, and LEOKA officer safety data via MCP. Supports STDIO or Streamable HTTP transport.276 npm1Apache 2.0
- AlicenseNot gradedqualityAmaintenanceGeocode places, fetch global weather forecasts, ERA5 historical climate, marine conditions, air quality, and terrain elevation via MCP. Provides 11 tools over STDIO or Streamable HTTP.683 npm8Apache 2.0
- AlicenseNot gradedqualityAmaintenanceEnables querying UNHCR refugee, IDP, stateless populations, asylum decisions, returns, and resettlement statistics via MCP, including staging large datasets for SQL queries. Supports stdio or Streamable HTTP transports.212 npm1Apache 2.0
- AlicenseNot gradedqualityBmaintenanceEnables searching DataCite datasets and software, fetching DOI metadata, tracing relations, and formatting citations via MCP, supporting stdio or Streamable HTTP.1Apache 2.0