docs
Documentation
Everything you need to go from zero to parsing. One page, no sidebar rabbit holes.
Quick start
Two things. Account, then one install command.
1. Create an account
Sign up at getfrenchie.dev/register. You get 100 free credits on your first signup, once per email — no credit card required.
Once you're in, grab your API key from the dashboard. It looks like this:
fr_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4Keep it somewhere safe. You'll need it in the next step.
2. Install Frenchie in your agent
From the root of your project:
npx @lab94/frenchie install --api-key fr_your_key_hereThe installer auto-detects your agent, copies the skill files, and writes a project-scoped stdio MCP config. Your agent will call ocr_to_markdown / transcribe_to_markdown with local file paths, and results are saved to .frenchie/<name>/result.md automatically.
You can also target a specific agent explicitly:
# Project-scoped (default):
npx @lab94/frenchie install --agent claude --api-key fr_...
npx @lab94/frenchie install --agent cursor --api-key fr_...
npx @lab94/frenchie install --agent codex --api-key fr_...
npx @lab94/frenchie install --agent vscode --api-key fr_...
npx @lab94/frenchie install --agent gemini --api-key fr_...
# User-level (requires --global):
npx @lab94/frenchie install --agent antigravity --global --api-key fr_...
npx @lab94/frenchie install --agent windsurf --global --api-key fr_...
npx @lab94/frenchie install --agent zed --global --api-key fr_...
npx @lab94/frenchie install --agent claude-desktop --global --api-key fr_...User-level installs (Antigravity, Claude Desktop) need the --global flag — auto-detect never writes under $HOME.
Then ask your agent in plain English, or use the shortcuts where supported:
"OCR ./report.pdf with Frenchie"
"Transcribe ./meeting.mp3 and pull out action items"
/ocr invoice.pdf
/transcribe meeting.mp3For hosted / web agents (HTTP)
Lovable, Manus, Claude.ai, ChatGPT.com, Mistral Le Chat — these can't spawn local binaries. Connect them to the public HTTP endpoint instead:
Claude Code (CLI flag, for reference):
claude mcp add --transport http frenchie https://mcp.getfrenchie.dev --header "Authorization: Bearer fr_your_key_here"Cursor — add to .cursor/mcp.json:
{
"mcpServers": {
"frenchie": {
"url": "https://mcp.getfrenchie.dev",
"headers": {
"Authorization": "Bearer fr_your_key_here"
}
}
}
}Codex — add to ~/.codex/config.toml:
[mcp_servers.frenchie]
url = "https://mcp.getfrenchie.dev"
[mcp_servers.frenchie.headers]
Authorization = "Bearer fr_your_key_here"In HTTP mode, agents upload the file first via upload_file, then call OCR/transcription with uploaded_file_reference. The skill pack handles this automatically — install it the same way:
npx @lab94/frenchie install --agent claude
npx @lab94/frenchie install --agent cursor
npx @lab94/frenchie install --agent codexPick your agent
Every agent handles MCP a little differently. Pick yours for the exact invocation syntax and the gotchas we've hit in dogfood.
| Agent | Invoke | Example | Full guide |
|---|---|---|---|
| Claude Code | Slash command | /ocr TOR.pdf | claude-code → |
| Cursor | Natural language | Use Frenchie to OCR TOR.pdf | cursor → |
| Codex (Desktop / CLI / IDE) | @frenchie, /frenchie, NL | /frenchie TOR.pdf | codex → |
| Antigravity | /<server-name> | /frenchie TOR.pdf | antigravity → |
| VS Code Copilot | Slash command | /frenchie TOR.pdf | vscode → |
| Claude Desktop | Natural language | Use Frenchie to OCR TOR.pdf | claude-desktop → |
| Windsurf | Natural language | OCR TOR.pdf via Frenchie | windsurf → |
| Gemini CLI | Natural language | OCR TOR.pdf with Frenchie | gemini-cli → |
| Zed | Assistant panel | OCR TOR.pdf via Frenchie | zed → |
Something not working? See the symptom-first troubleshooting guide.
MCP tools
Frenchie exposes four core tools through MCP: ocr_to_markdown, transcribe_to_markdown, generate_image, and get_job_result. Your agent picks the right one automatically.
ocr_to_markdown
Converts a PDF or image to Markdown using OCR.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
file_path | string | Yes* | Local path to the file |
uploaded_file_reference | string | Yes* | Reference to an already-uploaded file |
api_key | string | No | Override the env API key for this request |
* One of file_path or uploaded_file_reference is required.
Supported file types:
application/pdf image/png image/jpeg image/webpCredits: 1 per page.
Routing:
| File | Mode | Behavior |
|---|---|---|
| Single-page PDF or small image | Sync | Result returned immediately |
| Multi-page PDF or large file | Async | Returns jobId — MCP auto-polls for up to 90s, then returns status |
Response (done — stdio mode):
{
"status": "done",
"savedTo": ".frenchie/invoice/result.md",
"wordCount": 312,
"imageCount": 1,
"creditsUsed": 4,
"resultExpiresAt": "2026-04-10T15:30:00Z"
}Stdio mode returns metadata only. The full markdown is written to savedTo on disk — read that file with your agent's file tool if you need the content. This keeps tool responses from flooding the agent's context window (a 17-page PDF drops from ~25k tokens to ~200 bytes).
Response (done — HTTP mode):
{
"status": "done",
"markdown": "# Invoice\n\n| Item | Amount |\n|------|--------|\n| Widget | $42.00 |",
"creditsUsed": 4,
"resultExpiresAt": "2026-04-10T15:30:00Z"
}HTTP mode keeps the markdown inline because remote clients have no local disk — there's no savedTo path they could read back. Images in the markdown are uploaded to server-side storage and referenced as frenchie-result: keys; fetch them with fetch_result_file.
Response (processing, both modes):
{
"status": "processing",
"jobId": "fr_job_29xk",
"estimatedCompletion": "2026-04-10T15:02:30Z"
}transcribe_to_markdown
Converts audio or video to a Markdown transcript.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
file_path | string | Yes* | Local path to the file |
uploaded_file_reference | string | Yes* | Reference to an already-uploaded file |
api_key | string | No | Override the env API key for this request |
* One of file_path or uploaded_file_reference is required.
Supported file types:
Audio: audio/mpeg audio/mp3 audio/m4a audio/wav audio/x-wav audio/wave audio/mp4 audio/webm
Video: video/mp4 video/quicktime (MOV) video/webmCredits: 2 per minute (rounded up).
Routing: Determined by duration + file size. Short clips may resolve sync. Longer files go async — chunked and reassembled automatically.
Response format: Same as ocr_to_markdown.
generate_image
Generates a single image from a text prompt using gpt-image-2.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
prompt | string | Yes | Plain-language description of the image to generate |
style | string | No | Free-text style direction; merged into the provider prompt by Frenchie |
size | enum | No | 1024x1024, 1536x1024, 1024x1536, auto |
quality | enum | No | low, medium, high, auto |
format | enum | No | png (default), jpeg, webp |
background | enum | No | transparent, opaque, auto (transparent rejected with jpeg) |
api_key | string | No | Override the env API key for this request |
Credits: 20 per image, refunded automatically on failure.
Limits: 50 images per hour, 250 images per day per user. v1 generates one image per call — call again for additional variants.
Response (done — stdio mode):
{
"status": "done",
"jobId": "fr_job_img_29xk",
"creditsUsed": 20,
"resultExpiresAt": "2026-04-23T15:30:00Z",
"result": {
"kind": "image",
"format": "png",
"mimeType": "image/png",
"size": "1024x1024",
"savedTo": ".frenchie/ramen-poster/generated.png"
}
}Stdio mode auto-downloads the image and writes it to result.savedTo next to your work. Tell the user the relative path; the file is already on disk.
Response metadata:
| Field | Mode | Description |
|---|---|---|
result.savedTo | stdio | Relative path to the generated image saved in the agent workspace |
result.imageUrl | HTTP | Short-lived URL for the generated image |
creditsUsed | both | Credits charged for the generation |
resultExpiresAt | both | When the result payload expires |
Response (done — HTTP mode):
{
"status": "done",
"jobId": "fr_job_img_29xk",
"creditsUsed": 20,
"resultExpiresAt": "2026-04-23T15:30:00Z",
"result": {
"kind": "image",
"format": "png",
"mimeType": "image/png",
"size": "1024x1024",
"imageUrl": "https://minio.getfrenchie.dev/...?signed=true"
}
}HTTP mode returns a presigned URL that expires in 30 minutes. Download the image to the user's machine (or render it inline) before the URL expires; the underlying object is deleted shortly after.
Missing prompts, insufficient credits, and provider failures use the standard MCP error envelope in the Errors section below.
get_job_result
Retrieves the result of an async job.
Most of the time you don't need to call this directly — the MCP server auto-polls for up to 90 seconds after submitting a job. You only need get_job_result if the auto-poll timed out and returned a "processing" status.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
job_id | string | Yes | The job ID from a previous async response |
api_key | string | No | Override the env API key for this request |
Response shape is the same as whichever tool submitted the job (see ocr_to_markdown above) — stdio returns metadata with savedTo, HTTP inlines markdown.
Response (done — stdio mode):
{
"status": "done",
"savedTo": ".frenchie/meeting/result.md",
"wordCount": 4821,
"imageCount": 0,
"creditsUsed": 96,
"resultExpiresAt": "2026-04-10T15:30:00Z"
}Response (done — HTTP mode):
{
"status": "done",
"markdown": "...",
"creditsUsed": 96,
"resultExpiresAt": "2026-04-10T15:30:00Z"
}Response (still processing):
{
"status": "processing",
"jobId": "fr_job_29xk",
"estimatedCompletion": "2026-04-10T15:02:30Z"
}Sync and async routing
You don't pick sync or async — Frenchie does.
Small files (single-page PDFs, short clips) resolve sync — the result comes back in the same request.
Larger files go async:
- Frenchie returns a
jobIdimmediately - The MCP server auto-polls every 3 seconds for up to 90 seconds
- If the job finishes within that window, you get the result back transparently
- If it doesn't, you get
{ "status": "processing", "jobId": "..." }— callget_job_resultlater
For most files, the auto-poll handles everything. Your agent doesn't need to know or care about the difference.

Supported files
OCR (→ Markdown):
| Type | MIME |
|---|---|
application/pdf | |
| PNG | image/png |
| JPEG | image/jpeg |
| WebP | image/webp |
Transcription (→ Markdown):
| Type | MIME |
|---|---|
| MP3 | audio/mpeg, audio/mp3 |
| M4A | audio/m4a |
| WAV | audio/wav, audio/x-wav, audio/wave |
| Audio MP4 | audio/mp4 |
| Audio WebM | audio/webm |
| MP4 video | video/mp4 |
| MOV | video/quicktime |
| WebM video | video/webm |
Image generation (text prompt → image):
| Output format | MIME |
|---|---|
| PNG (default) | image/png |
| JPEG | image/jpeg |
| WebP | image/webp |
Image generation takes a text prompt instead of a file upload. Sizes: 1024x1024, 1536x1024, 1024x1536, or auto.
Limits:
| Limit | Value |
|---|---|
| Max file size | 2 GB |
| Max concurrent jobs | 5 per user |
If you send an unsupported type, you'll get a 400 with "Unsupported file type for [filename]".
Credits
Everything runs on credits. No subscriptions, no monthly fees.
| Action | Cost |
|---|---|
| OCR | 1 credit per page ($0.01) |
| Transcription | 2 credits per minute, rounded up ($0.02) |
| Image generation | 20 credits per image ($0.20) |
$1 = 100 credits. That gets you 100 OCR pages, 50 minutes of transcription, or 5 generated images. New accounts start with 100 free credits. Failed jobs are refunded automatically.
Credit packs:
| Pack | Price | Per-credit |
|---|---|---|
| 100 credits | $1 | $0.010 |
| 500 credits | $5 | $0.010 |
| 2,000 credits | $20 | $0.010 |
| 10,000 credits | $100 | $0.010 |
First-time email signups get 100 free credits, once per email. No card required. Credits don't expire.
If your balance is too low for a job, the request fails with a 402:
{
"isError": true,
"content": [{ "type": "text", "text": "Insufficient credits" }]
}The response also includes your current balance and the required amount, so your agent (or you) knows exactly how many credits to top up.
Rate limits
Generous enough that you probably won't hit them. But here they are.
| Limit | Value |
|---|---|
| Requests per minute | 60 |
| Concurrent jobs per user | 5 |
| OCR pages per hour | 500 |
| Transcription minutes per hour | 120 |
| Image generations per hour | 50 |
| Image generations per day | 250 |
| Credits per day | 5,000 |
If you exceed a limit, you'll get a 422 with details on what you hit and when it resets.
For most users, these limits are invisible. If you're consistently hitting them, reach out — we want to hear about your use case.
Result expiry
Results are available for 30 minutes after first delivery. After that, Markdown content or generated-image URLs are deleted and the API returns 410 Gone.
{
"isError": true,
"content": [{ "type": "text", "text": "Job result has expired and is no longer available" }]
}The job record stays in the system (for your usage history), but the actual content is gone. If you need the result again, re-submit the file or prompt.
We don't store your files or results longer than necessary. That's the point.
Errors
When something goes wrong, Frenchie returns a standard MCP error:
{
"isError": true,
"content": [{ "type": "text", "text": "<error message>" }]
}Here's what you might see:
| HTTP | Condition | Message |
|---|---|---|
400 | Unsupported file type or bad payload | "Unsupported file type for [filename]" |
402 | Not enough credits | "Insufficient credits" + required/current balance |
408 | More than 5 concurrent jobs | "Concurrent job limit exceeded" |
410 | Result expired (>30 min) | "Job result has expired and is no longer available" |
422 | Rate limit exceeded | Error + quota details |
500 | Processing failed | "Frenchie job failed" + error details |
Most errors are self-explanatory. If you hit a 500, it's on us — retry once, and if it persists, let us know at support@getfrenchie.dev.
API key
Your API key is created when you sign up. Find it on your dashboard.
Format: fr_ followed by 32 hex characters.
fr_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4Two ways to authenticate:
- Environment variable (recommended): Set
FRENCHIE_API_KEYin your MCP config. The server reads it on startup. - Per-request parameter: Pass
api_keyin any tool call. This overrides the env key for that specific request — useful if you're managing multiple accounts.
Your key is hashed (SHA-256) before storage. We never store it in plaintext.
If your key is compromised, revoke it from the dashboard and generate a new one. Active jobs will finish, but new requests with the old key will be rejected.
Upgrading from an older Frenchie
Frenchie 0.3.0 ships three changes that affect existing installs: stdio tool responses are now metadata-only (markdown stays on disk), MCP configs use absolute npx paths so GUI agents work, and mcp --help / --version / --selftest now short-circuit instead of hanging.
If you're on 0.2.x
Re-run install. That's it.
cd <your project>
npx @lab94/frenchie@latest install --api-key fr_your_key_hereRestart your agent fully (not a soft reload) — MCP subprocesses persist across reloads on some agents.
If you're on 0.1.x (HTTP-primary)
0.2.0 switched the primary integration path from HTTP to stdio. The old HTTP entry at user or local scope will shadow the new project stdio entry — you have to remove it first:
Claude Code:
claude mcp list # check scope
claude mcp remove frenchie -s local # or -s user / -s project as neededCursor:
Edit ~/.cursor/mcp.json and remove the frenchie block that has a url field.
Codex:
Edit ~/.codex/config.toml and remove any [mcp_servers.frenchie] block with url = "...".
Then re-run install and restart your agent. Verify with claude mcp list (or equivalent) that the entry now shows a command: "/.../npx" rather than a URL.
For the full upgrade guide with every version gap (including 0.3.0 → 0.3.1 critical pin fix and 0.3.1 → 0.3.2 API key reuse), see the migration guide.
FAQ
My agent says "Frenchie tool not found" — what's wrong?
Your MCP server probably isn't connected yet. If you used npx @lab94/frenchie install, restart your agent — that picks up the new config. If you set up HTTP manually, check that the snippet is in the right file for your tool, the API key is correct, and the agent has been restarted.
Can I use Frenchie without MCP? Like a REST API?
Frenchie is designed MCP-first. Local coding agents run the server over stdio via npx @lab94/frenchie install. Hosted/web agents connect to https://mcp.getfrenchie.dev over HTTP. The skill pack helps Tier-A agents (Claude Code, Cursor, Codex, Antigravity, Claude Desktop) use both paths cleanly. We do not position Frenchie as a general-purpose REST wrapper.
How do I check my credit balance?
Log into your dashboard at getfrenchie.dev/dashboard. Your balance and usage history are on the main page. Or use the /frenchie-status slash command directly in your agent.
I sent a large PDF and got a "processing" status. Now what?
Wait a bit and call get_job_result with the jobId. The MCP server auto-polls for 90 seconds, so if you got processing back, the job is taking longer than usual. Most large files finish within 5 minutes.
Can I parse a password-protected PDF?
No. Remove the password first, then send it through. We can't unlock files on your behalf.
My OCR result has weird formatting. What gives?
OCR quality depends on the source. Clean digital PDFs produce near-perfect Markdown. Scanned documents, handwriting, and low-res images are best-effort — usually good, occasionally imperfect. If something looks consistently wrong, let us know.
Do you support [language X] for transcription?
Transcription handles most major languages automatically. We don't require you to specify a language — it's detected from the audio.
I'm hitting rate limits. Can you increase them?
Reach out at support@getfrenchie.dev with your use case. We're happy to discuss higher limits for legitimate workloads.