
Wraps the FBI Crime Data Explorer API so Claude can pull UCR estimates, LEOKA officer safety stats, and agency-level offense data. Four tools are live (crime estimates, agency offenses, LEOKA, arson), hitting the CDE summarized and LEOKA endpoints. Eight legacy tools (arrests, hate crimes, NIBRS, agency search) are decommissioned and return errors pointing to the FBI's bulk CSV downloads. You get per-100k rates and raw counts by month for violent crime, property crime, robbery, burglary, larceny, motor vehicle theft, arson, assault, rape, and homicide at national, state, or agency scope. Built on the mcp-ts-core framework with STDIO and Streamable HTTP transports. Requires an api.data.gov key, though DEMO_KEY works for light exploration.
Exposes the FBI Crime Data Explorer API — monthly UCR offense rates and counts by national, state, or agency scope, plus LEOKA officer-safety statistics via MCP. STDIO or Streamable HTTP.
Public Hosted Server: https://fbi-crime.caseyjhand.com/mcp
FBI Crime Data Explorer (CDE) data — monthly UCR offense rates, counts, population denominators and reporting coverage by national, state, or agency scope, plus Law Enforcement Officers Killed and Assaulted (LEOKA) statistics. Two tools are callable. Nine legacy tools remain visible with disabled reasons on the HTTP operator landing page; they are absent from MCP discovery and cannot be called. No resources are registered. Runs as a stdio process, a local Streamable HTTP server, or the public hosted endpoint above.
| Tool | Description |
|---|---|
fbi_get_crime_estimates | Monthly UCR offense rates and counts by national, state, or agency scope, from the CDE summarized endpoint |
fbi_get_leoka | Officer fatality, weapon, and circumstance data (LEOKA) by month or year-to-date |
fbi_get_arson | [DISABLED] Use fbi_get_crime_estimates with offense="arson" |
fbi_search_agencies | [DISABLED] UCR agency search backend decommissioned |
fbi_get_agency | [DISABLED] UCR agency profile backend decommissioned |
fbi_get_arrests | [DISABLED] UCR arrests backend decommissioned |
fbi_get_hate_crimes | [DISABLED] UCR hate crimes backend decommissioned |
fbi_get_human_trafficking | [DISABLED] UCR human trafficking backend decommissioned |
fbi_get_nibrs_breakdown | [DISABLED] UCR NIBRS breakdown backend decommissioned |
fbi_get_participation | [DISABLED] UCR and CDE participation backends decommissioned |
fbi_list_code_table | [DISABLED] UCR code table backend decommissioned |
The nine unavailable entries above are disabled operator records, not callable tools. Visit FBI CDE for retired datasets.
fbi_get_crime_estimates toolscope: national (no identifiers), state (only state_abbr), or agency (only ori). State identifiers require two ASCII letters; ORIs require nine ASCII letters/digits. Case is normalized to uppercase; DC, PR and codes such as DCMPD0000 are accepted without a membership whitelist.offense: violent-crime, property-crime, robbery, burglary, larceny, motor-vehicle-theft, arson, aggravated-assault, rape, or homicide. Set from_year/from_month through to_year/to_month (2000–2030; months default to January and December). The complete start must be no later than the end.entity_name, per-100k rates, raw actual/clearance counts and nullable population, participated_population, population_coverage_percent for each offense month. Coverage is the provider's percentage for that exact entity/month; missing coverage stays null, even when populations are equal or comparison entities report coverage. Participation may be incomplete. Denominator-only months do not create offense rows.data_last_updated and max_data_date retain provider dates when supplied.scope_param_missing, scope_param_incompatible, invalid_date_range, no_data. Invalid identifier grammar is rejected locally; well-formed unknown identifiers can return no_data, whose recovery asks to verify the identifier before offense/date availability.fbi_get_leoka toolperiod: ytd (year-to-date; omit month) or monthly (requires month, 1–12); year is required (2000–2030)totals preserves the six fields from incidents_victim_officer_totals_ytd: total_officers, total_incidents, total_officers_dod, total_officers_doi, total_incidents_dod, and total_incidents_doi. This CDE YTD block appears in both periods; it is not a combined-fatality count or a requested-month total. The upstream dod/doi abbreviations have unspecified interpretation.total_officers_felonious with total_officers_dod, total_officers_accidental with total_officers_doi, total_incidents_felonious with total_incidents_dod, and total_incidents_accidental with total_incidents_doi. The four aliases are removed; values are unchanged.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, and weather_conditions.offender_demographic and officer_demographic map demographic dimensions to category counts. officer_death_by_time_of_day maps killing types to literal hour keys (including -1); officer_death_by_month maps killing types to years to month counts, retaining comparison years and zeros.deaths_by_region and optional deaths_by_year retain explicitly classified killing types such as Felonious/Accidental. Monthly responses omit the annual series and provide month-specific breakdowns alongside the separate CDE YTD totals block.month_required (monthly period without month), month_not_applicable (YTD with month), no_dataBuilt 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/) for national, state, and per-agency offense rates and counts across 10 offense types/cde/leoka/) for officer fatality, weapon, and circumstance data by month or year-to-dateDEMO_KEY works without registration (shared, rate-limited pool); a registered api.data.gov key raises throughputAgent-friendly output:
null rather than fabricated zerosdata_last_updated and max_data_date echo the CDE's own datesA 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"
}
}
}
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/mcp
DEMO_KEY works for testing but is rate-limited to ~1,000 req/hr from a shared pool.git clone https://github.com/cyanheads/fbi-crime-mcp-server.git
cd fbi-crime-mcp-server
bun install
cp .env.example .env
# edit .env and set FBI_API_KEY
All configuration is validated at startup via Zod schemas in src/config/server-config.ts. Key environment variables:
| Variable | Description | Default |
|---|---|---|
FBI_API_KEY | Required. api.data.gov API key for the FBI CDE API. Use DEMO_KEY for limited testing. | — |
FBI_API_BASE_UCR | Override UCR base URL. | https://api.usa.gov/crime/fbi/ucr |
FBI_API_BASE_CDE | Override CDE base URL. | https://api.usa.gov/crime/fbi/cde |
FBI_REQUEST_TIMEOUT_MS | Per-request timeout in milliseconds. | 15000 |
MCP_TRANSPORT_TYPE | Transport: stdio or http. | stdio |
MCP_HTTP_PORT | Port for HTTP server. | 3010 |
MCP_HTTP_ENDPOINT_PATH | HTTP endpoint path. | /mcp |
MCP_SESSION_MODE | HTTP session mode: stateless, stateful, or auto (resolves to stateful). The server declares stateless in code; set this to override it. | stateless |
MCP_PUBLIC_URL | Public origin override for TLS-terminating reverse-proxy deployments. | none |
MCP_AUTH_MODE | Auth mode: none, jwt, or oauth. | none |
MCP_LOG_LEVEL | Log level (RFC 5424). | info |
MCP_GC_PRESSURE_INTERVAL_MS | Opt-in Bun-only forced-GC pressure interval (ms). Try 60000 if RSS grows under sustained HTTP load. | 0 |
LOGS_DIR | Directory for log files (Node.js only). | <project-root>/logs |
STORAGE_PROVIDER_TYPE | Storage backend: in-memory, filesystem, supabase, cloudflare-kv/r2/d1. | in-memory |
OTEL_ENABLED | Enable OpenTelemetry instrumentation. | false |
OTEL_EXPORTER_OTLP_ENDPOINT | OTLP base URL for trace and metric export; does not enable log export. | none |
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT | Explicit OTLP log export endpoint; the Docker image includes the required optional peers. | none |
See .env.example for the full list of optional overrides.
Build and run:
# One-time build
bun run rebuild
# Run the built server
bun run start:stdio
# or
bun run start:http
Run 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 build -t fbi-crime-mcp-server .
docker run --rm -e FBI_API_KEY=your-key -p 3010:3010 fbi-crime-mcp-server
The 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.
| Directory | Purpose |
|---|---|
src/index.ts | createApp() entry point — registers tools, resources, and inits services. |
src/config | Server-specific environment variable parsing and validation with Zod. |
src/mcp-server/tools | Tool definitions (*.tool.ts). 2 callable tools and 9 disabled operator entries. |
src/mcp-server/resources | Retired agency/state definitions, not registered. |
src/services | FBI CDE API service with response validation and shared retry/timeout logic. |
tests/ | Unit and integration tests mirroring src/. |
See CLAUDE.md for development guidelines and architectural rules. The short version:
try/catch in tool logicctx.log for request-scoped logging, ctx.state for tenant-scoped storagecreateApp() in src/index.ts/cde/summarized/, /cde/leoka/); retired definitions are wrapped with disabledTool and omitted from MCP registrationIssues are welcome. Run checks and tests before submitting:
bun run devcheck
bun run test
Apache-2.0 — see LICENSE for details.
MCP_LOG_LEVELdefault: infoSets the minimum log level for output (e.g., 'debug', 'info', 'warn').
MCP_HTTP_HOSTdefault: 127.0.0.1The hostname for the HTTP server.
MCP_HTTP_PORTdefault: 3010The port to run the HTTP server on.
MCP_HTTP_ENDPOINT_PATHdefault: /mcpThe endpoint path for the MCP server.
MCP_AUTH_MODEdefault: noneAuthentication mode to use: 'none', 'jwt', or 'oauth'.