@frihet/mcp-server 1.12.0-beta.1 → 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.
@@ -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 152 Frihet ERP tools on an McpServer.
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 152 tools are instrumented at zero per-tool cost.
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 152 Frihet ERP tools on an McpServer.
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 152 tools are instrumented at zero per-tool cost.
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 152 tools are
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.12.0-beta.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",