uk-police-crime-mcp-server

v0.1.1 pre-1.0

Search UK street-level crime, police outcomes, stop and search, and neighbourhood teams from data.police.uk: England, Wales and Northern Ireland forces, plus British Transport Police (Scotland's only coverage), via MCP. STDIO or Streamable HTTP.

uk-police-crime.caseyjhand.com/mcp
claude mcp add --transport http uk-police-crime-mcp-server https://uk-police-crime.caseyjhand.com/mcp
codex mcp add uk-police-crime-mcp-server --url https://uk-police-crime.caseyjhand.com/mcp
{
  "mcpServers": {
    "uk-police-crime-mcp-server": {
      "url": "https://uk-police-crime.caseyjhand.com/mcp"
    }
  }
}
gemini mcp add --transport http uk-police-crime-mcp-server https://uk-police-crime.caseyjhand.com/mcp
{
  "mcpServers": {
    "uk-police-crime-mcp-server": {
      "command": "bunx",
      "args": [
        "mcp-remote",
        "https://uk-police-crime.caseyjhand.com/mcp"
      ]
    }
  }
}
{
  "mcpServers": {
    "uk-police-crime-mcp-server": {
      "type": "http",
      "url": "https://uk-police-crime.caseyjhand.com/mcp"
    }
  }
}
curl -X POST https://uk-police-crime.caseyjhand.com/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"curl","version":"1.0.0"}}}'

Tools

6

ukcrime_list_reference

open-world

Decode the vocabulary the other ukcrime tools take: police force ids, crime category slugs, a force's neighbourhood ids, and data availability — the published months, and which forces published stop and search in each. Use it when a force, category, neighbourhood id or month is unknown; name_contains filters forces and neighbourhoods by name. The Metropolitan Police has about 680 neighbourhoods, so filter by name there; to get the force and neighbourhood covering a latitude and longitude, call ukcrime_find_neighbourhood instead.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "ukcrime_list_reference",
    "arguments": {
      "topic": "<topic>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "topic": {
      "type": "string",
      "enum": [
        "forces",
        "categories",
        "availability",
        "neighbourhoods"
      ],
      "description": "What to list: 'forces' (force ids), 'categories' (crime category slugs), 'availability' (published months and stop-and-search publication), or 'neighbourhoods' (one force's neighbourhood ids; needs force)."
    },
    "force": {
      "description": "Force id such as 'leicestershire'; trimmed, lower-cased, spaces and underscores become hyphens. Required for topic 'neighbourhoods'. On 'availability', where 'btp' (British Transport Police) is also accepted, adds the months this force did and did not publish stop and search. Not accepted on 'forces' or 'categories'.",
      "type": "string",
      "maxLength": 100,
      "pattern": "^[a-z]+(-[a-z]+)*$"
    },
    "name_contains": {
      "description": "Words that must all appear in the name, in any order — case, accents and punctuation ignored, so the value needs at least one letter or digit. Topics 'forces' and 'neighbourhoods' only.",
      "type": "string",
      "maxLength": 100
    },
    "month": {
      "description": "Month as YYYY-MM. Topic 'availability' only: return just that month's row.",
      "type": "string",
      "pattern": "^\\d{4}-(0[1-9]|1[0-2])$"
    }
  },
  "required": [
    "topic"
  ],
  "additionalProperties": false
}
view source ↗

ukcrime_search_crimes

open-world

Search street-level crimes recorded in one month inside an area — a point with a 1-mile radius, a polygon, a snapped location_id from an earlier result, or a police neighbourhood — or list the crimes a force could not place on the map (area 'force_unplaced'). Returns the total, counts by category and by latest police outcome, the busiest map points, and a page of crimes, each with the persistent_id that ukcrime_get_crime_outcomes takes; for what police resolved in a month, whenever the crime was recorded, use ukcrime_search_outcomes. Locations are anonymised map points, not crime sites. data.police.uk can refuse an area holding more than about 10,000 crimes, whatever the category — then search smaller polygons.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "ukcrime_search_crimes",
    "arguments": {
      "area": "<area>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "area": {
      "type": "string",
      "enum": [
        "point",
        "polygon",
        "location",
        "neighbourhood",
        "force_unplaced"
      ],
      "description": "Area to search: 'point' (lat and lng; a 1-mile radius), 'polygon' (polygon), 'location' (location_id), 'neighbourhood' (force and neighbourhood_id), or 'force_unplaced' (force alone: the crimes that force could not place on the map). An area field the chosen area does not use is rejected."
    },
    "lat": {
      "description": "Latitude, WGS84 decimal degrees, for area 'point'.",
      "type": "number",
      "minimum": -90,
      "maximum": 90
    },
    "lng": {
      "description": "Longitude, WGS84 decimal degrees, for area 'point'.",
      "type": "number",
      "minimum": -180,
      "maximum": 180
    },
    "polygon": {
      "description": "For area 'polygon': 3–2,500 vertices as { lat, lng } objects, or the string 'lat,lng:lat,lng:…'. The ring closes itself (the last vertex joins the first). [lng, lat] pairs are not accepted.",
      "minItems": 3,
      "maxItems": 2500,
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "lat": {
            "type": "number",
            "minimum": -90,
            "maximum": 90,
            "description": "Latitude, WGS84 decimal degrees."
          },
          "lng": {
            "type": "number",
            "minimum": -180,
            "maximum": 180,
            "description": "Longitude, WGS84 decimal degrees."
          }
        },
        "required": [
          "lat",
          "lng"
        ],
        "additionalProperties": false,
        "description": "One polygon vertex as { lat, lng }."
      }
    },
    "location_id": {
      "description": "For area 'location': location.location_id from an earlier result — one anonymised map point.",
      "type": "string",
      "pattern": "^\\d{1,12}$"
    },
    "force": {
      "description": "Force id such as 'leicestershire' (ukcrime_list_reference topic 'forces' lists them); trimmed, lower-cased, spaces and underscores become hyphens. For area 'neighbourhood' (with neighbourhood_id) and 'force_unplaced' (alone), where 'btp' (British Transport Police) is also accepted.",
      "type": "string",
      "maxLength": 100,
      "pattern": "^[a-z]+(-[a-z]+)*$"
    },
    "neighbourhood_id": {
      "description": "For area 'neighbourhood', with force: a neighbourhood id from ukcrime_list_reference topic 'neighbourhoods', or from ukcrime_find_neighbourhood for a point. Case-sensitive; only trimmed.",
      "type": "string",
      "maxLength": 100,
      "pattern": "^[^/\\\\?#]+$"
    },
    "month": {
      "description": "Month as YYYY-MM. Omitted: the latest published month, echoed as month in the result.",
      "type": "string",
      "pattern": "^\\d{4}-(0[1-9]|1[0-2])$"
    },
    "category": {
      "description": "Crime category slug such as 'burglary', or its display name such as 'Violence and sexual offences'; case-insensitive, and spaces, underscores and hyphens match each other ('vehicle_crime' finds 'vehicle-crime'). Omitted: every category (all-crime). ukcrime_list_reference topic 'categories' lists them.",
      "type": "string",
      "maxLength": 100
    },
    "limit": {
      "default": 25,
      "description": "Crimes on this page, 1–200. Default 25.",
      "type": "integer",
      "minimum": 1,
      "maximum": 200
    },
    "offset": {
      "default": 0,
      "description": "Rows to skip before this page; pass next_offset from the previous result. Default 0.",
      "type": "integer",
      "minimum": 0,
      "maximum": 9007199254740991
    }
  },
  "required": [
    "area",
    "limit",
    "offset"
  ],
  "additionalProperties": false
}
view source ↗

ukcrime_search_outcomes

open-world

List the police outcomes recorded in one month inside an area — a point with a 1-mile radius, a polygon, a location_id, or a neighbourhood — for crimes recorded that month or any earlier one. Returns the total, counts by outcome and by the month each crime was recorded, and a page of outcomes, each with its crime and persistent_id. Use it for what police resolved in a month; ukcrime_search_crimes gives the latest outcome of the crimes recorded in a month instead. Court results are not published; the result says when a force publishes no outcomes.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "ukcrime_search_outcomes",
    "arguments": {
      "area": "<area>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "area": {
      "type": "string",
      "enum": [
        "point",
        "polygon",
        "location",
        "neighbourhood"
      ],
      "description": "Area to search: 'point' (lat and lng; a 1-mile radius), 'polygon' (polygon), 'location' (location_id), or 'neighbourhood' (force and neighbourhood_id). An area field the chosen area does not use is rejected."
    },
    "lat": {
      "description": "Latitude, WGS84 decimal degrees, for area 'point'.",
      "type": "number",
      "minimum": -90,
      "maximum": 90
    },
    "lng": {
      "description": "Longitude, WGS84 decimal degrees, for area 'point'.",
      "type": "number",
      "minimum": -180,
      "maximum": 180
    },
    "polygon": {
      "description": "For area 'polygon': 3–2,500 vertices as { lat, lng } objects, or the string 'lat,lng:lat,lng:…'. The ring closes itself (the last vertex joins the first). [lng, lat] pairs are not accepted.",
      "minItems": 3,
      "maxItems": 2500,
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "lat": {
            "type": "number",
            "minimum": -90,
            "maximum": 90,
            "description": "Latitude, WGS84 decimal degrees."
          },
          "lng": {
            "type": "number",
            "minimum": -180,
            "maximum": 180,
            "description": "Longitude, WGS84 decimal degrees."
          }
        },
        "required": [
          "lat",
          "lng"
        ],
        "additionalProperties": false,
        "description": "One polygon vertex as { lat, lng }."
      }
    },
    "location_id": {
      "description": "For area 'location': location.location_id from an earlier result — one anonymised map point.",
      "type": "string",
      "pattern": "^\\d{1,12}$"
    },
    "force": {
      "description": "For area 'neighbourhood', with neighbourhood_id: a force id such as 'leicestershire' (ukcrime_list_reference topic 'forces' lists them); trimmed, lower-cased, spaces and underscores become hyphens.",
      "type": "string",
      "maxLength": 100,
      "pattern": "^[a-z]+(-[a-z]+)*$"
    },
    "neighbourhood_id": {
      "description": "For area 'neighbourhood', with force: a neighbourhood id from ukcrime_list_reference topic 'neighbourhoods', or from ukcrime_find_neighbourhood for a point. Case-sensitive; only trimmed.",
      "type": "string",
      "maxLength": 100,
      "pattern": "^[^/\\\\?#]+$"
    },
    "month": {
      "description": "Month as YYYY-MM. Omitted: the latest published month, echoed as month in the result.",
      "type": "string",
      "pattern": "^\\d{4}-(0[1-9]|1[0-2])$"
    },
    "category": {
      "description": "Count only outcomes for crimes in this category: a slug such as 'burglary' or a display name such as 'Violence and sexual offences'; case-insensitive, and spaces, underscores and hyphens match each other ('vehicle_crime' finds 'vehicle-crime'). Omitted or 'all-crime': every category. ukcrime_list_reference topic 'categories' lists them.",
      "type": "string",
      "maxLength": 100
    },
    "limit": {
      "default": 20,
      "description": "Outcomes on this page, 1–200. Default 20.",
      "type": "integer",
      "minimum": 1,
      "maximum": 200
    },
    "offset": {
      "default": 0,
      "description": "Rows to skip before this page; pass next_offset from the previous result. Default 0.",
      "type": "integer",
      "minimum": 0,
      "maximum": 9007199254740991
    }
  },
  "required": [
    "area",
    "limit",
    "offset"
  ],
  "additionalProperties": false
}
view source ↗

ukcrime_get_crime_outcomes

open-world

Fetch the full police outcome history of up to 25 crimes by persistent_id — the 64-character id on crimes from ukcrime_search_crimes and ukcrime_search_outcomes. Returns each crime with every outcome, oldest first, and lists the ids data.police.uk does not hold. Anti-social behaviour records carry no persistent id.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "ukcrime_get_crime_outcomes",
    "arguments": {
      "persistent_ids": "<persistent_ids>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "persistent_ids": {
      "description": "1–25 persistent ids from the persistent_id field of ukcrime_search_crimes or ukcrime_search_outcomes results. A comma- or space-separated string is also accepted; ids are lower-cased and repeats dropped.",
      "minItems": 1,
      "maxItems": 25,
      "type": "array",
      "items": {
        "type": "string",
        "pattern": "^[0-9a-f]{64}$",
        "description": "A 64-character hexadecimal persistent id."
      }
    }
  },
  "required": [
    "persistent_ids"
  ],
  "additionalProperties": false
}
view source ↗

ukcrime_search_stops

open-world

Search police stop and search records for one month inside an area — a point with a 1-mile radius, a polygon, a location_id from an earlier result, or a police neighbourhood — or across a whole force with area 'force', which includes stops the force could not place. Returns the total and counts by search type, self-defined and officer-defined ethnicity, outcome, object of search, legislation, age range and gender, plus a page of stops. filters narrow the counts and the page together, so filtering one field and reading another's breakdown gives a cross-tab. Forces skip months and some publish none; the result says when the force did not publish for the month.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "ukcrime_search_stops",
    "arguments": {
      "area": "<area>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "area": {
      "type": "string",
      "enum": [
        "point",
        "polygon",
        "location",
        "neighbourhood",
        "force"
      ],
      "description": "Area to search: 'point' (lat and lng; a 1-mile radius), 'polygon' (polygon), 'location' (location_id), 'neighbourhood' (force and neighbourhood_id), or 'force' (force alone: every stop the force published for the month, placed or not). An area field the chosen area does not use is rejected."
    },
    "lat": {
      "description": "Latitude, WGS84 decimal degrees, for area 'point'.",
      "type": "number",
      "minimum": -90,
      "maximum": 90
    },
    "lng": {
      "description": "Longitude, WGS84 decimal degrees, for area 'point'.",
      "type": "number",
      "minimum": -180,
      "maximum": 180
    },
    "polygon": {
      "description": "For area 'polygon': 3–2,500 vertices as { lat, lng } objects, or the string 'lat,lng:lat,lng:…'. The ring closes itself (the last vertex joins the first). [lng, lat] pairs are not accepted.",
      "minItems": 3,
      "maxItems": 2500,
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "lat": {
            "type": "number",
            "minimum": -90,
            "maximum": 90,
            "description": "Latitude, WGS84 decimal degrees."
          },
          "lng": {
            "type": "number",
            "minimum": -180,
            "maximum": 180,
            "description": "Longitude, WGS84 decimal degrees."
          }
        },
        "required": [
          "lat",
          "lng"
        ],
        "additionalProperties": false,
        "description": "One polygon vertex as { lat, lng }."
      }
    },
    "location_id": {
      "description": "For area 'location': location.location_id from an earlier result — one anonymised map point.",
      "type": "string",
      "pattern": "^\\d{1,12}$"
    },
    "force": {
      "description": "Force id such as 'leicestershire' (ukcrime_list_reference topic 'forces' lists them); trimmed, lower-cased, spaces and underscores become hyphens. For area 'neighbourhood' (with neighbourhood_id) and 'force' (alone), where 'btp' (British Transport Police) is also accepted.",
      "type": "string",
      "maxLength": 100,
      "pattern": "^[a-z]+(-[a-z]+)*$"
    },
    "neighbourhood_id": {
      "description": "For area 'neighbourhood', with force: a neighbourhood id from ukcrime_list_reference topic 'neighbourhoods', or from ukcrime_find_neighbourhood for a point. Case-sensitive; only trimmed.",
      "type": "string",
      "maxLength": 100,
      "pattern": "^[^/\\\\?#]+$"
    },
    "month": {
      "description": "Month as YYYY-MM. Omitted: the latest published month, echoed as month in the result.",
      "type": "string",
      "pattern": "^\\d{4}-(0[1-9]|1[0-2])$"
    },
    "filters": {
      "description": "Up to 8 filters, all of which a stop must match; they narrow the counts and the page together. Fields: type, self_defined_ethnicity, officer_defined_ethnicity, outcome, object_of_search, legislation, age_range, gender.",
      "maxItems": 8,
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "field": {
            "type": "string",
            "enum": [
              "type",
              "self_defined_ethnicity",
              "officer_defined_ethnicity",
              "outcome",
              "object_of_search",
              "legislation",
              "age_range",
              "gender"
            ],
            "description": "Stop field to match."
          },
          "value": {
            "description": "Value to match exactly, case-insensitively, as the breakdowns show it; '(not recorded)' matches stops with no value.",
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          }
        },
        "required": [
          "field",
          "value"
        ],
        "additionalProperties": false,
        "description": "Keep only stops whose field equals value."
      }
    },
    "limit": {
      "default": 15,
      "description": "Stops on this page, 1–200. Default 15.",
      "type": "integer",
      "minimum": 1,
      "maximum": 200
    },
    "offset": {
      "default": 0,
      "description": "Rows to skip before this page; pass next_offset from the previous result. Default 0.",
      "type": "integer",
      "minimum": 0,
      "maximum": 9007199254740991
    }
  },
  "required": [
    "area",
    "limit",
    "offset"
  ],
  "additionalProperties": false
}
view source ↗

ukcrime_find_neighbourhood

open-world

Find the police force and neighbourhood policing team for a point, or look one up by force and neighbourhood id. Returns the team's description, contact channels and police stations, its current priorities with the action taken, team members' ranks and names, and upcoming engagement events. include adds the boundary polygon, needed only to split a neighbourhood too large to search into smaller polygons: ukcrime_search_crimes, ukcrime_search_outcomes and ukcrime_search_stops take the neighbourhood directly through area 'neighbourhood'.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "ukcrime_find_neighbourhood",
    "arguments": {}
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "lat": {
      "description": "Latitude, WGS84 decimal degrees, to look up by point (with lng).",
      "type": "number",
      "minimum": -90,
      "maximum": 90
    },
    "lng": {
      "description": "Longitude, WGS84 decimal degrees, to look up by point (with lat).",
      "type": "number",
      "minimum": -180,
      "maximum": 180
    },
    "force": {
      "description": "Force id such as 'leicestershire' (ukcrime_list_reference topic 'forces' lists them), to look up by id (with neighbourhood_id); trimmed, lower-cased, spaces and underscores become hyphens.",
      "type": "string",
      "maxLength": 100,
      "pattern": "^[a-z]+(-[a-z]+)*$"
    },
    "neighbourhood_id": {
      "description": "Neighbourhood id from ukcrime_list_reference topic 'neighbourhoods', to look up by id (with force). Case-sensitive; only trimmed.",
      "type": "string",
      "maxLength": 100,
      "pattern": "^[^/\\\\?#]+$"
    },
    "include": {
      "description": "Optional sections to load: 'priorities', 'team', 'events', 'boundary'. Omitted: priorities, team and events. [] loads none of them.",
      "maxItems": 4,
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "priorities",
          "team",
          "events",
          "boundary"
        ],
        "description": "An optional section to load."
      }
    }
  },
  "additionalProperties": false
}
view source ↗