@frihet/mcp-server 2.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +93 -360
- package/dist/claude-output-privacy.js +9 -0
- package/dist/demo-client.d.ts +1 -0
- package/dist/demo-client.js +1 -0
- package/dist/server-composition.d.ts +1 -0
- package/dist/server-composition.js +11 -0
- package/dist/tool-exposure.d.ts +1 -0
- package/dist/tool-exposure.js +8 -2
- package/dist/tools/clients.js +2 -0
- package/dist/tools/shared.d.ts +1 -1
- package/dist/tools/vendors.js +2 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,13 +18,9 @@
|
|
|
18
18
|
<p align="center">
|
|
19
19
|
<a href="https://www.npmjs.com/package/@frihet/mcp-server"><img src="https://img.shields.io/npm/v/@frihet/mcp-server?style=flat&color=18181b&labelColor=09090b" alt="npm version"></a>
|
|
20
20
|
<a href="https://www.npmjs.com/package/@frihet/mcp-server"><img src="https://img.shields.io/npm/dm/@frihet/mcp-server?style=flat&color=18181b&labelColor=09090b&label=downloads" alt="npm downloads"></a>
|
|
21
|
-
<a href="https://smithery.ai/servers/frihet/frihet-mcp"><img src="https://smithery.ai/badge/frihet/frihet-mcp" alt="Smithery installs"></a>
|
|
22
21
|
<a href="https://registry.modelcontextprotocol.io/?q=io.frihet"><img src="https://img.shields.io/badge/MCP_Registry-io.frihet%2Ferp-4A90D9?style=flat&logo=anthropic&logoColor=white" alt="MCP Registry"></a>
|
|
23
|
-
<a href="https://github.com/mcp/io.frihet/erp"><img src="https://img.shields.io/badge/GitHub_MCP_Registry-Listed-18181b?style=flat&labelColor=09090b&logo=github" alt="Listed in GitHub MCP Registry"></a>
|
|
24
|
-
<a href="https://github.com/Frihet-io/frihet-mcp/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-18181b?style=flat&labelColor=09090b" alt="license"></a>
|
|
25
22
|
<img src="https://img.shields.io/badge/catalogue-156_operations-18181b?style=flat&labelColor=09090b" alt="156 canonical catalogue operations">
|
|
26
|
-
<img src="https://img.shields.io/badge/
|
|
27
|
-
<a href="https://www.typescriptlang.org/"><img src="https://img.shields.io/badge/TypeScript-18181b?style=flat&labelColor=09090b" alt="TypeScript"></a>
|
|
23
|
+
<a href="https://github.com/Frihet-io/frihet-mcp/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-18181b?style=flat&labelColor=09090b" alt="license"></a>
|
|
28
24
|
</p>
|
|
29
25
|
|
|
30
26
|
<p align="center">
|
|
@@ -33,49 +29,9 @@
|
|
|
33
29
|
|
|
34
30
|
---
|
|
35
31
|
|
|
36
|
-
## Distribution
|
|
37
|
-
|
|
38
|
-
Reviewed 2026-10-31. A directory entry is not proof of a working installation, vendor endorsement, or adoption. Package releases, hosted deployments and reviewed app versions are independent.
|
|
39
|
-
|
|
40
|
-
### Use Frihet
|
|
41
|
-
|
|
42
|
-
| Channel | Availability | Entry point |
|
|
43
|
-
|---|---|---|
|
|
44
|
-
| npm | Published package; see its own release version | [npm](https://www.npmjs.com/package/@frihet/mcp-server) · `npx @frihet/mcp-server` |
|
|
45
|
-
| Remote MCP | Hosted connection route; OAuth or API key | `https://mcp.frihet.io/mcp` |
|
|
46
|
-
| ChatGPT app | Published 1.0.2, confirmed by the maintainer; not the separate 1.0.3 candidate | [Frihet in ChatGPT](https://chatgpt.com/plugins/plugin_asdk_app_6ac6b51bd7648191a0696bd1e725bfe6) |
|
|
47
|
-
|
|
48
|
-
### Find Frihet
|
|
49
|
-
|
|
50
|
-
These are discovery links, not a count of independent integrations. Some third-party descriptions and cached copies are outdated.
|
|
51
|
-
|
|
52
|
-
| Surface | Evidence at review | Entry |
|
|
53
|
-
|---|---|---|
|
|
54
|
-
| Official MCP Registry | Specific registry entry indexed | [io.frihet/erp](https://registry.modelcontextprotocol.io/?q=io.frihet) |
|
|
55
|
-
| GitHub MCP Registry | Listing content retrieved; not a GitHub-verified connector claim | [Frihet ERP](https://github.com/mcp/io.frihet/erp) |
|
|
56
|
-
| ToolSDK MCP Registry | Entry read in upstream repository | [Registry entry](https://github.com/toolsdk-ai/toolsdk-mcp-registry/blob/main/packages/finance-fintech/frihet-mcp.json) |
|
|
57
|
-
| Glama | Specific listing indexed; summary needs refresh | [Listing](https://glama.ai/mcp/servers/Frihet-io/frihet-mcp) |
|
|
58
|
-
| Smithery | Specific listing indexed; live recheck blocked | [Listing](https://smithery.ai/servers/%40frihet/frihet-mcp) |
|
|
59
|
-
| mcpservers.org | Listing content retrieved; description outdated | [Listing](https://mcpservers.org/servers/frihet-io/frihet-mcp) |
|
|
60
|
-
| Cursor Directory | Community listing retrieved; separate from the official Cursor Marketplace | [Listing](https://cursor.directory/plugins/mcp-frihet) |
|
|
61
|
-
| mcp.so | Specific listing indexed; current installation not verified | [Listing](https://mcp.so/servers/frihet-mcp) |
|
|
62
|
-
|
|
63
|
-
Frihet also appears in **[punkpeye/awesome-mcp-servers](https://github.com/punkpeye/awesome-mcp-servers)**, **[TensorBlock/awesome-mcp-servers](https://github.com/TensorBlock/awesome-mcp-servers/blob/main/docs/finance--crypto.md)** and **[ever-works/awesome-mcp-servers](https://github.com/ever-works/awesome-mcp-servers/blob/master/details/frihetmcp-server.md)**. Their retrieved descriptions still use an old tool count; the ever-works entry also links to a non-canonical repository URL. Use this repository's install instructions rather than copying those descriptions.
|
|
64
|
-
|
|
65
|
-
### Pending or not reverified
|
|
66
|
-
|
|
67
|
-
- **Docker:** [submission PR #1711](https://github.com/docker/mcp-registry/pull/1711) remains open; do not describe it as published in Docker's catalog.
|
|
68
|
-
- **Cline:** [submission #2053](https://github.com/cline/mcp-marketplace/issues/2053) remains open; client compatibility is not marketplace approval.
|
|
69
|
-
- **PulseMCP:** historically reported; a current primary listing was not retrieved in this review.
|
|
70
|
-
- **Official Cursor Marketplace and Claude Directory:** public approval was not reverified here. This does not change existing submissions or manual MCP installation support.
|
|
71
|
-
|
|
72
|
-
> **Surface truth:** the catalogue contains 156 canonical operations. The local full profile serves 161 tool names, 11 resources, and 10 prompts (156 canonical operations plus 5 fiscal aliases). The hosted grouped profile serves 164 tool names, 7 resources, and 10 prompts (the same names plus 3 discovery tools, with API-backed resources kept local-only). The separately reviewed OpenAI profile serves 33 tool names, 0 resources, and 0 prompts. Catalogue membership is not a promise that a backing API is enabled for every workspace.
|
|
73
|
-
|
|
74
|
-
---
|
|
75
|
-
|
|
76
32
|
## What is this
|
|
77
33
|
|
|
78
|
-
An MCP server that connects your AI assistant to [Frihet](https://frihet.io/?utm_source=github_readme&utm_medium=mcp_readme&utm_campaign=mcp_landgrab)
|
|
34
|
+
An MCP server that connects your AI assistant to [Frihet](https://frihet.io/?utm_source=github_readme&utm_medium=mcp_readme&utm_campaign=mcp_landgrab), the AI-native ERP for businesses of any size. Ask for invoices, expenses, clients, quotes, banking and tax figures in plain language, from the tools you already use.
|
|
79
35
|
|
|
80
36
|
```
|
|
81
37
|
You: "Create an invoice for TechStart SL, 40 hours of consulting at 75 EUR/hour, due March 1st"
|
|
@@ -84,110 +40,84 @@ Claude: Done. Invoice INV-2026-089 created. Total: 3,000.00 EUR + 21% IVA = 3,6
|
|
|
84
40
|
|
|
85
41
|
156 canonical operations. Five fiscal aliases. Ten prompts. The local package serves 11 resources; the hosted Worker deliberately serves the 7 static resources, while API-backed workspace resources remain local-profile only.
|
|
86
42
|
|
|
87
|
-
<!-- v1.12.0-beta.1 — D4-B megasprint: HR (9), payroll (2), permissions (2), period close (3), webhook test (1) = +17 = 156 tools total
|
|
88
|
-
(Onboarding's two tools were withdrawn via #124 because the Frihet-ERP
|
|
89
|
-
/v1/onboarding/* family does not exist; the original +19 = 158 count
|
|
90
|
-
dropped to +17 = 156. The withdrawal is permanent until Frihet-ERP
|
|
91
|
-
ships the family.) -->
|
|
92
|
-
|
|
93
43
|
---
|
|
94
44
|
|
|
95
|
-
##
|
|
45
|
+
## Quick start
|
|
96
46
|
|
|
97
|
-
|
|
47
|
+
**Remote, one step.** Add `https://mcp.frihet.io/mcp` as a remote MCP server in any client that supports OAuth. You sign in with your browser; there is nothing to install and no key to copy.
|
|
48
|
+
|
|
49
|
+
**Local, with your API key** (create one in [app.frihet.io](https://app.frihet.io) under Settings > API):
|
|
98
50
|
|
|
99
51
|
```bash
|
|
100
|
-
|
|
52
|
+
claude mcp add frihet -s user -e FRIHET_API_KEY=fri_your_key_here -- npx -y @frihet/mcp-server
|
|
101
53
|
```
|
|
102
54
|
|
|
103
|
-
|
|
55
|
+
**No account yet?** Try demo mode. It answers from example data, persists nothing and makes no network calls:
|
|
104
56
|
|
|
105
|
-
|
|
57
|
+
```bash
|
|
58
|
+
FRIHET_DEMO=1 npx -y @frihet/mcp-server
|
|
59
|
+
```
|
|
106
60
|
|
|
107
|
-
|
|
61
|
+
Local installs need Node 22.23.4+, 24.22.1+ or 26.12.1+. Other clients are covered under [Client setup](#client-setup).
|
|
108
62
|
|
|
109
|
-
|
|
63
|
+
---
|
|
110
64
|
|
|
111
|
-
|
|
65
|
+
## Distribution
|
|
112
66
|
|
|
113
|
-
|
|
114
|
-
|------|-------|
|
|
115
|
-
| Onboarding contract: quickstart per client, auth, safe workflow, human-authority tool lists, error recovery | [`docs/agent-onboarding.json`](./docs/agent-onboarding.json) — also shipped inside the npm package |
|
|
116
|
-
| Per-tool capability truth: `callability`, `writesFrihet`, `externalInteraction`, `externalSideEffects` | `_meta["io.frihet/capability"]` on every entry of `tools/list` |
|
|
117
|
-
| How to behave once connected | the `instructions` string returned by `initialize` — your client hands it to you automatically |
|
|
67
|
+
### Where to get it
|
|
118
68
|
|
|
119
|
-
|
|
69
|
+
| Channel | Status | Entry point |
|
|
70
|
+
|---|---|---|
|
|
71
|
+
| npm | Published | [`@frihet/mcp-server`](https://www.npmjs.com/package/@frihet/mcp-server) · `npx -y @frihet/mcp-server` |
|
|
72
|
+
| Remote MCP | Live, OAuth or API key | `https://mcp.frihet.io/mcp` (running version at [`/health`](https://mcp.frihet.io/health)) |
|
|
73
|
+
| ChatGPT app | Live | [Frihet in ChatGPT](https://chatgpt.com/plugins/plugin_asdk_app_6ac6b51bd7648191a0696bd1e725bfe6) |
|
|
74
|
+
| Raycast | Coming to the Raycast Store | [Submission](https://github.com/raycast/extensions/pull/33017) |
|
|
75
|
+
| Official MCP Registry | Listed | [`io.frihet/erp`](https://registry.modelcontextprotocol.io/?q=io.frihet) |
|
|
76
|
+
| GitHub MCP Registry | Listed | [Frihet ERP](https://github.com/mcp/io.frihet/erp) |
|
|
120
77
|
|
|
121
|
-
|
|
122
|
-
2. **Draft, show, stop.** `create_invoice`, `create_quote` and `create_credit_note` all default to `status=draft` — no fiscal number, no hash, nothing sent to a tax authority. Present the draft and hand back.
|
|
123
|
-
3. **Human authority is not yours to assume.** Any tool with a non-empty `externalSideEffects` reaches a client's inbox, a webhook, money, or AEAT / VeriFactu / TicketBAI / FACe. Several also take `confirm=true`; that flag records a human decision — never set it to satisfy your own plan.
|
|
78
|
+
Also listed on [Glama](https://glama.ai/mcp/servers/Frihet-io/frihet-mcp), [Cursor Directory](https://cursor.directory/plugins/mcp-frihet), [mcp.so](https://mcp.so/servers/frihet-mcp), [mcpservers.org](https://mcpservers.org/servers/frihet-io/frihet-mcp) and the [ToolSDK registry](https://github.com/toolsdk-ai/toolsdk-mcp-registry/blob/main/packages/finance-fintech/frihet-mcp.json).
|
|
124
79
|
|
|
125
|
-
|
|
80
|
+
> **Surface truth:** the catalogue contains 156 canonical operations. The local full profile serves 161 tool names, 11 resources, and 10 prompts (156 canonical operations plus 5 fiscal aliases). The hosted grouped profile serves 164 tool names, 7 resources, and 10 prompts (the same names plus 3 discovery tools, with API-backed resources kept local-only). The separately reviewed OpenAI profile serves 33 tool names, 0 resources, and 0 prompts. These counts describe the current source release; the hosted server reports the version it is running at `/health`. Catalogue membership is not a promise that a backing API is enabled for every workspace.
|
|
126
81
|
|
|
127
82
|
---
|
|
128
83
|
|
|
129
|
-
##
|
|
130
|
-
|
|
131
|
-
Local npm/stdio installs require Node **22.23.4+ (22.x), 24.22.1+ (24.x), or 26.12.1+ (26.x)**, covering the [October 2026 Node security fixes](https://nodejs.org/en/blog/vulnerability/october-2026-security-releases). Node 20 is end-of-life: upgrade the Node runtime used by your MCP client before starting the server. CI uses Node 22.23.4.
|
|
132
|
-
|
|
133
|
-
This source change does not update previously published npm package metadata or users' installed Node runtimes. Updated package metadata requires a separate npm release; users can upgrade their runtime now. The remote Cloudflare Worker has a separate runtime and release process.
|
|
134
|
-
|
|
135
|
-
### One-line (Claude Code, Cursor, Copilot, Codex, Windsurf, Gemini CLI, and more)
|
|
136
|
-
|
|
137
|
-
```bash
|
|
138
|
-
npx skills add Frihet-io/frihet-mcp
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
### Claude Code plugin (skill + MCP server in one install)
|
|
84
|
+
## What you can do
|
|
142
85
|
|
|
143
|
-
|
|
86
|
+
Real prompts, grouped by area.
|
|
144
87
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
88
|
+
| Area | Try asking |
|
|
89
|
+
|------|------------|
|
|
90
|
+
| Invoicing | "Show me all unpaid invoices" · "Create an invoice for Acme SL with 10h of consulting at 95/hour" · "Mark invoice abc123 as paid" |
|
|
91
|
+
| Expenses | "Log a 59.99 EUR expense for Adobe Creative Cloud, category: software, tax-deductible" · "What did I spend on travel last quarter?" |
|
|
92
|
+
| Clients and CRM | "Add a new client: TechStart SL, NIF B12345678" · "Log a call with TechStart: discussed Q2 proposal" · "Show all activities for Acme SL" |
|
|
93
|
+
| Quotes | "Create a quote for Design Studio: logo design (2000 EUR) + brand guidelines (3500 EUR)" |
|
|
94
|
+
| Tax and reporting | "Prepare my quarterly tax summary" · "Give me this month's profit and loss" |
|
|
95
|
+
| Webhooks | "Set up a webhook to notify https://my-app.com/hook when invoices are paid" |
|
|
149
96
|
|
|
150
|
-
|
|
97
|
+
Beyond these, the catalogue covers banking, e-invoicing, time tracking, recurring invoices, HR and payroll, POS and more. See the [full catalogue](#full-catalogue).
|
|
151
98
|
|
|
152
|
-
|
|
99
|
+
---
|
|
153
100
|
|
|
154
|
-
|
|
155
|
-
real receivables, document-drafting and monthly-review skills with the existing
|
|
156
|
-
public remote MCP. Load `plugins/claude/frihet-erp` in Claude Code or upload a ZIP
|
|
157
|
-
of that folder in chat/Cowork. Its skills guide behavior; they do not filter the
|
|
158
|
-
hosted catalogue or establish Directory approval. The root local MCP setup above
|
|
159
|
-
keeps its existing API-key configuration.
|
|
101
|
+
## Client setup
|
|
160
102
|
|
|
161
|
-
### Claude Code
|
|
103
|
+
### Claude Code
|
|
162
104
|
|
|
163
105
|
```bash
|
|
164
106
|
claude mcp add frihet -s user -e FRIHET_API_KEY=fri_your_key_here -- npx -y @frihet/mcp-server
|
|
165
107
|
claude mcp list # verify: frihet ✓ Connected
|
|
166
108
|
```
|
|
167
109
|
|
|
168
|
-
|
|
110
|
+
Or use the hosted server: `claude mcp add --transport http frihet https://mcp.frihet.io/mcp`.
|
|
169
111
|
|
|
170
|
-
### Codex CLI
|
|
112
|
+
### Codex CLI
|
|
171
113
|
|
|
172
114
|
```bash
|
|
173
115
|
codex mcp add frihet --env FRIHET_API_KEY=fri_your_key_here -- npx -y @frihet/mcp-server
|
|
174
|
-
codex mcp list # verify
|
|
175
116
|
```
|
|
176
117
|
|
|
177
|
-
Codex
|
|
178
|
-
|
|
179
|
-
```toml
|
|
180
|
-
[mcp_servers.frihet]
|
|
181
|
-
command = "npx"
|
|
182
|
-
args = ["-y", "@frihet/mcp-server"]
|
|
118
|
+
Codex reads TOML, not JSON. Pasting a JSON `mcpServers` block into `~/.codex/config.toml` is a parse error that breaks the whole file, so use the command above.
|
|
183
119
|
|
|
184
|
-
|
|
185
|
-
FRIHET_API_KEY = "fri_your_key_here"
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
> Pasting a JSON `mcpServers` block into `~/.codex/config.toml` is a TOML parse error that takes down your **whole** Codex config, not just this server. Use the command above.
|
|
189
|
-
|
|
190
|
-
### Claude Desktop, Cursor, Windsurf, Cline — JSON config
|
|
120
|
+
### Claude Desktop, Cursor, Windsurf, Cline
|
|
191
121
|
|
|
192
122
|
```json
|
|
193
123
|
{
|
|
@@ -203,20 +133,14 @@ FRIHET_API_KEY = "fri_your_key_here"
|
|
|
203
133
|
}
|
|
204
134
|
```
|
|
205
135
|
|
|
206
|
-
|
|
|
136
|
+
| Client | Config file |
|
|
207
137
|
|------|------------|
|
|
208
138
|
| Claude Desktop | `~/Library/Application Support/Claude/claude_desktop_config.json` |
|
|
209
139
|
| Cursor | `.cursor/mcp.json` or `~/.cursor/mcp.json` |
|
|
210
140
|
| Windsurf | `~/.windsurf/mcp.json` |
|
|
211
141
|
| Cline | VS Code settings or `.cline/mcp.json` |
|
|
212
142
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
### Remote (no install)
|
|
216
|
-
|
|
217
|
-
Use the hosted endpoint at `mcp.frihet.io` -- zero local dependencies, runs on Cloudflare Workers.
|
|
218
|
-
|
|
219
|
-
**With API key:**
|
|
143
|
+
### Remote server with an API key
|
|
220
144
|
|
|
221
145
|
```json
|
|
222
146
|
{
|
|
@@ -232,70 +156,42 @@ Use the hosted endpoint at `mcp.frihet.io` -- zero local dependencies, runs on C
|
|
|
232
156
|
}
|
|
233
157
|
```
|
|
234
158
|
|
|
235
|
-
|
|
159
|
+
Clients that support OAuth 2.1 with PKCE can connect to the same URL and sign in through the browser instead.
|
|
236
160
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
### Get your API key
|
|
240
|
-
|
|
241
|
-
1. Log into [app.frihet.io](https://app.frihet.io)
|
|
242
|
-
2. Go to **Settings > API**
|
|
243
|
-
3. Click **Create API key**
|
|
244
|
-
4. Copy the key (starts with `fri_`) -- it's only shown once
|
|
245
|
-
|
|
246
|
-
---
|
|
247
|
-
|
|
248
|
-
## What you can do
|
|
161
|
+
### ChatGPT
|
|
249
162
|
|
|
250
|
-
|
|
163
|
+
Open [Frihet in ChatGPT](https://chatgpt.com/plugins/plugin_asdk_app_6ac6b51bd7648191a0696bd1e725bfe6) and connect your account.
|
|
251
164
|
|
|
252
|
-
###
|
|
165
|
+
### Skill for coding agents
|
|
253
166
|
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
"Create an invoice for Acme SL with 10h of consulting at 95/hour"
|
|
257
|
-
"Mark invoice abc123 as paid"
|
|
258
|
-
"How much has ClientName been invoiced this year?"
|
|
259
|
-
```
|
|
260
|
-
|
|
261
|
-
### Expenses
|
|
262
|
-
|
|
263
|
-
```
|
|
264
|
-
"Log a 59.99 EUR expense for Adobe Creative Cloud, category: software, tax-deductible"
|
|
265
|
-
"List all expenses from January"
|
|
266
|
-
"What did I spend on travel last quarter?"
|
|
167
|
+
```bash
|
|
168
|
+
npx skills add Frihet-io/frihet-mcp
|
|
267
169
|
```
|
|
268
170
|
|
|
269
|
-
|
|
171
|
+
Installs the Frihet skill for Claude Code, Cursor, Codex, Windsurf, Gemini CLI and others. It routes requests to the MCP tools and asks for confirmation before writes. Tax figures come from your Frihet data, not from the skill. [Skill docs](https://docs.frihet.io/desarrolladores/skill-claude-code).
|
|
270
172
|
|
|
271
|
-
|
|
272
|
-
"Add a new client: TechStart SL, NIF B12345678, email admin@techstart.es"
|
|
273
|
-
"Show me all my clients"
|
|
274
|
-
"Update ClientName's address to Calle Mayor 1, Madrid 28001"
|
|
275
|
-
```
|
|
173
|
+
### Get your API key
|
|
276
174
|
|
|
277
|
-
|
|
175
|
+
1. Log into [app.frihet.io](https://app.frihet.io)
|
|
176
|
+
2. Go to **Settings > API**
|
|
177
|
+
3. Click **Create API key**
|
|
178
|
+
4. Copy the key (starts with `fri_`). It is only shown once.
|
|
278
179
|
|
|
279
|
-
|
|
280
|
-
"Add a contact to Acme SL: Ana Garcia, CTO, ana@acme.es"
|
|
281
|
-
"Log a call with TechStart: discussed Q2 proposal, they're interested in upgrade"
|
|
282
|
-
"Add a note to ClientName: prefers invoices in English, payment NET 30"
|
|
283
|
-
"Show me all activities for Acme SL"
|
|
284
|
-
```
|
|
180
|
+
---
|
|
285
181
|
|
|
286
|
-
|
|
182
|
+
## For AI agents
|
|
287
183
|
|
|
288
|
-
|
|
289
|
-
"Create a quote for Design Studio: logo design (2000 EUR) + brand guidelines (3500 EUR)"
|
|
290
|
-
"Show me all pending quotes"
|
|
291
|
-
```
|
|
184
|
+
If you are an agent reading this repository, the contract is machine-readable and generated from the running server.
|
|
292
185
|
|
|
293
|
-
|
|
186
|
+
| What | Where |
|
|
187
|
+
|------|-------|
|
|
188
|
+
| Onboarding contract: quickstart per client, auth, safe workflow, human-authority tool lists, error recovery | [`docs/agent-onboarding.json`](./docs/agent-onboarding.json), also shipped in the npm package |
|
|
189
|
+
| Per-tool capability truth: `callability`, `writesFrihet`, `externalInteraction`, `externalSideEffects` | `_meta["io.frihet/capability"]` on every entry of `tools/list` |
|
|
190
|
+
| How to behave once connected | the `instructions` string returned by `initialize` |
|
|
294
191
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
```
|
|
192
|
+
1. **Orient before acting.** `get_business_context` and the `frihet://tax/rates` resource decide the correct fiscal treatment. Do not recall a tax rate from memory.
|
|
193
|
+
2. **Draft, show, stop.** `create_invoice`, `create_quote` and `create_credit_note` default to `status=draft`: no fiscal number, no hash, nothing sent to a tax authority.
|
|
194
|
+
3. **Human authority is not yours to assume.** Any tool with a non-empty `externalSideEffects` reaches a client's inbox, a webhook, money, or a tax authority. Several also take `confirm=true`; that flag records a human decision.
|
|
299
195
|
|
|
300
196
|
---
|
|
301
197
|
|
|
@@ -303,32 +199,14 @@ Talk to your ERP. These are real prompts, not marketing copy.
|
|
|
303
199
|
|
|
304
200
|
This MCP is a **structured data interface** -- you describe what you want in natural language, and the AI creates, queries, or modifies business records in Frihet. Most of the 156 canonical operations are CRUD operations over the REST API; the rest are read-only summaries and fiscal/e-invoice actions. Alias and discovery names are counted separately.
|
|
305
201
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
```
|
|
309
|
-
"Create an invoice for TechStart SL, 40h consulting at 75 EUR/h" --> creates the invoice
|
|
310
|
-
"Show unpaid invoices over 1,000 EUR" --> queries and filters
|
|
311
|
-
"Log a 120 EUR expense for the Madrid train, category: travel" --> records the expense
|
|
312
|
-
"Update client Acme's email to billing@acme.es" --> modifies the record
|
|
313
|
-
```
|
|
314
|
-
|
|
315
|
-
**Does not do:**
|
|
316
|
-
|
|
317
|
-
- OCR or PDF scanning -- you cannot upload an invoice image and have it read
|
|
318
|
-
- File upload or attachment handling
|
|
319
|
-
- Image processing of any kind
|
|
320
|
-
|
|
321
|
-
If you need to digitize paper invoices or receipts, extract the data first (e.g., Claude Vision API, a dedicated OCR service, or manual entry), then use the MCP to create the record:
|
|
322
|
-
|
|
323
|
-
```
|
|
324
|
-
1. Scan/photograph the invoice
|
|
325
|
-
2. Use Claude Vision: "Read this invoice image and extract the vendor, items, amounts, and dates"
|
|
326
|
-
3. Then: "Create an expense in Frihet for [extracted data]"
|
|
327
|
-
```
|
|
202
|
+
It does not do OCR, PDF scanning, file upload or image processing. To digitize a paper invoice, extract the data first (a vision model, an OCR service, or manual entry), then ask the assistant to create the record. One API key maps to one Frihet workspace, and you need a Frihet account at [app.frihet.io](https://app.frihet.io).
|
|
328
203
|
|
|
329
204
|
---
|
|
330
205
|
|
|
331
|
-
##
|
|
206
|
+
## Full catalogue
|
|
207
|
+
|
|
208
|
+
<details>
|
|
209
|
+
<summary><strong>All 156 canonical operations, resources and prompts</strong></summary>
|
|
332
210
|
|
|
333
211
|
### Invoices (12)
|
|
334
212
|
|
|
@@ -436,7 +314,7 @@ If you need to digitize paper invoices or receipts, extract the data first (e.g.
|
|
|
436
314
|
| Tool | What it does |
|
|
437
315
|
|------|-------------|
|
|
438
316
|
| `send_einvoice` | Dispatch an invoice in 11 formats (XRechnung, Factur-X, FatturaPA, PEPPOL, Facturae, UBL, CII) via email / Chorus Pro / SDI / PEPPOL / download |
|
|
439
|
-
| `get_einvoice_status` | Poll
|
|
317
|
+
| `get_einvoice_status` | Poll an e-invoice dispatch until it succeeds or fails; returns ackId, XML URL and PDF/A-3 URL |
|
|
440
318
|
| `validate_einvoice_xml` | Validate raw XML against format schema + schematron rules (KOSIT / Mustang / XSD / Schematron) |
|
|
441
319
|
| `export_datev` | Export accounting data as DATEV EXTF (Buchungsstapel / Debitoren / Kreditoren) in CP1252 encoding |
|
|
442
320
|
| `einvoice_export` | Export e-invoice data in machine-readable formats (JSON/XML) for archival or integration |
|
|
@@ -444,7 +322,7 @@ If you need to digitize paper invoices or receipts, extract the data first (e.g.
|
|
|
444
322
|
| `face_status` | Poll submission status from FACe for a submitted invoice |
|
|
445
323
|
| `ticketbai_submit` | Submit TicketBAI fiscal record to Basque Country tax authority (Hacienda) |
|
|
446
324
|
| `ticketbai_status` | Poll TicketBAI submission status from the Basque tax authority |
|
|
447
|
-
| `ksef_submit` | **NOT_DEPLOYED
|
|
325
|
+
| `ksef_submit` | **NOT_DEPLOYED**: submit an invoice to KSeF (Poland). Returns a labeled "unavailable" error until activated |
|
|
448
326
|
|
|
449
327
|
### Time Tracking (6)
|
|
450
328
|
|
|
@@ -493,8 +371,8 @@ If you need to digitize paper invoices or receipts, extract the data first (e.g.
|
|
|
493
371
|
|
|
494
372
|
| Tool | What it does |
|
|
495
373
|
|------|-------------|
|
|
496
|
-
| `frihet_gl_entry_approve` | Approve a GL journal entry (gestor/admin only
|
|
497
|
-
| `frihet_gl_entry_reject` | Reject a GL entry with a mandatory reason (
|
|
374
|
+
| `frihet_gl_entry_approve` | Approve a GL journal entry (gestor/admin only, restricted) |
|
|
375
|
+
| `frihet_gl_entry_reject` | Reject a GL entry with a mandatory reason (restricted) |
|
|
498
376
|
| `frihet_gl_entry_audit_log` | Retrieve full audit trail for a GL entry |
|
|
499
377
|
|
|
500
378
|
### White-label Portal Domain (3)
|
|
@@ -578,7 +456,6 @@ If you need to digitize paper invoices or receipts, extract the data first (e.g.
|
|
|
578
456
|
| `get_modelo_347_summary` | Annual third-party transactions >€3,005 (Modelo 347) |
|
|
579
457
|
| `verifactu_status` | Get VeriFactu submission status for a fiscal record |
|
|
580
458
|
| `verifactu_resubmit` | Resubmit a rejected VeriFactu fiscal record |
|
|
581
|
-
| `ticketbai_status` | Poll TicketBAI submission status — *cross-reference from E-Invoicing (10); NOT counted toward this section's 7* |
|
|
582
459
|
|
|
583
460
|
### Vacation Rentals / Stay (5)
|
|
584
461
|
|
|
@@ -631,11 +508,6 @@ If you need to digitize paper invoices or receipts, extract the data first (e.g.
|
|
|
631
508
|
| `payroll_export` | Read normalized payroll-ready employee data; the format value is an echoed destination label, not a generated file |
|
|
632
509
|
| `payroll_checklist` | List payable employees with payroll-profile readiness, missing fields, and monthly review state |
|
|
633
510
|
|
|
634
|
-
### Onboarding — withdrawn via #124
|
|
635
|
-
|
|
636
|
-
The `onboarding_status` and `onboarding_persona_set` tools were withdrawn because the Frihet-ERP `/v1/onboarding/*` family does not exist as of this release. See [issue #124](https://github.com/Frihet-io/frihet-mcp/issues/124) for the audit and the precedent (`src/tools/igic.ts` shows the inline `NOT_DEPLOYED` stub pattern used for families whose backing API is not yet shipped; onboarding chose full withdrawal instead, because the catalogue should not over-promise tools that have no live handler).
|
|
637
|
-
|
|
638
|
-
If a future Frihet-ERP release ships the family, restore `src/tools/onboarding.ts`, the `onboardingStatusOutput` / `onboardingPersonaResultOutput` schemas in `src/tools/shared.ts`, the `getOnboardingStatus` / `setOnboardingPersona` methods on `IFrihetClient`, and the registration in `src/tools/register-all.ts` (the `Onboarding tools — withdrawn via #124` test in `src/__tests__/d4b-hr-payroll-onboarding-tools.test.ts` is the gate that will fail and remind the next maintainer what was here).
|
|
639
511
|
|
|
640
512
|
### Permissions (2)
|
|
641
513
|
|
|
@@ -649,8 +521,8 @@ If a future Frihet-ERP release ships the family, restore `src/tools/onboarding.t
|
|
|
649
521
|
| Tool | What it does |
|
|
650
522
|
|------|-------------|
|
|
651
523
|
| `period_close_status` | Get the current or selected YYYY fiscal-year range, open/closed state, and nullable closing details |
|
|
652
|
-
| `period_close` | Close an accounting period (gestor/admin only
|
|
653
|
-
| `period_reopen` | Reopen a closed period with a mandatory reason (
|
|
524
|
+
| `period_close` | Close an accounting period (gestor/admin only, restricted) |
|
|
525
|
+
| `period_reopen` | Reopen a closed period with a mandatory reason (restricted) |
|
|
654
526
|
|
|
655
527
|
All canonical operations (and their aliases) return **structured output** via `outputSchema` -- typed JSON, not raw text. List response shapes follow their API family; not every list endpoint is paginated.
|
|
656
528
|
|
|
@@ -663,11 +535,9 @@ On the full MCP surfaces, every `tools/list` entry includes `_meta["io.frihet/ca
|
|
|
663
535
|
- `writesFrihet`, `externalInteraction`, and `externalSideEffects` distinguish state changes and calls to external entities/providers;
|
|
664
536
|
- MCP action annotations remain the standard source for read-only, destructive, idempotent, and open-world hints.
|
|
665
537
|
|
|
666
|
-
The ChatGPT
|
|
538
|
+
The ChatGPT app uses a separately reviewed profile of 33 business operations, with no discovery meta-tools, prompts or resources. Its 17 reads and 16 writes are deliberately narrow, and every write requires literal `confirm=true`. Do not infer it from the full catalogue.
|
|
667
539
|
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
## Resources
|
|
540
|
+
### Resources
|
|
671
541
|
|
|
672
542
|
Context the AI can read to make smarter decisions.
|
|
673
543
|
|
|
@@ -694,9 +564,7 @@ The local package serves 11 resources: 7 static references plus 4 API-backed wor
|
|
|
694
564
|
| Overdue Invoices | `frihet://overdue-invoices` | All invoices past due date (up to 100) |
|
|
695
565
|
| Plan Limits | `frihet://status/plan-limits` | Live plan tier, usage counters, invoices/month, API rate limits |
|
|
696
566
|
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
## Prompts (10)
|
|
567
|
+
### Prompts (10)
|
|
700
568
|
|
|
701
569
|
Pre-built workflows the AI can execute as guided multi-step operations.
|
|
702
570
|
|
|
@@ -713,167 +581,47 @@ Pre-built workflows the AI can execute as guided multi-step operations.
|
|
|
713
581
|
| `invoice-aging-review` | AR aging analysis: group unpaid invoices by bucket (0-30/31-60/61-90/90+ days), top debtors, collection actions | -- |
|
|
714
582
|
| `expense-batch` | Process expenses in bulk: categorize, apply tax rates, flag missing receipts | `fiscalZone?` |
|
|
715
583
|
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
## How it works
|
|
719
|
-
|
|
720
|
-
```mermaid
|
|
721
|
-
graph LR
|
|
722
|
-
AI["Your AI assistant"]
|
|
723
|
-
MCP["frihet-mcp"]
|
|
724
|
-
API["api.frihet.io"]
|
|
725
|
-
DB["Frihet ERP"]
|
|
726
|
-
|
|
727
|
-
AI -- "create_invoice()" --> MCP
|
|
728
|
-
MCP -- "POST /v1/invoices" --> API
|
|
729
|
-
API --> DB
|
|
730
|
-
DB -- "201 + invoice data" --> API
|
|
731
|
-
API -- "structured JSON" --> MCP
|
|
732
|
-
MCP -- "typed response + suggestions" --> AI
|
|
733
|
-
|
|
734
|
-
style AI fill:#09090b,stroke:#4ade80,color:#fafafa
|
|
735
|
-
style MCP fill:#09090b,stroke:#fafafa,color:#fafafa
|
|
736
|
-
style API fill:#09090b,stroke:#3f3f46,color:#a1a1aa
|
|
737
|
-
style DB fill:#09090b,stroke:#3f3f46,color:#a1a1aa
|
|
738
|
-
```
|
|
739
|
-
|
|
740
|
-
The server translates tool calls into REST API requests. It handles authentication, rate limiting (automatic retry with backoff on 429), pagination, and error mapping.
|
|
584
|
+
</details>
|
|
741
585
|
|
|
742
|
-
|
|
743
|
-
- **stdio** (local) -- `npx @frihet/mcp-server` with `FRIHET_API_KEY`
|
|
744
|
-
- **Streamable HTTP** (remote) -- `https://mcp.frihet.io/mcp` with Bearer token or OAuth 2.0+PKCE
|
|
586
|
+
---
|
|
745
587
|
|
|
746
|
-
|
|
588
|
+
## Configuration
|
|
747
589
|
|
|
748
590
|
| Variable | Required | Default |
|
|
749
591
|
|----------|----------|---------|
|
|
750
592
|
| `FRIHET_API_KEY` | Yes (stdio) | -- |
|
|
751
593
|
| `FRIHET_API_URL` | No | `https://api.frihet.io/v1` |
|
|
752
594
|
| `FRIHET_TOOL_MODE` | No | `full` |
|
|
753
|
-
| `FRIHET_CLAUDE_MODE` | No (unreleased local candidate) | `false` |
|
|
754
|
-
|
|
755
|
-
The source-only Claude candidate requires building this checkout and running
|
|
756
|
-
`dist/index.js`; existing npm releases are not claimed to support the flag.
|
|
757
|
-
See [candidate scope, exclusions and checks](marketplace/anthropic/SUBMISSION.md).
|
|
758
|
-
It cannot be combined with `FRIHET_OPENAI_MODE=true` and is not enabled in the deployed Worker.
|
|
759
|
-
|
|
760
|
-
---
|
|
761
|
-
|
|
762
|
-
## Tool exposure: depth served on demand
|
|
763
|
-
|
|
764
|
-
Frihet's differentiator is **depth** — full ES/EU fiscal coverage plus native compliance (VeriFactu, TicketBAI, Facturae/FACe; KSeF Poland infra-ready, activation pending), banking, CRM, HR/payroll, stay/PMS and POS. But a flat list of every tool, loaded into an agent's context up front, is the 2026 context-rot problem: it crowds out the task and degrades tool selection before any work begins.
|
|
765
|
-
|
|
766
|
-
`FRIHET_TOOL_MODE` lets you choose how that depth is exposed.
|
|
767
|
-
|
|
768
|
-
| Mode | Behavior |
|
|
769
|
-
|------|----------|
|
|
770
|
-
| `full` (default) | Canonical tools and fiscal aliases are exposed with full descriptions and schemas. Public descriptors add conservative callability and side-effect truth; operation names, schemas and handlers are unchanged. |
|
|
771
|
-
| `grouped` | **Progressive disclosure.** Each tool's description collapses to a one-line `[group] summary — full schema via describe_tool('name')`, and three lightweight discovery tools are added. The agent loads depth only for the tools it actually needs. |
|
|
772
595
|
|
|
773
|
-
|
|
596
|
+
`FRIHET_TOOL_MODE=grouped` is progressive disclosure: each tool description collapses to one line and three discovery tools (`list_tool_groups`, `search_tools`, `describe_tool`) are added, so an agent loads full schemas only for the tools it needs. Operation names, input schemas and handlers do not change. The hosted server runs in grouped mode.
|
|
774
597
|
|
|
775
|
-
|
|
776
|
-
- **`search_tools(query)`** — free-text search across tool name, title, summary and group; returns matching tools with their group, summary, read-only flag and input fields. Optional `group` filter and `limit`.
|
|
777
|
-
- **`describe_tool(name)`** — the full original description and input fields for one tool, on demand, before you call it.
|
|
778
|
-
|
|
779
|
-
```jsonc
|
|
780
|
-
// claude_desktop_config.json — opt in to grouped mode
|
|
598
|
+
```json
|
|
781
599
|
{
|
|
782
600
|
"mcpServers": {
|
|
783
601
|
"frihet": {
|
|
784
602
|
"command": "npx",
|
|
785
603
|
"args": ["@frihet/mcp-server"],
|
|
786
|
-
"env": {
|
|
787
|
-
"FRIHET_API_KEY": "fri_...",
|
|
788
|
-
"FRIHET_TOOL_MODE": "grouped"
|
|
789
|
-
}
|
|
604
|
+
"env": { "FRIHET_API_KEY": "fri_...", "FRIHET_TOOL_MODE": "grouped" }
|
|
790
605
|
}
|
|
791
606
|
}
|
|
792
607
|
}
|
|
793
608
|
```
|
|
794
609
|
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
---
|
|
798
|
-
|
|
799
|
-
## API limits
|
|
800
|
-
|
|
801
|
-
| Limit | Value |
|
|
802
|
-
|-------|-------|
|
|
803
|
-
| Requests per minute | 100 per API key |
|
|
804
|
-
| Results per page | 100 max (50 default) |
|
|
805
|
-
| Request body | 1 MB max |
|
|
806
|
-
| Webhook payload | 100 KB max |
|
|
807
|
-
| Webhooks per account | 20 max |
|
|
808
|
-
|
|
809
|
-
Rate limiting is handled automatically with exponential backoff.
|
|
610
|
+
Rate limits depend on your plan; the server retries with exponential backoff on 429. See the [API reference](https://docs.frihet.io/desarrolladores/api-rest).
|
|
810
611
|
|
|
811
612
|
---
|
|
812
613
|
|
|
813
|
-
##
|
|
814
|
-
|
|
815
|
-
Beyond raw MCP tools, this repo includes a **Claude Code skill** that adds business context: Spanish tax rules, workflow recipes, financial reports, and natural language commands.
|
|
816
|
-
|
|
817
|
-
### Install the skill
|
|
818
|
-
|
|
819
|
-
```bash
|
|
820
|
-
git clone https://github.com/Frihet-io/frihet-mcp.git
|
|
821
|
-
ln -s "$(pwd)/frihet-mcp/skills/frihet-mcp" ~/.claude/skills/frihet-mcp
|
|
822
|
-
```
|
|
823
|
-
|
|
824
|
-
Or with the universal installer:
|
|
825
|
-
|
|
826
|
-
```bash
|
|
827
|
-
npx skills add Frihet-io/frihet-mcp
|
|
828
|
-
```
|
|
829
|
-
|
|
830
|
-
The skill routes requests to the Frihet MCP tools (invoices, quotes, expenses, clients, monthly review) and tells the agent when to ask for confirmation before writes. It does not add standalone `/frihet` subcommands, and it does not ship VAT rates or deductibility rules: tax figures come from your Frihet data, not from the skill.
|
|
831
|
-
|
|
832
|
-
Full documentation: [docs.frihet.io/desarrolladores/skill-claude-code](https://docs.frihet.io/desarrolladores/skill-claude-code)
|
|
833
|
-
|
|
834
|
-
---
|
|
835
|
-
|
|
836
|
-
## Development
|
|
837
|
-
|
|
838
|
-
```bash
|
|
839
|
-
git clone https://github.com/Frihet-io/frihet-mcp.git
|
|
840
|
-
cd frihet-mcp
|
|
841
|
-
npm install
|
|
842
|
-
npm run build
|
|
843
|
-
```
|
|
844
|
-
|
|
845
|
-
Run locally:
|
|
846
|
-
|
|
847
|
-
```bash
|
|
848
|
-
FRIHET_API_KEY=fri_xxx node dist/index.js
|
|
849
|
-
```
|
|
850
|
-
|
|
851
|
-
Test with the [MCP Inspector](https://modelcontextprotocol.io/docs/tools/inspector):
|
|
852
|
-
|
|
853
|
-
```bash
|
|
854
|
-
npx @modelcontextprotocol/inspector node dist/index.js
|
|
855
|
-
```
|
|
856
|
-
|
|
857
|
-
---
|
|
858
|
-
|
|
859
|
-
## Contributing
|
|
860
|
-
|
|
861
|
-
Contributions are welcome. Please open an issue first to discuss what you'd like to change.
|
|
614
|
+
## Development and contributing
|
|
862
615
|
|
|
863
616
|
```bash
|
|
864
617
|
git clone https://github.com/Frihet-io/frihet-mcp.git
|
|
865
618
|
cd frihet-mcp
|
|
866
619
|
npm install
|
|
867
620
|
npm run build # must pass before submitting
|
|
621
|
+
FRIHET_API_KEY=fri_xxx node dist/index.js
|
|
868
622
|
```
|
|
869
623
|
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
## Current limitations
|
|
873
|
-
|
|
874
|
-
- **No OCR or file upload** -- the MCP works with structured data, not images or PDFs.
|
|
875
|
-
- **Single company** -- one API key maps to one Frihet workspace.
|
|
876
|
-
- **Frihet account required** -- you need an active account at [app.frihet.io](https://app.frihet.io) and an API key (starts with `fri_`).
|
|
624
|
+
Test with the [MCP Inspector](https://modelcontextprotocol.io/docs/tools/inspector): `npx @modelcontextprotocol/inspector node dist/index.js`. Open an issue before large changes. Report vulnerabilities as described in [SECURITY.md](./SECURITY.md).
|
|
877
625
|
|
|
878
626
|
---
|
|
879
627
|
|
|
@@ -886,24 +634,9 @@ npm run build # must pass before submitting
|
|
|
886
634
|
| [`frihet`](https://www.npmjs.com/package/frihet) | CLI (`frihet invoices list --status overdue`) |
|
|
887
635
|
| [`n8n-nodes-frihet`](https://www.npmjs.com/package/n8n-nodes-frihet) | n8n community node for workflow automation |
|
|
888
636
|
| [REST API](https://docs.frihet.io/desarrolladores/api-rest) | OpenAPI 3.1 at `api.frihet.io/v1` |
|
|
889
|
-
| [Remote MCP](https://mcp.frihet.io) | Hosted endpoint on Cloudflare Workers (zero install) |
|
|
890
637
|
| [Webhooks](https://docs.frihet.io/desarrolladores/webhooks) | Real-time events with HMAC-SHA256 |
|
|
891
638
|
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
- [Frihet](https://frihet.io) -- The product
|
|
895
|
-
- [Documentation](https://docs.frihet.io) -- Full docs
|
|
896
|
-
- [API reference](https://docs.frihet.io/desarrolladores/api-rest) -- REST API
|
|
897
|
-
- [MCP server docs](https://docs.frihet.io/desarrolladores/mcp-server) -- Setup guides, troubleshooting
|
|
898
|
-
- [npm](https://www.npmjs.com/package/@frihet/mcp-server) -- Package registry
|
|
899
|
-
- [Smithery](https://smithery.ai/servers/frihet/frihet-mcp) -- Smithery marketplace
|
|
900
|
-
- [MCP Registry](https://registry.modelcontextprotocol.io/?q=io.frihet) -- Official Model Context Protocol registry
|
|
901
|
-
- [GitHub MCP Registry](https://github.com/mcp/io.frihet/erp) -- Frihet listing on GitHub
|
|
902
|
-
- [Remote endpoint](https://mcp.frihet.io) -- Hosted MCP server (Cloudflare Workers)
|
|
903
|
-
- [OpenAPI spec](https://api.frihet.io/openapi.json) -- Machine-readable API definition
|
|
904
|
-
- [Security policy](./SECURITY.md) -- Private vulnerability reporting guidance
|
|
905
|
-
|
|
906
|
-
---
|
|
639
|
+
[Frihet](https://frihet.io) · [Documentation](https://docs.frihet.io) · [MCP server docs](https://docs.frihet.io/desarrolladores/mcp-server) · [OpenAPI spec](https://api.frihet.io/openapi.json)
|
|
907
640
|
|
|
908
641
|
## License
|
|
909
642
|
|
|
@@ -164,6 +164,7 @@ const SAFE_META_FLAGS = [
|
|
|
164
164
|
"io.frihet/transportOutcomeUnknown", "io.frihet/retryable", "io.frihet/operationMayHaveCompleted",
|
|
165
165
|
"io.frihet/resourceNotFound", "io.frihet/accessDenied",
|
|
166
166
|
"io.frihet/confirmationRequired", "io.frihet/localValidationRejected", "io.frihet/requestRejected",
|
|
167
|
+
"io.frihet/searchScanTruncated", "io.frihet/cursorWithQueryUnsupported",
|
|
167
168
|
];
|
|
168
169
|
/** Claude does not expose opaque tool metadata as an actionable error message.
|
|
169
170
|
* Render fixed text from the typed flags, never provider text, codes or IDs.
|
|
@@ -185,6 +186,12 @@ function claudeErrorText(meta) {
|
|
|
185
186
|
if (meta["io.frihet/resourceNotFound"] === true) {
|
|
186
187
|
return "NOT_FOUND: No record is available with this ID in the authorized workspace. Check the ID; after deletion, verify absence with a list. Do not automatically repeat a write.";
|
|
187
188
|
}
|
|
189
|
+
if (meta["io.frihet/cursorWithQueryUnsupported"] === true) {
|
|
190
|
+
return "CURSOR_WITH_QUERY_UNSUPPORTED: Text search ('q') cannot be combined with cursor pagination ('after') because the ERP backend slices search results by 'offset'/'limit' and ignores 'after'. No action was performed. Use 'offset' and 'limit' when searching with 'q', or omit 'q' when paginating with 'after'.";
|
|
191
|
+
}
|
|
192
|
+
if (meta["io.frihet/searchScanTruncated"] === true) {
|
|
193
|
+
return "SEARCH_SCAN_TRUNCATED: The text search hit the ERP 500-document scan cap (truncated=true) and was withheld so an incomplete scan is never mistaken for an exhaustive list. Narrow 'q' with a more specific term or paginate without 'q' using 'after'.";
|
|
194
|
+
}
|
|
188
195
|
if (meta["io.frihet/localValidationRejected"] === true) {
|
|
189
196
|
return "VALIDATION_FAILED: The ID or input parameters were rejected before sending the request. No action was performed. Correct the ID and parameters before retrying.";
|
|
190
197
|
}
|
|
@@ -203,6 +210,8 @@ export function claudeToolResult(result, toolName) {
|
|
|
203
210
|
const fixedRemediation = error && (safeMeta["io.frihet/confirmationRequired"] === true
|
|
204
211
|
|| safeMeta["io.frihet/localValidationRejected"] === true
|
|
205
212
|
|| safeMeta["io.frihet/requestRejected"] === true
|
|
213
|
+
|| safeMeta["io.frihet/cursorWithQueryUnsupported"] === true
|
|
214
|
+
|| safeMeta["io.frihet/searchScanTruncated"] === true
|
|
206
215
|
|| safeMeta["io.frihet/operationOutcomeUnknown"] === true
|
|
207
216
|
|| safeMeta["io.frihet/transportOutcomeUnknown"] === true
|
|
208
217
|
|| safeMeta["io.frihet/operationMayHaveCompleted"] === true);
|
package/dist/demo-client.d.ts
CHANGED
|
@@ -22,6 +22,7 @@ import type { CreateWebhookInput, CreateWebhookResult, PaginatedResponse, Update
|
|
|
22
22
|
import { type BinaryDocument, type EInvoiceDocument, type EInvoiceExportResult, type McpEInvoiceExportFormat } from "./client.js";
|
|
23
23
|
type Rec = Record<string, unknown>;
|
|
24
24
|
export declare class DemoFrihetClient implements IFrihetClient {
|
|
25
|
+
readonly isDemoClient: true;
|
|
25
26
|
listInvoices(params?: {
|
|
26
27
|
limit?: number;
|
|
27
28
|
offset?: number;
|
package/dist/demo-client.js
CHANGED
|
@@ -75,6 +75,7 @@ const DEMO_OWNER_CAPABILITIES = Object.entries(DEMO_PERMISSION_MATRIX.owner)
|
|
|
75
75
|
.flatMap(([resource, actions]) => actions.map((action) => `${resource}:${action}`))
|
|
76
76
|
.sort();
|
|
77
77
|
export class DemoFrihetClient {
|
|
78
|
+
isDemoClient = true;
|
|
78
79
|
// ---------------------------------------------------------------- Invoices
|
|
79
80
|
async listInvoices(params) {
|
|
80
81
|
return demoPage(demoInvoices, params);
|
|
@@ -12,6 +12,7 @@ export interface McpSurfaceComposition {
|
|
|
12
12
|
readonly claudeMode?: boolean;
|
|
13
13
|
readonly googleMode?: boolean;
|
|
14
14
|
readonly museMode?: boolean;
|
|
15
|
+
readonly demoMode?: boolean;
|
|
15
16
|
}
|
|
16
17
|
export declare function localMcpSurfaceComposition(openaiMode: boolean, groupedMode: boolean, claudeMode?: boolean, googleMode?: boolean, museMode?: boolean): McpSurfaceComposition;
|
|
17
18
|
export declare function remoteMcpSurfaceComposition(openaiMode: boolean, groupedMode: boolean, claudeMode?: boolean, googleMode?: boolean, museMode?: boolean): McpSurfaceComposition;
|
|
@@ -33,6 +33,16 @@ export function registerMcpSurface(server, client, options) {
|
|
|
33
33
|
annotations: { ...config.annotations, title: config.title },
|
|
34
34
|
}, handler);
|
|
35
35
|
}
|
|
36
|
+
let clientIsDemo = false;
|
|
37
|
+
try {
|
|
38
|
+
clientIsDemo =
|
|
39
|
+
client?.isDemoClient === true ||
|
|
40
|
+
client?.constructor?.name === "DemoFrihetClient";
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
clientIsDemo = false;
|
|
44
|
+
}
|
|
45
|
+
const isDemoMode = options.demoMode === true || clientIsDemo;
|
|
36
46
|
if (options.groupedMode) {
|
|
37
47
|
if (options.openaiMode) {
|
|
38
48
|
applyOpenAIReviewProfiles(server);
|
|
@@ -48,6 +58,7 @@ export function registerMcpSurface(server, client, options) {
|
|
|
48
58
|
metaKey: CAPABILITY_META_KEY,
|
|
49
59
|
localDiscovery: buildLocalDiscoveryCapability,
|
|
50
60
|
},
|
|
61
|
+
demoMode: isDemoMode,
|
|
51
62
|
});
|
|
52
63
|
}
|
|
53
64
|
}
|
package/dist/tool-exposure.d.ts
CHANGED
|
@@ -141,6 +141,7 @@ export declare function applyToolExposureProfile(server: any, options?: {
|
|
|
141
141
|
type: "oauth2";
|
|
142
142
|
scopes: readonly string[];
|
|
143
143
|
}>;
|
|
144
|
+
demoMode?: boolean;
|
|
144
145
|
}): ToolExposureHandle;
|
|
145
146
|
/** Number of meta-tools added in grouped mode (for logging). */
|
|
146
147
|
export declare const GROUPED_META_TOOL_COUNT: number;
|
package/dist/tool-exposure.js
CHANGED
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
* @see ./openai-profile.ts — the sibling interceptor this mirrors.
|
|
42
42
|
*/
|
|
43
43
|
import { z } from "zod/v4";
|
|
44
|
+
import { READ_STAMP } from "./demo-fixtures.js";
|
|
44
45
|
/**
|
|
45
46
|
* Domain metadata. Keep these terse — they are the only group-level prose the
|
|
46
47
|
* agent loads up front. Depth lives in the per-tool descriptions, fetched via
|
|
@@ -390,7 +391,7 @@ server, options) {
|
|
|
390
391
|
};
|
|
391
392
|
// Register the meta-tools eagerly. Their handlers close over `catalog`/`groups`
|
|
392
393
|
// which finish populating as the real tools register right after this call.
|
|
393
|
-
registerMetaTools(originalRegisterTool, handle, visibleGroupIds, groupMetadata, options?.capabilityTruth, options?.securitySchemes, options?.describeToolExample);
|
|
394
|
+
registerMetaTools(originalRegisterTool, handle, visibleGroupIds, groupMetadata, options?.capabilityTruth, options?.securitySchemes, options?.describeToolExample, options?.demoMode === true);
|
|
394
395
|
return handle;
|
|
395
396
|
}
|
|
396
397
|
/* ------------------------------------------------------------------ */
|
|
@@ -420,8 +421,9 @@ function textResult(payload) {
|
|
|
420
421
|
*/
|
|
421
422
|
function registerMetaTools(
|
|
422
423
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
423
|
-
originalRegisterTool, handle, visibleGroupIds, groupMetadata, capabilityTruth, securitySchemes, describeToolExample = "create_invoice") {
|
|
424
|
+
originalRegisterTool, handle, visibleGroupIds, groupMetadata, capabilityTruth, securitySchemes, describeToolExample = "create_invoice", demoMode = false) {
|
|
424
425
|
const { catalog, groups } = handle;
|
|
426
|
+
const demoStamp = demoMode ? READ_STAMP : {};
|
|
425
427
|
const oauthConfig = securitySchemes
|
|
426
428
|
? {
|
|
427
429
|
securitySchemes: securitySchemes.map((scheme) => ({
|
|
@@ -529,6 +531,7 @@ originalRegisterTool, handle, visibleGroupIds, groupMetadata, capabilityTruth, s
|
|
|
529
531
|
}))
|
|
530
532
|
.filter((g) => g.toolCount > 0);
|
|
531
533
|
return textResult({
|
|
534
|
+
...demoStamp,
|
|
532
535
|
groups: out,
|
|
533
536
|
totalGroups: out.length,
|
|
534
537
|
totalTools: catalog.size,
|
|
@@ -591,6 +594,7 @@ originalRegisterTool, handle, visibleGroupIds, groupMetadata, capabilityTruth, s
|
|
|
591
594
|
score,
|
|
592
595
|
}));
|
|
593
596
|
return textResult({
|
|
597
|
+
...demoStamp,
|
|
594
598
|
query: query || null,
|
|
595
599
|
group: groupFilter ?? null,
|
|
596
600
|
count: scored.length,
|
|
@@ -626,6 +630,7 @@ originalRegisterTool, handle, visibleGroupIds, groupMetadata, capabilityTruth, s
|
|
|
626
630
|
{
|
|
627
631
|
type: "text",
|
|
628
632
|
text: JSON.stringify({
|
|
633
|
+
...demoStamp,
|
|
629
634
|
error: name
|
|
630
635
|
? `No tool named '${name}'.`
|
|
631
636
|
: "Provide { name } — the exact tool name from search_tools.",
|
|
@@ -638,6 +643,7 @@ originalRegisterTool, handle, visibleGroupIds, groupMetadata, capabilityTruth, s
|
|
|
638
643
|
};
|
|
639
644
|
}
|
|
640
645
|
return textResult({
|
|
646
|
+
...demoStamp,
|
|
641
647
|
name: entry.name,
|
|
642
648
|
group: entry.group,
|
|
643
649
|
title: entry.title,
|
package/dist/tools/clients.js
CHANGED
|
@@ -226,6 +226,7 @@ export function registerClientTools(server, client) {
|
|
|
226
226
|
"Error: la busqueda textual ('q') no se puede combinar con el cursor 'after' porque el backend pagina las busquedas mediante 'offset'/'limit' e ignora 'after'. Usa 'offset' y 'limit' con 'q', u omite 'q' al paginar con 'after'.",
|
|
227
227
|
}],
|
|
228
228
|
isError: true,
|
|
229
|
+
_meta: { "io.frihet/cursorWithQueryUnsupported": true },
|
|
229
230
|
};
|
|
230
231
|
}
|
|
231
232
|
let result = await client.listClients({ limit, offset, after, fields, q, stage });
|
|
@@ -278,6 +279,7 @@ export function registerClientTools(server, client) {
|
|
|
278
279
|
`Error: la busqueda de clientes alcanzo el limite de escaneo de 500 documentos del ERP (truncated=true; ${partialCount} coincidencia(s) en los primeros 500 registros) y no puede devolverse como resultado exhaustivo. Acota 'q' (o filtra por 'stage') o pagina sin 'q' usando 'after'.`,
|
|
279
280
|
}],
|
|
280
281
|
isError: true,
|
|
282
|
+
_meta: { "io.frihet/searchScanTruncated": true },
|
|
281
283
|
};
|
|
282
284
|
}
|
|
283
285
|
const cleanEnvelope = {
|
package/dist/tools/shared.d.ts
CHANGED
|
@@ -537,8 +537,8 @@ export declare const bankTransactionItemOutput: z.ZodObject<{
|
|
|
537
537
|
postedAt: z.ZodOptional<z.ZodString>;
|
|
538
538
|
category: z.ZodOptional<z.ZodString>;
|
|
539
539
|
status: z.ZodOptional<z.ZodEnum<{
|
|
540
|
-
pending: "pending";
|
|
541
540
|
posted: "posted";
|
|
541
|
+
pending: "pending";
|
|
542
542
|
excluded: "excluded";
|
|
543
543
|
}>>;
|
|
544
544
|
matchedDocId: z.ZodOptional<z.ZodString>;
|
package/dist/tools/vendors.js
CHANGED
|
@@ -209,6 +209,7 @@ export function registerVendorTools(server, client) {
|
|
|
209
209
|
"Error: la busqueda textual ('q') no se puede combinar con el cursor 'after' porque el backend pagina las busquedas mediante 'offset'/'limit' e ignora 'after'. Usa 'offset' y 'limit' con 'q', u omite 'q' al paginar con 'after'.",
|
|
210
210
|
}],
|
|
211
211
|
isError: true,
|
|
212
|
+
_meta: { "io.frihet/cursorWithQueryUnsupported": true },
|
|
212
213
|
};
|
|
213
214
|
}
|
|
214
215
|
let result = await client.listVendors({ q, limit, offset, after, fields });
|
|
@@ -253,6 +254,7 @@ export function registerVendorTools(server, client) {
|
|
|
253
254
|
`Error: la busqueda de proveedores alcanzo el limite de escaneo de 500 documentos del ERP (truncated=true; ${partialCount} coincidencia(s) en los primeros 500 registros) y no puede devolverse como resultado exhaustivo. Acota 'q' o pagina sin 'q' usando 'after'.`,
|
|
254
255
|
}],
|
|
255
256
|
isError: true,
|
|
257
|
+
_meta: { "io.frihet/searchScanTruncated": true },
|
|
256
258
|
};
|
|
257
259
|
}
|
|
258
260
|
const cleanEnvelope = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frihet/mcp-server",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "AI-native MCP server for Frihet ERP — 156 canonical operations for invoicing, expenses, CRM, banking, POS and ES/EU fiscal workflows, with conservative capability and side-effect metadata.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|