MCP Server
Frihet's MCP server implements the Model Context Protocol so AI assistants can interact directly with your ERP. Create invoices, log expenses, look up clients, or manage quotes — all from your IDE or terminal, in natural language.
Current version: 1.5.1 | 52 tools | 11 resources | 10 prompts | Structured output | Compatible with 30+ AI agents.
You: "Create an invoice for TechStart Ltd, 40 hours of consulting at 75 EUR/hour, due March 1st"
Claude: Done. Invoice INV-2026-089 created. Total: 3,000.00 EUR + 21% VAT = 3,630.00 EUR.
What to expect (and what not)
The MCP server is a stateless bridge between your AI assistant and the Frihet REST API. Each tool call translates into an HTTP request to the API.
What it DOES:
- Full CRUD on invoices, expenses, clients, products, quotes, and webhooks
- Invoice search by client name
- Access to reference data (tax rates, fiscal calendar, categories)
- Guided workflows (monthly close, tax prep, overdue follow-up)
What it DOES NOT:
- Document OCR (that's handled by the AI assistant inside the app)
- PDF generation (use the REST API directly:
GET /v1/invoices/:id/pdf) - Payment processing (payments are handled via Stripe Connect in the app)
Requirements
- Frihet account with API access (paid plans)
- API key generated from the dashboard
Getting your API key
- Sign in at app.frihet.io
- Go to Settings > Developers > API Keys
- Click Create API key
- Copy the key (starts with
fri_) — it is only shown once
Installation
Universal (30+ agents)
The fastest way to install the MCP server and the business skill:
npx skills add Frihet-io/frihet-mcp
Works with Claude Code, Cursor, Copilot, Codex, Windsurf, Gemini CLI, Goose, Roo Code, and 30+ more agents.
MCP Registry
The server is registered as io.frihet/erp on the official MCP Registry. MCP clients that support the registry can discover and install it automatically by its canonical name.
Direct npx
npx -y @frihet/mcp-server@latest
No global installation needed. npx downloads and runs the latest version automatically.
Manual configuration
There are two connection modes: local (the server runs on your machine via npx) and remote (direct connection to mcp.frihet.io, no installation needed).
Local (stdio)
{
"mcpServers": {
"frihet": {
"command": "npx",
"args": ["-y", "@frihet/mcp-server@latest"],
"env": {
"FRIHET_API_KEY": "fri_your_key_here"
}
}
}
}
Remote (streamable-http)
{
"mcpServers": {
"frihet": {
"type": "streamable-http",
"url": "https://mcp.frihet.io/mcp",
"headers": {
"Authorization": "Bearer fri_your_key_here"
}
}
}
}
Configuration by client
The JSON structure is identical across all clients. Only the config file location changes.
| Client | Config file |
|---|---|
| Claude Code | ~/.claude/mcp.json |
| Claude Desktop | ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) |
| Cursor | .cursor/mcp.json or ~/.cursor/mcp.json |
| Windsurf | ~/.windsurf/mcp.json |
| Cline | VS Code settings or .cline/mcp.json |
| Codex CLI | ~/.codex/config.toml (MCP section) |
If you use Claude Code, you can add the server with a single command:
claude mcp add frihet -- npx -y @frihet/mcp-server@latest
Then set the FRIHET_API_KEY variable in the resulting ~/.claude/mcp.json file.
Environment variables
| Variable | Required | Default |
|---|---|---|
FRIHET_API_KEY | Yes | — |
FRIHET_API_URL | No | https://api.frihet.io/v1 |
FRIHET_MCP_DEBUG | No | 0 |
FRIHET_API_URLis useful if you're pointing to a staging environment or a custom instance. Must behttps://with a hostname underfrihet.io.FRIHET_MCP_DEBUG=1enables debug-level logs (useful for troubleshooting).
OAuth flow
The MCP server supports OAuth authentication in addition to manual API key setup. When an MCP client initiates the OAuth flow:
- The user authenticates via Firebase Auth (email, Google, GitHub, or Microsoft)
- The client sends the session token to
POST /api/oauth/api-key - The server provisions a new
fri_xxx...key labeled "MCP OAuth" with a 365-day expiration - The key is used automatically for subsequent calls
This lets users connect the MCP server without manually copying keys.
Available tools (44)
The server exposes 52 tools organized across 7 categories. Every tool includes structured output (outputSchema + structuredContent) for reliable parsing, plus security and content annotations. Bilingual (EN/ES).
Invoices (6 tools)
| Tool | Description | Key parameters |
|---|---|---|
list_invoices | List invoices with pagination | limit, offset |
get_invoice | Get a single invoice by ID | id |
create_invoice | Create an invoice with line items | clientName, items[] (description, quantity, unitPrice), status, dueDate, notes, taxRate |
update_invoice | Update fields on an invoice (partial) | id, plus any field to modify |
delete_invoice | Permanently delete an invoice | id |
search_invoices | Search invoices by client name | clientName, limit, offset |
Possible invoice statuses: draft, sent, paid, overdue, cancelled
Expenses (5 tools)
| Tool | Description | Key parameters |
|---|---|---|
list_expenses | List expenses with pagination | limit, offset |
get_expense | Get a single expense by ID | id |
create_expense | Log a new expense | description, amount, category, date, vendor, taxDeductible |
update_expense | Modify an existing expense (partial) | id, plus any field to modify |
delete_expense | Permanently delete an expense | id |
Clients (5 tools)
| Tool | Description | Key parameters |
|---|---|---|
list_clients | List all clients | limit, offset |
get_client | Get a single client by ID | id |
create_client | Register a new client | name, email, phone, taxId, address (street, city, state, postalCode, country) |
update_client | Update client details (partial) | id, plus any field to modify |
delete_client | Permanently delete a client | id |
Products (5 tools)
| Tool | Description | Key parameters |
|---|---|---|
list_products | List products and services | limit, offset |
get_product | Get a single product by ID | id |
create_product | Create a product or service | name, unitPrice, description, taxRate |
update_product | Modify an existing product (partial) | id, plus any field to modify |
delete_product | Permanently delete a product | id |
Quotes (5 tools)
| Tool | Description | Key parameters |
|---|---|---|
list_quotes | List quotes | limit, offset |
get_quote | Get a single quote by ID | id |
create_quote | Create a quote for a client | clientName, items[] (description, quantity, unitPrice), validUntil, notes, status |
update_quote | Modify a quote (partial) | id, plus any field to modify |
delete_quote | Permanently delete a quote | id |
Possible quote statuses: draft, sent, accepted, rejected, expired
Webhooks (5 tools)
| Tool | Description | Key parameters |
|---|---|---|
list_webhooks | List configured webhooks | limit, offset |
get_webhook | Get webhook configuration | id |
create_webhook | Register a webhook endpoint | url, events[], active, secret |
update_webhook | Modify an existing webhook | id, plus any field to modify |
delete_webhook | Permanently delete a webhook | id |
Available webhook events: invoice.created, invoice.updated, invoice.deleted, invoice.paid, expense.created, expense.updated, expense.deleted, client.created, client.updated, client.deleted, product.created, product.updated, product.deleted, quote.accepted. See Webhooks for payload format and HMAC verification.
Available resources (8)
MCP resources are reference data the assistant can query without making API calls. They are static and always accessible.
| Resource | URI | Description |
|---|---|---|
| API schema | frihet://api/schema | Endpoint summary, authentication, rate limits, and error codes |
| Tax rates | frihet://tax/rates | VAT (21/10/4%), IGIC (7/3/0%), IPSI, intra-community, IRPF (15%/7%) |
| Tax calendar | frihet://tax/calendar | Quarterly filing dates (Modelo 303, 130, 420, 390) |
| Expense categories | frihet://config/expense-categories | 8 categories with deductibility rules and tax treatment |
| Invoice statuses | frihet://config/invoice-statuses | Status flow (draft, sent, paid, overdue, cancelled) with triggers |
| Vendors | frihet://config/vendors | List of registered vendors with tax and contact data |
| Active integrations | frihet://config/integrations | Status of connected integrations (Stripe, Shopify, etc.) |
| Business configuration | frihet://config/business | Company tax data, fiscal zone, currency, and preferences |
Available prompts (7)
Prompts are guided workflows the assistant executes step by step. Invoke them by name.
| Prompt | Description | Parameters |
|---|---|---|
monthly-close | Monthly close: review unpaid invoices, categorize expenses, verify tax obligations, generate summary | month (optional) |
onboard-client | Client onboarding: determine tax type by location, create record, generate welcome quote | clientName, country, region |
quarterly-tax-prep | Quarterly tax prep: gather invoices, calculate VAT/IGIC, generate Modelo 303/130 preview | quarter, fiscalZone |
overdue-followup | Overdue follow-up: identify overdue invoices, group by client, draft collection messages | — |
expense-batch | Batch expense processing: categorize, apply taxes, verify deductibility, create with confirmation | fiscalZone |
vendor-analysis | Vendor analysis: group expenses by vendor, calculate totals, identify trends and savings opportunities | period (optional) |
business-health | Business health check: key KPIs, month-over-month comparison, alerts, and actionable recommendations | month (optional) |
You can invoke a prompt directly: "Run the monthly close for February" or "Prep my Q1 taxes".
Usage examples
These are real natural-language requests that the assistant translates into MCP tool calls.
Create an invoice
"Create an invoice for Acme Ltd with 10 hours of consulting at 95 EUR/hour, due March 15th"
The assistant calls create_invoice with clientName: "Acme Ltd", one line item (10 x 95), and dueDate: "2026-03-15". The total is calculated automatically.
Log an expense
"Log an expense of 59.99 EUR for Adobe Creative Cloud, category software, tax deductible"
Calls create_expense with description, amount: 59.99, category: "software", and taxDeductible: true.
Search invoices by client
"Find all invoices for TechStart Ltd"
Calls search_invoices with clientName: "TechStart Ltd" and returns matches with totals and statuses.
Check overdue invoices
"Show me all unpaid invoices"
Calls list_invoices and filters by status: "sent" or "overdue", displaying overdue invoices sorted by amount.
Add a new client
"New client: Design Studio Ltd, VAT GB123456789, email hello@designstudio.co.uk, London EC1A 1BB"
Calls create_client with name, tax ID, email, and address.
Set up automation
"Create a webhook to notify https://my-app.com/hook when an invoice is paid"
Calls create_webhook with url and events: ["invoice.paid"].
Update a product
"Raise the hourly consulting rate to 85 EUR"
Calls update_product with the product id and unitPrice: 85. Only the specified field is modified.
Observability
The MCP server v1.5.1 includes structured logging and tool metrics.
Structured logging
All logs are emitted as JSON to stderr (MCP uses stdout for protocol messages). Each entry includes:
level:debug,info,warn,errorservice: alwaysfrihet-mcptimestamp: ISO 8601tool: tool name (when applicable)operation: operation type (tool_call,api_call,api_retry,startup,shutdown_metrics)durationMs: execution time in millisecondserror: error details (message, code, statusCode)
Enable debug logs with FRIHET_MCP_DEBUG=1.
Tool metrics
The server tracks in-memory metrics for each tool call: invocation count, errors, and average duration. On shutdown (SIGINT/SIGTERM), it emits a summary:
{
"level": "info",
"message": "Shutdown after 3600s — 42 calls, 1 errors",
"operation": "shutdown_metrics",
"metadata": {
"tools": {
"list_invoices": { "calls": 15, "errors": 0, "avgMs": 230 },
"create_invoice": { "calls": 8, "errors": 1, "avgMs": 450 }
},
"uptime": 3600
}
}
Automatic rate-limit retry
When the API responds with 429, the server retries automatically with exponential backoff (up to 3 retries). Retries are logged:
{
"level": "warn",
"message": "Rate limited, retrying GET /invoices (attempt 2, delay 2000ms)",
"operation": "api_retry"
}
You don't need to handle rate limiting manually — the server does it for you.
Transport
Frihet's MCP server supports two transport modes. Both expose the same 52 tools, 11 resources, and 10 prompts.
Local (stdio)
The server runs as a local process on your machine. Communication between the MCP client and server uses standard input/output (stdin/stdout).
- Requires: Node.js installed (downloaded automatically via
npx) - Advantage: Lower latency, works offline (except for API calls)
- Use case: Daily development, heavy usage, corporate environments with network restrictions
Remote (streamable-http)
The server runs on Cloudflare Workers. Your MCP client connects directly to https://mcp.frihet.io/mcp over HTTP.
- Requires: Only an internet connection
- Advantage: No local installation, no dependencies, works on any device
- Use case: Quick setup, teams that prefer not to install packages, clients that only support HTTP transport
If your MCP client doesn't support streamable-http (some older clients only support stdio), use local mode.
Error handling
Rate limiting
The API allows 100 requests per minute per key. If the limit is exceeded, the server returns a 429 error with the wait time in retryAfter.
The MCP server handles rate limiting automatically with exponential backoff: it retries the request after waiting the indicated time, with no user intervention needed. Maximum 3 retries.
Authentication errors
| Code | Cause | Solution |
|---|---|---|
401 | Invalid, expired, or missing API key | Verify the key in your config starts with fri_ and hasn't expired |
403 | The key doesn't have permissions for this resource | Generate a new key with the required permissions |
Other errors
| Code | Description |
|---|---|
400 | Invalid parameters or missing required fields |
404 | The requested resource doesn't exist |
408 | Request timeout (30 seconds) |
413 | The request body exceeds 1 MB |
422 | Valid data but not processable (e.g., fiscal profile not configured) |
429 | Rate limit exceeded (retried automatically) |
500 | Internal server error |
All errors return a descriptive bilingual message (EN/ES) so the assistant can communicate the problem clearly to the user.
Limits
| Concept | Value |
|---|---|
| Requests per minute | 100 per API key |
| Results per page | 100 maximum (50 default) |
| Request body | 1 MB maximum |
| Webhook payload | 100 KB maximum |
| Webhooks per account | 20 maximum |
| Request timeout | 30 seconds |
| Rate limit retries | 3 maximum |
MCP Server vs REST API
| MCP Server | REST API | |
|---|---|---|
| Designed for | AI assistants (Claude, Cursor, Windsurf) | Applications, scripts, integrations |
| Communication | Natural language through the MCP client | Direct HTTP/JSON |
| Authentication | Environment variable in client config (or OAuth) | X-API-Key header or Authorization: Bearer |
| Format | Formatted text + structured output for the assistant | Raw JSON |
| Rate limiting | Handled automatically (exponential backoff, 3 retries) | Manual (consumer must implement retries) |
| Observability | Structured logging + per-tool metrics | Request logs via X-Request-Id |
| Use case | Talk to your ERP from your IDE | Build programmatic integrations |
Internally, the MCP server translates each tool call into a REST API request. It doesn't duplicate logic — it's a stateless bridge.
Development
To contribute or run the server in development mode:
git clone https://github.com/Frihet-io/frihet-mcp.git
cd frihet-mcp
npm install
npm run build
Run locally:
FRIHET_API_KEY=fri_your_key node dist/index.js
Run with debug logs:
FRIHET_MCP_DEBUG=1 FRIHET_API_KEY=fri_your_key node dist/index.js
Test with the MCP Inspector:
npx @modelcontextprotocol/inspector node dist/index.js
Related resources
- REST API — Full endpoint reference, authentication, and error codes
- Webhooks — Available events, HMAC-SHA256 verification, and retry policy
- Claude Code Skill — Business intelligence layer on top of the MCP server
- Source code (GitHub) — MCP server repository
- npm package —
@frihet/mcp-server@1.5.1 - MCP Registry —
io.frihet/erp - Remote endpoint — MCP server hosted on Cloudflare Workers
- MCP specification — Protocol documentation