OkraPDF

v1.0.0

OkraPDF — upload PDFs, read extracted content, ask questions, extract structured data, and manage collections. Covers MCP, CLI, and HTTP.

0· 109·0 current·0 all-time
bySteven Tsao@steventsao

Install

OpenClaw Prompt Flow

Install with OpenClaw

Best for remote or guided setup. Copy the exact prompt, then paste it into OpenClaw for steventsao/okra.

Previewing Install & Setup.
Prompt PreviewInstall & Setup
Install the skill "OkraPDF" (steventsao/okra) from ClawHub.
Skill page: https://clawhub.ai/steventsao/okra
Keep the work scoped to this skill only.
After install, inspect the skill metadata and help me finish setup.
Use only the metadata you can verify from ClawHub; do not invent missing requirements.
Ask before making any broader environment changes.

Command Line

CLI Commands

Use the direct CLI path if you want to install manually and keep every step visible.

OpenClaw CLI

Bare skill slug

openclaw skills install okra

ClawHub CLI

Package manager switcher

npx clawhub@latest install okra
Security Scan
VirusTotalVirusTotal
Benign
View report →
OpenClawOpenClaw
Benign
medium confidence
Purpose & Capability
SKILL.md describes a PDF-upload + extraction/chat service (MCP, CLI, HTTP) and all instructions map to that purpose. However, the registry metadata lists no required credentials or env vars while the README clearly expects an API key (OKRA_API_KEY / 'YOUR_API_KEY'). This mismatch is a minor incoherence.
Instruction Scope
Instructions are scoped to uploading/reading/asking about PDFs via api.okrapdf.com, installing a CLI, and adding an MCP entry. They explicitly instruct modifying user agent config files (~/.claude/mcp.json, .cursor/mcp.json) and installing a global npm package; those actions are within the stated purpose but are side-effects the user should be aware of and which are not declared in the registry metadata.
Install Mechanism
There is no formal install spec in the registry, but the SKILL.md tells users to run 'npm install -g okrapdf' and to set an API key. Installing a global npm package carries normal supply-chain risk; the skill does not provide a vetted install source or manifest entry for this step.
Credentials
The service reasonably requires an API key (Authorization: Bearer), and the SKILL.md references OKRA_API_KEY and a user API key. That credential is proportional to the service, but the skill metadata did not declare required env vars or the primary credential — an inconsistency worth noting.
Persistence & Privilege
The skill does not request always: true and does not require system-wide privileges. It asks the user to add an MCP server entry and to install a CLI, which is normal for an integration; these are not excessive privileges.
Assessment
This appears to be a legitimate PDF-extraction/QA integration, but take these precautions before installing or using it: 1) Confirm the remote domain (api.okrapdf.com / okrapdf.com) is one you trust and review their privacy/TOS because uploaded PDFs will be sent to that service. 2) The SKILL.md expects an API key (OKRA_API_KEY) and tells you to store it in agent config or environment; the skill metadata did not declare this — do not paste sensitive or org-confidential documents until you verify the provider. 3) The CLI install uses npm - audit the npm package 'okrapdf' (check publisher, version history, and repository/source code) before global installation. 4) If you must proceed, create a least-privilege API key, test with non-sensitive files, and consider running the CLI in a sandboxed environment. 5) If you need higher assurance, ask the publisher for source/release links or a homepage and request that required env vars and install steps be declared in the registry metadata.

Like a lobster shell, security has layers — review code before you run it.

latestvk97cthcfp966q3jgh8sqqm8afn83xvk7
109downloads
0stars
1versions
Updated 4w ago
v1.0.0
MIT-0

OkraPDF

Upload a PDF, get an API. Extract tables, ask questions, get structured JSON — via MCP, CLI, or HTTP.

Designed for subagents. Every document is its own stateless endpoint. Fire off parallel queries to different documents — no shared state, no locks, no ordering issues. Ideal as a tool inside agent loops (Claude, GPT, custom orchestrators).

Setup

MCP (Claude Code, Cursor, OpenCode)

Add to ~/.claude/mcp.json or .cursor/mcp.json:

{
  "mcpServers": {
    "okra-pdf": {
      "type": "url",
      "url": "https://api.okrapdf.com/mcp",
      "headers": { "Authorization": "Bearer YOUR_API_KEY" }
    }
  }
}

CLI

npm install -g okrapdf
okra auth set-key YOUR_API_KEY

HTTP

All endpoints use https://api.okrapdf.com with header Authorization: Bearer $OKRA_API_KEY.

Get a free API key at okrapdf.com (Settings > API Keys).


Upload a PDF

CLI

okra extract invoice.pdf
okra extract https://arxiv.org/pdf/2307.09288
okra extract report.pdf --processor llamaparse
okra run report.pdf "What was total revenue?"     # upload + ask in one shot

Options: -o json|markdown|table, --processor, --tables-only, --text-only, -d <dir> (agentic workspace), -q (quiet, for piping)

MCP

upload_document(url: "https://example.com/report.pdf")
upload_document(url: "https://arxiv.org/pdf/2307.09288", wait: true)
upload_document(url: "https://example.com/invoice.pdf", page_images: "lazy")

Parameters: url (required), wait (default: true), document_id (optional), page_images (none/cover/lazy), processor

HTTP

# From URL
curl -X POST https://api.okrapdf.com/v1/documents \
  -H "Authorization: Bearer $OKRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://arxiv.org/pdf/2307.09288"}'

# From file
curl -X POST https://api.okrapdf.com/v1/documents \
  -H "Authorization: Bearer $OKRA_API_KEY" \
  -F "file=@report.pdf" -F "page_images=cover"

Response: {"document_id": "doc-abc123", "phase": "extracting", "pages_total": 42}


Check Status

CLI

okra status doc-abc123

MCP

get_document_status(document_id: "doc-abc123")

HTTP

curl https://api.okrapdf.com/v1/documents/doc-abc123/status \
  -H "Authorization: Bearer $OKRA_API_KEY"

Response: {"phase": "complete", "page_count": 42, "total_nodes": 318}

Documents must reach phase: "complete" before reading/asking. Use wait: true on upload (MCP) or poll status.


Read Content

CLI

okra read doc-abc123
okra page get doc-abc123 1
okra toc doc-abc123
okra tree doc-abc123
okra search doc-abc123 "revenue"

MCP

read_document(document_id: "doc-abc123")
read_document(document_id: "doc-abc123", pages: "1-5")
read_document(document_id: "arxiv:2307.09288")

document_id accepts: doc-abc123, arxiv:2307.09288, or https://arxiv.org/pdf/2307.09288.

HTTP

# Full markdown
curl https://api.okrapdf.com/v1/documents/doc-abc123/full.md \
  -H "Authorization: Bearer $OKRA_API_KEY"

# Specific page
curl "https://api.okrapdf.com/v1/documents/doc-abc123/pages/3" \
  -H "Authorization: Bearer $OKRA_API_KEY"

# All pages as JSON
curl https://api.okrapdf.com/v1/documents/doc-abc123/pages \
  -H "Authorization: Bearer $OKRA_API_KEY"

Ask Questions

CLI

okra chat doc-abc123
okra chat send doc-abc123 -m "What are the key findings?"

MCP

ask_document(document_id: "doc-abc123", question: "What was total revenue in 2024?")

Returns answer with page citations (page number + supporting snippet).

HTTP (OpenAI-compatible)

curl -X POST https://api.okrapdf.com/document/doc-abc123/chat/completions \
  -H "Authorization: Bearer $OKRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [{"role": "user", "content": "What was total revenue in 2024?"}],
    "stream": false
  }'

Supports "stream": true for SSE streaming.


Extract Structured Data

CLI

okra extract report.pdf -o json -q | jq '.entities[] | select(.type == "table")'

MCP

extract_data(
  document_id: "doc-abc123",
  prompt: "Extract all line items from this invoice",
  json_schema: {
    "type": "object",
    "properties": {
      "line_items": {
        "type": "array",
        "items": {
          "type": "object",
          "properties": {
            "description": {"type": "string"},
            "quantity": {"type": "number"},
            "unit_price": {"type": "number"},
            "total": {"type": "number"}
          }
        }
      },
      "grand_total": {"type": "number"}
    }
  }
)

HTTP

curl -X POST https://api.okrapdf.com/document/doc-abc123/chat/completions \
  -H "Authorization: Bearer $OKRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [{"role": "user", "content": "Extract all line items"}],
    "response_format": {
      "type": "json_schema",
      "json_schema": {
        "name": "invoice",
        "schema": {
          "type": "object",
          "properties": {
            "line_items": {"type": "array", "items": {"type": "object", "properties": {"description": {"type": "string"}, "amount": {"type": "number"}}}},
            "total": {"type": "number"}
          }
        },
        "strict": true
      }
    },
    "stream": false
  }'

Tables and Entities

CLI

okra tables doc-abc123
okra tables get doc-abc123 table-0
okra entities list doc-abc123
okra entities images doc-abc123
okra query doc-abc123 "table:has(revenue)"

HTTP

curl https://api.okrapdf.com/v1/documents/doc-abc123/entities/tables \
  -H "Authorization: Bearer $OKRA_API_KEY"

curl https://api.okrapdf.com/v1/documents/doc-abc123/entities \
  -H "Authorization: Bearer $OKRA_API_KEY"

Collections

Group documents and query across all of them at once.

Create and manage

CLI:

okra collections create "Q4 Earnings" -d "Quarterly filings" --docs doc-abc123,doc-def456
okra collections list                          # or: okra col ls
okra collections show "Q4 Earnings"
okra collections add "Q4 Earnings" doc-ghi789
okra collections remove "Q4 Earnings" doc-abc123
okra collections delete "Q4 Earnings"

HTTP:

# Create with seed documents
curl -X POST https://api.okrapdf.com/v1/collections \
  -H "Authorization: Bearer $OKRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "Q4 Earnings", "document_ids": ["doc-abc123", "doc-def456"]}'

# Add documents
curl -X POST https://api.okrapdf.com/v1/collections/col-xxx/documents \
  -H "Authorization: Bearer $OKRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"document_ids": ["doc-ghi789"]}'

# List / get / delete
curl https://api.okrapdf.com/v1/collections -H "Authorization: Bearer $OKRA_API_KEY"
curl https://api.okrapdf.com/v1/collections/col-xxx -H "Authorization: Bearer $OKRA_API_KEY"
curl -X DELETE https://api.okrapdf.com/v1/collections/col-xxx -H "Authorization: Bearer $OKRA_API_KEY"

Query across documents

Two modes:

ModeBehaviorBest for
fanout (default)Separate completion per document, NDJSON streamPer-document answers
sandboxSingle LLM with grep/Python over all docsCross-doc search, comparisons

CLI:

okra chat -c "Q4 Earnings" -m "Compare revenue across companies"
okra chat "compare revenue" --doc doc-abc123,doc-def456

HTTP (fanout):

curl -X POST https://api.okrapdf.com/v1/collections/col-xxx/query \
  -H "Authorization: Bearer $OKRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "What was total revenue in Q4?"}'

Response (NDJSON):

{"type":"start","query_id":"...","doc_count":7}
{"type":"result","doc_id":"doc-xxx","answer":"..."}
{"type":"done","completed":7,"failed":0}

HTTP (sandbox):

curl -X POST https://api.okrapdf.com/v1/collections/col-xxx/query \
  -H "Authorization: Bearer $OKRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"prompt": "Compare R&D spending. Show a table.", "mode": "sandbox"}'

Export

# NDJSON stream
curl -N "https://api.okrapdf.com/v1/collections/col-xxx/export?format=markdown" \
  -H "Authorization: Bearer $OKRA_API_KEY"

# Zip archive
curl -L "https://api.okrapdf.com/v1/collections/col-xxx/export?format=zip" \
  -H "Authorization: Bearer $OKRA_API_KEY" -o collection.zip

Exports

# CLI
okra extract report.pdf -o json -q > report.json

# HTTP
curl https://api.okrapdf.com/exports/doc-abc123/markdown -H "Authorization: Bearer $OKRA_API_KEY"
curl -o report.xlsx https://api.okrapdf.com/exports/doc-abc123/excel -H "Authorization: Bearer $OKRA_API_KEY"
curl -o report.docx https://api.okrapdf.com/exports/doc-abc123/docx -H "Authorization: Bearer $OKRA_API_KEY"
curl https://api.okrapdf.com/exports/doc-abc123/snapshot -H "Authorization: Bearer $OKRA_API_KEY"

Page Images

Deterministic, CDN-cached URLs:

https://res.okrapdf.com/v1/documents/{id}/pg_1.png
https://res.okrapdf.com/v1/documents/{id}/w_400,h_300/pg_1.png

Document Management

CLI

okra list
okra read doc-abc123
okra status doc-abc123
okra delete doc-abc123
okra auth set-key YOUR_API_KEY
okra auth whoami

HTTP

curl "https://api.okrapdf.com/v1/documents?limit=20" -H "Authorization: Bearer $OKRA_API_KEY"

Subagent & Parallel Patterns

OkraPDF is built for agent-to-agent use. Each document is an isolated Durable Object with its own SQLite — queries to different documents never contend. Run them in parallel freely.

As tools inside an agent loop

Map each document to a callable tool. The orchestrating agent picks which docs to query:

import { createOkra } from '@okrapdf/runtime';

const okra = createOkra({ apiKey: process.env.OKRA_API_KEY! });

const docs = [
  { id: 'doc-abc123', label: 'NVIDIA 10-K' },
  { id: 'doc-def456', label: 'AMD 10-K' },
  { id: 'doc-ghi789', label: 'Intel 10-K' },
];

// Each doc becomes a tool the agent can call
const sessions = Object.fromEntries(
  docs.map((d) => [d.label, okra.sessions.from(d.id)]),
);

// Execute in parallel when agent calls multiple tools at once
const results = await Promise.all(
  toolCalls.map(tc => sessions[tc.name].prompt(tc.input.question))
);

Fan-out: same question across N documents

// SDK — fire all in parallel, collect answers
const question = 'What was total revenue and YoY growth?';
const answers = await Promise.all(
  docIds.map(id => okra.sessions.from(id).prompt(question))
);
# curl — parallel background requests
for doc_id in doc-abc123 doc-def456 doc-ghi789; do
  curl -s -X POST "https://api.okrapdf.com/document/$doc_id/chat/completions" \
    -H "Authorization: Bearer $OKRA_API_KEY" \
    -H "Content-Type: application/json" \
    -d "{\"messages\": [{\"role\": \"user\", \"content\": \"$question\"}], \"stream\": false}" &
done
wait
# CLI — multi-doc in one command
okra chat "compare revenue" --doc doc-abc123,doc-def456,doc-ghi789

MCP subagent pattern (Claude Code)

When Claude Code spawns subagents, each can independently call OkraPDF MCP tools:

# Subagent 1: ask_document(document_id: "doc-abc123", question: "What was revenue?")
# Subagent 2: ask_document(document_id: "doc-def456", question: "What was revenue?")
# Subagent 3: ask_document(document_id: "doc-ghi789", question: "What was revenue?")
# All run concurrently — no contention

When to use which pattern

PatternBest for
MCP toolsAgent picks which docs to query dynamically
SDK Promise.allYou know the doc set upfront, want max parallelism
Collections querySame question across a predefined group, server handles fan-out
okra chat --docQuick CLI comparison of 2-5 docs

Piping and Scripting

# Batch extract
for pdf in *.pdf; do okra extract "$pdf" -o json -q > "${pdf%.pdf}.json"; done

# Fan-out: same question to multiple docs in parallel
for doc_id in doc-abc123 doc-def456 doc-ghi789; do
  curl -s -X POST "https://api.okrapdf.com/document/$doc_id/chat/completions" \
    -H "Authorization: Bearer $OKRA_API_KEY" \
    -H "Content-Type: application/json" \
    -d "{\"messages\": [{\"role\": \"user\", \"content\": \"What was total revenue?\"}], \"stream\": false}" &
done
wait

Available Processors

ProcessorBest ForSpeed
textlayerNative PDFs with selectable textFast
llamaparseComplex layouts, mixed contentMedium
unstructuredGeneral purposeMedium
azure-diForms, invoices, receiptsMedium
docaiHigh-accuracy OCRSlow
geminiVision-based extractionMedium
qwenOpen-source VLM extractionMedium

Error Codes

StatusMeaning
202Accepted, processing async
400Bad request
401Missing or invalid API key
404Document not found
409Conflict (document exists)
429Rate limited

Links

Comments

Loading comments...