DEV Community

Ken Imoto
Ken Imoto

Posted on

MCP Can't Upload Files — Here's What I Learned Building a Production Workaround

TL;DR

  • Connected MCP to a cloud accounting API → expense registration went from 8 hours to 30 minutes
  • Tried to upload a receipt → 400 error (MCP can't do multipart/form-data)
  • This is a protocol-level limitation, not a server bug
  • Built CLI Skills (curl + jq) as a workaround → 90% token reduction + full file upload support

The Setup

I'm a freelance engineer. Every year, expense registration in cloud accounting software takes hours of manual work — checking receipts one by one, entering amounts, selecting account categories, uploading images. Last year it took about 8 hours.

This year, I connected MCP (Model Context Protocol) to the accounting API via Claude Code. The AI reads receipts, extracts amounts, determines account categories, and registers everything automatically.

8 hours → 30 minutes. I was thrilled.

Then I tried to attach receipt images.

The 400 Error

Tool: mcp_server__api_post
Parameters:
  path: /api/v1/receipts
  body: {"company_id": "xxx", "description": "Electricity July"}

Response:
  API Error: 400
  Detail: Content-Type must be "multipart/form-data"
Enter fullscreen mode Exit fullscreen mode

The accounting API requires multipart/form-data for file uploads. But the MCP tool sends Content-Type: application/json — hardcoded, no option to change it.

This Isn't a Server Bug

I dug into the MCP specification. The protocol itself has no file transfer mechanism.

Evidence from the official repo:

  • Discussion #1197 — "No defined way to pass files from client to server"
  • SEP-1306 — Proposed "Binary Mode Elicitation for File Uploads" — still unimplemented as of March 2026

This means every MCP server that wraps an API requiring file uploads has this same limitation. It's not specific to accounting — it applies to any service that needs multipart/form-data.

The Solution: CLI Skills

CLI Skills are simple: call the API directly via curl, filter responses with jq.

File upload (impossible via MCP, trivial via curl):

TOKEN=$(jq -r .access_token ~/.config/mcp-server/tokens.json)

# Step 1: Upload receipt (multipart/form-data)
RECEIPT_ID=$(curl -s -X POST "$API_URL/receipts" \
  -H "Authorization: Bearer $TOKEN" \
  -F "company_id=$COMPANY_ID" \
  -F "receipt=@/path/to/receipt.pdf" \
  -F "description=Electricity July" | jq -r '.receipt.id')

# Step 2: Register expense with receipt attached
curl -s -X POST "$API_URL/deals" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "company_id": "'$COMPANY_ID'",
    "issue_date": "2025-07-15",
    "type": "expense",
    "details": [{"account_item_id": YOUR_ID, "amount": 8432}],
    "receipt_ids": ['$RECEIPT_ID']
  }' | jq '{id: .deal.id, amount: .deal.amount, receipts: (.deal.receipts | length)}'
Enter fullscreen mode Exit fullscreen mode

And the token savings are massive

On top of solving the file upload problem, CLI Skills dramatically reduce token consumption. The MCP server was loading 270 API tool definitions into context every turn, and a single chart of accounts call returned 108,734 characters.

API MCP CLI Skill (jq filtered) Reduction
Transaction list (3 items) ~3,000 chars ~200 chars 93%
Chart of accounts 108,734 chars ~200 chars 99.8%
Expense registration ~800 chars/item ~80 chars/item 90%

Chart of accounts went from 108,734 characters to 200. That's a 500x reduction.

Per expense entry, MCP consumed ~17,500 input tokens vs ~2,500 for CLI Skills — 7x more, mostly from tool definition overhead.

MCP vs CLI Skills: When to Use Each

Use Case MCP CLI Skills
Exploring an unfamiliar API
Prototyping
Defined, recurring workflows
File uploads (multipart)
Token-sensitive production
Binary data

My recommendation: Start with MCP, graduate to CLI Skills.

MCP is incredible for discovery — having 270 API endpoints available means the AI fully understands the API spec. Use that knowledge to build your CLI Skills, then switch to CLI for production.

They're not competing. They're sequential.

Takeaway

MCP is revolutionary. It turned 8 hours of manual work into 30 minutes. But it's not magic — it has real constraints that matter in production.

Know what it can do. Know what it can't. Build accordingly.

SEP-1306 is tracking binary support. The protocol will get there — but for now, CLI Skills fill the gap.


Japanese version of this series (3 articles with more detailed examples) is available on Qiita.

Top comments (0)