io.github.cyanheads/cpsc-recalls-mcp-server

US consumer product recalls from the CPSC — hazards, remedies, and affected products.

E-CommerceTypeScriptv0.2.1

@cyanheads/cpsc-recalls-mcp-server

Search and retrieve US consumer product recalls from the CPSC (Consumer Product Safety Commission) via MCP. STDIO or Streamable HTTP.

3 Tools

Version License Docker MCP SDK npm TypeScript Bun

Install in Claude Desktop Install in Cursor Install in VS Code

Framework


Overview

Consumer product recalls from the CPSC saferproducts.gov database — toys, electronics, furniture, appliances, children's products, tools, and clothing. Search recalls by product, brand, retailer, or hazard, fetch full detail for a specific recall number, or pull a recent-recalls feed for a date window. Runs as a stdio process, a local Streamable HTTP server, or the public hosted endpoint above.

Tools

ToolDescription
cpsc_search_recallsSearch consumer product recalls by title, product name, brand, retailer, importer, distributor, hazard, remedy, or description keyword, with optional date filtering and offset paging
cpsc_get_recallFull detail for a single recall by recall number — hazards, remedy, products, injuries, images, and the official CPSC page
cpsc_get_recentFetch the most recent recalls ordered newest-first, scoped to a configurable date window

CPSC jurisdiction is consumer products only — food/drugs (FDA), motor vehicles/tires (NHTSA), boats (USCG), and pesticides (EPA) are not in this database; every response carries a jurisdiction note.

Capability reference

cpsc_search_recalls tool

  • Optional substring filters — product_name, manufacturer, retailer, importer, distributor, title_search, description_search, remedy (free-text instructions, not the remedy_options enum) — all combine with AND; title_search is usually highest-signal, hazard_search matches hazard text, product names, or remedy instructions client-side (the upstream Hazard parameter never matches, so it isn't exposed)
  • Two independent date axes: date_start/date_end bound the recall issue date, updated_start/updated_end bound the date CPSC last published it; all four must be real calendar dates and a reversed range throws invalid_date_range
  • Client-side limit (1–200, default 20) and offset (default 0) applied after the full upstream fetch; total_found counts after hazard_search and before offset/limit narrow the window, has_more is the paging signal, truncated is limit-only
  • Returns hazard descriptions, remedy options and instructions, products, UPCs, manufacturer/importer/retailer/distributor names, images, cpsc_url, and per-recall data_quality_notes; manufacturer and importer render as separate roles — try importer, retailer, or distributor when manufacturer comes back empty
  • no_results is a typed error, not an empty array; upstream_rejected (non-retryable) relays CPSC's own rejection, upstream_error (retryable) covers transient outages

cpsc_get_recall tool

  • Accepts modern 5-digit recall numbers (e.g. "25043") and historical 1998–2001 records with letter suffixes (e.g. "99003a")
  • Returns the complete record — full description, all hazard and remedy detail, every product variant, UPCs, incident/injury narrative, manufacturer/importer/retailer/distributor names, country of manufacture, images, and coordinated-agency recall URLs
  • description is nullable — a small number of genuine CPSC records carry no description text, and model numbers are usually embedded there rather than in a structured field
  • Manufacturer and importer render under separate headings so role attribution survives into content[]
  • data_quality_notes records gaps in the upstream record (absent description, hazard text, or product entries); empty when nothing is missing
  • not_found when the recall number doesn't exist — resolve one via cpsc_search_recalls or cpsc_get_recent first; upstream_rejected (non-retryable) relays CPSC's own rejection, upstream_error (retryable) covers transient outages

cpsc_get_recent tool

  • Look-back window of 1–365 days (default 30), always applied — without one the upstream API returns 9,800+ records
  • limit (1–100, default 20) and offset (default 0) page through total_found; narrowing days cannot page further back — the window is anchored to today, so shrinking it drops the oldest records rather than advancing past the newest
  • has_more is the paging signal; truncated stays limit-only and doesn't move with offset
  • Returns a lightweight record per recall — number, date, title, hazards, remedy types, product names, cpsc_url — plus data_quality_notes for gaps CPSC left empty
  • Use cpsc_get_recall to retrieve full detail for any result

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.

CPSC-specific:

  • Full client for the CPSC saferproducts.gov public recalls API — search, single-recall detail, and a recent-recalls feed
  • Client-side filtering (hazard_search) applied over the complete upstream result set so total_found and has_more stay accurate
  • Handles both modern 5-digit recall numbers and historical 1998–2001 records with letter suffixes
  • Jurisdiction boundary documented in every response — flags food, vehicle, and drug recalls as out of scope before an agent misattributes them

Agent-friendly output:

  • Provenance on every response — source_note, cpsc_url, and (CPSC source text) blockquote labels distinguish relayed CPSC narrative from the server's own guidance
  • Pagination discriminators — total_found, offset, has_more, and truncated on every search/recent response so agents can tell when results are clipped and page with offset
  • data_quality_notes on every response — gaps observed in the upstream record (missing hazard text, no product entries), derived from which fields CPSC left blank rather than any judgment call
  • Jurisdiction note (cpsc_jurisdiction) on every response — lets agents route callers to the correct agency (FDA, NHTSA, USCG, EPA) when a product is out of scope

Getting started

Public Hosted Instance

A public instance is available at https://cpsc-recalls.caseyjhand.com/mcp — no installation required. Point any MCP client at it via Streamable HTTP:

{
  "mcpServers": {
    "cpsc-recalls-mcp-server": {
      "type": "streamable-http",
      "url": "https://cpsc-recalls.caseyjhand.com/mcp"
    }
  }
}

Self-Hosted / Local

Add the following to your MCP client configuration file.

{
  "mcpServers": {
    "cpsc-recalls-mcp-server": {
      "type": "stdio",
      "command": "bunx",
      "args": ["@cyanheads/cpsc-recalls-mcp-server@latest"],
      "env": {
        "MCP_TRANSPORT_TYPE": "stdio",
        "MCP_LOG_LEVEL": "info"
      }
    }
  }
}

Or with npx (no Bun required):

{
  "mcpServers": {
    "cpsc-recalls-mcp-server": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@cyanheads/cpsc-recalls-mcp-server@latest"],
      "env": {
        "MCP_TRANSPORT_TYPE": "stdio",
        "MCP_LOG_LEVEL": "info"
      }
    }
  }
}

Or with Docker:

{
  "mcpServers": {
    "cpsc-recalls-mcp-server": {
      "type": "stdio",
      "command": "docker",
      "args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT_TYPE=stdio", "ghcr.io/cyanheads/cpsc-recalls-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/mcp

Prerequisites

  • Bun v1.4.0 or higher (or Node.js v24+).
  • No API key required — the CPSC public recalls API is freely accessible.

Installation

  1. Clone the repository:
git clone https://github.com/cyanheads/cpsc-recalls-mcp-server.git
  1. Navigate into the directory:
cd cpsc-recalls-mcp-server
  1. Install dependencies:
bun install
  1. Configure environment:
cp .env.example .env
# edit .env if needed — no required API keys

Configuration

All configuration is validated at startup via Zod schemas. Key environment variables:

VariableDescriptionDefault
MCP_TRANSPORT_TYPETransport: stdio or httpstdio
MCP_HTTP_PORTHTTP server port3010
MCP_HTTP_HOSTHTTP server host127.0.0.1
MCP_HTTP_ENDPOINT_PATHHTTP endpoint path/mcp
MCP_PUBLIC_URLPublic origin for TLS-terminating reverse-proxy deployments—
MCP_SESSION_MODESession handling: auto, stateful, or stateless. The server declares stateless in src/index.ts; setting this overrides it.stateless
MCP_AUTH_MODEAuthentication: none, jwt, or oauthnone
MCP_LOG_LEVELLog level (debug, info, warning, error)info
LOGS_DIRDirectory for log files (Node.js only)<project-root>/logs
STORAGE_PROVIDER_TYPEStorage backend: in-memory, filesystem, supabase, cloudflare-kv/r2/d1in-memory
OTEL_ENABLEDEnable OpenTelemetryfalse

No server-specific API keys are required. See .env.example for the full list of optional 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:stdio
    # or
    bun run start:http
    
  • Run checks and tests:

    bun run devcheck  # Lint, format, typecheck, security
    bun run test      # Vitest test suite
    

Docker

docker build -t cpsc-recalls-mcp-server .
docker run --rm -p 3010:3010 cpsc-recalls-mcp-server

The Dockerfile defaults to HTTP transport, stateless session mode, and logs to /var/log/cpsc-recalls-mcp-server. OpenTelemetry peer dependencies are installed by default — build with --build-arg OTEL_ENABLED=false to omit them.

Project structure

DirectoryPurpose
src/index.tscreateApp() entry point — registers tools and inits the CPSC service
src/mcp-server/toolsTool definitions (*.tool.ts). Three tools: search, get-recall, get-recent
src/services/cpsc-recallCPSC recall service — API client, types, normalization

Development guide

See CLAUDE.md / AGENTS.md for development guidelines and architectural rules. The short version:

  • Handlers throw, framework catches — no try/catch in tool logic
  • Use ctx.log for request-scoped logging, ctx.state for tenant-scoped storage
  • Register new tools in the createApp() arrays in src/index.ts
  • 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 test

License

Apache-2.0 — see LICENSE for details.

Installation

Source-derived launch command. Check the maintainer’s required arguments and credentials before running:

bash
npx -y @cyanheads/cpsc-recalls-mcp-server

Set up in your AI client

Merge this template into ~/Library/Application Support/Claude/claude_desktop_config.json. Keep existing servers. Add any arguments, credentials, and permissions required by the maintainer; this template has not been install-tested.

json
{
  "mcpServers": {
    "io-github-cyanheads-cpsc-recalls-mcp-server": {
      "command": "npx",
      "args": [
        "-y",
        "@cyanheads/cpsc-recalls-mcp-server"
      ]
    }
  }
}

Restart Claude Desktop completely for changes to take effect. Confirm the server appears connected in the client’s tool list, then try a read-only example from its documentation.

Claude Desktop setup reference

Package

@cyanheads/cpsc-recalls-mcp-servernpm

Compatible MCP Clients

io.github.cyanheads/cpsc-recalls-mcp-server works with any MCP-compatible client. Copy the config snippet from the Configuration section above and add it to the file shown for your client, then restart the application.

  • Claude Desktop~/Library/Application Support/Claude/claude_desktop_config.jsonRestart Claude Desktop completely for changes to take effect.
  • Cursor~/.cursor/mcp.jsonRestart Cursor for changes to take effect.
  • VS Code.vscode/mcp.jsonReload VS Code window for changes to take effect.
  • Windsurf~/.codeium/windsurf/mcp_config.jsonRestart Windsurf for changes to take effect.
  • Claude Code.mcp.jsonSave at the project root, then start Claude Code in that project and review the MCP server approval prompt. Keep real credentials out of shared files.

Learn More