@frihet/mcp-server 1.12.0 → 1.13.0
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 +49 -5
- package/dist/__tests__/tool-exposure.test.d.ts +11 -0
- package/dist/__tests__/tool-exposure.test.d.ts.map +1 -0
- package/dist/__tests__/tool-exposure.test.js +249 -0
- package/dist/__tests__/tool-exposure.test.js.map +1 -0
- package/dist/index.js +20 -3
- package/dist/index.js.map +1 -1
- package/dist/tool-exposure.d.ts +127 -0
- package/dist/tool-exposure.d.ts.map +1 -0
- package/dist/tool-exposure.js +453 -0
- package/dist/tool-exposure.js.map +1 -0
- package/dist/tools/register-all.d.ts +2 -2
- package/dist/tools/register-all.js +3 -3
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
| **ChatGPT Apps** | Coming soon | [chatgpt.com](https://chatgpt.com) |
|
|
40
40
|
| **Anthropic Claude Directory** | Coming soon | [claude.ai/settings/connectors](https://claude.ai/settings/connectors) |
|
|
41
41
|
|
|
42
|
-
> **Tool count:** npm `latest` (1.
|
|
42
|
+
> **Tool count:** npm `latest` (1.13.0) ships all 151 tools, same as the remote endpoint (`mcp.frihet.io`).
|
|
43
43
|
|
|
44
44
|
---
|
|
45
45
|
|
|
@@ -52,7 +52,7 @@ You: "Create an invoice for TechStart SL, 40 hours of consulting at 75 EUR/h
|
|
|
52
52
|
Claude: Done. Invoice INV-2026-089 created. Total: 3,000.00 EUR + 21% IVA = 3,630.00 EUR.
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
-
151 tools.
|
|
55
|
+
151 tools. 11 resources. 10 prompts. Structured output on every tool. Zero boilerplate.
|
|
56
56
|
|
|
57
57
|
<!-- v1.12.0-beta.1 — D4-B megasprint: HR (9), payroll (2), onboarding (2), permissions (2), period close (3), webhook test (1) = +19 = 151 tools total -->
|
|
58
58
|
|
|
@@ -549,7 +549,7 @@ All 151 tools return **structured output** via `outputSchema` -- typed JSON, not
|
|
|
549
549
|
|
|
550
550
|
---
|
|
551
551
|
|
|
552
|
-
## Resources (
|
|
552
|
+
## Resources (11)
|
|
553
553
|
|
|
554
554
|
Context the AI can read to make smarter decisions.
|
|
555
555
|
|
|
@@ -562,6 +562,8 @@ Context the AI can read to make smarter decisions.
|
|
|
562
562
|
| Tax Calendar | `frihet://tax/calendar` | Quarterly filing deadlines: Modelo 303, 130, 390, 420, VeriFactu timeline |
|
|
563
563
|
| Expense Categories | `frihet://config/expense-categories` | 8 categories with deductibility rules, IVA treatment, amortization |
|
|
564
564
|
| Invoice Statuses | `frihet://config/invoice-statuses` | Status flow (draft > sent > paid/overdue > cancelled), transition rules, webhook events |
|
|
565
|
+
| Currencies | `frihet://config/currencies` | 40 supported currencies with ISO codes, symbols, decimal places, locale formatting |
|
|
566
|
+
| Countries | `frihet://config/countries` | 61 supported countries with fiscal zones, default tax rates, currencies, invoice prefixes |
|
|
565
567
|
|
|
566
568
|
**Dynamic** (live data from your account):
|
|
567
569
|
|
|
@@ -570,10 +572,11 @@ Context the AI can read to make smarter decisions.
|
|
|
570
572
|
| Business Profile | `frihet://business-profile` | Your business info, plan, defaults, recent activity, top clients |
|
|
571
573
|
| Monthly Snapshot | `frihet://monthly-snapshot` | Current month P&L, revenue, expenses, tax liability |
|
|
572
574
|
| Overdue Invoices | `frihet://overdue-invoices` | All invoices past due date (up to 100) |
|
|
575
|
+
| Plan Limits | `frihet://status/plan-limits` | Live plan tier, usage counters, invoices/month, API rate limits |
|
|
573
576
|
|
|
574
577
|
---
|
|
575
578
|
|
|
576
|
-
## Prompts (
|
|
579
|
+
## Prompts (10)
|
|
577
580
|
|
|
578
581
|
Pre-built workflows the AI can execute as guided multi-step operations.
|
|
579
582
|
|
|
@@ -585,6 +588,9 @@ Pre-built workflows the AI can execute as guided multi-step operations.
|
|
|
585
588
|
| `overdue-followup` | Find overdue invoices, draft follow-up messages, suggest payment reminders | -- |
|
|
586
589
|
| `new-client-invoice` | Create a client + first invoice in one workflow with tax rate lookup | `clientName`, `country?` |
|
|
587
590
|
| `expense-report` | Generate expense report grouped by category with deductible totals | `month?` (YYYY-MM) |
|
|
591
|
+
| `year-end-close` | Full annual closing: quarterly review, pending invoices, uncategorized expenses, year-end checklist | `year` (YYYY) |
|
|
592
|
+
| `cash-flow-forecast` | Project cash flow for coming months: recurring income, expenses, overdue receivables, tax deadlines | `months?` (default: 3) |
|
|
593
|
+
| `invoice-aging-review` | AR aging analysis: group unpaid invoices by bucket (0-30/31-60/61-90/90+ days), top debtors, collection actions | -- |
|
|
588
594
|
| `expense-batch` | Process expenses in bulk: categorize, apply tax rates, flag missing receipts | `fiscalZone?` |
|
|
589
595
|
|
|
590
596
|
---
|
|
@@ -623,6 +629,44 @@ Two transports:
|
|
|
623
629
|
|----------|----------|---------|
|
|
624
630
|
| `FRIHET_API_KEY` | Yes (stdio) | -- |
|
|
625
631
|
| `FRIHET_API_URL` | No | `https://api.frihet.io/v1` |
|
|
632
|
+
| `FRIHET_TOOL_MODE` | No | `full` |
|
|
633
|
+
|
|
634
|
+
---
|
|
635
|
+
|
|
636
|
+
## Tool exposure: depth served on demand
|
|
637
|
+
|
|
638
|
+
Frihet's differentiator is **depth** — full ES/EU fiscal coverage plus native compliance (VeriFactu, TicketBAI, Facturae/FACe/KSeF), 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.
|
|
639
|
+
|
|
640
|
+
`FRIHET_TOOL_MODE` lets you choose how that depth is exposed.
|
|
641
|
+
|
|
642
|
+
| Mode | Behavior |
|
|
643
|
+
|------|----------|
|
|
644
|
+
| `full` (default) | All tools are exposed with their full descriptions and schemas. **Unchanged from previous releases** — existing setups are unaffected. |
|
|
645
|
+
| `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. |
|
|
646
|
+
|
|
647
|
+
In `grouped` mode the tools are unchanged — same names, same input schemas, same behavior. Only the *up-front context cost* changes. Discovery flows through three meta-tools:
|
|
648
|
+
|
|
649
|
+
- **`list_tool_groups()`** — the domain map (invoicing, expenses, fiscal/compliance, banking, CRM, HR/payroll, stay/PMS, POS, intelligence, products, platform) with a one-line blurb and tool count for each.
|
|
650
|
+
- **`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`.
|
|
651
|
+
- **`describe_tool(name)`** — the full original description and input fields for one tool, on demand, before you call it.
|
|
652
|
+
|
|
653
|
+
```jsonc
|
|
654
|
+
// claude_desktop_config.json — opt in to grouped mode
|
|
655
|
+
{
|
|
656
|
+
"mcpServers": {
|
|
657
|
+
"frihet": {
|
|
658
|
+
"command": "npx",
|
|
659
|
+
"args": ["@frihet/mcp-server"],
|
|
660
|
+
"env": {
|
|
661
|
+
"FRIHET_API_KEY": "fri_...",
|
|
662
|
+
"FRIHET_TOOL_MODE": "grouped"
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
```
|
|
668
|
+
|
|
669
|
+
> This is an opt-in exposure layer only — it never changes a tool's logic, name or behavior, and `full` mode is byte-identical to before. The same interceptor pattern powers the OpenAI-safe profile (`FRIHET_OPENAI_MODE`).
|
|
626
670
|
|
|
627
671
|
---
|
|
628
672
|
|
|
@@ -724,7 +768,7 @@ npm run build # must pass before submitting
|
|
|
724
768
|
|
|
725
769
|
| Package | What it is |
|
|
726
770
|
|---------|-----------|
|
|
727
|
-
| [`@frihet/mcp-server`](https://www.npmjs.com/package/@frihet/mcp-server) | This MCP server (151 tools,
|
|
771
|
+
| [`@frihet/mcp-server`](https://www.npmjs.com/package/@frihet/mcp-server) | This MCP server (151 tools, 11 resources, 10 prompts) |
|
|
728
772
|
| [`@frihet/sdk`](https://github.com/Frihet-io/frihet-sdk) | TypeScript SDK (`frihet.invoices.create()`) |
|
|
729
773
|
| [`frihet`](https://www.npmjs.com/package/frihet) | CLI (`frihet invoices list --status overdue`) |
|
|
730
774
|
| [`n8n-nodes-frihet`](https://www.npmjs.com/package/n8n-nodes-frihet) | n8n community node for workflow automation |
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the grouped tool-exposure profile (progressive disclosure).
|
|
3
|
+
*
|
|
4
|
+
* The default ("full") mode must stay BYTE-IDENTICAL to the un-profiled server
|
|
5
|
+
* so existing users are unaffected. The opt-in "grouped" mode collapses the 151
|
|
6
|
+
* full tool descriptions into terse one-liners and adds three discovery
|
|
7
|
+
* meta-tools (list_tool_groups, search_tools, describe_tool) so agents load
|
|
8
|
+
* depth on demand instead of a flat 151-tool wall of context.
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=tool-exposure.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-exposure.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/tool-exposure.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the grouped tool-exposure profile (progressive disclosure).
|
|
3
|
+
*
|
|
4
|
+
* The default ("full") mode must stay BYTE-IDENTICAL to the un-profiled server
|
|
5
|
+
* so existing users are unaffected. The opt-in "grouped" mode collapses the 151
|
|
6
|
+
* full tool descriptions into terse one-liners and adds three discovery
|
|
7
|
+
* meta-tools (list_tool_groups, search_tools, describe_tool) so agents load
|
|
8
|
+
* depth on demand instead of a flat 151-tool wall of context.
|
|
9
|
+
*/
|
|
10
|
+
import { describe, test } from "node:test";
|
|
11
|
+
import assert from "node:assert/strict";
|
|
12
|
+
import { readdirSync, readFileSync } from "node:fs";
|
|
13
|
+
import { fileURLToPath } from "node:url";
|
|
14
|
+
import { dirname, join } from "node:path";
|
|
15
|
+
import { applyToolExposureProfile, resolveToolMode, groupForTool, GROUPS, FILE_TO_GROUP, GROUPED_META_TOOL_COUNT, } from "../tool-exposure.js";
|
|
16
|
+
import { registerAllTools } from "../tools/register-all.js";
|
|
17
|
+
import { registerAllPrompts } from "../prompts/register-all.js";
|
|
18
|
+
class StubMcpServer {
|
|
19
|
+
tools = new Map();
|
|
20
|
+
prompts = [];
|
|
21
|
+
resources = [];
|
|
22
|
+
registerTool(name, config, handler) {
|
|
23
|
+
this.tools.set(name, { name, config, handler });
|
|
24
|
+
}
|
|
25
|
+
registerPrompt(name) {
|
|
26
|
+
this.prompts.push(name);
|
|
27
|
+
}
|
|
28
|
+
registerResource(name) {
|
|
29
|
+
this.resources.push(name);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function makeClient() {
|
|
33
|
+
return new Proxy({}, {
|
|
34
|
+
get: () => async (input) => ({
|
|
35
|
+
data: [],
|
|
36
|
+
total: 0,
|
|
37
|
+
limit: 10,
|
|
38
|
+
offset: 0,
|
|
39
|
+
input,
|
|
40
|
+
}),
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
const asMcp = (s) => s;
|
|
44
|
+
/** Build a server with NO profile (current/full behavior). */
|
|
45
|
+
function makeFullServer() {
|
|
46
|
+
const server = new StubMcpServer();
|
|
47
|
+
registerAllTools(asMcp(server), makeClient());
|
|
48
|
+
registerAllPrompts(asMcp(server));
|
|
49
|
+
return server;
|
|
50
|
+
}
|
|
51
|
+
/** Build a server with the grouped tool-exposure profile applied. */
|
|
52
|
+
function makeGroupedServer() {
|
|
53
|
+
const server = new StubMcpServer();
|
|
54
|
+
const handle = applyToolExposureProfile(server);
|
|
55
|
+
registerAllTools(asMcp(server), makeClient());
|
|
56
|
+
registerAllPrompts(asMcp(server));
|
|
57
|
+
return { server, handle };
|
|
58
|
+
}
|
|
59
|
+
const META_TOOLS = ["list_tool_groups", "search_tools", "describe_tool"];
|
|
60
|
+
describe("tool-exposure: mode resolution", () => {
|
|
61
|
+
test("default and 'full' resolve to full; only 'grouped' opts in", () => {
|
|
62
|
+
assert.equal(resolveToolMode({}), "full");
|
|
63
|
+
assert.equal(resolveToolMode({ FRIHET_TOOL_MODE: undefined }), "full");
|
|
64
|
+
assert.equal(resolveToolMode({ FRIHET_TOOL_MODE: "full" }), "full");
|
|
65
|
+
assert.equal(resolveToolMode({ FRIHET_TOOL_MODE: "anything-else" }), "full");
|
|
66
|
+
assert.equal(resolveToolMode({ FRIHET_TOOL_MODE: "grouped" }), "grouped");
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
describe("tool-exposure: full mode is byte-identical", () => {
|
|
70
|
+
test("registers exactly 151 tools, no meta-tools, descriptions untouched", () => {
|
|
71
|
+
const full = makeFullServer();
|
|
72
|
+
assert.equal(full.tools.size, 151);
|
|
73
|
+
for (const meta of META_TOOLS) {
|
|
74
|
+
assert.equal(full.tools.has(meta), false, `${meta} must NOT exist in full mode`);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
test("grouped mode does not mutate the un-profiled tool descriptions", () => {
|
|
78
|
+
// Capture the full-mode description of a representative tool, then confirm
|
|
79
|
+
// the grouped server's collapse did not leak back into the base registration.
|
|
80
|
+
const full = makeFullServer();
|
|
81
|
+
const fullDesc = full.tools.get("list_invoices").config.description;
|
|
82
|
+
// Full description is the rich, multi-sentence original (not a collapsed line).
|
|
83
|
+
assert.ok(fullDesc.length > 117, "full description should be the rich original");
|
|
84
|
+
assert.equal(fullDesc.includes("describe_tool"), false);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
describe("tool-exposure: grouped mode", () => {
|
|
88
|
+
test("registers 151 tools + 3 meta-tools and a complete catalog", () => {
|
|
89
|
+
const { server, handle } = makeGroupedServer();
|
|
90
|
+
assert.equal(server.tools.size, 151 + GROUPED_META_TOOL_COUNT);
|
|
91
|
+
assert.equal(GROUPED_META_TOOL_COUNT, 3);
|
|
92
|
+
for (const meta of META_TOOLS) {
|
|
93
|
+
assert.equal(server.tools.has(meta), true, `${meta} must exist in grouped mode`);
|
|
94
|
+
}
|
|
95
|
+
// Catalog holds every real tool (meta-tools excluded).
|
|
96
|
+
assert.equal(handle.catalog.size, 151);
|
|
97
|
+
for (const meta of META_TOOLS) {
|
|
98
|
+
assert.equal(handle.catalog.has(meta), false);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
test("collapses each real tool's description to a terse pointer line", () => {
|
|
102
|
+
const { server } = makeGroupedServer();
|
|
103
|
+
const li = server.tools.get("list_invoices");
|
|
104
|
+
assert.match(li.config.description, /^\[invoicing\] /);
|
|
105
|
+
assert.match(li.config.description, /describe_tool\('list_invoices'\)/);
|
|
106
|
+
// Terse: collapsed lines are far shorter than the rich originals.
|
|
107
|
+
assert.ok(li.config.description.length < 200);
|
|
108
|
+
// Every collapsed description names its group and the describe_tool pointer.
|
|
109
|
+
for (const [name, tool] of server.tools) {
|
|
110
|
+
if (META_TOOLS.includes(name))
|
|
111
|
+
continue;
|
|
112
|
+
assert.match(tool.config.description, /^\[[a-z]+\] /, `${name} description must start with [group]`);
|
|
113
|
+
assert.ok(tool.config.description.includes(`describe_tool('${name}')`), `${name} description must point to describe_tool`);
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
test("does NOT change tool names, annotations, or input schemas", () => {
|
|
117
|
+
const full = makeFullServer();
|
|
118
|
+
const { server: grouped } = makeGroupedServer();
|
|
119
|
+
// Same set of real tool names (grouped adds only the 3 meta-tools).
|
|
120
|
+
const realGrouped = [...grouped.tools.keys()].filter((n) => !META_TOOLS.includes(n));
|
|
121
|
+
assert.deepEqual([...full.tools.keys()].sort(), realGrouped.sort());
|
|
122
|
+
// Annotations + input schema identity preserved for a mutating tool.
|
|
123
|
+
const ci = "create_invoice";
|
|
124
|
+
assert.deepEqual(grouped.tools.get(ci).config.annotations, full.tools.get(ci).config.annotations);
|
|
125
|
+
assert.deepEqual(Object.keys(grouped.tools.get(ci).config.inputSchema ?? {}), Object.keys(full.tools.get(ci).config.inputSchema ?? {}));
|
|
126
|
+
});
|
|
127
|
+
test("tool logic is unchanged — collapsed tool still invokes its handler", async () => {
|
|
128
|
+
const { server } = makeGroupedServer();
|
|
129
|
+
const result = await server.tools.get("list_invoices").handler({});
|
|
130
|
+
assert.ok(Array.isArray(result.content));
|
|
131
|
+
// The real handler ran (returns content), proving behavior is untouched.
|
|
132
|
+
assert.ok(result.content.length >= 1);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
describe("tool-exposure: group taxonomy", () => {
|
|
136
|
+
test("groupForTool reproduces the source-file grouping for all 151 tools", () => {
|
|
137
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
138
|
+
const toolsDir = join(here, "..", "..", "src", "tools");
|
|
139
|
+
let checked = 0;
|
|
140
|
+
const mismatches = [];
|
|
141
|
+
for (const f of readdirSync(toolsDir)) {
|
|
142
|
+
if (!f.endsWith(".ts"))
|
|
143
|
+
continue;
|
|
144
|
+
const base = f.replace(/\.ts$/, "");
|
|
145
|
+
if (base === "register-all" || base === "shared")
|
|
146
|
+
continue;
|
|
147
|
+
const fileGroup = FILE_TO_GROUP[base];
|
|
148
|
+
assert.ok(fileGroup, `source file ${base}.ts must be mapped in FILE_TO_GROUP`);
|
|
149
|
+
const txt = readFileSync(join(toolsDir, f), "utf8");
|
|
150
|
+
for (const m of txt.matchAll(/registerTool\(\s*"([a-z_0-9]+)"/g)) {
|
|
151
|
+
const name = m[1];
|
|
152
|
+
checked += 1;
|
|
153
|
+
const ng = groupForTool(name);
|
|
154
|
+
if (ng !== fileGroup)
|
|
155
|
+
mismatches.push(`${name}: name=${ng} file=${fileGroup}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
assert.equal(checked, 151, "should have scanned all 151 registration sites");
|
|
159
|
+
assert.deepEqual(mismatches, [], "groupForTool must match the source-file group");
|
|
160
|
+
});
|
|
161
|
+
test("every group used by a tool has metadata", () => {
|
|
162
|
+
const { handle } = makeGroupedServer();
|
|
163
|
+
for (const group of handle.groups.keys()) {
|
|
164
|
+
assert.ok(GROUPS[group], `group ${group} must have metadata`);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
test("group ids are stable and cover the brief's 9 domains", () => {
|
|
168
|
+
for (const id of [
|
|
169
|
+
"invoicing",
|
|
170
|
+
"expenses",
|
|
171
|
+
"fiscal",
|
|
172
|
+
"banking",
|
|
173
|
+
"crm",
|
|
174
|
+
"hr",
|
|
175
|
+
"stay",
|
|
176
|
+
"pos",
|
|
177
|
+
"intelligence",
|
|
178
|
+
]) {
|
|
179
|
+
assert.ok(GROUPS[id], `${id} must be a defined group`);
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
describe("tool-exposure: meta-tools", () => {
|
|
184
|
+
test("list_tool_groups returns non-empty groups with counts summing to 151", async () => {
|
|
185
|
+
const { server } = makeGroupedServer();
|
|
186
|
+
const res = await server.tools.get("list_tool_groups").handler({});
|
|
187
|
+
const payload = JSON.parse(res.content[0].text);
|
|
188
|
+
assert.equal(payload.totalTools, 151);
|
|
189
|
+
const sum = payload.groups.reduce((acc, g) => acc + g.toolCount, 0);
|
|
190
|
+
assert.equal(sum, 151);
|
|
191
|
+
// No empty groups are listed.
|
|
192
|
+
assert.ok(payload.groups.every((g) => g.toolCount > 0));
|
|
193
|
+
// Fiscal is a headline group (compliance depth).
|
|
194
|
+
assert.ok(payload.groups.some((g) => g.group === "fiscal" && g.toolCount > 0));
|
|
195
|
+
});
|
|
196
|
+
test("search_tools finds fiscal tools by free-text and ranks the exact name top", async () => {
|
|
197
|
+
const { server } = makeGroupedServer();
|
|
198
|
+
const res = await server.tools.get("search_tools").handler({
|
|
199
|
+
query: "modelo 303",
|
|
200
|
+
});
|
|
201
|
+
const payload = JSON.parse(res.content[0].text);
|
|
202
|
+
assert.ok(payload.count > 0);
|
|
203
|
+
assert.equal(payload.tools[0].name, "get_modelo_303_summary");
|
|
204
|
+
assert.equal(payload.tools[0].group, "fiscal");
|
|
205
|
+
});
|
|
206
|
+
test("search_tools honors the group filter", async () => {
|
|
207
|
+
const { server } = makeGroupedServer();
|
|
208
|
+
const res = await server.tools.get("search_tools").handler({
|
|
209
|
+
query: "",
|
|
210
|
+
group: "banking",
|
|
211
|
+
limit: 50,
|
|
212
|
+
});
|
|
213
|
+
const payload = JSON.parse(res.content[0].text);
|
|
214
|
+
assert.ok(payload.tools.length > 0);
|
|
215
|
+
assert.ok(payload.tools.every((t) => t.group === "banking"));
|
|
216
|
+
});
|
|
217
|
+
test("describe_tool returns the full original description on demand", async () => {
|
|
218
|
+
const full = makeFullServer();
|
|
219
|
+
const fullDesc = full.tools.get("get_modelo_303_summary").config.description;
|
|
220
|
+
const { server } = makeGroupedServer();
|
|
221
|
+
const res = await server.tools.get("describe_tool").handler({
|
|
222
|
+
name: "get_modelo_303_summary",
|
|
223
|
+
});
|
|
224
|
+
const payload = JSON.parse(res.content[0].text);
|
|
225
|
+
assert.equal(payload.name, "get_modelo_303_summary");
|
|
226
|
+
assert.equal(payload.group, "fiscal");
|
|
227
|
+
// describe_tool serves the FULL original description (depth on demand).
|
|
228
|
+
assert.equal(payload.description, fullDesc);
|
|
229
|
+
});
|
|
230
|
+
test("describe_tool errors with suggestions for an unknown name", async () => {
|
|
231
|
+
const { server } = makeGroupedServer();
|
|
232
|
+
const res = await server.tools.get("describe_tool").handler({ name: "modelo" });
|
|
233
|
+
assert.equal(res.isError, true);
|
|
234
|
+
const payload = JSON.parse(res.content[0].text);
|
|
235
|
+
assert.ok(payload.error.length > 0);
|
|
236
|
+
// 'modelo' is a substring of several real tool names → suggestions offered.
|
|
237
|
+
assert.ok(payload.suggestions.length > 0);
|
|
238
|
+
assert.ok(payload.suggestions.every((s) => s.includes("modelo")));
|
|
239
|
+
});
|
|
240
|
+
test("meta-tools are read-only and closed-world", () => {
|
|
241
|
+
const { server } = makeGroupedServer();
|
|
242
|
+
for (const meta of META_TOOLS) {
|
|
243
|
+
const ann = server.tools.get(meta).config.annotations;
|
|
244
|
+
assert.equal(ann?.readOnlyHint, true, `${meta} must be read-only`);
|
|
245
|
+
assert.equal(ann?.openWorldHint, false, `${meta} must be closed-world`);
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
//# sourceMappingURL=tool-exposure.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-exposure.test.js","sourceRoot":"","sources":["../../src/__tests__/tool-exposure.test.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -15,6 +15,7 @@ import { registerAllTools } from "./tools/register-all.js";
|
|
|
15
15
|
import { registerAllResources } from "./resources/register-all.js";
|
|
16
16
|
import { registerAllPrompts } from "./prompts/register-all.js";
|
|
17
17
|
import { applyOpenAIProfile, OPENAI_ALLOWED_TOOL_COUNT, OPENAI_EXCLUDED_COUNT, OPENAI_EXCLUDED_RESOURCE_COUNT } from "./openai-profile.js";
|
|
18
|
+
import { resolveToolMode, applyToolExposureProfile, GROUPED_META_TOOL_COUNT } from "./tool-exposure.js";
|
|
18
19
|
import { log } from "./logger.js";
|
|
19
20
|
import { registerShutdownHook } from "./metrics.js";
|
|
20
21
|
import { setTraceContext } from "./observability.js";
|
|
@@ -59,7 +60,7 @@ function main() {
|
|
|
59
60
|
});
|
|
60
61
|
const server = new McpServer({
|
|
61
62
|
name: "frihet-erp",
|
|
62
|
-
version: "1.
|
|
63
|
+
version: "1.13.0",
|
|
63
64
|
description: "AI-native MCP server for Frihet ERP — invoices, expenses, clients, products, quotes, webhooks, and deposits. " +
|
|
64
65
|
"Provides 151 tools (including business context, monthly summaries, quarterly taxes, invoice duplication, CRM subcollections, and deposit management), " +
|
|
65
66
|
"11 resources (8 static + 3 live), and 10 workflow prompts for business management " +
|
|
@@ -75,6 +76,22 @@ function main() {
|
|
|
75
76
|
operation: "startup",
|
|
76
77
|
});
|
|
77
78
|
}
|
|
79
|
+
// Apply grouped tool-exposure profile if enabled (progressive disclosure).
|
|
80
|
+
// FRIHET_TOOL_MODE=grouped collapses the 151 full tool descriptions into terse
|
|
81
|
+
// one-liners + adds list_tool_groups / search_tools / describe_tool meta-tools,
|
|
82
|
+
// so agents load depth on demand instead of a flat 151-tool wall of context.
|
|
83
|
+
// Default (unset / "full") is byte-identical to current behavior. Independent
|
|
84
|
+
// of OpenAI mode; if both are set, OpenAI's allowlist runs first, then this
|
|
85
|
+
// collapses whatever survived.
|
|
86
|
+
const toolMode = resolveToolMode();
|
|
87
|
+
if (toolMode === "grouped") {
|
|
88
|
+
applyToolExposureProfile(server);
|
|
89
|
+
log({
|
|
90
|
+
level: "info",
|
|
91
|
+
message: `Grouped tool-exposure active — 151 tools collapsed to terse summaries, ${GROUPED_META_TOOL_COUNT} discovery meta-tools added (list_tool_groups, search_tools, describe_tool); full depth served on demand`,
|
|
92
|
+
operation: "startup",
|
|
93
|
+
});
|
|
94
|
+
}
|
|
78
95
|
// Register tools (62 full / 60 in OpenAI mode)
|
|
79
96
|
registerAllTools(server, client);
|
|
80
97
|
// Register 11 resources (8 static + 3 dynamic via API)
|
|
@@ -86,12 +103,12 @@ function main() {
|
|
|
86
103
|
// Connect via stdio transport
|
|
87
104
|
const transport = new StdioServerTransport();
|
|
88
105
|
server.connect(transport).then(() => {
|
|
89
|
-
console.error("[frihet-mcp] v1.
|
|
106
|
+
console.error("[frihet-mcp] v1.13.0 | 151 tools | https://github.com/Frihet-io/frihet-mcp");
|
|
90
107
|
log({
|
|
91
108
|
level: "info",
|
|
92
109
|
message: "Frihet MCP server running on stdio",
|
|
93
110
|
operation: "startup",
|
|
94
|
-
metadata: { version: "1.
|
|
111
|
+
metadata: { version: "1.13.0", transport: "stdio" },
|
|
95
112
|
});
|
|
96
113
|
}).catch((error) => {
|
|
97
114
|
log({
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool-exposure profile for the Frihet MCP server — progressive disclosure.
|
|
3
|
+
*
|
|
4
|
+
* Activated by FRIHET_TOOL_MODE=grouped (env var or Worker binding).
|
|
5
|
+
* Default (unset, or FRIHET_TOOL_MODE=full) leaves the server BYTE-IDENTICAL
|
|
6
|
+
* to today: all 151 tools registered with their full descriptions/schemas.
|
|
7
|
+
*
|
|
8
|
+
* ── Why ──────────────────────────────────────────────────────────────────
|
|
9
|
+
* Context rot is the 2026 problem: a flat list of 151 tool descriptions,
|
|
10
|
+
* each a multi-paragraph bilingual blob, eats the agent's context window and
|
|
11
|
+
* degrades tool selection before any work begins. Leaders cut flat lists.
|
|
12
|
+
*
|
|
13
|
+
* Frihet's differentiator is DEPTH (full ES/EU fiscal + native compliance —
|
|
14
|
+
* VeriFactu / TicketBAI / Facturae — plus banking, CRM, HR/payroll, stay/PMS,
|
|
15
|
+
* POS) — but depth should be SERVED ON DEMAND, not dumped up front.
|
|
16
|
+
*
|
|
17
|
+
* ── How ──────────────────────────────────────────────────────────────────
|
|
18
|
+
* In `grouped` mode this module intercepts `registerTool` (reusing the exact
|
|
19
|
+
* pattern of `openai-profile.ts`) and:
|
|
20
|
+
*
|
|
21
|
+
* 1. Records every tool into an in-memory CATALOG (name → group, title,
|
|
22
|
+
* one-line summary, full description, input field list) before it reaches
|
|
23
|
+
* the server.
|
|
24
|
+
* 2. Still registers every tool so it stays INVOCABLE (nothing breaks; tool
|
|
25
|
+
* logic, names and behavior are untouched) — but COLLAPSES its registered
|
|
26
|
+
* description to a single terse "[group] summary — full schema via
|
|
27
|
+
* describe_tool('name')" line. That is the context saving.
|
|
28
|
+
* 3. Adds three lightweight META-TOOLS as the entry point for discovery:
|
|
29
|
+
* • list_tool_groups() — the 9-domain map with per-group counts
|
|
30
|
+
* • search_tools(query) — fuzzy match → matching tool summaries
|
|
31
|
+
* • describe_tool(name) — full original description + input fields
|
|
32
|
+
*
|
|
33
|
+
* The agent loads ~3 meta-tool descriptions + 151 terse one-liners instead of
|
|
34
|
+
* 151 full bilingual blobs, then pulls full depth only for the handful of
|
|
35
|
+
* tools it actually needs. Progressive disclosure, zero behavior change.
|
|
36
|
+
*
|
|
37
|
+
* IMPORTANT: this is purely an EXPOSURE layer. It does NOT live in
|
|
38
|
+
* src/tools/*.ts, so the audited tool count stays 151 (+ meta). The meta-tools
|
|
39
|
+
* are added only in grouped mode and are NOT counted as ERP tools.
|
|
40
|
+
*
|
|
41
|
+
* @see ./openai-profile.ts — the sibling interceptor this mirrors.
|
|
42
|
+
*/
|
|
43
|
+
/** Stable domain identifiers used by the group router. */
|
|
44
|
+
export type ToolGroupId = "invoicing" | "expenses" | "fiscal" | "banking" | "crm" | "hr" | "stay" | "pos" | "intelligence" | "catalog" | "platform";
|
|
45
|
+
interface GroupMeta {
|
|
46
|
+
/** Human label, bilingual. */
|
|
47
|
+
label: string;
|
|
48
|
+
/** One-line domain blurb shown in list_tool_groups. */
|
|
49
|
+
blurb: string;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Domain metadata. Keep these terse — they are the only group-level prose the
|
|
53
|
+
* agent loads up front. Depth lives in the per-tool descriptions, fetched via
|
|
54
|
+
* describe_tool / search_tools.
|
|
55
|
+
*/
|
|
56
|
+
export declare const GROUPS: Record<ToolGroupId, GroupMeta>;
|
|
57
|
+
/**
|
|
58
|
+
* Map a tool's source file (basename, no extension) to its domain group.
|
|
59
|
+
*
|
|
60
|
+
* Driving the mapping off the SOURCE FILE — not a hand-maintained per-tool
|
|
61
|
+
* list — means new tools added to an existing file inherit the right group
|
|
62
|
+
* automatically, so the taxonomy never drifts from the registration sites.
|
|
63
|
+
*/
|
|
64
|
+
export declare const FILE_TO_GROUP: Record<string, ToolGroupId>;
|
|
65
|
+
/**
|
|
66
|
+
* Assign a group by tool NAME. The name-based mapping reproduces the
|
|
67
|
+
* source-file grouping exactly for all 151 current tools (verified), with the
|
|
68
|
+
* eight cross-file cases pinned via NAME_OVERRIDES. Driven off the name (not a
|
|
69
|
+
* hand-kept list) so a future tool lands somewhere sensible automatically.
|
|
70
|
+
*
|
|
71
|
+
* Order matters: e-invoicing ("einvoice") is checked before generic "invoice".
|
|
72
|
+
*/
|
|
73
|
+
export declare function groupForTool(name: string): ToolGroupId;
|
|
74
|
+
interface CatalogEntry {
|
|
75
|
+
name: string;
|
|
76
|
+
group: ToolGroupId;
|
|
77
|
+
title: string;
|
|
78
|
+
/** First sentence of the description, single line (for terse listings). */
|
|
79
|
+
summary: string;
|
|
80
|
+
/** Full original description (served by describe_tool). */
|
|
81
|
+
description: string;
|
|
82
|
+
/** Whether the tool mutates state (from annotations.readOnlyHint). */
|
|
83
|
+
readOnly: boolean;
|
|
84
|
+
/** Input field names, for quick schema shape without dumping zod. */
|
|
85
|
+
inputFields: string[];
|
|
86
|
+
}
|
|
87
|
+
/** Returned for visibility/tests; the live catalog after registration. */
|
|
88
|
+
export interface ToolExposureHandle {
|
|
89
|
+
/** All registered tools, keyed by name. */
|
|
90
|
+
catalog: Map<string, CatalogEntry>;
|
|
91
|
+
/** Group → tool names. */
|
|
92
|
+
groups: Map<ToolGroupId, string[]>;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Resolve the active tool mode from an env-like bag.
|
|
96
|
+
* Default is "full" — current behavior, untouched.
|
|
97
|
+
*/
|
|
98
|
+
export declare function resolveToolMode(env?: Record<string, string | undefined>): "full" | "grouped";
|
|
99
|
+
/**
|
|
100
|
+
* Apply the grouped tool-exposure profile to an MCP server.
|
|
101
|
+
*
|
|
102
|
+
* Must be called BEFORE registerAllTools(). Intercepts registerTool to record
|
|
103
|
+
* a catalog + collapse descriptions, then (after tools are registered) adds the
|
|
104
|
+
* meta-tools. Because registration is synchronous and ordered, the caller wires
|
|
105
|
+
* it as:
|
|
106
|
+
*
|
|
107
|
+
* ```ts
|
|
108
|
+
* if (resolveToolMode() === "grouped") applyToolExposureProfile(server);
|
|
109
|
+
* registerAllTools(server, client); // tools recorded + collapsed here
|
|
110
|
+
* // applyToolExposureProfile already queued the meta-tools to register last
|
|
111
|
+
* ```
|
|
112
|
+
*
|
|
113
|
+
* The meta-tools are registered immediately (eagerly) so they appear in
|
|
114
|
+
* tools/list; they read from the catalog object, which is populated lazily as
|
|
115
|
+
* the real tools register. This is safe: tool HANDLERS run long after all
|
|
116
|
+
* registration completes.
|
|
117
|
+
*
|
|
118
|
+
* @param server The McpServer (typed loosely to match the openai-profile shim).
|
|
119
|
+
* @returns a handle exposing the live catalog (useful for tests/logging).
|
|
120
|
+
*/
|
|
121
|
+
export declare function applyToolExposureProfile(server: any): ToolExposureHandle;
|
|
122
|
+
/** Number of meta-tools added in grouped mode (for logging). */
|
|
123
|
+
export declare const GROUPED_META_TOOL_COUNT: number;
|
|
124
|
+
/** Exposed for logging/tests. */
|
|
125
|
+
export declare const TOOL_GROUP_IDS: ToolGroupId[];
|
|
126
|
+
export {};
|
|
127
|
+
//# sourceMappingURL=tool-exposure.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-exposure.d.ts","sourceRoot":"","sources":["../src/tool-exposure.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAQH,0DAA0D;AAC1D,MAAM,MAAM,WAAW,GACnB,WAAW,GACX,UAAU,GACV,QAAQ,GACR,SAAS,GACT,KAAK,GACL,IAAI,GACJ,MAAM,GACN,KAAK,GACL,cAAc,GACd,SAAS,GACT,UAAU,CAAC;AAEf,UAAU,SAAS;IACjB,8BAA8B;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,WAAW,EAAE,SAAS,CAkDjD,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAyCrD,CAAC;AAuBF;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAkBtD;AAMD,UAAU,YAAY;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,WAAW,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,OAAO,EAAE,MAAM,CAAC;IAChB,2DAA2D;IAC3D,WAAW,EAAE,MAAM,CAAC;IACpB,sEAAsE;IACtE,QAAQ,EAAE,OAAO,CAAC;IAClB,qEAAqE;IACrE,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAkBD,0EAA0E;AAC1E,MAAM,WAAW,kBAAkB;IACjC,2CAA2C;IAC3C,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACnC,0BAA0B;IAC1B,MAAM,EAAE,GAAG,CAAC,WAAW,EAAE,MAAM,EAAE,CAAC,CAAC;CACpC;AAYD;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAgB,GACrD,MAAM,GAAG,SAAS,CAEpB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,wBAAgB,wBAAwB,CAEtC,MAAM,EAAE,GAAG,GACV,kBAAkB,CAqDpB;AA+LD,gEAAgE;AAChE,eAAO,MAAM,uBAAuB,QAAuB,CAAC;AAE5D,iCAAiC;AACjC,eAAO,MAAM,cAAc,EAA0B,WAAW,EAAE,CAAC"}
|
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool-exposure profile for the Frihet MCP server — progressive disclosure.
|
|
3
|
+
*
|
|
4
|
+
* Activated by FRIHET_TOOL_MODE=grouped (env var or Worker binding).
|
|
5
|
+
* Default (unset, or FRIHET_TOOL_MODE=full) leaves the server BYTE-IDENTICAL
|
|
6
|
+
* to today: all 151 tools registered with their full descriptions/schemas.
|
|
7
|
+
*
|
|
8
|
+
* ── Why ──────────────────────────────────────────────────────────────────
|
|
9
|
+
* Context rot is the 2026 problem: a flat list of 151 tool descriptions,
|
|
10
|
+
* each a multi-paragraph bilingual blob, eats the agent's context window and
|
|
11
|
+
* degrades tool selection before any work begins. Leaders cut flat lists.
|
|
12
|
+
*
|
|
13
|
+
* Frihet's differentiator is DEPTH (full ES/EU fiscal + native compliance —
|
|
14
|
+
* VeriFactu / TicketBAI / Facturae — plus banking, CRM, HR/payroll, stay/PMS,
|
|
15
|
+
* POS) — but depth should be SERVED ON DEMAND, not dumped up front.
|
|
16
|
+
*
|
|
17
|
+
* ── How ──────────────────────────────────────────────────────────────────
|
|
18
|
+
* In `grouped` mode this module intercepts `registerTool` (reusing the exact
|
|
19
|
+
* pattern of `openai-profile.ts`) and:
|
|
20
|
+
*
|
|
21
|
+
* 1. Records every tool into an in-memory CATALOG (name → group, title,
|
|
22
|
+
* one-line summary, full description, input field list) before it reaches
|
|
23
|
+
* the server.
|
|
24
|
+
* 2. Still registers every tool so it stays INVOCABLE (nothing breaks; tool
|
|
25
|
+
* logic, names and behavior are untouched) — but COLLAPSES its registered
|
|
26
|
+
* description to a single terse "[group] summary — full schema via
|
|
27
|
+
* describe_tool('name')" line. That is the context saving.
|
|
28
|
+
* 3. Adds three lightweight META-TOOLS as the entry point for discovery:
|
|
29
|
+
* • list_tool_groups() — the 9-domain map with per-group counts
|
|
30
|
+
* • search_tools(query) — fuzzy match → matching tool summaries
|
|
31
|
+
* • describe_tool(name) — full original description + input fields
|
|
32
|
+
*
|
|
33
|
+
* The agent loads ~3 meta-tool descriptions + 151 terse one-liners instead of
|
|
34
|
+
* 151 full bilingual blobs, then pulls full depth only for the handful of
|
|
35
|
+
* tools it actually needs. Progressive disclosure, zero behavior change.
|
|
36
|
+
*
|
|
37
|
+
* IMPORTANT: this is purely an EXPOSURE layer. It does NOT live in
|
|
38
|
+
* src/tools/*.ts, so the audited tool count stays 151 (+ meta). The meta-tools
|
|
39
|
+
* are added only in grouped mode and are NOT counted as ERP tools.
|
|
40
|
+
*
|
|
41
|
+
* @see ./openai-profile.ts — the sibling interceptor this mirrors.
|
|
42
|
+
*/
|
|
43
|
+
/**
|
|
44
|
+
* Domain metadata. Keep these terse — they are the only group-level prose the
|
|
45
|
+
* agent loads up front. Depth lives in the per-tool descriptions, fetched via
|
|
46
|
+
* describe_tool / search_tools.
|
|
47
|
+
*/
|
|
48
|
+
export const GROUPS = {
|
|
49
|
+
invoicing: {
|
|
50
|
+
label: "Invoicing & receivables / Facturación y cobros",
|
|
51
|
+
blurb: "Invoices, credit notes, quotes, recurring invoices, deposits — create, send, pay, PDF.",
|
|
52
|
+
},
|
|
53
|
+
expenses: {
|
|
54
|
+
label: "Expenses & vendors / Gastos y proveedores",
|
|
55
|
+
blurb: "Expenses (with OCR) and vendor/supplier records.",
|
|
56
|
+
},
|
|
57
|
+
fiscal: {
|
|
58
|
+
label: "Fiscal & compliance / Fiscal y cumplimiento",
|
|
59
|
+
blurb: "Spanish/EU fiscal depth served on demand: Modelo 303/130/390/180/347/200/202/415/425/418, " +
|
|
60
|
+
"VeriFactu, TicketBAI, Facturae/FACe/KSeF e-invoicing, IGIC/AIEM, GL audit, period close, VIES.",
|
|
61
|
+
},
|
|
62
|
+
banking: {
|
|
63
|
+
label: "Banking / Banca",
|
|
64
|
+
blurb: "Bank accounts, transactions, categorization, reconciliation, bank rules.",
|
|
65
|
+
},
|
|
66
|
+
crm: {
|
|
67
|
+
label: "CRM & clients / CRM y clientes",
|
|
68
|
+
blurb: "Clients/customers plus contacts, activities and notes.",
|
|
69
|
+
},
|
|
70
|
+
hr: {
|
|
71
|
+
label: "HR & payroll / RRHH y nóminas",
|
|
72
|
+
blurb: "Leave, attendance, overtime, time tracking, payroll export, team, onboarding, permissions.",
|
|
73
|
+
},
|
|
74
|
+
stay: {
|
|
75
|
+
label: "Stay / PMS / Alojamientos",
|
|
76
|
+
blurb: "Vacation-rental reservations, properties and channel sync.",
|
|
77
|
+
},
|
|
78
|
+
pos: {
|
|
79
|
+
label: "POS / TPV",
|
|
80
|
+
blurb: "Point-of-sale terminals, sales and refunds.",
|
|
81
|
+
},
|
|
82
|
+
intelligence: {
|
|
83
|
+
label: "Intelligence / Inteligencia",
|
|
84
|
+
blurb: "Business context, monthly/quarterly summaries and gestoría collaboration — call first in a session.",
|
|
85
|
+
},
|
|
86
|
+
catalog: {
|
|
87
|
+
label: "Products / Productos",
|
|
88
|
+
blurb: "Product/service catalog with pricing.",
|
|
89
|
+
},
|
|
90
|
+
platform: {
|
|
91
|
+
label: "Platform / Plataforma",
|
|
92
|
+
blurb: "Webhooks and white-label portal domain configuration.",
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* Map a tool's source file (basename, no extension) to its domain group.
|
|
97
|
+
*
|
|
98
|
+
* Driving the mapping off the SOURCE FILE — not a hand-maintained per-tool
|
|
99
|
+
* list — means new tools added to an existing file inherit the right group
|
|
100
|
+
* automatically, so the taxonomy never drifts from the registration sites.
|
|
101
|
+
*/
|
|
102
|
+
export const FILE_TO_GROUP = {
|
|
103
|
+
invoices: "invoicing",
|
|
104
|
+
quotes: "invoicing",
|
|
105
|
+
recurring: "invoicing",
|
|
106
|
+
deposits: "invoicing",
|
|
107
|
+
expenses: "expenses",
|
|
108
|
+
vendors: "expenses",
|
|
109
|
+
fiscal: "fiscal",
|
|
110
|
+
igic: "fiscal",
|
|
111
|
+
impuesto_sociedades: "fiscal",
|
|
112
|
+
einvoice: "fiscal",
|
|
113
|
+
audit_gl: "fiscal",
|
|
114
|
+
accountingClose: "fiscal",
|
|
115
|
+
onboard_vies: "fiscal",
|
|
116
|
+
banking: "banking",
|
|
117
|
+
bank_rules: "banking",
|
|
118
|
+
clients: "crm",
|
|
119
|
+
crm: "crm",
|
|
120
|
+
hr: "hr",
|
|
121
|
+
payroll: "hr",
|
|
122
|
+
time: "hr",
|
|
123
|
+
team: "hr",
|
|
124
|
+
onboarding: "hr",
|
|
125
|
+
permissions: "hr",
|
|
126
|
+
stay: "stay",
|
|
127
|
+
pos: "pos",
|
|
128
|
+
intelligence: "intelligence",
|
|
129
|
+
gestoria: "intelligence",
|
|
130
|
+
products: "catalog",
|
|
131
|
+
webhooks: "platform",
|
|
132
|
+
portal_domain: "platform",
|
|
133
|
+
};
|
|
134
|
+
/**
|
|
135
|
+
* Per-tool overrides where the source FILE places a tool in a different group
|
|
136
|
+
* than a naive name match would (verified against the 151 registration sites).
|
|
137
|
+
* These eight names live in a file whose domain differs from their name prefix
|
|
138
|
+
* (e.g. e-invoicing tools say "invoice" but belong to fiscal/compliance).
|
|
139
|
+
*/
|
|
140
|
+
const NAME_OVERRIDES = {
|
|
141
|
+
send_einvoice: "fiscal",
|
|
142
|
+
get_einvoice_status: "fiscal",
|
|
143
|
+
validate_einvoice_xml: "fiscal",
|
|
144
|
+
einvoice_export: "fiscal",
|
|
145
|
+
export_datev: "fiscal",
|
|
146
|
+
match_transaction_to_invoice: "banking",
|
|
147
|
+
get_quarterly_taxes: "intelligence",
|
|
148
|
+
duplicate_invoice: "intelligence",
|
|
149
|
+
frihet_portal_onboard_link_generate: "fiscal",
|
|
150
|
+
// Lives in invoices.ts (an invoice action that returns its e-invoice XML),
|
|
151
|
+
// so it stays in the "invoicing" group with its sibling invoice tools.
|
|
152
|
+
get_invoice_einvoice: "invoicing",
|
|
153
|
+
};
|
|
154
|
+
/**
|
|
155
|
+
* Assign a group by tool NAME. The name-based mapping reproduces the
|
|
156
|
+
* source-file grouping exactly for all 151 current tools (verified), with the
|
|
157
|
+
* eight cross-file cases pinned via NAME_OVERRIDES. Driven off the name (not a
|
|
158
|
+
* hand-kept list) so a future tool lands somewhere sensible automatically.
|
|
159
|
+
*
|
|
160
|
+
* Order matters: e-invoicing ("einvoice") is checked before generic "invoice".
|
|
161
|
+
*/
|
|
162
|
+
export function groupForTool(name) {
|
|
163
|
+
if (NAME_OVERRIDES[name])
|
|
164
|
+
return NAME_OVERRIDES[name];
|
|
165
|
+
const n = name.toLowerCase();
|
|
166
|
+
// Fiscal/compliance FIRST so "einvoice"/"modelo" never fall into invoicing.
|
|
167
|
+
if (/(modelo|verifactu|ticketbai|einvoice|datev|face_|ksef|igic|aiem|gl_entry|period_close|period_reopen|vies|portal_onboard)/.test(n))
|
|
168
|
+
return "fiscal";
|
|
169
|
+
if (/(invoice|quote|credit_note|late_fee|recurring|deposit)/.test(n))
|
|
170
|
+
return "invoicing";
|
|
171
|
+
if (/(expense|vendor)/.test(n))
|
|
172
|
+
return "expenses";
|
|
173
|
+
if (/(bank|transaction)/.test(n))
|
|
174
|
+
return "banking";
|
|
175
|
+
if (/(client|contact|activit|note)/.test(n))
|
|
176
|
+
return "crm";
|
|
177
|
+
if (/(leave|attendance|overtime|anomaly|payroll|time_entr|time_summary|team|onboarding|permission)/.test(n))
|
|
178
|
+
return "hr";
|
|
179
|
+
if (/(reservation|propert|channel)/.test(n))
|
|
180
|
+
return "stay";
|
|
181
|
+
if (/(terminal|sale)/.test(n))
|
|
182
|
+
return "pos";
|
|
183
|
+
if (/(business_context|monthly_summary|gestoria)/.test(n))
|
|
184
|
+
return "intelligence";
|
|
185
|
+
if (/(product)/.test(n))
|
|
186
|
+
return "catalog";
|
|
187
|
+
if (/(webhook|portal_domain)/.test(n))
|
|
188
|
+
return "platform";
|
|
189
|
+
return "platform";
|
|
190
|
+
}
|
|
191
|
+
/** Collapse a long bilingual description to its first English sentence. */
|
|
192
|
+
function firstSentence(desc) {
|
|
193
|
+
if (!desc)
|
|
194
|
+
return "";
|
|
195
|
+
// English half comes before the " / " language separator the repo uses.
|
|
196
|
+
const englishHalf = desc.split(" / ")[0];
|
|
197
|
+
// First sentence: up to the first ". " that ends a clause.
|
|
198
|
+
const match = englishHalf.match(/^(.*?[.!?])(\s|$)/);
|
|
199
|
+
const sentence = (match ? match[1] : englishHalf).trim();
|
|
200
|
+
// Hard cap so a runaway description can't reintroduce context bloat.
|
|
201
|
+
return sentence.length > 160 ? sentence.slice(0, 157).trimEnd() + "…" : sentence;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Read process.env without referencing the `process` global type — keeps this
|
|
205
|
+
* module compilable in environments without @types/node (e.g. the Cloudflare
|
|
206
|
+
* Worker build, which passes its own env bag explicitly instead).
|
|
207
|
+
*/
|
|
208
|
+
function defaultEnv() {
|
|
209
|
+
const g = globalThis;
|
|
210
|
+
return g.process?.env ?? {};
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Resolve the active tool mode from an env-like bag.
|
|
214
|
+
* Default is "full" — current behavior, untouched.
|
|
215
|
+
*/
|
|
216
|
+
export function resolveToolMode(env = defaultEnv()) {
|
|
217
|
+
return env.FRIHET_TOOL_MODE === "grouped" ? "grouped" : "full";
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Apply the grouped tool-exposure profile to an MCP server.
|
|
221
|
+
*
|
|
222
|
+
* Must be called BEFORE registerAllTools(). Intercepts registerTool to record
|
|
223
|
+
* a catalog + collapse descriptions, then (after tools are registered) adds the
|
|
224
|
+
* meta-tools. Because registration is synchronous and ordered, the caller wires
|
|
225
|
+
* it as:
|
|
226
|
+
*
|
|
227
|
+
* ```ts
|
|
228
|
+
* if (resolveToolMode() === "grouped") applyToolExposureProfile(server);
|
|
229
|
+
* registerAllTools(server, client); // tools recorded + collapsed here
|
|
230
|
+
* // applyToolExposureProfile already queued the meta-tools to register last
|
|
231
|
+
* ```
|
|
232
|
+
*
|
|
233
|
+
* The meta-tools are registered immediately (eagerly) so they appear in
|
|
234
|
+
* tools/list; they read from the catalog object, which is populated lazily as
|
|
235
|
+
* the real tools register. This is safe: tool HANDLERS run long after all
|
|
236
|
+
* registration completes.
|
|
237
|
+
*
|
|
238
|
+
* @param server The McpServer (typed loosely to match the openai-profile shim).
|
|
239
|
+
* @returns a handle exposing the live catalog (useful for tests/logging).
|
|
240
|
+
*/
|
|
241
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
242
|
+
export function applyToolExposureProfile(
|
|
243
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
244
|
+
server) {
|
|
245
|
+
const catalog = new Map();
|
|
246
|
+
const groups = new Map();
|
|
247
|
+
const handle = { catalog, groups };
|
|
248
|
+
const originalRegisterTool = server.registerTool.bind(server);
|
|
249
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
250
|
+
server.registerTool = (name, config, handler) => {
|
|
251
|
+
// Never re-process our own meta-tools (they are registered via the original
|
|
252
|
+
// bound fn below, so they won't hit this interceptor — but guard anyway).
|
|
253
|
+
if (META_TOOL_NAMES.has(name)) {
|
|
254
|
+
return originalRegisterTool(name, config, handler);
|
|
255
|
+
}
|
|
256
|
+
const group = groupForTool(name);
|
|
257
|
+
const fullDescription = typeof config?.description === "string" ? config.description : "";
|
|
258
|
+
const inputFields = config?.inputSchema
|
|
259
|
+
? Object.keys(config.inputSchema)
|
|
260
|
+
: [];
|
|
261
|
+
const readOnly = config?.annotations?.readOnlyHint === true;
|
|
262
|
+
const entry = {
|
|
263
|
+
name,
|
|
264
|
+
group,
|
|
265
|
+
title: typeof config?.title === "string" ? config.title : name,
|
|
266
|
+
summary: firstSentence(fullDescription),
|
|
267
|
+
description: fullDescription,
|
|
268
|
+
readOnly,
|
|
269
|
+
inputFields,
|
|
270
|
+
};
|
|
271
|
+
catalog.set(name, entry);
|
|
272
|
+
const list = groups.get(group);
|
|
273
|
+
if (list)
|
|
274
|
+
list.push(name);
|
|
275
|
+
else
|
|
276
|
+
groups.set(group, [name]);
|
|
277
|
+
// Collapse the registered description to a single terse pointer line. The
|
|
278
|
+
// tool stays fully invocable with its original handler, schema, and
|
|
279
|
+
// annotations — only the description string the agent loads is trimmed.
|
|
280
|
+
config.description =
|
|
281
|
+
`[${group}] ${entry.summary} ` +
|
|
282
|
+
`— full schema via describe_tool('${name}').`;
|
|
283
|
+
return originalRegisterTool(name, config, handler);
|
|
284
|
+
};
|
|
285
|
+
// Register the meta-tools eagerly. Their handlers close over `catalog`/`groups`
|
|
286
|
+
// which finish populating as the real tools register right after this call.
|
|
287
|
+
registerMetaTools(originalRegisterTool, handle);
|
|
288
|
+
return handle;
|
|
289
|
+
}
|
|
290
|
+
/* ------------------------------------------------------------------ */
|
|
291
|
+
/* Meta-tools */
|
|
292
|
+
/* ------------------------------------------------------------------ */
|
|
293
|
+
const META_TOOL_NAMES = new Set([
|
|
294
|
+
"list_tool_groups",
|
|
295
|
+
"search_tools",
|
|
296
|
+
"describe_tool",
|
|
297
|
+
]);
|
|
298
|
+
/** Closed-world read annotation for the meta-tools. */
|
|
299
|
+
const META_ANNOTATIONS = {
|
|
300
|
+
readOnlyHint: true,
|
|
301
|
+
destructiveHint: false,
|
|
302
|
+
idempotentHint: true,
|
|
303
|
+
openWorldHint: false,
|
|
304
|
+
};
|
|
305
|
+
function textResult(payload) {
|
|
306
|
+
return {
|
|
307
|
+
content: [{ type: "text", text: JSON.stringify(payload, null, 2) }],
|
|
308
|
+
structuredContent: payload,
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Register the three progressive-disclosure meta-tools against the ORIGINAL
|
|
313
|
+
* (un-intercepted) registerTool so they don't get collapsed/catalogued.
|
|
314
|
+
*/
|
|
315
|
+
function registerMetaTools(
|
|
316
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
317
|
+
originalRegisterTool, handle) {
|
|
318
|
+
const { catalog, groups } = handle;
|
|
319
|
+
// -- list_tool_groups --
|
|
320
|
+
originalRegisterTool("list_tool_groups", {
|
|
321
|
+
title: "List tool groups",
|
|
322
|
+
description: "List the Frihet ERP tool domains (invoicing, expenses, fiscal/compliance, banking, CRM, " +
|
|
323
|
+
"HR/payroll, stay/PMS, POS, intelligence, products, platform) with a one-line blurb and tool " +
|
|
324
|
+
"count for each. Start here, then use search_tools(query) to find tools and describe_tool(name) " +
|
|
325
|
+
"for a tool's full schema. Frihet serves deep ES/EU fiscal + native compliance on demand. " +
|
|
326
|
+
"[openWorldHint: false — reads the in-process tool catalog only.] " +
|
|
327
|
+
"/ Lista los dominios de herramientas de Frihet ERP con descripción y recuento. Empieza aquí.",
|
|
328
|
+
annotations: META_ANNOTATIONS,
|
|
329
|
+
inputSchema: {},
|
|
330
|
+
}, async () => {
|
|
331
|
+
const out = Object.keys(GROUPS)
|
|
332
|
+
.map((id) => ({
|
|
333
|
+
group: id,
|
|
334
|
+
label: GROUPS[id].label,
|
|
335
|
+
blurb: GROUPS[id].blurb,
|
|
336
|
+
toolCount: groups.get(id)?.length ?? 0,
|
|
337
|
+
}))
|
|
338
|
+
.filter((g) => g.toolCount > 0);
|
|
339
|
+
return textResult({
|
|
340
|
+
groups: out,
|
|
341
|
+
totalGroups: out.length,
|
|
342
|
+
totalTools: catalog.size,
|
|
343
|
+
next: "search_tools(query) to find tools; describe_tool(name) for full schema.",
|
|
344
|
+
});
|
|
345
|
+
});
|
|
346
|
+
// -- search_tools --
|
|
347
|
+
originalRegisterTool("search_tools", {
|
|
348
|
+
title: "Search tools",
|
|
349
|
+
description: "Find Frihet ERP tools by free-text query (matches tool name, title, summary and group). " +
|
|
350
|
+
"Returns matching tools with their group, one-line summary, read-only flag and input field names — " +
|
|
351
|
+
"progressive disclosure so you load only what you need. Optionally filter by group. " +
|
|
352
|
+
"Then call describe_tool(name) for a tool's full description and call it by its real name. " +
|
|
353
|
+
"[openWorldHint: false — reads the in-process tool catalog only.] " +
|
|
354
|
+
"/ Busca herramientas por texto libre. Devuelve coincidencias con grupo, resumen y campos.",
|
|
355
|
+
annotations: META_ANNOTATIONS,
|
|
356
|
+
inputSchema: {
|
|
357
|
+
// Plain JSON-schema-ish shape; the SDK accepts a raw shape object here.
|
|
358
|
+
// Kept dependency-free (no zod import) so this layer stays minimal.
|
|
359
|
+
},
|
|
360
|
+
},
|
|
361
|
+
// The SDK passes parsed args; we read defensively to stay schema-light.
|
|
362
|
+
async (args = {}) => {
|
|
363
|
+
const query = typeof args.query === "string" ? args.query.trim().toLowerCase() : "";
|
|
364
|
+
const groupFilter = typeof args.group === "string" ? args.group : undefined;
|
|
365
|
+
const limit = typeof args.limit === "number" && args.limit > 0 ? Math.floor(args.limit) : 25;
|
|
366
|
+
const terms = query.split(/\s+/).filter(Boolean);
|
|
367
|
+
const scored = [...catalog.values()]
|
|
368
|
+
.filter((e) => !groupFilter || e.group === groupFilter)
|
|
369
|
+
.map((e) => {
|
|
370
|
+
const haystack = `${e.name} ${e.title} ${e.summary} ${e.group}`.toLowerCase();
|
|
371
|
+
// Score = number of query terms present; name/title hits weighted up.
|
|
372
|
+
let score = 0;
|
|
373
|
+
for (const t of terms) {
|
|
374
|
+
if (!haystack.includes(t))
|
|
375
|
+
continue;
|
|
376
|
+
score += 1;
|
|
377
|
+
if (e.name.toLowerCase().includes(t))
|
|
378
|
+
score += 2;
|
|
379
|
+
if (e.title.toLowerCase().includes(t))
|
|
380
|
+
score += 1;
|
|
381
|
+
}
|
|
382
|
+
return { e, score };
|
|
383
|
+
})
|
|
384
|
+
// With no terms, return everything (lets agents browse a group).
|
|
385
|
+
.filter(({ score }) => terms.length === 0 || score > 0)
|
|
386
|
+
.sort((a, b) => b.score - a.score || a.e.name.localeCompare(b.e.name))
|
|
387
|
+
.slice(0, limit)
|
|
388
|
+
.map(({ e, score }) => ({
|
|
389
|
+
name: e.name,
|
|
390
|
+
group: e.group,
|
|
391
|
+
title: e.title,
|
|
392
|
+
summary: e.summary,
|
|
393
|
+
readOnly: e.readOnly,
|
|
394
|
+
inputFields: e.inputFields,
|
|
395
|
+
score,
|
|
396
|
+
}));
|
|
397
|
+
return textResult({
|
|
398
|
+
query: query || null,
|
|
399
|
+
group: groupFilter ?? null,
|
|
400
|
+
count: scored.length,
|
|
401
|
+
tools: scored,
|
|
402
|
+
next: "describe_tool(name) for full schema, then call the tool by its real name.",
|
|
403
|
+
});
|
|
404
|
+
});
|
|
405
|
+
// -- describe_tool --
|
|
406
|
+
originalRegisterTool("describe_tool", {
|
|
407
|
+
title: "Describe tool",
|
|
408
|
+
description: "Return the full original description, group, read-only flag and input field names for a specific " +
|
|
409
|
+
"Frihet ERP tool by its exact name (as returned by search_tools). Use this to load a tool's full " +
|
|
410
|
+
"depth on demand before calling it. " +
|
|
411
|
+
"[openWorldHint: false — reads the in-process tool catalog only.] " +
|
|
412
|
+
"/ Devuelve la descripción completa de una herramienta por su nombre exacto.",
|
|
413
|
+
annotations: META_ANNOTATIONS,
|
|
414
|
+
inputSchema: {},
|
|
415
|
+
}, async (args = {}) => {
|
|
416
|
+
const name = typeof args.name === "string" ? args.name.trim() : "";
|
|
417
|
+
const entry = catalog.get(name);
|
|
418
|
+
if (!entry) {
|
|
419
|
+
// Suggest near matches to keep the agent unstuck.
|
|
420
|
+
const suggestions = [...catalog.keys()]
|
|
421
|
+
.filter((k) => name && k.includes(name.toLowerCase()))
|
|
422
|
+
.slice(0, 5);
|
|
423
|
+
return {
|
|
424
|
+
content: [
|
|
425
|
+
{
|
|
426
|
+
type: "text",
|
|
427
|
+
text: JSON.stringify({
|
|
428
|
+
error: name
|
|
429
|
+
? `No tool named '${name}'.`
|
|
430
|
+
: "Provide { name } — the exact tool name from search_tools.",
|
|
431
|
+
suggestions,
|
|
432
|
+
next: "Call search_tools(query) to find the right name.",
|
|
433
|
+
}, null, 2),
|
|
434
|
+
},
|
|
435
|
+
],
|
|
436
|
+
isError: true,
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
return textResult({
|
|
440
|
+
name: entry.name,
|
|
441
|
+
group: entry.group,
|
|
442
|
+
title: entry.title,
|
|
443
|
+
readOnly: entry.readOnly,
|
|
444
|
+
inputFields: entry.inputFields,
|
|
445
|
+
description: entry.description,
|
|
446
|
+
});
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
/** Number of meta-tools added in grouped mode (for logging). */
|
|
450
|
+
export const GROUPED_META_TOOL_COUNT = META_TOOL_NAMES.size;
|
|
451
|
+
/** Exposed for logging/tests. */
|
|
452
|
+
export const TOOL_GROUP_IDS = Object.keys(GROUPS);
|
|
453
|
+
//# sourceMappingURL=tool-exposure.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-exposure.js","sourceRoot":"","sources":["../src/tool-exposure.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Barrel module that registers all
|
|
2
|
+
* Barrel module that registers all 151 Frihet ERP tools on an McpServer.
|
|
3
3
|
*
|
|
4
4
|
* Used by both the local (stdio) and remote (Cloudflare Workers) servers
|
|
5
5
|
* so tool definitions stay in sync — one source of truth.
|
|
6
6
|
*
|
|
7
7
|
* Langfuse observability is injected by patching server.registerTool once
|
|
8
8
|
* before any tool registration. This wraps every tool callback with
|
|
9
|
-
* traceMCPTool so all
|
|
9
|
+
* traceMCPTool so all 151 tools are instrumented at zero per-tool cost.
|
|
10
10
|
*/
|
|
11
11
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
12
12
|
import type { IFrihetClient } from "../client-interface.js";
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Barrel module that registers all
|
|
2
|
+
* Barrel module that registers all 151 Frihet ERP tools on an McpServer.
|
|
3
3
|
*
|
|
4
4
|
* Used by both the local (stdio) and remote (Cloudflare Workers) servers
|
|
5
5
|
* so tool definitions stay in sync — one source of truth.
|
|
6
6
|
*
|
|
7
7
|
* Langfuse observability is injected by patching server.registerTool once
|
|
8
8
|
* before any tool registration. This wraps every tool callback with
|
|
9
|
-
* traceMCPTool so all
|
|
9
|
+
* traceMCPTool so all 151 tools are instrumented at zero per-tool cost.
|
|
10
10
|
*/
|
|
11
11
|
import { traceMCPTool } from "../observability.js";
|
|
12
12
|
import { registerInvoiceTools } from "./invoices.js";
|
|
@@ -42,7 +42,7 @@ import { registerAccountingCloseTools } from "./accountingClose.js";
|
|
|
42
42
|
/**
|
|
43
43
|
* Patches server.registerTool to wrap every tool callback with Langfuse tracing.
|
|
44
44
|
*
|
|
45
|
-
* The patch is applied once before tool registration so all
|
|
45
|
+
* The patch is applied once before tool registration so all 151 tools are
|
|
46
46
|
* instrumented without per-tool edits. Tool call signatures are unchanged —
|
|
47
47
|
* existing MCP clients continue to work identically.
|
|
48
48
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frihet/mcp-server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.13.0",
|
|
4
4
|
"description": "AI-native MCP server for business management — invoices, expenses, deposits, clients, products, quotes, webhooks, CRM, e-invoicing (Facturae/XRechnung/Factur-X/FatturaPA/PEPPOL + FACe Spain B2G + TicketBAI Basque Country + KSeF Poland), vacation rentals, POS, banking, fiscal (Modelo 303/130/390/180/347/415/425/418, VeriFactu, TicketBAI, IS M200/M202), IGIC, AIEM, GL audit approval, white-label portal domain, VIES EU VAT lookup, bank rules, time tracking, recurring invoices, team management, gestoria. 151 tools. Remote endpoint at mcp.frihet.io (zero install). Works with Claude, Cursor, Windsurf, Cline.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
11
|
"build": "tsc",
|
|
12
|
-
"test": "npm run build && node --test dist/__tests__/einvoice-tools.test.js dist/__tests__/einvoice-day4-tools.test.js dist/__tests__/stay-tools.test.js dist/__tests__/pos-tools.test.js dist/__tests__/banking-tools.test.js dist/__tests__/fiscal-tools.test.js dist/__tests__/time-tools.test.js dist/__tests__/recurring-tools.test.js dist/__tests__/team-tools.test.js dist/__tests__/d4b-hr-payroll-onboarding-tools.test.js",
|
|
12
|
+
"test": "npm run build && node --test dist/__tests__/openai-profile.test.js dist/__tests__/tool-exposure.test.js dist/__tests__/einvoice-tools.test.js dist/__tests__/einvoice-day4-tools.test.js dist/__tests__/stay-tools.test.js dist/__tests__/pos-tools.test.js dist/__tests__/banking-tools.test.js dist/__tests__/fiscal-tools.test.js dist/__tests__/time-tools.test.js dist/__tests__/recurring-tools.test.js dist/__tests__/team-tools.test.js dist/__tests__/d4b-hr-payroll-onboarding-tools.test.js",
|
|
13
13
|
"start": "node dist/index.js",
|
|
14
14
|
"postinstall": "node scripts/postinstall.js || true",
|
|
15
15
|
"prepublishOnly": "npm run build && npm run audit:mcp-refs -- --repo frihet-mcp",
|