DEV Community

Cover image for freee + MCP: I Automated My Japanese Tax Return With Claude Code — 3 Things That Broke in Production
Ken Imoto
Ken Imoto

Posted on • Originally published at kenimoto.dev

freee + MCP: I Automated My Japanese Tax Return With Claude Code — 3 Things That Broke in Production

Context for readers outside Japan

Japan's kakutei shinkoku is the annual self-employed tax return — the local equivalent of the US Schedule C or the UK Self-Assessment. Every sole trader files one. The bookkeeping underneath it is the same problem every self-employed engineer knows: categorize a year of receipts correctly, or the tax office comes back with questions.

freee is the country's dominant cloud accounting SaaS for this. In late 2025 they shipped an MCP server. So this year I let Claude Code drive it, and filed the whole thing with the AI in the loop.

That is the part that worked. Below is the part that did not.

The setup that worked

The MCP wiring is standard OAuth + PKCE. Claude Desktop config:

{
  "mcpServers": {
    "freee": {
      "command": "npx",
      "args": ["@him0/freee-mcp"],
      "env": {
        "FREEE_CLIENT_ID": "your_client_id",
        "FREEE_CLIENT_SECRET": "your_client_secret"
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

The browser opens once for OAuth consent. After that, refresh tokens keep the session alive.

The first useful moment came about ten seconds after setup. I typed one line:

Log a ¥3,200 stationery purchase from Amazon on Dec 15.
Enter fullscreen mode Exit fullscreen mode

Claude picked the account (消耗品費 / office supplies), computed the tax code (課対仕入10% / 10% input-taxable purchase), and wrote it to freee. Three seconds. Doing that by hand through the freee UI takes two or three minutes.

Ten in a row, all correctly categorized:

Input Account picked Correct?
Amazon stationery Office supplies
Taxi fare Travel expense
Coworking monthly Rent
Domain renewal Communication
Accountant fee Professional fee
AWS invoice Communication
Business dinner Entertainment
Coffee (client meeting) Meeting expense
Book (technical) Books/subscriptions
Printer paper Office supplies

Ten for ten. Batch mode was better — I threw a CSV of 32 transactions at it and had them all in freee in about three minutes. Manual: over an hour.

That is the 90% story. Now the 10%.

Break #1: receipts cannot be attached

Japanese tax law requires receipts (ryōshūsho) to be attached to bookkeeping entries. This is not optional. Storage of the digital copy is legally mandatory.

freee's REST API supports it. MCP does not. The moment I tried:

Tool: mcp_server__api_post
Path: /api/v1/receipts
Body: { "company_id": "xxx", "description": "electric bill, July" }
Response: 400 — Content-Type must be "multipart/form-data"
Enter fullscreen mode Exit fullscreen mode

MCP's JSON-RPC transport cannot send multipart/form-data. Binary uploads are not in the protocol. This is not a freee bug — this is the shape of MCP itself.

Fix: I stopped trying. Receipt uploads went into a separate Python script that hits freee's REST endpoint directly with requests.post(files={...}). MCP handles the bookkeeping; the CLI handles the file uploads. Two tools, clean seam.

Break #2: 270 tools, several thousand tokens gone before "hello"

The freee MCP server exposes 270 tools. Accounting, HR, invoicing, timekeeping — the whole company platform. All 270 schemas load into every conversation turn.

I only need maybe 8 of them for a tax return. The other 262 sit in context taking up space I would rather spend on transaction descriptions.

This is a real 2026 pattern: SaaS vendors are shipping "one MCP server for the whole product" instead of narrow, task-scoped servers. It works right up until you connect a second one. Two of these and you're spending most of the context window on tool descriptions before the model reads a single receipt. I know this because I ran freee alongside a second MCP for two days before I noticed Claude cheerfully "forgetting" transactions I'd told it about three turns earlier.

Fix: Client-side allow-listing of tool names. Claude Desktop supports this via config. Not every MCP client does — Cursor did at the time, some smaller clients didn't. If your client doesn't, you're stuck with the full 270.

Break #3: rate limits, silently

freee's API rate-limits per minute. Batch-processing 32 transactions in a row hits it. What happens then depends on the tool:

  • REST API: 429 with Retry-After
  • MCP tool call: the tool returns an error string. Claude sees a text failure and moves on. No backoff, no retry, no queue.

MCP has no rate-limit primitive in the protocol. There is no retry_after field for a tool response to signal "wait 30s then try me again." So the model just fails the row and continues.

Fix: I put a 1-second sleep between transaction writes in my prompt ("write these one at a time, wait one second between them"). Ugly, but it holds. The 2026 story here is that MCP gateways with per-tool token-bucket enforcement are starting to appear — that is where the fix eventually lives, not in the model or the server.

What the final workflow actually looks like

The end state was a hybrid, not a pure-MCP flow:

  1. Import bank/card CSV → my script, not MCP
  2. Categorize + write transactions to freee → MCP + Claude
  3. Upload receipt PDFs → my script, not MCP
  4. Reconciliation & review → MCP + Claude
  5. Final tax form generation → freee's own UI (with MCP-produced data underneath)

MCP owns the parts where language understanding matters — reading a messy expense description, picking an account, catching a typo in a memo. The CLI owns the parts where MCP structurally cannot go — files, throttling, orchestration.

"MCP will replace my accountant" turned out to be the wrong framing. "MCP is a 90% automation layer, the last 10% is CLI glue" is the right one. And the last 10% is where all the design work is.

The one-paragraph summary

freee + MCP + Claude Code cut my bookkeeping time from a weekend of drudgery to about two hours. Three things broke in production: no binary uploads, 270-tool context bloat, silent rate-limit failures. All three had workarounds. None of the workarounds lived inside MCP. If you're building an MCP integration for a real business process, plan for the seams before you plan for the happy path.


The full MCP threat model, plus the seven-service comparison of where each MCP server actually hits these ceilings, is here:

MCP Security in Practice — production integration patterns and the failure modes nobody talks about

Top comments (0)