@frihet/mcp-server 1.16.6 → 2.0.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.
Files changed (108) hide show
  1. package/README.md +168 -82
  2. package/dist/agent-onboarding.d.ts +98 -0
  3. package/dist/agent-onboarding.js +275 -0
  4. package/dist/api-origin.d.ts +9 -0
  5. package/dist/api-origin.js +41 -0
  6. package/dist/canary/mcp-harness.d.ts +77 -0
  7. package/dist/canary/mcp-harness.js +552 -0
  8. package/dist/capability-truth.d.ts +29 -0
  9. package/dist/capability-truth.js +262 -0
  10. package/dist/claude-candidate-contract.d.ts +482 -0
  11. package/dist/claude-candidate-contract.js +87 -0
  12. package/dist/claude-output-privacy.d.ts +17 -0
  13. package/dist/claude-output-privacy.js +240 -0
  14. package/dist/claude-profile.d.ts +18 -0
  15. package/dist/claude-profile.js +260 -0
  16. package/dist/client-interface.d.ts +47 -33
  17. package/dist/client.d.ts +172 -40
  18. package/dist/client.js +708 -165
  19. package/dist/demo-client.d.ts +41 -32
  20. package/dist/demo-client.js +202 -31
  21. package/dist/demo-fixtures.d.ts +3 -3
  22. package/dist/demo-fixtures.js +7 -7
  23. package/dist/deposit-contract.d.ts +29 -0
  24. package/dist/deposit-contract.js +76 -0
  25. package/dist/fetch-no-redirect.d.ts +3 -0
  26. package/dist/fetch-no-redirect.js +16 -0
  27. package/dist/fiscal-aliases.d.ts +44 -0
  28. package/dist/fiscal-aliases.js +62 -0
  29. package/dist/fiscal-period.d.ts +26 -0
  30. package/dist/fiscal-period.js +31 -0
  31. package/dist/google-profile.d.ts +19 -0
  32. package/dist/google-profile.js +122 -0
  33. package/dist/index.js +68 -59
  34. package/dist/logger.d.ts +2 -2
  35. package/dist/logger.js +140 -8
  36. package/dist/muse-profile.d.ts +19 -0
  37. package/dist/muse-profile.js +122 -0
  38. package/dist/observability.d.ts +18 -29
  39. package/dist/observability.js +194 -106
  40. package/dist/openai-profile.d.ts +26 -22
  41. package/dist/openai-profile.js +1193 -157
  42. package/dist/openai-review-contract.d.ts +15 -6
  43. package/dist/openai-review-contract.js +714 -41
  44. package/dist/openai-review-oauth.d.ts +149 -2
  45. package/dist/openai-review-oauth.js +220 -6
  46. package/dist/prompts/register-all.d.ts +2 -0
  47. package/dist/prompts/register-all.js +34 -78
  48. package/dist/public-capability-contract.d.ts +39 -0
  49. package/dist/public-capability-contract.js +186 -0
  50. package/dist/redaction.d.ts +15 -0
  51. package/dist/redaction.js +83 -0
  52. package/dist/resources/register-all.js +154 -64
  53. package/dist/retry-after.d.ts +19 -0
  54. package/dist/retry-after.js +84 -0
  55. package/dist/server-composition.d.ts +19 -0
  56. package/dist/server-composition.js +74 -0
  57. package/dist/tool-exposure.d.ts +31 -27
  58. package/dist/tool-exposure.js +211 -87
  59. package/dist/tools/accountingClose.d.ts +14 -5
  60. package/dist/tools/accountingClose.js +41 -23
  61. package/dist/tools/audit_gl.js +8 -8
  62. package/dist/tools/backend-availability.d.ts +8 -29
  63. package/dist/tools/backend-availability.js +31 -30
  64. package/dist/tools/bank_rules.js +66 -15
  65. package/dist/tools/banking.js +5 -13
  66. package/dist/tools/clients.js +261 -15
  67. package/dist/tools/crm.js +22 -21
  68. package/dist/tools/deposits.js +114 -85
  69. package/dist/tools/einvoice.d.ts +16 -2
  70. package/dist/tools/einvoice.js +59 -12
  71. package/dist/tools/expenses.d.ts +16 -0
  72. package/dist/tools/expenses.js +47 -27
  73. package/dist/tools/fiscal.d.ts +3 -4
  74. package/dist/tools/fiscal.js +93 -60
  75. package/dist/tools/gestoria.js +15 -7
  76. package/dist/tools/hr.d.ts +2 -3
  77. package/dist/tools/hr.js +49 -20
  78. package/dist/tools/igic.d.ts +9 -8
  79. package/dist/tools/igic.js +34 -64
  80. package/dist/tools/impuesto_sociedades.d.ts +4 -5
  81. package/dist/tools/impuesto_sociedades.js +16 -37
  82. package/dist/tools/intelligence.js +166 -12
  83. package/dist/tools/invoices.js +224 -54
  84. package/dist/tools/kitchen.js +19 -9
  85. package/dist/tools/payroll.d.ts +5 -8
  86. package/dist/tools/payroll.js +19 -32
  87. package/dist/tools/permissions.d.ts +4 -7
  88. package/dist/tools/permissions.js +11 -14
  89. package/dist/tools/products.js +6 -6
  90. package/dist/tools/quotes.js +172 -26
  91. package/dist/tools/recurring.js +26 -25
  92. package/dist/tools/register-all.d.ts +2 -2
  93. package/dist/tools/register-all.js +9 -6
  94. package/dist/tools/search.d.ts +19 -0
  95. package/dist/tools/search.js +84 -0
  96. package/dist/tools/shared.d.ts +1445 -109
  97. package/dist/tools/shared.js +478 -102
  98. package/dist/tools/team.js +24 -28
  99. package/dist/tools/time.js +4 -12
  100. package/dist/tools/vendors.js +249 -15
  101. package/dist/tools/webhooks.js +35 -30
  102. package/dist/types.d.ts +31 -5
  103. package/dist/workflows/daily-operational-brief.d.ts +163 -0
  104. package/dist/workflows/daily-operational-brief.js +881 -0
  105. package/docs/agent-onboarding.json +391 -0
  106. package/package.json +49 -12
  107. package/dist/tools/onboarding.d.ts +0 -19
  108. package/dist/tools/onboarding.js +0 -64
@@ -0,0 +1,186 @@
1
+ /**
2
+ * Generated public-surface contract captured through the real MCP SDK.
3
+ *
4
+ * This is the mechanical source for catalogue, alias, discovery, resource, and
5
+ * prompt truth across the npm/full, remote/grouped, and OpenAI/full profiles.
6
+ */
7
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
8
+ import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
9
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
10
+ import { CAPABILITY_META_KEY, } from "./capability-truth.js";
11
+ import { FISCAL_MODELO_ALIASES } from "./fiscal-aliases.js";
12
+ import { MCP_PROMPT_COUNT } from "./prompts/register-all.js";
13
+ import { localMcpSurfaceComposition, registerMcpSurface, remoteMcpSurfaceComposition, } from "./server-composition.js";
14
+ import { GROUPED_META_TOOL_COUNT } from "./tool-exposure.js";
15
+ export const PUBLIC_CAPABILITY_CONTRACT_VERSION = 2;
16
+ function makeRegistrationClient() {
17
+ return new Proxy({}, {
18
+ get: () => async () => ({ data: [], total: 0, limit: 0, offset: 0 }),
19
+ });
20
+ }
21
+ function isMethodNotFound(error) {
22
+ return (typeof error === "object" &&
23
+ error !== null &&
24
+ "code" in error &&
25
+ error.code === -32601);
26
+ }
27
+ async function listPromptsOrEmpty(client) {
28
+ try {
29
+ const result = await client.listPrompts();
30
+ return result.prompts.map((prompt) => prompt.name).sort();
31
+ }
32
+ catch (error) {
33
+ if (isMethodNotFound(error))
34
+ return [];
35
+ throw error;
36
+ }
37
+ }
38
+ async function listResourcesOrEmpty(client) {
39
+ try {
40
+ const result = await client.listResources();
41
+ return result.resources.map((resource) => resource.uri).sort();
42
+ }
43
+ catch (error) {
44
+ if (isMethodNotFound(error))
45
+ return [];
46
+ throw error;
47
+ }
48
+ }
49
+ function compactDiscoveryTool(value) {
50
+ if (typeof value !== "object" || value === null) {
51
+ throw new Error("Grouped discovery returned a non-object tool result");
52
+ }
53
+ const record = value;
54
+ if (typeof record.name !== "string") {
55
+ throw new Error("Grouped discovery result omitted the tool name");
56
+ }
57
+ return {
58
+ name: record.name,
59
+ ...(record.annotations && typeof record.annotations === "object"
60
+ ? { annotations: record.annotations }
61
+ : {}),
62
+ ...(record.capability && typeof record.capability === "object"
63
+ ? { capability: record.capability }
64
+ : {}),
65
+ };
66
+ }
67
+ async function captureDiscoveryTruth(client) {
68
+ const searched = await client.callTool({
69
+ name: "search_tools",
70
+ arguments: { query: "send_invoice", limit: 1 },
71
+ });
72
+ const searchTools = searched.structuredContent?.tools;
73
+ if (!Array.isArray(searchTools) || searchTools.length !== 1) {
74
+ throw new Error("search_tools did not return the pinned send_invoice result");
75
+ }
76
+ const described = await client.callTool({
77
+ name: "describe_tool",
78
+ arguments: { name: "send_invoice" },
79
+ });
80
+ return {
81
+ searchTools: compactDiscoveryTool(searchTools[0]),
82
+ describeTool: compactDiscoveryTool(described.structuredContent),
83
+ };
84
+ }
85
+ async function captureSurface(configure, captureDiscovery = false) {
86
+ const server = new McpServer({ name: "public-capability-capture", version: "1.0.0" });
87
+ configure(server, makeRegistrationClient());
88
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
89
+ const client = new Client({ name: "public-capability-capture-client", version: "1.0.0" }, { capabilities: {} });
90
+ try {
91
+ await Promise.all([server.connect(serverTransport), client.connect(clientTransport)]);
92
+ const listed = await client.listTools();
93
+ const tools = listed.tools
94
+ .map((tool) => {
95
+ const meta = tool._meta;
96
+ const capability = meta?.[CAPABILITY_META_KEY];
97
+ return {
98
+ name: tool.name,
99
+ ...(tool.annotations
100
+ ? { annotations: tool.annotations }
101
+ : {}),
102
+ ...(capability ? { capability } : {}),
103
+ };
104
+ })
105
+ .sort((left, right) => left.name.localeCompare(right.name));
106
+ const [resources, prompts] = await Promise.all([
107
+ listResourcesOrEmpty(client),
108
+ listPromptsOrEmpty(client),
109
+ ]);
110
+ const discovery = captureDiscovery
111
+ ? await captureDiscoveryTruth(client)
112
+ : undefined;
113
+ return { tools, resources, prompts, ...(discovery ? { discovery } : {}) };
114
+ }
115
+ finally {
116
+ await Promise.allSettled([client.close(), server.close()]);
117
+ }
118
+ }
119
+ export async function capturePublicCapabilityContract() {
120
+ const localFull = await captureSurface((server, client) => {
121
+ registerMcpSurface(server, client, localMcpSurfaceComposition(false, false));
122
+ });
123
+ const remoteGrouped = await captureSurface((server, client) => {
124
+ registerMcpSurface(server, client, remoteMcpSurfaceComposition(false, true));
125
+ }, true);
126
+ const openaiFull = await captureSurface((server, client) => {
127
+ registerMcpSurface(server, client, remoteMcpSurfaceComposition(true, false));
128
+ });
129
+ const aliases = new Set(Object.keys(FISCAL_MODELO_ALIASES));
130
+ const discovery = new Set(["list_tool_groups", "search_tools", "describe_tool"]);
131
+ const canonicalOperations = remoteGrouped.tools.filter((tool) => !aliases.has(tool.name) && !discovery.has(tool.name)).length;
132
+ if (localFull.prompts.length !== MCP_PROMPT_COUNT ||
133
+ remoteGrouped.prompts.length !== MCP_PROMPT_COUNT) {
134
+ throw new Error(`Prompt count drift: registered ${localFull.prompts.length}/${remoteGrouped.prompts.length}, expected ${MCP_PROMPT_COUNT}`);
135
+ }
136
+ return {
137
+ contractVersion: PUBLIC_CAPABILITY_CONTRACT_VERSION,
138
+ catalogue: {
139
+ canonicalOperations,
140
+ aliasNames: aliases.size,
141
+ discoveryNames: GROUPED_META_TOOL_COUNT,
142
+ },
143
+ surfaces: { localFull, remoteGrouped, openaiFull },
144
+ };
145
+ }
146
+ function canonicalize(contract) {
147
+ const clone = structuredClone(contract);
148
+ for (const surface of Object.values(clone.surfaces)) {
149
+ surface.tools.sort((left, right) => left.name.localeCompare(right.name));
150
+ surface.resources.sort();
151
+ surface.prompts.sort();
152
+ }
153
+ return clone;
154
+ }
155
+ export function serializePublicCapabilityContract(contract) {
156
+ return `${JSON.stringify(canonicalize(contract), null, 2)}\n`;
157
+ }
158
+ export function assertPublicCapabilityContract(actual, expected) {
159
+ if (actual.contractVersion !== PUBLIC_CAPABILITY_CONTRACT_VERSION) {
160
+ throw new Error(`Unsupported public capability contract ${actual.contractVersion}`);
161
+ }
162
+ const fullSurfaces = [actual.surfaces.localFull, actual.surfaces.remoteGrouped];
163
+ for (const surface of fullSurfaces) {
164
+ for (const tool of surface.tools) {
165
+ assertFullToolTruth(tool);
166
+ }
167
+ }
168
+ const serializedActual = serializePublicCapabilityContract(actual);
169
+ const serializedExpected = serializePublicCapabilityContract(expected);
170
+ if (serializedActual !== serializedExpected) {
171
+ throw new Error("Public capability contract drift; regenerate and review the runtime capture");
172
+ }
173
+ }
174
+ function assertFullToolTruth(tool) {
175
+ if (!tool.capability) {
176
+ throw new Error(`Full-surface tool ${tool.name} lacks ${CAPABILITY_META_KEY}`);
177
+ }
178
+ if (tool.capability.callability === "available") {
179
+ throw new Error(`Tool ${tool.name} conflates registration with availability`);
180
+ }
181
+ if (tool.capability.externalInteraction &&
182
+ tool.annotations?.openWorldHint !== true) {
183
+ throw new Error(`External tool ${tool.name} must set openWorldHint=true`);
184
+ }
185
+ }
186
+ //# sourceMappingURL=public-capability-contract.js.map
@@ -17,6 +17,21 @@
17
17
  */
18
18
  /** Sentinel left in place of a redacted value in cloned (tracing) payloads. */
19
19
  export declare const REDACTED = "[redacted]";
20
+ /**
21
+ * Return a backend request id only when it is a short opaque token that is
22
+ * safe to echo to an agent (no whitespace, no prose, no instructions).
23
+ */
24
+ export declare function safeRequestId(value: unknown): string | undefined;
25
+ /**
26
+ * Allowlist short, single-line human remediation prose for agent display.
27
+ *
28
+ * Server text is omitted unless it matches one of two explicit semantics: an
29
+ * API-key scope instruction matching the ERP contract, or a conservative
30
+ * workspace-owner access instruction. A small character alphabet and the
31
+ * structural checks below are defense in depth. Rejection is intentional: an
32
+ * omitted hint is safer than attempting to redact arbitrary backend prose.
33
+ */
34
+ export declare function sanitizeServerRemediation(value: unknown, errorCode?: string): string | undefined;
20
35
  /**
21
36
  * Field names whose VALUES must never appear in logs, traces, or any surface
22
37
  * outside the process boundary. Includes snake_case + locale synonyms because
package/dist/redaction.js CHANGED
@@ -17,6 +17,73 @@
17
17
  */
18
18
  /** Sentinel left in place of a redacted value in cloned (tracing) payloads. */
19
19
  export const REDACTED = "[redacted]";
20
+ /** Opaque backend correlation id (publicApi `meta.requestId` / `X-Request-Id`). */
21
+ const REQUEST_ID_PATTERN = /^[A-Za-z0-9._:-]{1,128}$/;
22
+ /**
23
+ * Return a backend request id only when it is a short opaque token that is
24
+ * safe to echo to an agent (no whitespace, no prose, no instructions).
25
+ */
26
+ export function safeRequestId(value) {
27
+ return typeof value === "string" && REQUEST_ID_PATTERN.test(value) ? value : undefined;
28
+ }
29
+ const MAX_SERVER_REMEDIATION_CHARS = 500;
30
+ const SERVER_REMEDIATION_PROSE_ALPHABET = /^[\p{L}\p{N} .,;:!?'"()_/*+\-]+$/u;
31
+ const LIVE_EINVOICE_SCOPE_REMEDIATION = "This API key requires the einvoice:read or einvoice:write scope to access e-invoicing endpoints. Add the scope when creating or updating the key.";
32
+ const OWNER_ACCESS_REMEDIATION = /^(?:Ask (?:a|the|your) workspace owner to (?:grant (?:you )?access to (?:this|the) (?:resource|feature|action)|grant (?:the )?required (?:scope|permission))|Contact (?:a|the|your) workspace owner to request access to (?:this|the) (?:resource|feature|action))\.$/iu;
33
+ const GENERIC_REMEDIATION_MESSAGES = new Set([
34
+ "forbidden",
35
+ "access denied",
36
+ "access forbidden",
37
+ "authorization failed",
38
+ "not authorized",
39
+ ]);
40
+ /**
41
+ * Allowlist short, single-line human remediation prose for agent display.
42
+ *
43
+ * Server text is omitted unless it matches one of two explicit semantics: an
44
+ * API-key scope instruction matching the ERP contract, or a conservative
45
+ * workspace-owner access instruction. A small character alphabet and the
46
+ * structural checks below are defense in depth. Rejection is intentional: an
47
+ * omitted hint is safer than attempting to redact arbitrary backend prose.
48
+ */
49
+ export function sanitizeServerRemediation(value, errorCode = "") {
50
+ if (typeof value !== "string")
51
+ return undefined;
52
+ const remediation = value.trim();
53
+ if (!remediation || remediation.length > MAX_SERVER_REMEDIATION_CHARS)
54
+ return undefined;
55
+ if (!SERVER_REMEDIATION_PROSE_ALPHABET.test(remediation))
56
+ return undefined;
57
+ const normalized = remediation.toLowerCase();
58
+ if (normalized === errorCode.trim().toLowerCase()
59
+ || GENERIC_REMEDIATION_MESSAGES.has(normalized)) {
60
+ return undefined;
61
+ }
62
+ if (remediation !== LIVE_EINVOICE_SCOPE_REMEDIATION
63
+ && !OWNER_ACCESS_REMEDIATION.test(remediation)) {
64
+ return undefined;
65
+ }
66
+ const credentialOrInternalShape = [
67
+ // Frihet API keys are `fri_` plus 32 random bytes encoded base64url (43 chars).
68
+ /\bfri_[A-Za-z0-9_-]{32,128}(?=$|[^A-Za-z0-9_-])/u,
69
+ // Header name is not required for a leaked bearer/basic credential to be dangerous.
70
+ /\b(?:bearer|basic)\s+[A-Za-z0-9+/_-]{8,}={0,2}(?:\s|[.,;!?]|$)/iu,
71
+ /\b(?:api[_ -]?key|access[_ -]?token|refresh[_ -]?token|token|client[_ -]?secret|password)\s*(?::|=|\bis\b|\bwas\b)\s*["']?\S+/iu,
72
+ /\b(?:whsec_[A-Za-z0-9_-]+|ya29\.[A-Za-z0-9._-]+)\b/u,
73
+ /\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/u,
74
+ /(?:^|\s)at\s+(?:async\s+)?[^\s]+\s*\([^)]*:\d+(?::\d+)?\)/u,
75
+ // Any whitespace-delimited absolute path is outside the remediation contract.
76
+ /(?:^|[\s("'])\/(?:[A-Za-z0-9._-]+(?:\/[A-Za-z0-9._-]+)*)/u,
77
+ /(?:^|[\s("'])[A-Za-z]:\\/u,
78
+ // Relative application/source paths (for example `src/auth.ts`).
79
+ /(?:^|\s)(?:(?:\.\.?\/)?(?:src|lib|dist|build|node_modules|functions|workers)\/)[A-Za-z0-9_./-]+/iu,
80
+ /\b[A-Za-z0-9_./-]+\.(?:c|m)?(?:j|t)sx?(?::\d+(?::\d+)?)?\b/u,
81
+ /\b(?:node_modules|file:\/\/)/iu,
82
+ ];
83
+ if (credentialOrInternalShape.some((pattern) => pattern.test(remediation)))
84
+ return undefined;
85
+ return remediation;
86
+ }
20
87
  /**
21
88
  * Field names whose VALUES must never appear in logs, traces, or any surface
22
89
  * outside the process boundary. Includes snake_case + locale synonyms because
@@ -24,6 +91,8 @@ export const REDACTED = "[redacted]";
24
91
  */
25
92
  export const SENSITIVE_FIELD_NAMES = [
26
93
  "taxId", "tax_id", // Primary field name + snake_case variant
94
+ "taxIdNormalized", "tax_id_normalized", // Search-normalized government tax ID
95
+ "clientTaxId", "client_tax_id", // Per-document client government tax ID
27
96
  "nif", "cif", "vatNumber", // Spanish/EU synonyms for government tax ID
28
97
  "vat_number", "vatId", "vat_id",
29
98
  "secret", // Webhook signing credential
@@ -39,6 +108,20 @@ export const SENSITIVE_FIELD_NAMES = [
39
108
  "requestId", "request_id", "traceId", "trace_id",
40
109
  "sessionId", "session_id", "userId", "user_id",
41
110
  "verifactuHash", "verifactu_hash",
111
+ // Raw Firestore records can contain nested fiscal-submission state and
112
+ // capability/payment identifiers that are not part of any public response
113
+ // contract. Redact the parent objects so future nested fields fail closed.
114
+ "verifactu", "verifactuAnulacion", "verifactu_anulacion",
115
+ "verifactuSubmission", "verifactu_submission",
116
+ "paymentDetails", "payment_details",
117
+ "paymentUrl", "payment_url", "paymentLink", "payment_link",
118
+ "paymentIntentId", "payment_intent_id", "stripePaymentIntentId",
119
+ "publicHash", "public_hash",
120
+ "bankTransactionId", "bank_transaction_id",
121
+ "bankIncomeAuthority", "bank_income_authority",
122
+ // Backend search indexes can embed normalized names, email addresses and
123
+ // government identifiers even when the canonical fields are removed.
124
+ "searchTokens", "search_tokens",
42
125
  ];
43
126
  /** Recursively removes named fields from an object/array tree, IN PLACE. */
44
127
  export function deepRedact(obj, fields = SENSITIVE_FIELD_NAMES) {