Skip to main content
Glama
okrapdf

okraPDF PDF MCP server

Official
by okrapdf

Server Configuration

Describes the environment variables required to run the server.

NameRequiredDescriptionDefault
OKRA_MCP_URLNoOverride the okraPDF MCP endpoint URL (default: https://okrapdf.com/mcp)

Capabilities

Features and capabilities supported by this server

CapabilityDetails
tools
{
  "listChanged": true
}
resources
{
  "listChanged": true
}

Tools

Functions exposed to the LLM to take actions

NameDescription
view_htmlA

Drop a PDF (by public URL) and get back a LIVE, streaming, screen-reader-friendly HTML twin of the document — the same accessible rendering okrapdf.com serves. Returns a viewer_url that progressively streams semantic HTML (headings, lists, tables, figure alt text) as the page is parsed, plus an SSE events_url and a static download_url. Use this to make any PDF readable by assistive tech. (Reserved: policy="wcag" will add the WCAG 2.2 AA / PDF-UA / Section 508 conformance audit — not yet implemented.)

verify_sourceA

Verify a source for a PDF-backed claim and show the result as an inline citation state card. Default mode is high-level: pass document_id, claim, and any rough locator/pages/quote/number; Okra searches parsed nodes, handles not-found and multiple-candidate states, and when a confident source is found mints a durable link.okrapdf.com proof card with quote, page image, bbox focus, confidence, and reasons. For lawfully-reachable PDFs (permitted source or a doc the user has rights to; not paywalled/license-gated) that should not go through full upload/parse yet, pass pdf_url plus page/pages and the tool uses eager_url mode to fetch only that PDF page. verification_requirement defines what counts as verified and defaults to "bbox": eager_url must resolve coordinate evidence before proof is minted, and the API fails rather than returning a proof without bbox. Pass verification_requirement="text_match" only when a deterministic page-text/text-layer match is enough; that returns a found state without a durable proof link, page-image proof, or bbox-backed confidence claim. Use mode="exact_node" with nodes only when the agent is already grounded in exact query rows or node ids. This is the one model-facing source-verification tool; the result also includes create_view arguments for arranging returned card(s) in a custom view.

upload_documentA

Upload a PDF from a lawfully-reachable URL or base64 PDF bytes for Gemini Flash VLM extraction when later okraPDF tool calls need a document_id for SQL/search/docs.read, render_ui, view_document/review_extraction, parsed-node verification, or workflow runs. URL ingest is corpus-gated (see internal/content-rights-policy.md): open/public-record sources or docs the user has rights to — not paywalled/license-gated. For such URLs, prefer resolve_pdf_url so the D1 URL registry can reuse prior ingests. If you only need to read or understand PDF content for reasoning, use normal web_fetch/browser reading first; for arXiv papers, prefer arxiv.org/html/... when available. Do not use this for "verify", "cite", "prove", "source", or "where in the PDF" requests; call verify_source directly with pdf_url + page/pages instead. Opens a live document viewer immediately; the app polls status, page images, and extracted blocks as they arrive. Set wait=true only for legacy blocking status behavior.

resolve_pdf_urlA

Resolve a lawfully-reachable PDF URL to a stable okraPDF document_id using the D1 URL registry when later okraPDF tool calls need that document_id: execute_code SQL/search/docs.read, render_ui, view_document/review_extraction, verify_source with parsed-node evidence, or workflow runs. Intended for open/public-record sources (SEC EDGAR, gov, public-domain, permissively-licensed arXiv) and for documents the user supplies or has the rights to process. Do NOT use it to fetch behind a paywall, login, or anti-bot wall, and do NOT treat the resulting derivative as a public mirror of a copyrighted source — see internal/content-rights-policy.md (mirror levels). If you only need to read or understand the PDF content for reasoning, use normal web_fetch/browser reading first; for arXiv papers, prefer the arxiv.org/html/... page when available because it is cleaner and cheaper than OCR. Do not use this for "verify", "cite", "prove", "source", or "where in the PDF" requests; call verify_source directly with pdf_url + page/pages instead. Opens the live document viewer immediately while ingest/parse/page previews finish. Do not set wait_for/wait_ms unless the very next tool call depends on query/page/visual readiness.

describe_collectionA

Call this FIRST, before any execute_code or render_ui SQL. Returns collection metadata, the document list, the SQLite schema, the node data model (what a node / table / cell is, that cell values are raw strings and that row/column headers are positional sibling cells, not labels), and example queries — the context you need to write a working query on the first try.

execute_codeA

Execute JavaScript against Code Mode namespaces — the primary tool for exploring, querying, grepping, reading, and citing documents (results come back here). For a finished chart, table, metric, or HTML display, pass the working query to render_ui instead.

Code Mode API: docs.list() → [{id, file_name, status, total_pages}] docs.status({ docId }) → {phase, totalPages, totalNodes, ...} docs.read({ docId, pages? }) → {markdown, page_count, total_pages, truncated?} docs.ask({ docId, question }) → {answer, citations, trace_id} (LLM — slow, use sparingly) docs.extract({ docId, prompt, jsonSchema }) → {data, trace_id} (LLM — slow, use sparingly) sql.query({ docId, sql }) → {rows, count} — read-only SQL on one doc; rows include document_id sql.queryMany({ docIds, sql }) → {results:[{docId, rows, count}], count} — run the same query across docs sql.search({ query, docId?, docIds? }) → FTS5 keyword/phrase search across one doc or the current collection sql.grep({ query, docId?, docIds?, pages? }) → literal substring search across node values; useful when FTS tokenization is too strict citation.create({ claim, nodes, docId?, minQuality? }) → {id, url, verification} — mint a shareable link.okrapdf.com/c/{id} proof card; minQuality defaults to "high". citation.batch({ items }) → {citations:[{id,url,...}], count} — mint many proof links from multi-doc results.

SQLite tables per doc: nodes (id, parent_id, type, label, value, status, page_number, confidence, metadata, sort_order) nodes_fts — FTS5 on label+value (use MATCH) meta (key, value) page_ledger (page_number, status, pass, vendor, attempt, confidence, error)

Node data model — the nodes table is the extracted document tree:

  • Each row is one element. type is the raw parser type and varies by vendor, so always run SELECT DISTINCT type FROM nodes first (common values: text, table, row, cell, figure, heading).

  • Tables come in TWO shapes depending on the parser; check with SELECT DISTINCT type: (a) a single table node with the whole table serialized in its value (e.g. the gemini-vision default), or (b) a nested tablerowcell tree: row.parent_id = table.id, cell.parent_id = row.id, and each cell.value holds one cell's text (table/row values are usually empty). For shape (b) read a grid by joining BOTH levels and ordering by sort_order; row/column headers are the first/edge cells positionally, NOT stored on label (which is usually NULL).

  • value is always raw text exactly as printed ("88,268", "16 %", "Sept 30, 2025") — never a typed number/date. Cast in SQL for arithmetic, e.g. CAST(REPLACE(REPLACE(value,',',''),'$','') AS REAL).

Prefer SQL/FTS for discovery, filtering, keyword search, counting, and reading content. SQL is fast, free, and deterministic. Only fall back to reading full pages when SQL doesn't have what you need. When answering numerical or factual questions, use sql.query() to find exact values, then compute the answer step by step. If the first query doesn't return what you need, try a different query — iterate rather than guessing from partial data. IMPORTANT: nodes_fts rows contain full page text (5KB+ each). Always use substr() and JOIN nodes for page_number: SELECT n.page_number, substr(n.value, 1, 200) as excerpt FROM nodes_fts f JOIN nodes n ON n.id = f.node_id WHERE nodes_fts MATCH 'keyword' LIMIT 10 High-grade citation workflow:

  • Never cite from memory or from a generated answer alone. First use SQL/FTS/search to find exact candidate nodes.

  • For MCP users, prefer verify_source when you have a claim plus rough locator/pages/quote; pass pdf_url + page/pages for eager one-page public-PDF verification without full ingest, or document_id for parsed-node verification. It renders found/not-found/multiple-candidate states as an inline card.

  • verification_requirement controls what counts as verified. The default is verification_requirement="bbox": require coordinate evidence and only mint a proof link when a bbox is available. Pass verification_requirement="text_match" only when deterministic page-text/text-layer matching is sufficient; it returns a found state without a durable proof link, page-image proof, or bbox-backed confidence claim.

  • If you already have exact rows, pass rows that include id, document_id, page_number, value, bbox_x/y/w/h, confidence, and status to verify_source mode="exact_node" or citation.create.

  • For user-facing, audit, legal, or financial claims, visually verify the page/bbox with view_document or review_extraction before presenting the citation as final when the card says multiple_candidates, not_found, or low confidence.

  • Treat the returned verification_grade, verification_score, and verification_warnings as the citation quality gate; if warnings mention missing bbox, low confidence, or unverified status, disclose that or find better evidence. Only use ask/extract when the answer requires LLM reasoning, not data lookup.

Page images: https://api.okrapdf.com/v1/documents/{docId}/pg_{N}.png

view_documentA

Show the VISUAL extraction of a parsed okraPDF document: page images with colored bounding-box overlays over the extracted blocks that have bbox data (a bounded set per page), plus a clickable block list (click a box to highlight its text, and vice versa). Use after upload_document to let the user see and verify the extraction. Pass document_id and optional pages. For the structured text output instead (parsed nodes as plain reading HTML, no images), use inspect_html.

view_pdfA

Open the document as a PDF reader/navigation surface inside the MCP host. Shows page images and, when available, extracted bounding boxes. Use this for quick visual page inspection; use review_extraction when the user is specifically verifying parser output.

review_extractionA

Open the extraction review workflow: page images with bbox overlays and clickable extracted blocks that can be verified or flagged. This is the bbox/provenance workflow surface, not a PDF editor.

verify_blockA

Mark an extracted block as verified (✓) or wrong (✗), persisted to the document. Called by the view_document viewer when the user clicks a block; also usable directly with document_id + node_id.

interactA

Drive the document viewer the user already has open (from view_document): navigate, highlight a node, or auto-highlight text with the pdf-server-style highlight_text arguments query/page/color/content. Pass document_id plus view_uuid or viewUUID from the view_document result. Use commands or actions for ordered batches. Do NOT call view_document again to navigate — that opens a separate viewer.

poll_document_viewA

Internal: the viewer widget refreshes upload/parse/render status, page images, and extracted blocks. Not for direct agent use.

poll_view_commandsA

Internal: the viewer widget drains queued interact commands. Not for direct agent use.

inspect_htmlA

Show the STRUCTURED text output of a parsed document: the canonical extracted nodes rendered as plain webpage-like HTML pages — no page images, no bounding boxes. Omit page to render the available pages together (capped, ~50 pages for long docs); pass page for one specific page. Uses canonical DocumentAgent nodes (not vendor playground facets) and returns status="ready" only when node-backed HTML exists, else a not-ready state. For the visual extraction (page images + bounding-box overlays) instead, use view_document.

view_structuredA

Open the parsed structured text/HTML view inside the MCP host. Use this for reading extracted document content as HTML without page images or bbox overlays; use view_pdf/review_extraction for visual source pages.

render_uiA

Run codemode against docs/sql/citation and render the returned visualization as an MCP App. Use for charts, tables, metrics, and small dashboards derived from PDFs after you know the schema. Call describe_collection first, and validate exploratory SQL with execute_code before embedding it here; SQL failures are returned as codemode errors. The codemode sandbox includes the preinstalled "okra-render-ui" module with querySql() plus prebuilt component helpers like TimeSeriesLineChart(), DataTable(), MetricCards(), dashboard(), and chart/table/html specs; keep query code separate from reusable UI helpers, return compact data, not screenshots, and do not rely on CDN scripts such as Chart.js inside html().

create_viewA

Render a model-authored chart, table, metric, proof card, or dashboard as an MCP App and progressively preview safe component JSON while tool arguments stream. Use when you already have the data. For document-backed SQL/codemode work, use render_ui instead. Input view is a compact JSON string; top-level arrays are dashboard items. Do not pass HTML, scripts, iframes, or CDN-dependent payloads.

get_render_payloadA

Internal: the render_ui viewer fetches a large visualization payload that was stashed out-of-band. Not for direct agent use.

play_music_scoreA

Render and play a PDF music-score extraction from semantic note events with normalized page bboxes. Use this when demonstrating non-text PDF accessibility: each note has page coordinates, pitch, timing, and an accessible label; the UI synchronizes the source bbox overlay with a playable score. If no input is provided, it opens a seeded public-domain Beethoven Moonlight Sonata excerpt from Mutopia. For real OMR output, pass abc_notation plus notes[].

list_workflowsA

List workflows you authored, newest first, so you can recover workflow_id values from prior MCP sessions before calling view_workflow or run_workflow.

list_workflow_runsA

List workflow runs you started, newest first, so you can recover run_id values from prior MCP sessions before calling view_workflow_run.

view_workflow_runA

Monitor a workflow run. For agent workflow scripts, returns status, stats, controller_output, per-agent outputs, event stream, logs, and failed-agent details. For step-definition workflows (e.g. invoice extraction), returns overall status, per-document rows, exceptions, dropped docs, stats, and the human-approval state. Pass the run_id from POST /v1/runs.

view_workflowA

Confirm a workflow as it is being built: finite definitions return visualization + validation, while agent workflow scripts return parse/readiness state and a best-effort plan estimate. Accepts a catalog name (e.g. staff.table-ingest) or a workflow id you authored.

approve_runA

Approve or reject a run that is waiting_for_review (the human-in-the-loop gate). Approving emits the validated rows; rejecting discards the run. Returns the refreshed run snapshot.

draft_workflowA

Author, create, build, or draft a workflow. For custom/agentic work, pass code as a high-level agent workflow script using agent() / parallel() / pipeline() / phase(). Minimal working code: phase("Extract"); const result = await agent("Extract the invoice total.", { label:"extract", schema:{ type:"object", required:["total"], properties:{ total:{ type:"string" } } } }); return { result };. Each agent call must include a task prompt and a JSON Schema under the schema key, e.g. agent("task", { label:"extract", schema:{ type:"object", required:["rows"], properties:{ rows:{ type:"array" } } } }) or agent({ prompt:"task", label:"extract", schema:{...} }). Plain JS acts as the deterministic controller. This is the path for provider A/B tests, N-provider parser fan-out, judge/compare barriers, and human review via an explicit detect/apply phase split. definition and catalog_workflow_id are only for finite catalog pipelines. Returns workflow_id, readiness, blueprint, and AST when available. Persisted to your account — same as POST /v1/workflows.

run_workflowA

Start a run of a runnable workflow you authored (the write counterpart to view_workflow_run). Pass the workflow_id from draft_workflow and inputs (e.g. {files:["doc-…"]} or {document_id:"doc_…"}). Catalog/step-definition invoice pipelines may surface waiting_for_review; agent workflow scripts run through the hosted JS controller harness and return controller_output plus per-agent outputs and event stream when complete. Same as POST /v1/runs.

submit_usage_feedbackA

Submit detailed feedback about using the okraPDF MCP server. Use this only when the user asks to report feedback or when a reproducible MCP tool/app issue, confusing workflow, missing affordance, or useful agent-DX note should be sent to the okraPDF team. Pass the long feedback string in feedback; include workflow_id or wf_id, run_id, document_id, tool_name, client_name, tags, and other JSON-serializable debug context in options. Do not include secrets or API keys.

Prompts

Interactive templates invoked by user choice

NameDescription

No prompts

Resources

Contextual data attached and managed by the client

NameDescription
okraPDF accessible HTML twin
okraPDF accessibility report
okraPDF Viewer
Parsed HTML Pages
Render UI
Playable Music Score

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/okrapdf/pdf-mcp'

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