fbi-crime-mcp-server
This server has a remotely hosted endpoint. Open its connector listing for connection details, authentication requirements, and setup instructions:
Public Hosted Server: https://fbi-crime.caseyjhand.com/mcp
Overview
US crime statistics from the FBI Crime Data Explorer: monthly offense rates and counts with population and reporting coverage, hate crime, arrest, and NIBRS totals at the national, state, or agency level, agency ORI search by state, and statistics on law enforcement officers killed and assaulted (LEOKA).
Tools
Tool | Description |
| Find agencies in one state and their ORI codes from the CDE agency directory; filter by name words, county, and agency type; paged |
| Monthly UCR offense rates and counts by national, state, or agency scope, from the CDE summarized endpoint; optional state and national comparison rates |
| Hate crime incidents by bias motivation and category, plus offense, location, offender, and judicial-district breakdowns, 1991 through the current year |
| Arrest counts by offense, arrestee sex and race, and age group, 1985 through the current year |
| NIBRS victim, offender, weapon, and related-offense breakdowns for one offense, 1991 through the current year |
| Officer fatality, weapon, and circumstance data (LEOKA) by month or year-to-date, 2020 through the current year |
| [DISABLED] Use |
| [DISABLED] UCR agency profile backend decommissioned; use |
| [DISABLED] UCR human trafficking backend decommissioned |
| [DISABLED] UCR and CDE participation backends decommissioned |
| [DISABLED] UCR code table backend decommissioned |
The five unavailable entries above are disabled operator records, not callable tools. Visit FBI CDE for retired datasets.
Related MCP server: mcp-fbi-crime
Capability reference
fbi_search_agencies tool
state_abbr(required, two letters, case-insensitive) selects the state directory. Optional filters run over the whole state:name_contains(every word must appear in the agency name, any order, case-insensitive),county(equals any of the agency's counties, case-insensitive), andagency_type(City,County,Other State Agency,Other,State Police,University or College, orTribal).limit(1–200, default 50) andoffset(default 0) page the matches, sorted by agency name then ORI.Returns
state_abbr,total(matches before paging),offset,next_offset(absent on the last page), andagencies[]withori,agency_name,agency_type,counties,is_nibrs, andnibrs_start_datewhen CDE lists one.A code CDE has no directory for (
AS,MP, or an unrecognized code) returns zero agencies with a notice; anoffsetpast the end returns zero agencies with a notice naming the total.
fbi_get_crime_estimates tool
scope:national(no identifiers),state(onlystate_abbr, two letters), oragency(onlyori, nine letters/digits; find it withfbi_search_agencies). Case is normalized to uppercase.offense:violent-crime,property-crime,robbery,burglary,larceny,motor-vehicle-theft,arson,aggravated-assault,rape, orhomicide. Date range runsfrom_year/from_monththroughto_year/to_month(1985 through the current year; months default to January and December), and the start must not be later than the end. A year after the current UTC year fails withyear_out_of_range; current-year months past the latest data return null values.Returns
entity_name, rates per 100,000 participating population, actual/clearance counts, and nullablepopulation,participated_population, andpopulation_coverage_percentfor each offense month, plusdata_last_updatedandmax_data_datewhen the provider supplies them.include_comparisons(defaultfalse; state or agency scope only) addscomparisons: one entry per comparison entity in CDE's order (an agency's state, then the United States; a state's United States), each{ entity_name, months[] }withyear,month,rate_per_100k,clearance_rate_per_100k,population,participated_population, andpopulation_coverage_percentfor exactly the scoped months. CDE sends no counts for comparison entities, and the list is empty when CDE sends none.Typed errors:
scope_param_missing,scope_param_incompatible(alsoinclude_comparisons: trueon national scope),year_out_of_range,invalid_date_range,no_data(also returned for a well-formed identifier the provider doesn't know).Results return the largest chronological prefix within 24,000 UTF-8 bytes of serialized
{content, structuredContent}, including continuation fields on both surfaces; SDK protocol metadata and JSON-RPC/HTTP framing are excluded. Whennext_from: { from_year, from_month }is present, repeat the call with that start, keeping scope, identifiers, offense,include_comparisonsand the original end date.from/toecho the requested interval andtotal_monthscounts returned rows. Comparison rows stay aligned. A single indivisible month and its metadata that exceed the budget are returned intact withoversize_notice.
fbi_get_hate_crimes tool
scope,state_abbr,ori, and thefrom_year/from_month/to_year/to_monthwindow follow thefbi_get_crime_estimatescontract, with years from 1991 through the current year; counts are summed over the window.bias:all(default) or one of 34 CDE bias codes (11–85, listed on the field). With a code, incidents count that motivation plus multiple-bias incidents that include it.Returns
incident_section(bias,bias_category) andbias_section(offense_type,location_type,offender_race,offender_ethnicity,judicial_district) as category-to-count maps with every category CDE sends, zeros and nulls kept. Thebias_sectiondimensions are counted on different bases and do not sum to one total or to the incident count. Alsobias_labelanddata_last_updatedwhen supplied.An all-zero result succeeds with a
notice, since CDE returns zeros both for no reports and for an unrecognized state or ORI.Typed errors:
scope_param_missing,scope_param_incompatible,year_out_of_range,invalid_date_range.
fbi_get_arrests tool
Scope, identifiers, and window as
fbi_get_hate_crimes, with years from 1985 through the current year.offense:all(default) or one of 46 CDE numeric offense codes (for example30Robbery,11Murder and Nonnegligent Homicide; listed on the field). Word slugs such asrobberyare rejected. Code23(Rape - Not Specified) is excluded because CDE returns only zeros for it.Returns
offense_name,offense_category,offense_breakdown,arrestee_sex,arrestee_race,male_arrests_by_age, andfemale_arrests_by_ageas category-to-count maps, plusoffense_label,data_last_updated, andmax_data_datewhen supplied. Each grouping is tallied separately, so their sums differ.Same all-zero
noticeand typed errors asfbi_get_hate_crimes.
fbi_get_nibrs_breakdown tool
Scope, identifiers, and window as
fbi_get_hate_crimes, with years from 1991 through the current year.offense(required, case-insensitive): one of 71 CDE NIBRS offense codes (for example13AAggravated Assault,120Robbery; listed on the field). There is noall, and23*(Not Specified) is excluded because CDE returns only nulls for it.Returns
victim(age,sex,race,location,ethnicity,relationship),offender(age,sex,race,ethnicity), andoffense_details(weapons,related_offenses) as category-to-count maps with every category CDE sends, zeros and nulls kept, plusoffense_label,data_last_updated, andmax_data_datewhen supplied. Dimensions are tallied separately and do not sum to one total.Typed errors:
scope_param_missing,scope_param_incompatible,year_out_of_range,invalid_date_range,no_data(CDE has no value for any category: an unrecognized state or ORI, no NIBRS data for the window, or an offense with no reported incidents in the window; for an agency,fbi_search_agenciesshowsis_nibrsandnibrs_start_date).
fbi_get_leoka tool
period:ytd(year-to-date; omitmonth) ormonthly(requiresmonth, 1–12);yearis required (2020 through the current year; a later year fails withyear_out_of_range)totalscarriestotal_officers,total_incidents,total_officers_dod,total_officers_doi,total_incidents_dod, andtotal_incidents_doifrom the CDE year-to-date block. It appears for both periods and is not a requested-month total. The upstream does not definedod/doi; they replace the removedtotal_*_feloniousandtotal_*_accidentalfields with the same values.Category-to-count maps:
weapons,officer_activity,lighting_conditions,body_armor_worn,location_of_attack,offender_previously_known_to_agency,offender_prior_mental_illness,offender_prior_relationship,officer_circumstances_time_of_attack,officer_incident_type,officer_type_of_assignment,weather_conditions,offender_demographic,officer_demographic,officer_death_by_time_of_day, andofficer_death_by_month.deaths_by_regionand optionaldeaths_by_yearbreak out killing types such asFelonious/Accidental; monthly responses omit the annual series.deaths_by_yearspans the window CDE returns, which can include years after the requested one, and the current year's entry is year-to-date. Absent sections are omitted, null counts mean unavailable, and zero stays zero.officer_death_by_monthkeeps only months CDE covers, so a fill zero never reads as a count: monthly responses hold just the requested month under each year, and current-year YTD responses hold months through the current UTC month. Past-year YTD responses keep the series as returned.Typed errors:
month_required(monthly period withoutmonth),month_not_applicable(YTD withmonth),year_out_of_range,no_data
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.
FBI CDE-specific:
CDE summarized endpoint (
/cde/summarized/) for national, state, and per-agency offense rates and counts across 10 offense types, with opt-in state and national comparison ratesCDE agency directory (
/cde/agency/byStateAbbr/) for ORI discovery by state, name, county, and agency typeCDE hate-crime (
/cde/hate-crime/), arrest (/cde/arrest/), and NIBRS (/cde/nibrs/) totals at national, state, and agency scope; NIBRS covers 71 offense codesCDE LEOKA endpoint (
/cde/leoka/) for officer fatality, weapon, and circumstance data by month or year-to-dateDEMO_KEYworks without registration (shared, rate-limited pool); a registered api.data.gov key raises throughputFive retired tools retain disabled reasons on the HTTP landing page; MCP clients discover only the six callable tools and no resources
Agent-friendly output:
Typed error reasons carry recovery hints for scope, date and period corrections, or unavailable data
Sparse upstream rates, clearances, counts, populations and coverage surface as
nullrather than fabricated zerosdata_last_updatedandmax_data_dateecho the CDE's own dates
Getting started
Public Hosted Instance
A public instance is available at https://fbi-crime.caseyjhand.com/mcp — no installation required. No API key needed; the instance supplies its own. Point any MCP client at it via Streamable HTTP:
{
"mcpServers": {
"fbi-crime-mcp-server": {
"type": "streamable-http",
"url": "https://fbi-crime.caseyjhand.com/mcp"
}
}
}Self-Hosted / Local
Add the following to your MCP client configuration file. See the FBI CDE API key registration to obtain a key — DEMO_KEY works for exploration but is rate-limited.
{
"mcpServers": {
"fbi-crime-mcp-server": {
"type": "stdio",
"command": "bunx",
"args": ["@cyanheads/fbi-crime-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info",
"FBI_API_KEY": "your-api-key"
}
}
}
}Or with npx (no Bun required):
{
"mcpServers": {
"fbi-crime-mcp-server": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@cyanheads/fbi-crime-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info",
"FBI_API_KEY": "your-api-key"
}
}
}
}Or with Docker:
{
"mcpServers": {
"fbi-crime-mcp-server": {
"type": "stdio",
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "MCP_TRANSPORT_TYPE=stdio",
"-e", "FBI_API_KEY=your-api-key",
"ghcr.io/cyanheads/fbi-crime-mcp-server:latest"
]
}
}
}For Streamable HTTP, set the transport and start the server:
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 FBI_API_KEY=... bun run start:http
# Server listens at http://localhost:3010/mcpPrerequisites
Bun v1.4.0 or higher (or Node.js v24+).
An api.data.gov API key for the FBI CDE API.
DEMO_KEYworks for testing but is rate-limited to ~1,000 req/hr from a shared pool.
Installation
Clone the repository:
git clone https://github.com/cyanheads/fbi-crime-mcp-server.gitNavigate into the directory:
cd fbi-crime-mcp-serverInstall dependencies:
bun installConfigure environment:
cp .env.example .env
# edit .env and set FBI_API_KEYConfiguration
All configuration is validated at startup via Zod schemas in src/config/server-config.ts. Key environment variables:
Variable | Description | Default |
| Required. api.data.gov API key for the FBI CDE API. Use | — |
| Override UCR base URL. |
|
| Override CDE base URL. |
|
| Per-request timeout in milliseconds. |
|
| Transport: |
|
| Port for HTTP server. |
|
| HTTP endpoint path. |
|
| HTTP session mode: |
|
| Public origin override for TLS-terminating reverse-proxy deployments. | none |
| Auth mode: |
|
| Log level (RFC 5424). |
|
| Opt-in Bun-only forced-GC pressure interval (ms). Try |
|
| Directory for log files (Node.js only). |
|
| Storage backend: |
|
| Enable OpenTelemetry instrumentation. |
|
| OTLP base URL for trace and metric export; does not enable log export. | none |
| Explicit OTLP log export endpoint; the Docker image includes the required optional peers. | none |
See .env.example for the full list of optional overrides.
Running the server
Local development
Build and run:
# One-time build bun run rebuild # Run the built server bun run start:stdio # or bun run start:httpRun checks and tests:
bun run devcheck # Lint, format, typecheck, security bun run test # Vitest test suite bun run lint:mcp # Validate MCP definitions against spec
Docker
docker build -t fbi-crime-mcp-server .
docker run --rm -e FBI_API_KEY=your-key -p 3010:3010 fbi-crime-mcp-serverThe Dockerfile defaults to HTTP transport, stateless session mode, and logs to /var/log/fbi-crime-mcp-server. OpenTelemetry peer dependencies are installed by default — build with --build-arg OTEL_ENABLED=false to omit them.
Project structure
Directory | Purpose |
|
|
| Server-specific environment variable parsing and validation with Zod. |
| Tool definitions ( |
| Retired agency/state definitions, not registered. |
| FBI CDE API service with response validation and shared retry/timeout logic. |
| Unit and integration tests mirroring |
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 request-scoped logging,ctx.statefor tenant-scoped storageRegister new tools and resources in the arrays passed to
createApp()insrc/index.tsActive tools call the CDE API (
/cde/summarized/,/cde/agency/byStateAbbr/,/cde/hate-crime/,/cde/arrest/,/cde/nibrs/,/cde/leoka/); retired definitions are wrapped withdisabledTooland omitted from MCP registrationWrap FBI 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
Apache-2.0 — see LICENSE for details.
This server cannot be deployed
Maintenance
Related MCP Connectors
UK Police MCP — wraps the UK Police Data API (free, no auth) https://data.police.uk/api
FBI Crime Data Explorer MCP — UCR/NIBRS statistics
Query SEC EDGAR filings, XBRL financials, and company data via MCP. STDIO or Streamable HTTP.
Chicago and Milwaukee crime data: query incidents, stats, and map-linked MCP UI resources.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceAccess FEC campaign finance data through MCP. Query data about candidates, money trails, and election filings. STDIO & Streamable HTTP.203 npm2Apache 2.0
- AlicenseNot gradedqualityCmaintenanceEnables querying FBI crime data using UCR/NIBRS statistics through a simple MCP interface.462 npmMIT
- AlicenseNot gradedqualityBmaintenanceBrowse and query the U.S. Energy Information Administration API v2 — electricity, petroleum, natural gas, coal, forecasts, and more via MCP. STDIO or Streamable HTTP.603 npm3Apache 2.0
- AlicenseNot gradedqualityBmaintenanceSearch and fetch ~800K Federal Reserve economic time-series from the FRED API via MCP, with STDIO or Streamable HTTP transport.115 npm1Apache 2.0