/** * Tool-exposure profile for the Frihet MCP server — progressive disclosure. * * Activated by FRIHET_TOOL_MODE=grouped (env var or Worker binding). Default * full mode keeps full descriptions/schemas and does not add discovery names; * the outer public composition may still attach capability truth metadata. * * ── Why ────────────────────────────────────────────────────────────────── * Context rot is the 2026 problem: a flat list of 157 tool descriptions, * each a multi-paragraph bilingual blob, eats the agent's context window and * degrades tool selection before any work begins. Leaders cut flat lists. * * Frihet's differentiator is DEPTH (full ES/EU fiscal + native compliance — * VeriFactu / TicketBAI / Facturae — plus banking, CRM, HR/payroll, stay/PMS, * POS) — but depth should be SERVED ON DEMAND, not dumped up front. * * ── How ────────────────────────────────────────────────────────────────── * In `grouped` mode this module intercepts `registerTool` (reusing the exact * pattern of `openai-profile.ts`) and: * * 1. Records every tool into an in-memory CATALOG (name → group, title, * one-line summary, full description, input field list) before it reaches * the server. * 2. Still registers every tool so it stays INVOCABLE (nothing breaks; tool * logic, names and behavior are untouched) — but COLLAPSES its registered * description to a single terse "[group] summary — full description and * input fields via describe_tool('name')" line. That is the context saving. * 3. Adds three lightweight META-TOOLS as the entry point for discovery: * • list_tool_groups() — the 11-domain map with per-group counts * • search_tools(query) — fuzzy match → matching tool summaries * • describe_tool(name) — full original description + input fields * * The agent loads ~3 meta-tool descriptions + 157 terse one-liners instead of * 157 full bilingual blobs, then pulls full depth only for the handful of * tools it actually needs. Progressive disclosure, zero behavior change. * * IMPORTANT: this is purely an EXPOSURE layer. It does NOT live in * src/tools/*.ts, so the audited tool count stays 157 (+ meta). The meta-tools * are added only in grouped mode and are NOT counted as ERP tools. * * @see ./openai-profile.ts — the sibling interceptor this mirrors. */ /** Stable domain identifiers used by the group router. */ export type ToolGroupId = "invoicing" | "expenses" | "fiscal" | "banking" | "crm" | "hr" | "stay" | "pos" | "intelligence" | "catalog" | "platform"; export interface GroupMeta { /** Human label, bilingual. */ label: string; /** One-line domain blurb shown in list_tool_groups. */ blurb: string; } /** * Domain metadata. Keep these terse — they are the only group-level prose the * agent loads up front. Depth lives in the per-tool descriptions, fetched via * describe_tool / search_tools. */ export declare const GROUPS: Record; /** * Map a tool's source file (basename, no extension) to its domain group. * * Driving the mapping off the SOURCE FILE — not a hand-maintained per-tool * list — means new tools added to an existing file inherit the right group * automatically, so the taxonomy never drifts from the registration sites. */ export declare const FILE_TO_GROUP: Record; /** * Assign a group by tool NAME. The name-based mapping reproduces the * source-file grouping exactly for all 157 current tools (verified), with the * eight cross-file cases pinned via NAME_OVERRIDES. Driven off the name (not a * hand-kept list) so a future tool lands somewhere sensible automatically. * * Order matters: e-invoicing ("einvoice") is checked before generic "invoice". */ export declare function groupForTool(name: string): ToolGroupId; interface CatalogEntry { name: string; group: ToolGroupId; title: string; /** First sentence of the description, single line (for terse listings). */ summary: string; /** Full original description (served by describe_tool). */ description: string; /** Whether the tool mutates state (from annotations.readOnlyHint). */ readOnly: boolean; /** Full MCP action annotations, exposed only on the full capability profile. */ annotations?: Record; /** Conservative runtime capability fact, exposed only on the full profile. */ capability?: Record; /** Input field names, for quick schema shape without dumping zod. */ inputFields: string[]; } /** Returned for visibility/tests; the live catalog after registration. */ export interface ToolExposureHandle { /** All registered tools, keyed by name. */ catalog: Map; /** Group → tool names. */ groups: Map; } /** * Resolve the active tool mode from an env-like bag. * Default is "full" — current behavior, untouched. */ export declare function resolveToolMode(env?: Record): "full" | "grouped"; /** * Apply the grouped tool-exposure profile to an MCP server. * * Must be called BEFORE registerAllTools(). Intercepts registerTool to record * a catalog + collapse descriptions, then (after tools are registered) adds the * meta-tools. Because registration is synchronous and ordered, the caller wires * it as: * * ```ts * if (resolveToolMode() === "grouped") applyToolExposureProfile(server); * registerAllTools(server, client); // tools recorded + collapsed here * // applyToolExposureProfile already queued the meta-tools to register last * ``` * * The meta-tools are registered immediately (eagerly) so they appear in * tools/list; they read from the catalog object, which is populated lazily as * the real tools register. This is safe: tool HANDLERS run long after all * registration completes. * * @param server The McpServer (typed loosely to match the openai-profile shim). * @param options.allowlist When provided, ONLY tools whose name is in this set * are catalogued + collapsed; any other tool is passed through untouched. * This supports a deliberately restricted grouped surface without allowing * discovery to reveal or describe tools outside that surface. Omit (default) * for mcp.frihet.io, which catalogs every registered tool. The reviewed * OpenAI host does not use grouped exposure: it publishes the exact 33-tool * allow-list with complete descriptions and no discovery meta-tools. * @returns a handle exposing the live catalog (useful for tests/logging). */ export declare function applyToolExposureProfile(server: any, options?: { allowlist?: ReadonlySet; groupMetadata?: Partial>; capabilityTruth?: { metaKey: string; localDiscovery: (name: string) => object; }; securitySchemes?: ReadonlyArray<{ type: "oauth2"; scopes: readonly string[]; }>; }): ToolExposureHandle; /** Number of meta-tools added in grouped mode (for logging). */ export declare const GROUPED_META_TOOL_COUNT: number; /** Exposed for logging/tests. */ export declare const TOOL_GROUP_IDS: ToolGroupId[]; export {}; //# sourceMappingURL=tool-exposure.d.ts.map