Every ERP has a REST API. Ours has one too. But what made everything click was building an MCP server on top of it — 55 tools that let any AI agent manage invoices, expenses, clients, taxes, and accounting through natural language.
This is the story of how I built it, what I learned, and why MCP might be the most underrated protocol in business software right now.
Why MCP, not just REST
I had a perfectly good REST API. Cursor pagination, field selection, 6 filter types, OpenAPI spec. But here is the thing: APIs are designed for developers. MCP is designed for AI agents.
With REST, you need to:
- Read the docs
- Figure out the auth
- Chain multiple endpoints
- Parse responses
- Handle errors
With MCP, an AI agent gets a tool catalog with typed parameters, calls what it needs, and gets structured responses. No docs required. The schema IS the documentation.
// REST: 4 calls, manual orchestration
const clients = await fetch('/api/v1/clients?q=Acme', { headers });
const client = await clients.json();
const invoice = await fetch('/api/v1/invoices', {
method: 'POST',
body: JSON.stringify({ clientId: client.data[0].id, items: [...] })
});
// MCP: 1 natural language instruction
// "Create an invoice for Acme Corp, 10 hours of consulting at €150/hr"
// The agent calls search_clients → create_invoice automatically
The architecture: 55 tools in 6 domains
I organized the tools by business domain, not by CRUD operations. This is the key insight — agents think in workflows, not in database tables.
| Domain | Tools | Examples |
|---|---|---|
| Invoicing | 12 |
create_invoice, send_invoice, record_payment, generate_pdf
|
| Expenses | 8 |
create_expense, scan_receipt (OCR), categorize_expenses
|
| Clients/CRM | 10 |
search_clients, create_client, client_health_score
|
| Products | 6 |
list_products, create_product, update_stock
|
| Accounting | 11 |
trial_balance, profit_and_loss, record_journal_entry
|
| System | 8 |
configure_webhook, get_dashboard, export_data
|
Every tool has structured output
This was a hard lesson. Early versions returned free-text descriptions. Agents would hallucinate field names, misparse amounts, confuse dates. Now every tool returns outputSchema + structuredContent:
// Tool definition with input AND output schema
{
name: 'create_invoice',
description: 'Create a new invoice for a client',
inputSchema: {
type: 'object',
properties: {
clientId: { type: 'string', description: 'Client ID or search term' },
items: {
type: 'array',
items: {
type: 'object',
properties: {
description: { type: 'string' },
quantity: { type: 'number' },
unitPrice: { type: 'number' },
taxRate: { type: 'number', description: 'Tax rate (e.g., 21 for 21% VAT)' }
}
}
},
currency: { type: 'string', default: 'EUR' }
},
required: ['clientId', 'items']
},
outputSchema: {
type: 'object',
properties: {
id: { type: 'string' },
number: { type: 'string' },
total: { type: 'number' },
status: { type: 'string', enum: ['draft', 'issued'] },
pdfUrl: { type: 'string' }
}
}
}
5 Resources: static context without API calls
Tools execute actions. But agents also need reference data — tax rates, filing deadlines, expense categories. Making an API call every time the agent needs to know the VAT rate is wasteful.
MCP Resources solve this. They are static data the agent can read at connection time:
frihet://tax/rates → VAT 21/10/4%, IGIC 7/3/0%, IRPF rates
frihet://tax/calendar → Quarterly filing deadlines (Modelo 303, 130, 420)
frihet://config/categories → 8 expense categories with deductibility rules
frihet://config/statuses → Invoice lifecycle: draft → issued → sent → paid
frihet://api/schema → All endpoints, auth methods, rate limits
The agent reads these once and has permanent context. No API calls consumed. No hallucinated tax rates.
5 Prompts: guided multi-step workflows
This is where it gets interesting. A Prompt chains multiple tools into a workflow:
monthly-close → Review unpaid invoices + categorize expenses + tax summary
onboard-client → Detect tax type by location + create record + welcome quote
quarterly-tax-prep → Gather period invoices + calculate VAT/IGIC + IRPF preview
overdue-followup → Find overdue invoices + draft reminders + schedule sends
collections-report → Aging analysis + risk scoring + action recommendations
A monthly close that takes an hour of clicking through dashboards becomes a 2-minute conversation.
Remote endpoint: zero install
The server runs at mcp.frihet.io — no npm install needed. Any MCP client can connect:
{
"mcpServers": {
"frihet": {
"url": "https://mcp.frihet.io/sse",
"headers": {
"Authorization": "Bearer fri_your_api_key"
}
}
}
}
Works with Claude Desktop, Cursor, Windsurf, Cline, Gemini CLI, and any MCP-compatible client.
Or install locally:
npx @frihet/mcp-server --api-key fri_your_key
What I learned building this
1. Domain-first, not CRUD-first. create_invoice is obvious. monthly_close is where the real value is. Agents need business workflows, not database operations.
2. Structured output is non-negotiable. Without outputSchema, agents hallucinate field names across chained tool calls. One wrong field name cascades into a broken workflow.
3. Resources eliminate the "context window tax". Every time an agent asks "what is the VAT rate in Spain?" and you answer, that is context window wasted. Resources pre-load this knowledge.
4. Security annotations matter. Each tool declares if it is readOnly or mutating, and its impact level (low/medium/high). Creating an invoice is high — the agent knows to confirm before executing.
5. The 55-tool ceiling is artificial. I could have 200 tools. But agents perform better with fewer, well-designed tools that compose cleanly. 55 covers every business workflow without overwhelming the tool catalog.
The numbers
- 55 tools across 6 business domains
- 5 resources for tax and configuration context
- 5 prompts for guided multi-step workflows
- v1.5.4 on npm, 1,796 downloads
- 17 languages supported in responses
- 365 fiscal positions across 123 countries
- Zero install via remote endpoint at mcp.frihet.io
Try it
The MCP server is open for any Frihet user (free plan included):
- Create a free account
- Generate an API key in Settings → Integrations
- Add the server config above to your MCP client
- Ask your AI: "Show me my dashboard"
The full documentation covers every tool, resource, and prompt with examples.
This is Part 1 of the "Building an AI-Native ERP" series. Next: how we architected a production ERP with React + Firebase that handles real money.
Frihet is a free, AI-native ERP for modern businesses. API Docs · SDK · MCP Server · GitHub
Top comments (0)