vin-ocr
Extracts vehicle identification number (VIN) from images using OCR. Provide a direct image URL to get the VIN.
Instructions
Recognize and extract the VIN from a vehicle image URL using OCR
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| imageUrl | Yes | Direct URL to an image of a vehicle's VIN (photo or scan) |
Implementation Reference
- src/tools/vinOcr.ts:14-97 (handler)The main handler function for the vin-ocr tool. Registers a tool named 'vin-ocr' on the MCP server that accepts an imageUrl, calls the CarsXE VIN OCR API, and returns the formatted result.
export function registerVinOcrTool( server: McpServer, getApiKey: () => string | null, ) { server.tool( "vin-ocr", "Recognize and extract the VIN from a vehicle image URL using OCR", { imageUrl: z .string() .url() .describe("Direct URL to an image of a vehicle's VIN (photo or scan)"), }, async ({ imageUrl }) => { if (!imageUrl) { return { content: [ { type: "text", text: "❌ VIN OCR failed. Image URL is required.", }, ], }; } // POST request with body as imageUrl const apiKey = getApiKey(); console.log("apiKey vinOcr", apiKey ? `***${apiKey.slice(-4)}` : "null"); if (!apiKey) { return { content: [ { type: "text", text: "❌ API key not provided. Please ensure X-API-Key header is set.", }, ], }; } const CARSXE_API_BASE = "https://api.carsxe.com"; const url = `${CARSXE_API_BASE}/vinocr?key=${apiKey}&source=mcp`; let data: CarsXEVinOcrResponse | null = null; try { const response = await fetch(url, { method: "POST", headers: { "Content-Type": "text/plain" }, body: imageUrl, }); if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); data = await response.json(); } catch (error) { return { content: [ { type: "text", text: `❌ VIN OCR failed. ${ error instanceof Error ? error.message : String(error) || "Unknown error." }`, }, ], }; } if (!data || !data.success) { return { content: [ { type: "text", text: `❌ VIN OCR failed. ${data?.message || "Unknown error."}`, }, ], }; } return { content: [ { type: "text", text: formatVinOcrResponse(data), }, ], }; }, ); - src/types/carsxe.ts:236-257 (schema)Type definition for CarsXEVinOcrResponse, defining the shape of the API response including success, vin, box, confidence, candidates, and message fields.
export interface CarsXEVinOcrResponse { success: boolean; vin?: string; box?: { xmin: number; xmax: number; ymin: number; ymax: number; }; confidence?: number; candidates?: Array<{ vin: string; confidence: number; box: { xmin: number; xmax: number; ymin: number; ymax: number; }; }>; message?: string; } - src/MyMCP.ts:40-45 (registration)Registration of the vin-ocr tool in the MyMCP class (Cloudflare Agent-based MCP server).
registerVinOcrTool(this.server, getApiKey); registerGetYearMakeModelTool(this.server, getApiKey); registerDecodeObdCodeTool(this.server, getApiKey); registerRecognizePlateImageTool(this.server, getApiKey); registerGetLienTheftTool(this.server, getApiKey); } - src/index.gcp.ts:56-60 (registration)Registration of the vin-ocr tool in the GCP/Node HTTP server-based MCP server.
registerVinOcrTool(server, getApiKey); registerGetYearMakeModelTool(server, getApiKey); registerDecodeObdCodeTool(server, getApiKey); registerRecognizePlateImageTool(server, getApiKey); registerGetLienTheftTool(server, getApiKey); - Helper formatter that transforms the CarsXEVinOcrResponse into a human-readable markdown string.
export function formatVinOcrResponse( data: import("../types/carsxe.js").CarsXEVinOcrResponse, ): string { if (!data.success) { return `❌ VIN OCR failed. ${data.message || "Unknown error."}`; } if (!data.vin) { return `No VIN detected in the image.`; } const lines = [ `### 🔍 VIN OCR Results`, data.vin ? `**VIN:** ${data.vin}` : "", data.confidence !== undefined ? `**Confidence:** ${(data.confidence * 100).toFixed(1)}%` : "", data.box ? `**Bounding Box:** [xmin: ${data.box.xmin}, xmax: ${data.box.xmax}, ymin: ${data.box.ymin}, ymax: ${data.box.ymax}]` : "", "", data.candidates && data.candidates.length > 0 ? `**Candidates:**\n${data.candidates .map( (c: any) => `- ${c.vin} (${(c.confidence * 100).toFixed(1)}%) [xmin: ${ c.box.xmin }, xmax: ${c.box.xmax}, ymin: ${c.box.ymin}, ymax: ${c.box.ymax}]`, ) .join("\n")}` : "", ]; return lines.filter(Boolean).join("\n"); }