在 Claude Code 裡做 OCR:接上 space ocr MCP server
把託管的 space ocr MCP server 接進 Claude Code:文件影像變成帶頁面座標的結構化欄位,附一份待複核清單,並可直接歸檔成隨時查詢的資料列。
Claude Code 可以把一張文件影像交給 OCR 服務,拿回帶名字的欄位,而不是一段還要自己解析的文字——發票、收據、名片、證件、各種表單都一樣。入口是 space ocr 的 MCP server:一個託管的端點,註冊一次之後,它的工具就和 Claude Code 內建的工具並列,遇到文件時助理會自己拿來用。
這改變了什麼,值得說清楚。把影像貼進對話是可行的,直到你需要每次都是同一組欄位、需要每個值在頁面上的位置紀錄、需要一個存放結果的地方。而自己架一套——OCR 引擎、解析層、資料庫——確實能解決這些,但也把三套東西的維護留給了你。這裡擷取跑在伺服器端並回傳固定形狀,每個值都帶著它被讀出來的座標,同一個 server 還會把文件歸檔進一個工作區,第二次不必重讀,直接查詢。
接上
server 位於 https://mcp.space-ocr.com/mcp,以 Streamable HTTP 上的 MCP 溝通。沒有東西要安裝,也沒有行程要常駐:你註冊一個 URL,API 金鑰以 bearer 標頭隨每次請求送出。在 Claude Code 裡就是一行指令。
claude mcp add --transport http space-ocr https://mcp.space-ocr.com/mcp \
--header "Authorization: Bearer YOUR_API_KEY"Cursor、VS Code、Windsurf 把同樣的 URL 與標頭寫進 mcp.json。設不了標頭的用戶端——claude.ai、Claude 桌面版、Claude 行動版——把同一個 URL 加成自訂連接器,OAuth 同意頁會讓你選擇要用哪把 API 金鑰。兩條路都一樣:金鑰只用於那一次請求,伺服器端不保存。
{
"mcpServers": {
"space-ocr": {
"url": "https://mcp.space-ocr.com/mcp",
"headers": { "Authorization": "Bearer YOUR_API_KEY" }
}
}
}金鑰與計費
到 space-ocr.com → Developer → API Keys 申請一把金鑰(以 spocr_ 開頭)。每個帳號每月有 100 點免費額度,免信用卡,每月重置;超出的部分每點數 $0.05。
計費單位是頁。三個讀取工具的任一個,或一張上傳的影像,各計恰好 1 點數,讀取失敗的會自動退還。其餘都免費——瀏覽目錄樹、查詢已存的資料列、更正儲存格、發放上傳連結。餘量由 space_balance 回報:免費額度、方案額度、預付餘額,依這個順序消耗。大批處理之前值得先呼叫一次。
十三個工具
先讓它讀 space_guide——這是 server 對自己的說明,六個短主題(start、upload、schemas、verification、queries、workflows)直接讀進對話,不打網路請求,也不花點數。
三個工具負責讀取,而且什麼都不存:
ocr_extract—— 擷取帶名字的欄位,可以自行宣告fieldsschema,也可以用autoFields: true讓模型提出一份。ocr_markdown—— 保留版面的 Markdown:標題、清單、表格(每個儲存格帶row/col),並逐元素附座標。ocr_text—— 還原閱讀順序的純文字,多欄版面不會交錯回來,並逐區塊附座標。
另外十個負責工作區:space_list 瀏覽目錄樹,space_view 讀取並查詢項目,space_create 建資料夾/工作表/文件束/備忘錄,space_inbox 發放上傳連結,space_upload 收已經是 URL 的影像,space_job 查上傳工作,space_edit 更正儲存格或改寫備忘錄,space_balance 回報餘量,space_delete 分兩段刪除。完整工具表,連同每個工具背後的 REST 路由,在 API 文件。
影像怎麼送進去
工具呼叫裝不下影像的位元組,第一次嘗試多半卡在這裡。space_upload 只收已經是公開 https:// URL 的影像(每次最多 20 張,每張 20MB)。其餘一律走 space_inbox:本機磁碟上的檔案、對話裡附上的照片、正要掃的一份紙本。它針對某一張工作表或文件束發放一個有時效的上傳連結,位元組從那台機器直接送到 space ocr,不經過對話。回應裡兩種結局都給了:能執行 shell 指令就用其中的 curl 行,不能就把連結顯示給使用者。
上傳是非同步的。space_upload 會立刻回傳工作,一頁大約二十秒;你可以用 space_job 輪詢,也可以稍等片刻直接用 space_view 讀目標,資料列都會在。只處理影像:PDF 會毫無怨言地上傳,然後永遠不會被讀,所以要先把它的頁面轉成影像再送。
一次性讀取,或留下來的資料列
ocr_* 什麼都不存,適合沒人會重複的一次查詢,或在設計工作表欄位之前用 autoFields 先抽樣看一份陌生文件。使用者可能回頭再看的東西,應該放進工作區。
建立結構靠 space_create。資料夾用來分組,也是根目錄唯一接受的型別。工作表會從投進去的每張影像擷取一組固定的 columns,一張影像一列——要比較、要篩選的值放這裡。文件束把每一頁轉成 markdown(要讀的正文)或 text(要搜的詞),並讓這些頁保持在一起。備忘錄就是純文字。
有一條位址規則能省你半天:資料夾用名稱定位,其餘一律用列表或建立呼叫回傳的 path,它的最後一段是項目的 uniqueKey 而不是顯示名稱——名為 March 的工作表並不住在 /invoices/March。把拿到的 path 留著,別用顯示名稱重新拼一個。
資料列就位之後,查詢也是 space_view 的工作:where 過濾(重複即 AND,運算子有 = != > >= < <= 與表示包含的 ~),sort 排序,select 投影欄位,limit / offset 分頁。座標預設不回傳以保持回應精簡——需要指著頁面說話、或讀每個儲存格的判定時,加上 boxes: true。讀取不花點數,所以把篩選推到伺服器端,正是讓答案與上下文都保持精簡的做法。
回傳的內容
三個讀取工具,以及上傳產生的資料列,共用同一種形狀。data.values 就是你宣告的 schema 裡的資料。data.cells 是以 path 為鍵的扁平映射(total、items[0].price),每一筆帶著該值被讀出位置的軸對齊 box 與四點 quad、判定 verified、review,以及背後的 evidence。data.review 是整頁的彙總,flagged 收著值得再看一眼的 path 與其 reasons。data.normalized 只在宣告了純量型別時出現,把解析後的值放在印出來的值旁邊。data.image 是把 0–1000 正規化座標換算回像素的依據。
{
"status": "success",
"data": {
"values": {
"store_name": "超市 ABC",
"date": "2025-04-10",
"invoice_no": "",
"total": "$4.94"
},
"cells": {
"total": {
"box": { "xmin": 380, "ymin": 720, "xmax": 530, "ymax": 742 },
"quad": [{"x":380,"y":720},{"x":530,"y":720},{"x":530,"y":742},{"x":380,"y":742}],
"verified": true,
"review": null,
"evidence": { "text_match": true, "source": "vision_symbol_match", "match_ratio": 1.0 },
"normalized": { "value": 4.94, "type": "number", "method": "deterministic" }
}
},
"review": {
"unit": "field",
"declared": 4,
"returned": 3,
"boxed": 3,
"verified": 3,
"flagged": [ { "path": "invoice_no", "reasons": ["missing"] } ],
"by_reason": { "missing": 1 }
},
"normalized": { "total": 4.94 },
"image": { "width": 1654, "height": 2339 }
}
}為什麼這些值查得證。 座標不是模型猜出來的位置:它重新錨定到頁面上實際辨識到的 OCR 符號,落在 0–1000 的正規化網格上,換算成像素的依據是 data.image。因為位置是真的,值可以畫在文件上,用肉眼與它被讀出的地方逐一對照。旁邊的 verified 是判定——立起複核理由是 false,比對跑過而什麼都沒立是 true,沒有可比對的對象是 null;逐字比對本身由 evidence.text_match 報告,所以「被你宣告的規則攔下、字元卻仍一致」是正常組合,不是矛盾。兩者都是證據而非證明——兩套引擎可能在同一處誤讀上取得一致——所以業務端的檢核要留著。
刪除要兩次呼叫
space_delete 第一次呼叫永遠不會刪東西。不帶 confirm 時,它回報這個路徑底下有什麼——目標,以及其下資料夾/工作表/文件束/備忘錄/影像的數量與一份樣本——並回傳一個約十分鐘有效的簽章 confirm 權杖。助理把這份摘要給使用者看,等到明確同意,再帶著權杖呼叫一次。權杖綁定呼叫方的金鑰與那個確切路徑,既編造不出來,為工作表簽發的權杖也刪不掉它的某一列。
之所以要這套程序,是因為刪除會連帶且無法復原:刪掉資料夾,裡面的影像也一起沒了。刪掉一列不會退還點數——那一頁在上傳時就已經讀過並計費。
四條習慣
server 把自己的使用準則寫明了。一個又省又能給出出處的助理,和一個燒點數靠猜的助理,差別就在這四條。
- 存下來,別倒出來。 超過一份文件就別直接呼叫
ocr_*,改走space_create→space_inbox,讓重的資料留在 API 後面,而不是貼回對話裡。 - 掃描前先查。 批次前
space_balance;再建一張工作表之前先space_list,看看是不是已經有了。已經成為一列的文件沒必要再付一次錢。 - 從存好的資料列回答。 用
where/sort/select/limit查工作表,而不是把每一列都拉進上下文。讀取免費,讀影像不免費。 - 標註位置,標出不確定。 每個值都帶著它被讀出的框與一個判定。
data.review.flagged列出來的,要當成待人工確認的東西呈上,而不是直接斷言;並且要求逐字照抄——頁面上沒有的值,無法錨定到頁面。