By the OCR-AI team
Language models are decent at reading images now, but ask one to pull the allocation number, VAT and line items out of a scanned Hebrew invoice and check them against a spreadsheet, and you'll spend more time correcting it than you saved. A better split is to give the agent a tool that reads the document into structured data, and let the model do the reasoning on top.
This post shows how to connect our remote MCP server to an MCP client like Claude or Cursor, what the tools do, and a few things we learned about running OCR behind MCP.
The server
The endpoint is https://ocr-ai.com/mcp. It uses the Streamable HTTP transport and is stateless: every POST gets one JSON response, with no session and no event stream. Supported protocol versions are 2025-11-25, 2025-06-18, 2025-03-26 and 2024-11-05.
initialize and tools/list work without a key, so a client can discover the tools before you've set anything up. Actually calling a tool needs an API key, sent as a Bearer token.
There are three tools:
| Tool | Arguments | Returns |
|---|---|---|
read_document |
file_url or file_base64; optional pages (e.g. "1-5,8"), hint (auto, invoice, receipt, card, table, text), language (en or he) |
Markdown text, plus structuredContent with the full document object |
check_balance |
none | balance, whether pages expired, a buy_url, the packs |
buy_pages |
pack (p100, p500, p2000), currency (USD or ILS) |
a buy_url for that pack |
Getting a key
Keys come from the agents page. You leave an email, get a sign-in link (no password, no card), and create a key on your account page. The key is shown once, so copy it then. An account's first key starts with 100 free pages.
Keep the key on the server side or in your local client config. Don't put it in a web page or a mobile app.
Configuration
Most MCP clients that support remote servers take a URL and headers. This is the config from our docs:
{
"mcpServers": {
"ocr-ai": {
"type": "http",
"url": "https://ocr-ai.com/mcp",
"headers": {
"Authorization": "Bearer ocrai_sk_…"
}
}
}
}
Replace ocrai_sk_… with your key. In Cursor that block goes in your MCP config file (mcp.json). Clients differ a little in where the file lives and whether they want "type": "http" or another name for the transport, so check your client's own docs if it doesn't pick the server up. The fields that matter are the URL and the Authorization header.
If you want to check the server before wiring up a client, you can call a tool with plain curl:
curl https://ocr-ai.com/mcp \
-H "Authorization: Bearer ocrai_sk_…" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc": "2.0", "id": 1, "method": "tools/call",
"params": {"name": "read_document",
"arguments": {"file_url": "https://example.com/invoice.pdf"}}}'
What a session looks like
Once connected, you can just ask. Something like "read this invoice and tell me the vendor, invoice number and total" with a link to a PDF. The agent calls read_document with file_url and hint: "invoice", and gets back fields like vendor_name, document_number, issue_date and total, each with a normalized value (dates as YYYY-MM-DD, amounts as plain decimals), plus the line-item table and the full text in reading order.
Because the field keys are fixed, the model doesn't have to guess what "סה״כ לתשלום" means. It's total. That makes follow-up work much more reliable: comparing three supplier quotes, checking that a delivery note matches a purchase order, or filling a CSV with one row per invoice.
Fields that were hard to read come back with "uncertain": true. It's worth telling the agent, in your prompt or rules file, to point those out rather than silently using them.
file_url has to be a public https link with no credentials in it, and the download must finish within 20 seconds. For a local file, the client can send file_base64 instead.
Errors are written for the model
This is the design choice we'd recommend to anyone building an MCP server. Problems a model can act on don't come back as protocol errors. They come back as tool results with isError: true, a plain sentence, and a code in structuredContent.error.
The main example is running out of pages. A call is checked before anything is read, and if the account has fewer pages than the call needs, nothing is read and nothing is charged. The tool result says how many pages were needed, how many are left, and includes a buy_url. The agent can hand that link to you, you pay on Stripe's page, and the agent calls again. The link only adds pages to the key's account; it doesn't sign anyone in, so it's safe to pass along.
Other codes the model can handle the same way: missing_key and invalid_key (with a link to get a key), rate_limited, bad_pages, fetch_failed, unsupported_type.
Timeouts
One practical gotcha. A read_document call can cover up to 20 pages, and many pages can take more than a minute. MCP clients stop waiting after a set time; the MCP TypeScript SDK defaults to 60 seconds. If you see timeouts on long PDFs, raise the timeout for that tool if your client allows it, or ask the agent to read in smaller ranges with pages.
Billing on timeouts is spelled out in the docs: if the connection closes before the answer is sent, the call is refunded in full. If the client stops waiting but keeps the connection open, those pages are charged. Failed pages are always given back.
Why remote and not local
A local MCP server that wraps Tesseract is easy to build, and for clean English text it's fine. For Hebrew invoices you'd still own the bidi handling, the layout logic and the field extraction. A remote server means one URL and one header in the config, and the same tool works from any client.
The trade-off is that the document leaves your machine. Our docs and security page say files are read in memory and not stored on the server, but you should make that call for your own documents.
Links
Docs, including every error code: ocr-ai.com/en/docs. There's a Markdown copy at /docs.md you can drop into an agent's context. And if you want to see the output on your own invoice before setting anything up, the browser version reads 5 pages a day free with no signup.
Top comments (0)