Skip to main content

Compatibility

The MCP server uses the Streamable HTTP transport (MCP spec). Not all clients support it yet.
ClientStatusNotes
OpenCode✅ VerifiedTested and working
Claude Desktop⚠️ UntestedShould work — uses same streamable-http config. Report issues to us.
Claude Code (CLI)❌ Known issue406 due to missing Accept header (#54)
Cursor⚠️ UntestedConfig shown below; let us know if it works.
Windsurf⚠️ UntestedConfig shown below; let us know if it works.
We’re working on broader client support. If your MCP client isn’t listed, try the config below and open an issue if it doesn’t connect.

What you get

Connect OkraPDF to Claude and you can:
  • Upload any PDF by pasting a link — SEC filings, earnings reports, invoices, contracts
  • Ask questions and get answers with page citations (“What was Q3 revenue?” → “$60.9B, page 47”)
  • Extract tables as structured data that you can paste into Excel
  • Compare across documents — run the same question across a portfolio of filings at once
No coding required. Just talk to Claude like normal — OkraPDF handles the extraction behind the scenes.

Setup (2 minutes)

  1. Get your API key from okrapdf.com/settings/api-keys
  2. Open Claude Desktop → SettingsDeveloperEdit Config
  3. Add OkraPDF to your config file:
{
  "mcpServers": {
    "okrapdf": {
      "url": "https://api.okrapdf.com/mcp",
      "headers": {
        "Authorization": "Bearer okra_YOUR_API_KEY"
      }
    }
  }
}
  1. Replace okra_YOUR_API_KEY with your actual key
  2. Restart Claude Desktop
That’s it. Claude now has access to all OkraPDF tools. Start a new conversation and try: “Upload this 10-K and tell me the revenue: https://sec.gov/…”

Tools

The server exposes 7 tools:
ToolWhat it does
upload_documentUpload a PDF from any URL. Waits for extraction to complete by default.
get_document_statusCheck if a document is ready (phase, page count, data points extracted).
list_documentsList your uploaded documents sorted by most recent.
read_documentGet extracted text and tables as markdown, with optional page ranges.
ask_documentAsk a question and get an answer with page citations.
extract_dataExtract specific data points into structured JSON using a schema you define.
extract_collectionRun the same extraction across every document in a collection.

Quick Start

Once connected, just talk to Claude. Here are examples of what you can say:

Upload and ask questions

Upload this 10-K and tell me the total revenue:
https://www.sec.gov/Archives/edgar/data/1045810/000104581024000316/nvda-20240128.htm
Claude uploads the PDF, extracts all the content, and answers your question with the exact page number.

Read specific sections

Read pages 40-45 of my NVIDIA filing
Great for large filings when you know which pages have the financial statements.

Extract data for Excel

Extract revenue, net income, and EPS from this 10-K as a table I can paste into Excel
Claude extracts the data in a structured format you can copy straight into a spreadsheet.

Compare across filings

Compare NVIDIA's revenue across my last 4 uploaded 10-Ks
Claude queries each document in parallel and builds a comparison table — with page citations for every number.

Parallel Queries

MCP clients that support parallel tool calls (like Claude Code) can ask the same question across multiple documents simultaneously. This is the fastest way to compare data across filings.

Example: Operating margins across 3 companies

> What was the operating margin for PepsiCo, Amazon, and AMD?
The agent fires 3 ask_document calls in parallel — one per document — and results come back at the same time:
CompanyFYOperating Margin
PepsiCo202213.3%
Amazon20195.2%
AMD20225.4%
Each answer includes page citations back to the source filing. No sequential waiting — all three run concurrently against their respective Durable Objects.

How it works

Each document lives in its own Durable Object on Cloudflare’s edge. Parallel queries hit separate DOs — there’s no shared bottleneck.

Try It

# 1. Connect (one-time)
claude mcp add -s user -t http okrapdf https://api.okrapdf.com/mcp

# 2. Upload a PDF
> "Upload https://arxiv.org/pdf/2401.04088"
#  → upload_document(url, wait=true)
#  → doc-25725cc7... complete, 853 nodes extracted

# 3. Ask a question
> "What is this paper about?"
#  → ask_document(doc-25725cc7, "What is this paper about?")
#  → "Mixtral 8x7B, a sparse mixture of experts model..." (p.1)

# 4. Parallel queries — the killer feature
> "Compare operating margins for PepsiCo, Amazon, and AMD"
#
#  Agent fires 3 calls at once:
#  ┌─ ask_document(PepsiCo)  ──→  13.3%  (FY2022, p.40)
#  ├─ ask_document(Amazon)   ──→   5.2%  (FY2019, p.38)
#  └─ ask_document(AMD)      ──→   5.4%  (FY2022, p.48)
#
#  Each doc is its own Durable Object — no shared bottleneck.
#  All three return at the same time.

# 5. Structured extraction
> "Extract revenue by segment as JSON"
#  → extract_data(doc, prompt, json_schema)
#  → { "segments": [{ "name": "PBNA", "revenue": 26213 }, ...] }

Tool Reference

upload_document

ParameterTypeRequiredDescription
urlstringYesPublic URL of the PDF
document_idstringNoCustom ID (auto-generated if omitted)
waitbooleanNoWait for extraction to complete (default: true)
page_images"none" | "cover" | "lazy"NoPage image strategy (default: "cover")

read_document

ParameterTypeRequiredDescription
document_idstringYesDocument ID
pagesstringNoPage range, e.g. "1-5" or "3". Omit for all pages.

ask_document

ParameterTypeRequiredDescription
document_idstringYesDocument ID
questionstringYesNatural language question
modelstringNoOpenRouter ID (e.g. "anthropic/claude-haiku-4.5") or direct provider (e.g. "anthropic:claude-haiku-4-5-20251001", "openai:gpt-4o"). Omit for default.
instructionsstringNoExtra instructions appended to the system prompt.

extract_data

ParameterTypeRequiredDescription
document_idstringYesDocument ID
promptstringYesExtraction instruction
json_schemaobjectYesJSON Schema for desired output shape
modelstringNoOpenRouter ID or direct provider format. Omit for default.
instructionsstringNoExtra instructions appended to the system prompt.

Example: 10-K financial extraction

Prompt:
Extract key financial details from this 10-K: revenue, net income,
total assets, total liabilities, cash and equivalents, and EPS diluted.
Schema:
{
  "type": "object",
  "properties": {
    "company": { "type": "string" },
    "fiscal_year_ended": { "type": "string" },
    "revenue": { "type": "string" },
    "net_income": { "type": "string" },
    "total_assets": { "type": "string" },
    "total_liabilities": { "type": "string" },
    "cash_and_equivalents": { "type": "string" },
    "eps_diluted": { "type": "string" }
  }
}
Response:
{
  "company": "NVIDIA Corporation",
  "fiscal_year_ended": "January 25, 2026",
  "revenue": "$215,938 million",
  "net_income": "$120,067 million",
  "total_assets": "$206,803 million",
  "total_liabilities": "$49,510 million",
  "cash_and_equivalents": "$10,605 million",
  "eps_diluted": "$4.90"
}
Nested schemas work too — group fields into income_statement, balance_sheet, and cash_flow objects for cleaner output.

get_document_status

ParameterTypeRequiredDescription
document_idstringYesDocument ID

extract_collection

ParameterTypeRequiredDescription
collection_idstringYesCollection ID or name
promptstringYesExtraction instruction applied to each document
json_schemaobjectYesJSON Schema for the per-document output shape
merge_strategy"array" | "merge"NoHow to combine results (default: "array")
pagesstringNoPage range applied to each document (e.g. "1-10")
modelstringNoOpenRouter ID or direct provider format — applied to every document.
instructionsstringNoExtra instructions — applied to every document.

list_documents

ParameterTypeRequiredDescription
limitintegerNoMax documents to return (default: 20, max: 100)

Model & Prompt Configuration

ask_document, extract_data, and extract_collection accept optional model and instructions parameters.

Choose a model

Pass any OpenRouter model ID, or use a direct provider key:
> Ask doc-abc123 "What was total revenue?" using anthropic/claude-haiku-4.5
The agent calls:
{
  "document_id": "doc-abc123",
  "question": "What was total revenue?",
  "model": "anthropic/claude-haiku-4.5"
}
Popular choices:
ModelSpeedBest for
moonshotai/kimi-k2.5FastGeneral Q&A (default)
anthropic/claude-haiku-4.5FastPrecise extraction, concise answers
anthropic/claude-sonnet-4.5MediumComplex reasoning, multi-step analysis
google/gemini-2.5-flashFastLarge context, long documents

Use your own API key (direct provider)

Instead of routing through OpenRouter, call Anthropic, OpenAI, or Google directly with your own API key. Use provider:model format (colon, not slash):
FormatProviderExample
anthropic:model-idAnthropicanthropic:claude-haiku-4-5-20251001
openai:model-idOpenAIopenai:gpt-4o
google:model-idGooglegoogle:gemini-2.5-flash
vendor/model (slash)OpenRouteranthropic/claude-haiku-4.5
> Ask doc-abc123 "What was total revenue?" using anthropic:claude-haiku-4-5-20251001
To use direct providers, store your API key in OkraPDF (Settings > API Keys > Vendor Keys) or pass it via the X-Vendor-Keys header.

Add instructions

Append domain-specific hints to the system prompt:
> Extract revenue from doc-abc123. Note: amounts are in HKD millions
> and the fiscal year ends in March.
The agent calls:
{
  "document_id": "doc-abc123",
  "prompt": "Extract annual revenue",
  "json_schema": { "type": "object", "properties": { "revenue": { "type": "string" } } },
  "instructions": "Amounts are in HKD millions. Fiscal year ends in March."
}
Use instructions for:
  • Currency/unit context: “All values are in EUR thousands”
  • Fiscal year hints: “FY ends June 30”
  • Output format: “Use ISO dates (YYYY-MM-DD)”
  • Language: “Respond in Chinese”

curl examples

Ask a question via OpenRouter:
curl -X POST https://api.okrapdf.com/document/doc-abc123/chat/completions \
  -H "Authorization: Bearer okra_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [{"role": "user", "content": "What was total revenue?"}],
    "agentConfigOverride": {
      "llm": {"chat": {"provider": "openrouter", "model": "anthropic/claude-haiku-4.5"}}
    },
    "systemPromptSuffix": "Respond in one sentence with the page number."
  }'
Ask using your own Anthropic key directly:
curl -X POST https://api.okrapdf.com/document/doc-abc123/chat/completions \
  -H "Authorization: Bearer okra_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -H 'X-Vendor-Keys: {"anthropic": "sk-ant-..."}' \
  -d '{
    "messages": [{"role": "user", "content": "What was total revenue?"}],
    "agentConfigOverride": {
      "llm": {"chat": {"provider": "anthropic", "model": "claude-haiku-4-5-20251001"}}
    }
  }'
Structured extraction with OpenAI direct:
curl -X POST https://api.okrapdf.com/document/doc-abc123/chat/completions \
  -H "Authorization: Bearer okra_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -H 'X-Vendor-Keys: {"openai": "sk-..."}' \
  -d '{
    "messages": [{"role": "user", "content": "Extract key financials"}],
    "response_format": {
      "type": "json_schema",
      "json_schema": {
        "name": "extraction",
        "schema": {
          "type": "object",
          "properties": {
            "revenue": {"type": "string"},
            "net_income": {"type": "string"}
          }
        },
        "strict": true
      }
    },
    "agentConfigOverride": {
      "llm": {"chat": {"provider": "openai", "model": "gpt-4o"}}
    },
    "systemPromptSuffix": "Values should include currency symbol and unit."
  }'
When using the MCP tools (not curl), you don’t need agentConfigOverride or systemPromptSuffix — just pass model and instructions as tool parameters. The MCP server handles the translation.