
Mfds Food Safety
Query Korea MFDS-oriented food safety interview data through the k-skill proxy so agents can answer food-domain Q&A in niche health or compliance apps.
Overview
mfds-food-safety is an agent skill for the Build phase that integrates MFDS food-safety interview queries through the k-skill proxy API.
Install
npx skills add https://github.com/nomadamas/k-skill --skill mfds-food-safetyWhat is this skill?
- Calls MFDS food safety flows via configurable KSKILL_PROXY_BASE_URL (default k-skill-proxy.nomadamas.org)
- build_food_interview helper shapes domain, question, and symptoms fields for API payloads
- HTML-aware summarize_text cleanup for safe plain-text agent responses
- Explicit ApiError type with status_code and url for agent-visible failure handling
- Proxy can be disabled via env values off/false/0 for local-only testing
Adoption & trust: 1.8k installs on skills.sh; 5.4k GitHub stars; 2/3 security scanners passed (skills.sh audits).
What problem does it solve?
You are building a food-safety feature and need a consistent proxy client and payload shape instead of one-off urllib snippets in chat.
Who is it for?
Indie builders shipping Korean-market food, hygiene, or symptom-intake experiences that already rely on the k-skill proxy stack.
Skip if: Global SaaS products with no MFDS or Korea food-regulatory context, or teams that cannot use an external proxy endpoint.
When should I use this skill?
When integrating MFDS food safety or food interview flows via KSKILL_PROXY_BASE_URL during backend or agent tooling work.
What do I get? / Deliverables
Your agent returns summarized, HTML-stripped responses from the proxy-backed food interview API with traceable HTTP errors.
- JSON API responses from the food interview proxy path
- Normalized plain-text summaries suitable for agent user replies
Recommended Skills
Journey fit
External regulator-backed APIs are wired during Build when you integrate real data sources into a product or internal tool. integrations covers HTTP proxy clients and domain-specific API wrappers rather than generic UI work.
How it compares
Skill-packaged HTTP integration, not an MCP server or a licensed clinical decision support product.
Common Questions / FAQ
Who is mfds-food-safety for?
Developers and solo founders building Korea-focused food safety or symptom-guidance features who want agent-driven API calls through k-skill-proxy.
When should I use mfds-food-safety?
Use it in Build integrations while wiring food-domain Q&A; it is not a substitute for Validate landing tests or Launch SEO work.
Is mfds-food-safety safe to install?
Check the Security Audits panel on this Prism page and review what data you send to the proxy, since the skill performs outbound network calls and may handle health-related user input.
SKILL.md
READMESKILL.md - Mfds Food Safety
from __future__ import annotations import argparse import json import os import re import sys import urllib.error import urllib.parse import urllib.request from html import unescape from typing import Any PROXY_BASE_URL_ENV_VAR = "KSKILL_PROXY_BASE_URL" DEFAULT_PROXY_BASE_URL = "https://k-skill-proxy.nomadamas.org" class ApiError(RuntimeError): def __init__(self, message: str, *, status_code: int | None = None, url: str | None = None): super().__init__(message) self.status_code = status_code self.url = url def summarize_text(value: Any) -> str: if value is None: return "" text = unescape(str(value)) text = re.sub(r"<[^>]+>", " ", text) text = re.sub(r"\s+", " ", text).strip() return text def resolve_proxy_base_url(explicit_base_url: str | None = None, env: dict[str, str] | None = None) -> str: env = env or os.environ candidate = summarize_text(explicit_base_url or env.get(PROXY_BASE_URL_ENV_VAR)) if candidate.casefold() in {"off", "false", "0", "disable", "disabled", "none"}: raise ValueError("KSKILL_PROXY_BASE_URL 가 비활성화되어 있습니다.") if candidate and candidate != "replace-me": return candidate.rstrip("/") return DEFAULT_PROXY_BASE_URL def build_food_interview(question: str | None = None, symptoms: str | None = None) -> dict[str, Any]: return { "domain": "food", "question": summarize_text(question), "symptoms": summarize_text(symptoms), "must_ask": [ "누가 먹었거나 먹으려는지 알려주세요. (본인/아이/임산부/고령자)", "무엇을 언제 먹었는지, 얼마나 먹었는지 알려주세요.", "같이 먹은 음식이나 술, 복용 중인 약이 있는지 알려주세요.", "복통·구토·설사·발진 같은 증상이 언제부터 시작됐는지 알려주세요.", "기저질환, 임신 여부, 알레르기 여부를 알려주세요.", ], "red_flags": [ "호흡곤란, 입술·혀 붓기 같은 급성 알레르기 반응", "혈변 또는 검은변", "심한 탈수, 소변 감소, 계속되는 구토", "의식저하, 고열, 심한 복통", ], "urgent_action": "red flag 가 있으면 식품 조회보다 즉시 응급실·119·의료진 연결을 우선하세요.", "policy": "이 helper 는 공식 식품 안전정보 조회 전에 반드시 되묻기 흐름을 제공하며, 먹어도 되는지 단정하지 않습니다.", } def normalize_food_recall_row(row: dict[str, Any]) -> dict[str, Any]: return { "source": "foodsafetykorea_recall", "product_name": summarize_text(row.get("product_name") or row.get("PRDLST_NM") or row.get("PRDTNM")), "company_name": summarize_text(row.get("company_name") or row.get("BSSH_NM") or row.get("BSSHNM")), "reason": summarize_text(row.get("reason") or row.get("RTRVLPRVNS")), "created_at": summarize_text(row.get("created_at") or row.get("CRET_DTM")), "distribution_deadline": summarize_text(row.get("distribution_deadline") or row.get("DISTBTMLMT")), "category": summarize_text(row.get("category") or row.get("PRDLST_TYPE") or row.get("PRDLST_CD_NM")), } def normalize_improper_food_item(item: dict[str, Any]) -> dict[str, Any]: reason_parts = [ summarize_text(item.get("reason") or item.get("IMPROPT_ITM")), summarize_text(item.get("INSPCT_RESULT")), ] return { "source": "mfds_improper_food", "product_name": summarize_text(item.get("product_name") or item.get("PRDUCT")), "company_name": summarize_text(item.get("company_name") or item.get("ENTRPS")), "reason": "; ".join(part for part in reason_parts if part), "created_at": summarize_text(item.get("created_at") or item.get("REGIST_DT")), "category": summarize_text(item.get("category") or item.get("FOOD_TY")), } def filter_food_items(items: list[dict[str, Any]], query: str) -> list[dict[str, Any]]: needle = summarize_text(query).casefold() if not needle: return items product_matches = [item for item in items if needle in summarize_text(item.get("product_name")).casefold()] if product_matches: return product_matches company_matches = [item for item in items if needle in summarize_text(item.get("company_name")).casefold()] if company_