mcp server
fbi-crime-mcp-server
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.
Set up
Runs on your computer. Pick your agent and operating system.
Before you start
- Node.js 20 or newer
{
"mcpServers": {
"fbi-crime-mcp-server": {
"command": "npx",
"args": [
"-y",
"@cyanheads/fbi-crime-mcp-server"
],
"env": {
"FBI_API_KEY": "<your FBI_API_KEY>"
}
}
}
}same format Cursor, GitHub Copilot CLI, Junie, JetBrains AI Assistant, Augment (Auggie CLI), Factory Droid, Kiro, Devin CLI +12
Values in angle brackets are placeholders. Restart Claude Desktop after editing its config.Claude Desktop docs
README
synced Oct 17, 2026Public Hosted Server: https://fbi-crime.caseyjhand.com/mcp
Overview
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.
Tools
| 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.
Capability reference
fbi_get_crime_estimates tool
scope:national(no identifiers),state(onlystate_abbr), oragency(onlyori). State identifiers require two ASCII letters; ORIs require nine ASCII letters/digits. Case is normalized to uppercase; DC, PR and codes such asDCMPD0000are accepted without a membership whitelist.offense:violent-crime,property-crime,robbery,burglary,larceny,motor-vehicle-theft,arson,aggravated-assault,rape, orhomicide. Setfrom_year/from_monththroughto_year/to_month(2000–2030; months default to January and December). The complete start must be no later than the end.- Returns
entity_name, per-100k rates, raw actual/clearance counts and nullablepopulation,participated_population,population_coverage_percentfor 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_updatedandmax_data_dateretain provider dates when supplied.- Typed errors:
scope_param_missing,scope_param_incompatible,invalid_date_range,no_data. Invalid identifier grammar is rejected locally; well-formed unknown identifiers can returnno_data, whose recovery asks to verify the identifier before offense/date availability.
fbi_get_leoka tool
period:ytd(year-to-date; omitmonth) ormonthly(requiresmonth, 1–12);yearis required (2000–2030)totalspreserves the six fields fromincidents_victim_officer_totals_ytd:total_officers,total_incidents,total_officers_dod,total_officers_doi,total_incidents_dod, andtotal_incidents_doi. This CDE YTD block appears in both periods; it is not a combined-fatality count or a requested-month total. The upstreamdod/doiabbreviations have unspecified interpretation.- Totals field migration: replace
total_officers_feloniouswithtotal_officers_dod,total_officers_accidentalwithtotal_officers_doi,total_incidents_feloniouswithtotal_incidents_dod, andtotal_incidents_accidentalwithtotal_incidents_doi. The four aliases are removed; values are unchanged. - 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, andweather_conditions. offender_demographicandofficer_demographicmap demographic dimensions to category counts.officer_death_by_time_of_daymaps killing types to literal hour keys (including-1);officer_death_by_monthmaps killing types to years to month counts, retaining comparison years and zeros.deaths_by_regionand optionaldeaths_by_yearretain explicitly classified killing types such asFelonious/Accidental. Monthly responses omit the annual series and provide month-specific breakdowns alongside the separate CDE YTD totals block.- Structured and text output include every returned category and path. Absent/null sections are omitted, empty maps have no reported entries, null subgroups/counts are unavailable, and zero remains zero.
- Typed errors:
month_required(monthly period withoutmonth),month_not_applicable(YTD withmonth),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 - CDE LEOKA endpoint (
/cde/leoka/) for officer fatality, weapon, and circumstance data by month or year-to-date DEMO_KEYworks without registration (shared, rate-limited pool); a registered api.data.gov key raises throughput- Nine retired tools retain disabled reasons on the HTTP landing page; MCP clients discover only the two 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 zeros data_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/mcp
Prerequisites
- 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.git
- Navigate into the directory:
cd fbi-crime-mcp-server
- Install dependencies:
bun install
- Configure environment:
cp .env.example .env
# edit .env and set FBI_API_KEY
Configuration
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.
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: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
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.
Project structure
| 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/. |
Development guide
See CLAUDE.md for development guidelines and architectural rules. The short version:
- Handlers throw, framework catches — no
try/catchin tool logic - Use
ctx.logfor request-scoped logging,ctx.statefor tenant-scoped storage - Register new tools and resources in the arrays passed to
createApp()insrc/index.ts - Active tools call the CDE API (
/cde/summarized/,/cde/leoka/); retired definitions are wrapped withdisabledTooland omitted from MCP registration - Wrap 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 test
License
Apache-2.0 — see LICENSE for details.