Skip to main content
Glama
domdomegg

openfoodfacts-mcp

Upload product image

upload_image

Upload product images to Open Food Facts database to enhance food information with visual data like ingredients, nutrition labels, and packaging details.

Instructions

Upload a product image to Open Food Facts. Requires OFF_USER_ID and OFF_PASSWORD.

Prefer more photos over fewer. Panels with text (ingredients, nutrition, certifications, recycling instructions) are highest value as OFF can OCR them. Plain sides with just a colour or logo are lowest value but still worth uploading if you have them.

Use the most appropriate imagefield (front, ingredients, nutrition, packaging). Use "other" for additional photos — this uploads without selecting the image as a display image, which is useful when a good display image already exists or for supplementary angles.

The OFF server auto-selects images for front/nutrition/ingredients/packaging on upload unless one is already selected. If you get "status not ok" but a positive imgid, the image uploaded successfully but was not selected (e.g. a display image already exists).

For images on disk, base64-encode them first (e.g. via shell: base64 -i photo.jpg).

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
barcodeYesProduct barcode
imagefieldYesImage type
image_dataYesBase64-encoded image data (JPEG or PNG)
lcNoLanguage code for the image (default: en)en

Implementation Reference

  • The handler function for the upload_image tool. It processes the base64 input, constructs the multipart form data, and submits it to the OFF API.
    async (args) => {
    	const imageBuffer = Buffer.from(args.image_data, 'base64');
    
    	const formData = new FormData();
    	formData.set('code', args.barcode);
    	formData.set('imagefield', `${args.imagefield}_${args.lc}`);
    	formData.set(`imgupload_${args.imagefield}_${args.lc}`, new Blob([imageBuffer]), 'image.jpg');
    
    	const data = await offPostMultipart(config, '/cgi/product_image_upload.pl', formData);
    
    	return jsonResult(data as Record<string, unknown>);
    },
  • The Zod schema defining the input arguments for the upload_image tool.
    const inputSchema = strictSchemaWithAliases(
    	{
    		barcode: z.string().describe('Product barcode'),
    		imagefield: z.enum([
    			'front',
    			'ingredients',
    			'nutrition',
    			'packaging',
    			'other',
    		]).describe('Image type'),
    		image_data: z.string().describe('Base64-encoded image data (JPEG or PNG)'),
    		lc: z.string().default('en').describe('Language code for the image (default: en)'),
    	},
    	{
    		code: 'barcode',
    		type: 'imagefield',
    	},
    );
  • Function to register the 'upload_image' tool with the MCP server.
    export function registerUploadImage(server: McpServer, config: Config): void {
    	server.registerTool(
    		'upload_image',
    		{
    			title: 'Upload product image',
    			description: 'Upload a product image to Open Food Facts. Requires OFF_USER_ID and OFF_PASSWORD.\n\nPrefer more photos over fewer. Panels with text (ingredients, nutrition, certifications, recycling instructions) are highest value as OFF can OCR them. Plain sides with just a colour or logo are lowest value but still worth uploading if you have them.\n\nUse the most appropriate imagefield (front, ingredients, nutrition, packaging). Use "other" for additional photos — this uploads without selecting the image as a display image, which is useful when a good display image already exists or for supplementary angles.\n\nThe OFF server auto-selects images for front/nutrition/ingredients/packaging on upload unless one is already selected. If you get "status not ok" but a positive imgid, the image uploaded successfully but was not selected (e.g. a display image already exists).\n\nFor images on disk, base64-encode them first (e.g. via shell: `base64 -i photo.jpg`).',
    			inputSchema,
    			annotations: {
    				readOnlyHint: false,
    			},
    		},
    		async (args) => {
    			const imageBuffer = Buffer.from(args.image_data, 'base64');
    
    			const formData = new FormData();
    			formData.set('code', args.barcode);
    			formData.set('imagefield', `${args.imagefield}_${args.lc}`);
    			formData.set(`imgupload_${args.imagefield}_${args.lc}`, new Blob([imageBuffer]), 'image.jpg');
    
    			const data = await offPostMultipart(config, '/cgi/product_image_upload.pl', formData);
    
    			return jsonResult(data as Record<string, unknown>);
    		},
    	);
    }
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only indicate write operation (readOnlyHint:false). Description adds critical behavioral context: server auto-selects display images unless already set, specific error state interpretation ('status not ok' with positive imgid means upload succeeded but selection failed), and OCR capabilities. Would be 5 if it mentioned rate limits or idempotency.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Four distinct paragraphs each earning their place: (1) purpose+auth, (2) content value ranking, (3) imagefield selection logic, (4) server behavior+encoding. No redundancy with structured fields. Information-dense but readable, with logical flow from prerequisites → content strategy → execution → troubleshooting.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Sophisticated upload tool with 4 parameters and mutation semantics. Description covers critical output behavior (auto-selection logic, partial success states) despite no output schema. Minor gap: doesn't describe full success response structure, though it clarifies the ambiguous case.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema has 100% coverage (baseline 3). Description adds operational semantics beyond schema: practical base64 encoding example (shell command), selection criteria for imagefield enum (when to use 'other' vs specific fields), and language code context. Elevates from basic schema documentation to implementation guidance.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Opens with specific verb ('Upload') + resource ('product image') + target system ('Open Food Facts'). Clearly distinguishes from siblings like 'select_image' (which selects existing images) by describing the upload creation flow and auto-selection behavior.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides explicit when-to-use logic for each imagefield enum value (front/ingredients/nutrition/packaging vs 'other'). Details value hierarchy (text panels > plain sides) to guide prioritization. Explains prerequisite auth credentials (OFF_USER_ID/OFF_PASSWORD) and encoding requirements.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/domdomegg/openfoodfacts-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server