off-upload — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited off-upload (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
This is the recommended process for an LLM agent working through a folder of food packaging photos.
For remote setups, mcp-local-tunnel exposes local servers to remote clients. mcp-aggregator and mcp-auth-wrapper can combine and authenticate them.
00238366) instead of standard EAN-13 — these are valid on OFF. If the scanner returns a short code, trust it rather than assuming it's a misread of a "missing" EAN-13.get_product(barcode) to see what data already exists.upload_image tool description for imagefield guidance. Use the most appropriate imagefield (front, nutrition, ingredients, packaging), and other for additional photos that don't fit a specific panel or where a good display image already exists.Strip EXIF metadata before uploading — especially GPS location. Phone photos embed the GPS coordinates where they were taken (plus device make/model). Uploading these to a public database leaks where the photographer lives/shops. Always strip metadata before upload. Note that sips -r (below) preserves EXIF, so rotating is not enough on its own. If ImageMagick is available, magick in.jpg -rotate <deg> -strip -resize 1400x -quality 82 out.jpg rotates, strips all metadata, and downscales in one pass. To verify a file is clean, magick identify -verbose out.jpg | grep -iE 'gps|exif:' should return nothing. (Beware: on macOS, mdls may report stale cached GPS from Spotlight even after stripping — trust a raw check like identify/exiftool over mdls.)
Rotate images so text reads correctly before uploading. If a photo is rotated (upside down, sideways, etc.), rotate it so text is upright. The magick ... -rotate above handles this; alternatively on macOS sips -r <degrees> "/path/to/photo.jpg" rotates in place clockwise (but does NOT strip EXIF — see above). Common rotations: 180° for upside-down, 90° or 270° for sideways. This makes OCR and human review much easier on the OFF website.
Image upload requires base64 data, not file paths. Encode images via shell-exec-mcp. The recommended pattern using tool-sandbox-mcp:
First, discover the exact tool names (they depend on how MCPs are wired):
const tools = await tool('list_tools', {});
return tools.filter(t => t.name.includes('shell') || t.name.includes('openfoodfacts'));Then encode and upload:
const shell = await tool('tunnel__shell-exec__execute', { // use the shell tool name from list_tools
command: 'base64 -i "/path/to/photo.jpg"',
timeout: 10000
});
const b64 = shell.stdout.replace(/\n/g, '');
const result = await tool('openfoodfacts__upload_image', {
barcode: '1234567890123',
imagefield: 'front',
image_data: b64
});If one photo covers multiple panels (e.g. nutrition AND ingredients on the same side), upload it once then use select_image to assign it to the additional imagefield.
upload_image only stores the raw image; select_image is what assigns it as the displayed front/nutrition/ingredients panel. For a sparse product with no display images, select_image the front too, or it won't show one. Conversely, for an established product that already has good community-selected display images, don't reassign its panels — just upload your photos as additional raw images (upload_image alone) and leave the existing selections alone.
upload_image and select_image also take an lc (language) parameter; pass the product's language (e.g. lc: "de" for German packaging) to keep it consistent with the language used in add_or_edit_product.
add_or_edit_product. See tool description for field guidance. Key points:quantity (net weight, e.g. "400g") and serving_size (e.g. "1 pack (239g)") — they are separate fieldsproduct_name should include the product type if not in the headline (e.g. "Fajita Halloumi" on pack → "Fajita Halloumi Wrap")ingredients_text with underscores: _Wheat_ FlourChicken Breast (25%)"Thailand, United Kingdom"labels for certifications/claims: "High in Protein, FSC, Vegan"< values on nutrition labels, pass as string: "< 0.5"get_product. Open the product page for visual review: https://world.openfoodfacts.org/product/{barcode}Note: get_product returns nutrition under product.nutriments.per_100g.{energy_kcal, fat, proteins, ...} (and .per_serving), not the flat OFF-API energy-kcal_100g shape — don't read back energy-kcal_100g and conclude nothing saved. A nutriscore_grade that has become a letter is also a quick signal that nutrition was accepted.
trash, not rm, so they're recoverable)https://images.openfoodfacts.org/images/products/{path}/{imgid}.400.jpg). The product metadata may take time to update, or may not list images that didn't change the selected display image.00238366) instead of standard EAN-13. These are valid on OFF.salt field expects the salt value from the label, not sodium. Do not confuse the two — entering sodium as salt will understate the value by 2.5×. If a label shows sodium instead of salt, multiply by 2.5 before entering.language parameter (default: en) ensures fields are written/read in the correct language. Without it, writing product_name on a lang=fr product silently writes to product_name_fr.11.9 vs 11.5, 0.3 vs 0.4, 0.11 vs 0.13). Unless the nutrition table is completely clear and legible, crop and enlarge it before transcribing. On macOS: sips -c <height> <width> <input> --cropOffset <y> <x> -s format jpeg -o <output> to crop, then read the cropped image. This avoids incorrect edits that are hard to spot later.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.