US consumer product recalls from the CPSC — hazards, remedies, and affected products.
Search and retrieve US consumer product recalls from the CPSC (Consumer Product Safety Commission) via MCP. STDIO or Streamable HTTP.
Public Hosted Server: https://cpsc-recalls.caseyjhand.com/mcp
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.
| Tool | Description |
|---|---|
cpsc_search_recalls | Search 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_recall | Full detail for a single recall by recall number — hazards, remedy, products, injuries, images, and the official CPSC page |
cpsc_get_recent | Fetch 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.
cpsc_search_recalls toolproduct_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)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_rangelimit (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-onlycpsc_url, and per-recall data_quality_notes; manufacturer and importer render as separate roles — try importer, retailer, or distributor when manufacturer comes back emptyno_results is a typed error, not an empty array; upstream_rejected (non-retryable) relays CPSC's own rejection, upstream_error (retryable) covers transient outagescpsc_get_recall tool"25043") and historical 1998–2001 records with letter suffixes (e.g. "99003a")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 fieldcontent[]data_quality_notes records gaps in the upstream record (absent description, hazard text, or product entries); empty when nothing is missingnot_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 outagescpsc_get_recent toollimit (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 newesthas_more is the paging signal; truncated stays limit-only and doesn't move with offsetcpsc_url — plus data_quality_notes for gaps CPSC left emptycpsc_get_recall to retrieve full detail for any resultBuilt 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:
hazard_search) applied over the complete upstream result set so total_found and has_more stay accurateAgent-friendly output:
source_note, cpsc_url, and (CPSC source text) blockquote labels distinguish relayed CPSC narrative from the server's own guidancetotal_found, offset, has_more, and truncated on every search/recent response so agents can tell when results are clipped and page with offsetdata_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 callcpsc_jurisdiction) on every response — lets agents route callers to the correct agency (FDA, NHTSA, USCG, EPA) when a product is out of scopeA 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"
}
}
}
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
git clone https://github.com/cyanheads/cpsc-recalls-mcp-server.git
cd cpsc-recalls-mcp-server
bun install
cp .env.example .env
# edit .env if needed — no required API keys
All configuration is validated at startup via Zod schemas. Key environment variables:
| Variable | Description | Default |
|---|---|---|
MCP_TRANSPORT_TYPE | Transport: stdio or http | stdio |
MCP_HTTP_PORT | HTTP server port | 3010 |
MCP_HTTP_HOST | HTTP server host | 127.0.0.1 |
MCP_HTTP_ENDPOINT_PATH | HTTP endpoint path | /mcp |
MCP_PUBLIC_URL | Public origin for TLS-terminating reverse-proxy deployments | — |
MCP_SESSION_MODE | Session handling: auto, stateful, or stateless. The server declares stateless in src/index.ts; setting this overrides it. | stateless |
MCP_AUTH_MODE | Authentication: none, jwt, or oauth | none |
MCP_LOG_LEVEL | Log level (debug, info, warning, error) | info |
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 | false |
No server-specific API keys are required. See .env.example for the full list of optional overrides.
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 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.
| Directory | Purpose |
|---|---|
src/index.ts | createApp() entry point — registers tools and inits the CPSC service |
src/mcp-server/tools | Tool definitions (*.tool.ts). Three tools: search, get-recall, get-recent |
src/services/cpsc-recall | CPSC recall service — API client, types, normalization |
See CLAUDE.md / AGENTS.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() arrays in src/index.tsIssues are welcome. Run checks and tests before submitting:
bun run devcheck
bun run test
Apache-2.0 — see LICENSE for details.
Source-derived launch command. Check the maintainer’s required arguments and credentials before running:
npx -y @cyanheads/cpsc-recalls-mcp-serverMerge 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.
{
"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 referenceio.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.
~/Library/Application Support/Claude/claude_desktop_config.jsonRestart Claude Desktop completely for changes to take effect.~/.cursor/mcp.jsonRestart Cursor for changes to take effect..vscode/mcp.jsonReload VS Code window for changes to take effect.~/.codeium/windsurf/mcp_config.jsonRestart Windsurf for changes to take effect..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.