Household Recall Watch

io.github.MGrinv0.2.0更新於 Oct 2, 2026

Household recall checks over public CPSC, openFDA, EMA and EU Safety Gate data. No API key.

概覽

AI 產生的概覽

讓助理查詢美國與歐盟公開的產品、食品與藥品召回資料,並維護家庭關注清單以檢查召回情況。

功能
透過八個工具存取公開的政府召回資料來源:CPSC 與 openFDA 器材召回、openFDA 食品召回、openFDA 藥品與 EMA 藥品短缺警報,以及歐盟 Safety Gate 危險產品警報。本機家庭關注清單支援新增、列出與刪除,check_my_household 會用清單中的物品對照召回資料來源進行檢查。每筆結果都會正規化,包含來源、標題、危害、補救措施、日期、產品清單、官方公告連結,以及一句簡短的口播句子。
適用情境
適合讓助理回答某款嬰兒車、食品或藥品是否被召回,或主動用個人家庭物品清單對照近期召回公告進行檢查。它面向家庭安全類問題,而非通用的產品調查。
執行需求
以本機程序執行;需要 Node 20 或更新版本,或使用 Docker。召回資料無需 API 金鑰或帳號。OpenAI 相容金鑰或本機 Ollama 端點僅在需要模型驅動的助理路徑時使用,腳本模式無需金鑰。存取政府召回資料來源需要網路連線。
安裝前請注意
召回與短缺資料可能不完整或延遲,專案聲明其不構成醫療或安全建議;使用者應查看所連結的官方公告,並在採取行動前諮詢專業人士。關注清單儲存在本機檔案中。若使用可選的模型路徑,會讀取 OPENAI_API_KEY 環境變數,請求會傳送至所設定的模型服務商。

安裝

在 SourceWeft 中

  1. 開啟 儀表板中的 Household Recall Watch,將其新增到工作區。
  2. 為需要使用其工具的對話啟用該服務。

Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。

其他 MCP 客戶端

參照 儲存庫 中的啟動說明。

README

Recall Radar — an MCP server and voice front end for "has anything in my house been recalled?"

A self-hosted Model Context Protocol server that lets a voice assistant answer household safety questions from public government recall data:

  • US product recalls (CPSC) and home medical-device recalls (FDA),
  • US food recalls (FDA),
  • US medicine recalls (FDA) and EU medicine shortages (European Medicines Agency),
  • EU dangerous-product alerts (EU Safety Gate, formerly RAPEX),
  • and a household watchlist the assistant checks proactively: "has anything I own been recalled since the summer?"

Every answer carries a short spoken sentence for the voice reply, plus structured detail with the source URL and date of every item. No API keys, no accounts, no LLM needed to run it.

Built for the Amazon Developer Hackathon, Alexa+ track. MCP spec 2025-11-25, Streamable HTTP, stateless, @modelcontextprotocol/sdk 1.30.1.

This is a simulated smart-display experience backed by a real MCP server; it has not been connected to an Alexa+ device. See the judge-run guide, the verification record and the friction log.

Not medical or safety advice. Recall and shortage data can be incomplete or late. Always check the linked notice, and ask a pharmacist, doctor or the manufacturer before acting. Never stop a prescribed medicine on your own.

Run it (one command)

Node 20 or newer. One process serves the MCP server at /mcp, the voice UI at / and the agent at POST /api/ask:

sh
npm ci && npm start# recall-radar MCP server: http://127.0.0.1:3000/mcp (health: /healthz, ...)# Recall Radar voice UI:   http://127.0.0.1:3000/  (agent: POST /api/ask, model: Scripted mode, no LLM)

Judge path.

  1. No key, scripted mode. npm start, open http://127.0.0.1:3000/. The badge reads Scripted mode, no LLM: a fixed phrase-to-tool mapping stands in for the model, but the MCP calls and the recall data are real and live. Try the suggestion chips, or drive it from the URL: http://127.0.0.1:3000/?q=Watch%20my%20crib%20mattress&q=Has%20anything%20in%20my%20house%20been%20recalled%3F
  2. With a model. Set OPENAI_API_KEY outside the repository and run npm start. The badge identifies the selected model (gpt-6-luna by default). This path was run live on 2026-10-02 with that model; other models were not exercised (EVIDENCE.md).

Or Docker:

sh
docker build -t recall-radar-mcp . && docker run --rm -p 3000:3000 recall-radar-mcpdocker run --rm -p 3000:3000 -e OPENAI_API_KEY recall-radar-mcp   # with a model
envdefaultmeaning
PORT3000HTTP port
HOST127.0.0.1 (0.0.0.0 in Docker)bind address
WATCHLIST_PATH./data/watchlist.jsonwhere the household watchlist is stored
ALLOWED_ORIGINS(none)extra browser Origins allowed besides localhost (comma-separated, * for any)
MODEL_PROVIDERopenai if OPENAI_API_KEY is set, else scriptedopenai, ollama or scripted
OPENAI_API_KEY(none)OpenAI key; never logged or sent to the browser
OPENAI_MODELgpt-6-lunaany Chat Completions model with function calling
OPENAI_REASONING_EFFORTnonesent as reasoning_effort; GPT-6 models refuse function tools on Chat Completions without none. Set omit for a model that rejects the field
OPENAI_BASE_URLhttps://api.openai.com/v1for an OpenAI-compatible gateway
OLLAMA_URL / OLLAMA_MODELhttp://127.0.0.1:11434 / llama3.1Ollama's OpenAI-compatible endpoint (/v1/chat/completions)
MCP_URLthis process's own /mcppoint the agent at another Recall Radar MCP server

Endpoints: POST /mcp (MCP Streamable HTTP, stateless: no session id, no GET stream), GET /healthz, GET / (the UI), POST /api/ask ({"q": "...", "history": [...]}), GET /api/config, GET /api/watchlist. A stdio entry is also included: npm run start:stdio.

The stdio entry is listed in the MCP Registry as io.github.MGrin/household-recall-watch (server.json). Its package is an MCPB bundle attached to the GitHub release; npm run pack:mcpb rebuilds it and prints the SHA-256 that server.json must carry. The bundle keeps its watchlist at ~/.recall-radar/watchlist.json. Publishing is .github/workflows/publish-mcp.yml: on a v* tag (or gh workflow run publish-mcp.yml --ref vX.Y.Z) it checks the release bundle against server.json, then publishes with GitHub OIDC. Attach the bundle to the GitHub release before the workflow runs.

If your machine reaches the internet only through an HTTP proxy, Node's built-in fetch ignores HTTPS_PROXY unless you set NODE_USE_ENV_PROXY=1 (Node 24+). That applies to the OpenAI calls too.

The voice front end (a simulated smart display)

The track allows "a simulated Alexa+ experience in a web app"; this is ours, named Recall Radar and using no third-party marks.

  • Push to talk: hold the mic button (or the space bar) and speak; it uses the browser's Web Speech API (SpeechRecognition). Where that is missing the mic is disabled and the text box does the same job. Microphone behavior still needs a real-device check.
  • Spoken reply through speechSynthesis, with a mute toggle; a light bar along the bottom edge shows listening, thinking and speaking. Speaker output still needs a real-device check.
  • Tool trace: a chip for every MCP tool the agent called, with its argument and latency, so a viewer can see the answer came from the MCP server.
  • Recall cards: source, date, title, hazard, remedy, the watchlist item it matched, and a link to the official notice.
  • Household watchlist panel (add with the + box or by voice, remove with ×; both go through the agent), and a transcript.
  • ?q=... asks on load; repeat it (?q=a&q=b) for a scripted walkthrough, and add mute=1 for silent capture.

The agent (src/agent/)

agent.ts is a real MCP client (SDK Client + StreamableHTTPClientTransport): per question it connects to /mcp, lists the tools, maps them to the model's function format and runs the tool-call loop, capped at 6 model steps. Tool results go back to the model as the tool's structuredContent. A ModelAdapter (types.ts) is one method: messages + tools in, text or tool calls out.

adapterstatus
openai (openai.ts)OpenAI Chat Completions with tools over plain fetch, no SDK. Unit-tested against recorded-shape responses, including a 3-step tool loop. Run against the live API with gpt-6-luna on 2026-10-02 (EVIDENCE.md).
ollamaThe same class pointed at Ollama's OpenAI-compatible endpoint. Untested: Ollama was not installed on the build machine.
scripted (scripted.ts)Deterministic: a few phrasings map to one tool call, and the reply is the tool's own spoken sentence (first item only). Used by the tests and the no-key demo.

The system prompt (prompt.ts) keeps answers voice-first (two or three sentences: hazard, official remedy, what to do now), grounds every claim in a tool result, and forbids medical advice beyond the official remedy text.

Try it

MCP Inspector (no LLM key needed):

sh
npx @modelcontextprotocol/inspector

In the Inspector UI choose transport Streamable HTTP, URL http://127.0.0.1:3000/mcp, Connect, then Tools → List Tools and call any tool.

From the terminal, with the bundled SDK client:

sh
npm run call                                                   # list toolsnpm run call -- search_product_recalls '{"query":"stroller","limit":3}'npm run call -- watchlist_add '{"name":"crib mattress","kind":"product"}'npm run call -- check_my_household '{}'

Tools

Eight tools. search_eu_product_recalls was added in v0.2.0, after the demo video was recorded; the video shows and says seven.

toolwhat it answerssources
search_product_recalls"Has my stroller / space heater / crib been recalled?"CPSC, openFDA device
search_food_recalls"Is there a recall on peanut butter?" "Any listeria recalls?"openFDA food
search_medicine_alerts"Is my insulin recalled or short?" (region: us, eu, all)openFDA drug, EMA shortages
watchlist_add"Keep an eye on my Graco stroller." (kind: product, food, medicine, any)local file
watchlist_list"What am I watching?"local file
watchlist_remove"Stop watching the stroller."local file
search_eu_product_recalls (v0.2.0)"Was my USB charger recalled in the EU?" By product, brand, model or barcode (default: last 28 days, at most the 12 latest weekly reports)EU Safety Gate
check_my_household"Has anything in my house been recalled?" (default: last 180 days)CPSC, openFDA, EMA, by item kind (not Safety Gate)

Each tool declares a zod input schema and an outputSchema; results come back as structuredContent (validated by the SDK) and as text. Each recall is normalised to:

ts
{ source, id, title, hazard, remedy, date /* YYYY-MM-DD */, products[], url, summary /* one spoken sentence */ }

If one upstream is down, the others still answer and the result lists a warning; if every source for a question is down, the tool returns a clean MCP tool error (isError: true). Every upstream request has a 10-second timeout (20 seconds for Safety Gate). The EMA file is cached for an hour in memory, because EMA rate-limits repeated downloads. Safety Gate publishes one weekly report every Friday, each about 200-300 KB and 3-6 seconds to serve (2026-10-02): a search reads at most 12 reports, four at a time, caches the index for an hour and each published report for the life of the process. A cold four-week search took about 5 seconds; a repeat answers from the cache. When the window holds more than 12 reports, since in the answer is the oldest report actually read.

Data sources and terms

sourceendpointterms
US Consumer Product Safety Commissionsaferproducts.gov/RestWebServices/RecallUS government work, public domain
openFDA enforcement reports (food, drug, device)api.fda.gov/{food,drug,device}/enforcement.jsonpublic domain / CC0 per open.fda.gov/license; openFDA's own disclaimer: do not rely on it for medical-care decisions. Keyless use is rate-limited (terms).
European Medicines Agency, medicine shortages catalogueema.europa.eu/en/documents/report/shortages-output-json-report_en.jsonreuse permitted with EMA acknowledged as the source (legal notice); every EMA item carries its EMA URL
European Commission, EU Safety Gate weekly reportsec.europa.eu/safety-gate-alerts/api/download/weeklyReport/{list,detail}/xml/…reuse authorised if the alerts' meaning is not distorted and the source is acknowledged in the Commission's exact words (disclaimer, "Reuse of alerts", current revision 2026-06-11). Every answer carries that sentence as attribution and in its text; every item links its official alert

openFDA has no per-recall web page, so an FDA item's url is the openFDA API query that returns exactly that recall (search=recall_number:"…").

The Safety Gate attribution, verbatim: "Alerts from the Rapid Alert System for dangerous non-food products, published free of charge on the Safety Gate website (https://ec.europa.eu/safety-gate-alerts) © European Union, 2005 – 2026". The Commission also notes that brands in the alerts may have been used by the economic operators without the owner's permission.

Pre-existing code adapted

src/sources/ema.ts adapts our own earlier code from the ema-medicines-watch Apify Actor (same author): the EMA dd/mm/yyyy date parser, the {meta, data[]} shape check and the truncated-file guard. src/sources/safetygate.ts adapts the eu-recall-watchlist Apify Actor (same author): the Safety Gate XML parser settings, CDATA handling, the run-together measures field and the verbatim attribution. Everything else was written for this entry.

Development

sh
npm run build        # tsc -> dist/npm test             # vitest, against recorded fixtures in test/fixtures (no network)LIVE=1 npm test      # also runs test/live.test.ts against the real APIs

test/agent.test.ts runs the agent loop with the scripted adapter against the real MCP server over HTTP (upstreams mocked to fixtures), plus the /api/* routes and the static UI. test/openai.test.ts runs the OpenAI adapter against a mocked Chat Completions endpoint, alone and inside a multi-step agent loop.

The end-to-end test starts the HTTP server, connects with the SDK Client over StreamableHTTPClientTransport, checks that protocol 2025-11-25 is negotiated, lists the tools and calls every one, with upstream fetch routed to fixtures.

Licence

MIT, © 2026 Nikita Grishin Limited. See LICENSE. Friction log: FRICTION.md.

來源:README.md,提交 fbaff1d

工具

0
工具後設資料尚未被收錄。

版本歷史

1
  1. v0.2.0最新Oct 2, 2026