I tried to attach a receipt to my tax filing. MCP said no.
I run a small company in Japan. Every year, I file taxes using an accounting service called freee -- think QuickBooks, but Japanese. When freee released an official MCP server, I got excited. AI-powered bookkeeping! Automatic expense categorization! The future!
And it was great. I told Claude "register the December electricity bill, 8,500 yen, utilities" and it created the journal entry in 3 seconds. A task that takes 2-3 minutes in the freee UI. I processed 32 expenses in about 3 minutes. It would've taken over an hour by hand.
Then I tried to attach a receipt.
Tool: mcp_server__api_post
Parameters: path: /api/v1/receipts
body: {"company_id": "xxx", "description": "Electricity Jul"}
Response: API Error: 400
Detail: Content-Type must be "multipart/form-data"
freee's API supports file uploads via multipart/form-data. But MCP's JSON-RPC protocol can't send binary data. And in Japanese tax law, attaching receipts isn't optional -- it's a legal obligation.
I ended up writing a separate Python script just for receipt uploads. MCP handled 90% of my tax filing. The remaining 10% broke in a way that matters.
But here's the question that kept bugging me: is this just a freee problem, or is MCP itself broken for file uploads?
I tested 6 more services to find out.
The protocol gap
Before jumping into results, let me show you what's actually missing.
MCP tools can return three content types: TextContent, ImageContent (base64), and EmbeddedResource.
Notice what's not on that list? FileContent. It doesn't exist.
I'm not the only one who noticed. In MCP Discussion #1197, an Anthropic team member responded:
"I don't think you're overlooking anything, your use-case is currently finicky in the current state of the protocol."
Translation: yeah, it's broken. We know.
graph LR
subgraph "MCP Protocol Content Types"
A[TextContent] --> Z[✅ Supported]
B[ImageContent<br/>base64] --> Z
C[EmbeddedResource] --> Z
D[FileContent] --> X[❌ Does not exist]
end
subgraph "What services need"
E[Receipt PDF] --> D
F[Screenshot PNG] --> D
G[Log file] --> D
H[Attachment] --> D
end
style D fill:#ff6b6b,stroke:#c92a2a,color:#fff
style X fill:#ff6b6b,stroke:#c92a2a,color:#fff
7 services, 0 passes
Fully failed: 4 services
1. freee (accounting)
As described above. The API supports multipart/form-data, but MCP's JSON-RPC can't produce it. Receipt attachment -- a legal requirement -- is impossible through MCP.
2. Jira / Confluence (project management)
Atlassian ships an official remote MCP agent. Their community response is blunt:
"File uploads or image attachments via the MCP Remote Agent are not supported."
But it gets worse. The community MCP server mcp-atlassian requires the uploaded file to exist on the MCP server's filesystem. That means if you run the MCP server in Docker -- which, in 2026, is the default for anything -- file uploads silently fail:
{
"attachment_results": {
"failed": [{
"filename": "grafana.png",
"error": "File not found: /home/user/jira-mcp/grafana.png"
}]
}
}
Your file is on the host. The MCP server is in a container. Different filesystem. Game over.
This is the kind of bug that doesn't show up in local development demos but kills you in production. Containerization is not an edge case anymore -- it's the baseline. Any file upload mechanism that assumes shared filesystems is broken by design.
3. Notion (documentation)
Notion's official MCP docs are refreshingly honest:
"Image and file uploads are not currently supported in Notion MCP, but this is on our roadmap."
"On our roadmap" is corporate for "not happening soon." But at least they said it out loud.
4. GitHub (code management)
This one hurt the most. As someone who uses Claude Code daily, I wanted one thing: attach a screenshot to a PR. Visual diffs, UI changes, error screenshots. The workflow every developer wants.
From github-mcp-server Issue #738:
"The MCP needs to be able to upload images and at the moment that doesn't seem to be possible. By doing this we'll be able to have more descriptive / visual PRs."
Claude Code can generate code, run tests, create PRs -- but it can't attach a screenshot showing what changed. That last mile is missing.
Partially working: 3 services (with hacks)
5. Gmail
Some third-party Gmail MCP servers let you specify a local file path for attachments. It works -- but only because the MCP server reads from the local filesystem behind the protocol's back. It's a hack, not a feature.
6. Google Drive
Similar story. Community implementations like google-drive-mcp access the local filesystem and forward files to the Drive API. The file never travels through the MCP protocol itself. If your MCP server runs remotely, this breaks.
7. Slack
CData's Slack MCP server offers an UploadFile tool. Slack's official MCP server? Search and messaging only. No file uploads.
The scorecard
| Service | Category | Official MCP | File Upload | How critical |
|---|---|---|---|---|
| freee | Accounting | Yes | No | Legal obligation |
| Jira | Project mgmt | Yes | No | Core workflow |
| Notion | Docs | Yes | No | High frequency |
| GitHub | Code | Yes | No | High frequency |
| Gmail | Yes | Hack | High frequency | |
| Google Drive | Storage | Yes | Hack | Core feature |
| Slack | Chat | Yes | Hack | High frequency |
Full support: 0/7. Partial (filesystem hacks): 3/7. Completely blocked: 4/7.
This is not a freee problem. This is a protocol problem.
Why MCP blocks file uploads (and it's partly intentional)
Three reasons MCP doesn't support file uploads, and they're not all bugs.
1. JSON-RPC is text-first by design
MCP uses JSON-RPC over stdio or HTTP. The protocol is optimized for structured text and JSON. Binary data transfer was never in scope -- it's a deliberate design boundary, not an oversight.
┌─────────────┐ JSON-RPC ┌─────────────┐
│ MCP Client │ ──────────────► │ MCP Server │
│ (Claude) │ TextContent │ (freee) │
│ │ ImageContent │ │
│ │ EmbResource │ │
│ │ │ │
│ │ FileContent? │ │
│ │ ──── ✗ ──── │ │
└─────────────┘ └─────────────┘
2. Security risks multiply with file transfers
Opening a file upload channel creates real attack surface:
-
Command injection through crafted file paths (
../../etc/passwd,image.jpg; rm -rf /) - Data exfiltration -- an agent silently uploading files you didn't intend to share
- Malware distribution through uploaded executables
These aren't theoretical. MCP servers already have a CVE problem (30 CVEs in 60 days, per Adversa AI's March 2026 report). Adding file transfer before hardening authentication would be reckless.
3. Context window economics
If you try the obvious workaround -- base64 encode a file and pass it as text -- you hit math:
- Base64 adds 33% overhead. A 1 MB image becomes 1.33 MB of text
- 1.33 MB of text consumes tens of thousands of tokens
- Multiply by the number of files in a batch operation
I tried this with receipt PDFs. After sending a few base64-encoded files, Claude's context window filled up and it forgot what we were working on. "What task are we doing again?" is not what you want to hear mid-tax-filing.
SEP-1306: the proposed fix (still in draft)
SEP-1306, submitted in August 2025, proposes a binary mode for MCP. The design is actually clean:
{
"method": "elicitation/create",
"params": {
"mode": "binary",
"message": "Please upload the receipt",
"requestedSchema": {
"properties": {
"receiptFile": {
"type": "file",
"accept": ["image/*", "application/pdf"],
"maxSize": 5242880
}
}
},
"uploadEndpoints": {
"receiptFile": {
"url": "https://server.example.com/mcp/upload/session-abc123",
"method": "POST",
"uploadId": "550e8400-e29b-41d4-a716-446655440000"
}
}
}
}
The key insight: binary data never flows through the MCP protocol. Instead, the server returns a signed upload URL, and the client sends the file directly. The protocol stays text-only. The file transfer happens out-of-band.
This is basically the "signed URL" workaround I ended up building manually for freee -- but standardized at the protocol level.
Status as of March 2026: still draft. No timeline for adoption. Even after the spec is accepted, every MCP client (Claude Desktop, VS Code, etc.) needs to implement it.
What you can do today
If you're hitting this wall right now, here are the workarounds ranked by practicality:
1. CLI hybrid (the "it actually works" option)
Use MCP for what it's good at (data entry, queries, orchestration) and shell out to a script for file uploads. This is what I do for freee:
# MCP creates transactions, script uploads receipts
while IFS=, read -r tx_id receipt_file; do
python3 upload_receipt.py \
--transaction-id "$tx_id" \
--file "receipts/$receipt_file"
done < transaction_ids.txt
Not elegant. Works every time.
2. Signed URL pattern (if the API supports it)
If your target service offers pre-signed upload URLs, you can build a two-step flow: MCP tool returns the URL, a separate client uploads the file. Closest to what SEP-1306 will eventually standardize.
3. Base64 for tiny files only
Under a few hundred KB, you can get away with base64-encoding files as text. Anything larger and you're burning tokens and risking context window amnesia.
4. Shared filesystem (local dev only)
If MCP client and server share a filesystem, you can pass file paths. Breaks immediately in Docker, remote servers, or any production setup.
The bigger picture
MCP is doing something important -- giving AI agents structured access to external services. The protocol is one year old and already has 500+ servers. That's real adoption.
But the file upload gap isn't a minor inconvenience. Files are how work gets done. Receipts, screenshots, attachments, exports. When 0 out of 7 major services can handle file uploads, it tells you this isn't a "we'll fix it in the next sprint" situation. It's a fundamental protocol limitation that the MCP team is aware of but hasn't resolved.
SEP-1306 points in the right direction. The signed-URL design avoids the security pitfalls of piping binary through JSON-RPC. But "proposed" and "shipped" are different things, and the gap between them can be measured in quarters, not weeks.
For now, plan for hybrid workflows. MCP handles the 90%. You handle the 10%.
Have you hit this wall?
I'm curious -- has anyone found a clean solution I missed? A service that actually handles file uploads through MCP without filesystem hacks? Or are we all writing the same wrapper scripts?
Drop a comment or find me on X (@kenimo49).
This article is based on Chapter 5 of my book MCP Security in Practice: What OWASP Won't Tell You About AI Tool Integrations. It covers the full 7-service test, OWASP MCP Top 10, token cost analysis, and production workarounds.
Top comments (0)