MCP server for the Vision API — give your coding agent OCR and structured extraction over local images and PDFs.
Strong tool definitions with excellent descriptions (194 - 500+ chars, well above baseline 194 avg). All 7 tools have clear, LLM-optimized descriptions explaining WHAT, WHEN, and WHY. Input schemas present for all tools with proper types. However, output schemas are not documented in the source code, LLMs cannot see what fields to expect from responses. Tool names follow verb_noun convention (vision_list, vision_get, vision_analyze, vision_ask, vision_detect, vision_credits, vision_get_task). Parameters are well-described with format guidance (e.g., 'Page selection for PDFs, e.g. 1-5 or 1,3,5'). Error handling is mentioned in descriptions (e.g., 'Failures cost NOTHING') but recovery guidance is implicit rather than explicit in tool definitions. No tool annotations (readOnlyHint, destructiveHint, idempotentHint) detected in source.
Extract structured data from ONE image or PDF (JPEG, PNG, WebP, TIFF, PDF — detected by magic bytes, the extension is ignored). Cost: 1 credit per image, 2 per selected PDF page. Failures cost NOTHING — every non-2xx releases the reservation in full, so a failed call is safe to fix and repeat, and there is no cleanup to do. Choose ONE way to say what you want: - preset — a catalogue name, or "auto" to have the API classify the file first, for free. "auto" is the right default when you do not already know the document type. Do not call vision_list_presets just to guess a preset; the classifier is better at it and free. - schema — your own fields, {"field_name": "what to extract"}. The description IS the prompt: "the invoice number exactly as printed, without the #" extracts better than "invoice number". A schema can be passed alongside a preset to add fields to it. - schema_name — a schema saved in the account's dashboard. Not combinable with the others. Reading what comes back: - Fields the document did not carry are NOT printed as values — they are listed at the end under "Not found". A preset always defines every one of its fields, so an absent value means "this document does not have it", never "the call failed". - A "(mid)" or "(low)" after a value is its confidence; no marker means high. Decide deliberately what to do with a low-confidence number rather than treating it as fact. - Never hardcode a preset's field names from memory. Call vision_get_preset first if the names are going into code. Long documents: leave mode at "auto". The server kills a synchronous request at 60 seconds; this tool then re-submits it to the queue and polls, and you are charged exactly once because the timed-out attempt refunded itself. Pass mode:"async" up front for anything over ~10 pages. One file per call. To process a folder, call this once per file — and if you get too_many_tasks, wait for your own in-flight tasks rather than sleeping.
Ask up to 5 plain-language questions about ONE image or PDF and get answers with a verdict. Cost: 1 credit per image, and 1 per selected PDF page — half what extraction costs on a PDF, because the answer does not scale with the page. The questions themselves are free: asking five costs exactly what asking one costs. Failures cost NOTHING. Reach for this over vision_analyze when the answer is a judgement rather than a field — "is this signed?", "does the delivery address match the billing address?", "is anyone wearing safety equipment?". Reach for vision_analyze when you want values you will store or compute with; asking for a total and then parsing the prose is slower, dearer and less reliable than extracting it. Reading what comes back: branch on `verdict`, never on the prose. - "yes" / "no" — the images settle it. - "uncertain" — a yes/no question the images genuinely do not settle. Treat it as missing information, not as a "no". - "n-a" — the question was not a yes/no question; the answer is in the prose. Each verdict carries a confidence, shown as "(mid)" or "(low)"; no marker means high. Answers are at document level, not per page. Long documents behave exactly as in vision_analyze — leave mode at "auto", or pass "async" up front past ~10 pages.
Output schemas not documented. LLMs cannot see what fields to expect from vision_analyze, vision_ask, vision_detect, or vision_get_task responses. This forces agents to guess at response structure and risks parsing errors.
No tool annotations (readOnlyHint, destructiveHint, idempotentHint) present. All tools are read-only or safe to retry, but this is not declared in the tool definitions. Agents cannot infer idempotency or safety without explicit hints.
Error recovery guidance is implicit in descriptions rather than explicit in error responses. Descriptions mention 'Failures cost NOTHING' and 'safe to fix and repeat', but actual error responses from the tool are not shown in source. Agents need explicit error classification (retryable, user-fixable, fatal) in responses.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | D | 59 | 2026-07-28+ | v2 |
The account's credit balance and its per-bucket breakdown. Free — no credits consumed. Worth checking before a large batch, so you can tell the user up front that 300 files will not fit in the balance rather than stopping half way through with an insufficient_credits error. Buckets are spent in order: subscription, then rollover, then pack, then welcome.
Identify what a file IS without paying to extract it: the presets that could handle it, ranked, each with a confidence and a one-line reason. Cost: 1 credit per 5 calls, whatever the file — detection only ever reads page 1, so a 300-page PDF costs the same as a photo. Four calls in five report 0 credits used. `recommended` is exactly what preset:"auto" would run on this file, so you can probe first and trust the answer. Reach for this when the TYPE is the decision: routing a mixed inbox to different handlers, or refusing to spend two credits a page on a 40-page PDF sight unseen. Do NOT reach for it as a step before every vision_analyze call — if you want the data and do not care which preset produced it, preset:"auto" classifies the same way, for free, in the same request. Using detect first and then analyze is two calls where one would do. If `fallback` is true, nothing matched confidently and the recommendation is the generic preset. That is an honest "unknown", and it is worth surfacing to the user rather than extracting against a shape nobody has confirmed.
The full field list for one preset: every field name, its type, its description, and whether it is always present. Free — no credits, no API key needed. This is the ONLY correct source for a preset's field names. Never write one from memory: the catalogue is versioned and a name you remember from another project may not exist here, and a schema built on a guessed name fails at the point where you are parsing the response rather than at the point where you made it up. Fields marked ★ are present on every response; the rest appear only when the document carries them. A field holding null means the document did not have it, never that the call failed.
Status, and once finished the result, of a queued task. Free — no credits consumed. The result was already paid for when the task was submitted. Call this with the id from a call that was queued — either one you made with mode:"async", or one where polling ran out of time and the answer told you to come back. A task that is still "queued" or "processing" has NOT failed; it is working, and re-submitting the file would be a second charge for the same work. Results are kept for 7 days. After that only the metadata survives and the file has to be sent again.
List the preset catalogue: every named schema, with its field count and what it is for. Free — no credits, no API key needed. Read this when the user asks what document types are supported, or when you need a preset name to put in code. Do NOT call it to pick a preset before an extraction: preset:"auto" classifies the file server-side for free and is better at it than matching a description by eye, and vision_detect costs a fifth of a credit if you want the ranking. Listing 28 presets to guess one is the expensive path to a worse answer. The names here are catalogue entries, not field names. Call vision_get_preset before writing any field name into code.
vision_analyze and vision_ask accept both file_path and file_url but do not document mutual exclusivity or precedence. Descriptions should clarify: 'Pass either file_path (local) or file_url (HTTPS), not both.'
vision_analyze schema parameter is type 'object' with no structure definition. LLMs cannot validate custom field schemas before submission. Should document expected structure: {field_name: string (description)}.