Skip to content

MCP Server (Claude Desktop / Cursor / Moraya AI)

Picora ships a Model Context Protocol server (@picora/mcp-server) that lets AI assistants — Claude Desktop, Cursor, Moraya’s built-in AI — directly call your Picora hosting account as tools. No glue code, just one config snippet.

Typical prompts:

“Upload all images in ~/Pictures/blog-2026/ to Picora and give me a markdown list of URLs.”

“How much storage have I used this month?”

“Find all videos longer than 10 minutes — preview which ones to delete.”

Quick start

Open center.picora.me / Integration → click Create Key.

For AI assistants, choose the read + write scope (no DELETE) so the AI can upload and update but cannot accidentally delete your files. You can always create a separate read_write_delete Key for tasks that genuinely need to delete.

2. Configure your client

Claude Desktop

Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):

{
"mcpServers": {
"picora": {
"command": "npx",
"args": ["-y", "@picora/mcp-server"],
"env": {
"PICORA_API_KEY": "sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}
}
}
}

Restart Claude Desktop. The sidebar should show 8 picora.* tools.

Cursor

.cursor/mcp.json in your workspace root:

{
"mcpServers": {
"picora": {
"command": "npx",
"args": ["-y", "@picora/mcp-server"],
"env": {
"PICORA_API_KEY": "sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}
}
}
}

Moraya v0.35+

Settings → AI Assistant → toggle “Picora MCP”. The currently bound API Key is used automatically.

3. China region

Set PICORA_API_URL if you’re on the China deployment:

"env": {
"PICORA_API_KEY": "sk_live_...",
"PICORA_API_URL": "https://api.picora.cn"
}

Available tools

ToolWhat it doesRequired scope
picora.upload_imageUpload a local image (jpg/png/webp/gif/avif)read_write
picora.upload_videoUpload a video — async, poll get_media for statusread_write (Pro+)
picora.upload_audioUpload audio (mp3/m4a/wav/flac/ogg), syncs ID3 cover artread_write (Pro+)
picora.list_mediaList images / videos / audio with cursor paginationread
picora.get_mediaGet one item’s full detail (e.g. video transcoding status)read
picora.update_mediaRename or change visibility of an itemread_write
picora.delete_mediaDelete items — defaults to dryRun=true for safetyread_write_delete
picora.get_usageCurrent storage / bandwidth / planread

Prompt templates (try these)

Batch upload images

“Upload every .jpg and .png in /Users/me/Desktop/blog-cover-shots/ to Picora as public images. Then output a markdown list mapping local filename → cloud URL.”

Audit storage

“How much of my Picora storage is used? List my 5 largest videos by sizeBytes.”

Find and clean up by size (with preview)

“List all images smaller than 200 bytes — these are likely accidental tiny test uploads. Show me the list, then if I say yes, delete them.”

“Get my latest 10 image uploads from Picora. Output a markdown gallery with thumbnails as embedded images.”

Mass-flip visibility

“Find all videos uploaded in March 2026 and set them all to private.”

Safety: how delete_media works

delete_media is destructive — once an image / video / audio is deleted, the CDN URL returns 404 and there is no undo.

To make sure AI doesn’t surprise-delete things, the tool uses a two-step protocol:

  1. First call is dryRun=true (default). The tool returns a willDelete preview list. The AI reads this and asks you to confirm.
  2. Second call with dryRun=false actually deletes — only after you say yes.

If your API Key has scope read_write (no DELETE), delete_media returns INSUFFICIENT_SCOPE immediately, regardless of dryRun.

Troubleshooting

Claude Desktop / Cursor doesn’t show the picora tools

  • Make sure the config file is in the right location and valid JSON (no trailing commas).
  • Make sure npx is on PATH (run which npx in a terminal).
  • Restart the host app fully — quitting from the menubar isn’t always enough on macOS.
  • Check the host’s MCP log (Claude Desktop: ~/Library/Logs/Claude/mcp-*.log).

Server exits immediately on startup

Inspect stderr in the MCP log:

  • [picora-mcp] PICORA_API_KEY is required — the env var isn’t reaching the subprocess. Double-check the JSON.
  • [picora-mcp] Failed to verify API Key: ... — the key is invalid, revoked, or PICORA_API_URL is wrong.

Upload times out

By default, single uploads time out at 60 seconds. For very large videos on slow networks, increase via PICORA_MCP_UPLOAD_TIMEOUT (milliseconds):

"env": {
"PICORA_API_KEY": "sk_live_...",
"PICORA_MCP_UPLOAD_TIMEOUT": "180000"
}

INSUFFICIENT_SCOPE errors

Your API Key was created with read-only or read+write scope. Either:

  • Use only the tools that match your scope (see the table above), OR
  • Create a new Key with read_write_delete scope from center.picora.me / Integration.

RATE_LIMITED errors

Picora rate-limits API calls per Key (read 600/min, upload 60/min, mutation 120/min, hourly cap 5000). The MCP server has built-in client-side queuing to avoid most rate limits, but if you batch hundreds of operations the AI will see RATE_LIMITED with a retryAfter value — Claude usually handles this gracefully, just wait.

Chinese / non-ASCII filenames render as garbage

Make sure LANG and LC_ALL are set to a UTF-8 locale (en_US.UTF-8 works fine). On Linux:

Terminal window
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8

Compatibility matrix

@picora/mcp-serverPicora APINode.js@modelcontextprotocol/sdk
0.1.x≥ v0.12≥ 20.0^0.6.0

When Picora’s API has a breaking change we bump the MCP server’s minor version and document migration steps in CHANGELOG.md.

Privacy

The MCP server runs locally as a subprocess of your AI host app:

  • No telemetry, no analytics. We don’t phone home.
  • No file access outside filePath. We only read paths the AI explicitly passes.
  • API Key stays in env vars and HTTP Authorization headers. It is never written to disk by the MCP server.
  • Logs go to stderr only, captured by the host app for local debugging — never sent over the network.

Source: picora-service / apps/mcp.