DEV Community

berthelius
berthelius

Posted on • Originally published at frihet.io

I Built an MCP Server for My ERP — Here's What I Learned

There's a new way to manage a business. Not an app, not a dashboard, not another browser tab. You talk to your AI assistant and it accesses your invoices, expenses, and clients directly.

I built Frihet, an AI-native ERP for freelancers. When Anthropic released the MCP protocol, I knew the ERP needed to speak it natively. So I built an MCP server. Here's what I learned.

What the MCP Server Does

31 tools across 6 resources:

Resource Operations
Invoices Create, read, update, delete, search, list
Expenses Create, read, update, delete, list
Clients Create, read, update, delete, list
Products Create, read, update, delete, list
Quotes Create, read, update, delete, list
Webhooks Create, read, delete, list

In practice, you tell Claude:

"Create an invoice for Acme Ltd for 2,500 EUR for January consulting"

The invoice is created with correct tax details, VAT calculated, and invoice number assigned. No browser needed.

Architecture

User → AI Assistant (Claude/Cursor)
         ↓ MCP Protocol
    @frihet/mcp-server (npm)
         ↓ REST API calls
    api.frihet.io/v1
         ↓ Auth (X-API-Key)
    Firebase Cloud Functions
         ↓
    Firestore
Enter fullscreen mode Exit fullscreen mode

The MCP server is a thin bridge between any MCP client and Frihet's REST API. It runs in two modes:

Local (stdio): For Claude Code, Cursor, Windsurf. Runs as a local process.

{
  "mcpServers": {
    "frihet": {
      "command": "npx",
      "args": ["-y", "@frihet/mcp-server"],
      "env": { "FRIHET_API_KEY": "fri_your_key" }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Remote (streamable-http): Via mcp.frihet.io, a Cloudflare Worker that accepts MCP connections from anywhere.

What I Learned Building It

1. MCP tools need great descriptions

The AI reads your tool descriptions to decide which tool to use. Vague descriptions = wrong tool selection. I spent more time writing tool descriptions than writing the actual tool code.

Bad:

name: "create_invoice"
description: "Creates an invoice"
Enter fullscreen mode Exit fullscreen mode

Good:

name: "create_invoice"
description: "Create a new invoice for a client. Requires client_id, at least one line item with description and amount. Optionally accepts tax_rate, due_date, currency, and notes. Returns the created invoice with assigned number and calculated totals."
Enter fullscreen mode Exit fullscreen mode

2. Input validation is critical

AI models will send unexpected input shapes. Zod schemas for every tool input saved me from countless edge cases. The AI doesn't always respect your schema perfectly, so validate aggressively and return clear error messages.

3. Bilingual tool responses matter

Frihet supports ES and EN. The MCP server accepts a locale parameter and returns responses in the user's language. This sounds trivial but it makes a huge difference in UX when the AI passes through the response.

4. The remote MCP is harder than local

Local stdio is straightforward. The remote Cloudflare Worker using streamable-http had more edge cases: CORS, authentication headers, connection timeouts, and the fact that MCP sessions are stateful but Cloudflare Workers are stateless.

5. Open source builds trust

When your MCP server accesses financial data, users need to trust it. Making it MIT and putting the code on GitHub was a deliberate choice. Users can inspect exactly what the server does with their data. No black boxes.

The Difference Between API and MCP

Frihet already had a REST API. The MCP server is fundamentally different:

  • API = for developers who write code
  • MCP = for anyone who uses an AI assistant

A freelancer who doesn't code but uses Claude daily can now manage invoicing without opening a browser. A dev team in Cursor can query company data without switching context.

Real Usage Patterns I've Seen

  1. "Create invoice for [client] for [amount]" — Most common. The AI resolves the client by name, applies tax rules, assigns the number.

  2. "Show unpaid invoices older than 30 days" — Uses search_invoices with filters. The AI formats results into a readable summary.

  3. "What's my total revenue this quarter?" — Combines multiple API calls. The AI calculates totals from invoice data.

  4. "Record a 47.50 EUR fuel expense from yesterday" — Creates expense with correct date, category suggestion, and VAT deduction.

Try It

npx @frihet/mcp-server
Enter fullscreen mode Exit fullscreen mode

Source: github.com/berthelius/frihet-mcp
npm: @frihet/mcp-server
Docs: docs.frihet.io

MIT license. 31 tools. Works with Claude Code, Cursor, Windsurf, and any MCP client.

If you're building an MCP server for your product, happy to share more details in the comments.


I'm Viktor, solo founder of Frihet — an AI-native ERP for freelancers. Free plan, no credit card: app.frihet.io

Top comments (0)