@frihet/mcp-server 1.16.5 → 1.17.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 (68) hide show
  1. package/README.md +92 -31
  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 +549 -0
  8. package/dist/capability-truth.d.ts +29 -0
  9. package/dist/capability-truth.js +254 -0
  10. package/dist/client-interface.d.ts +22 -14
  11. package/dist/client.d.ts +114 -18
  12. package/dist/client.js +520 -58
  13. package/dist/demo-client.d.ts +16 -13
  14. package/dist/demo-client.js +209 -29
  15. package/dist/demo-fixtures.d.ts +3 -3
  16. package/dist/demo-fixtures.js +7 -7
  17. package/dist/fiscal-aliases.d.ts +44 -0
  18. package/dist/fiscal-aliases.js +62 -0
  19. package/dist/index.js +41 -56
  20. package/dist/logger.d.ts +2 -2
  21. package/dist/logger.js +140 -8
  22. package/dist/observability.d.ts +18 -29
  23. package/dist/observability.js +192 -105
  24. package/dist/openai-profile.d.ts +27 -15
  25. package/dist/openai-profile.js +1191 -145
  26. package/dist/openai-review-contract.d.ts +49 -0
  27. package/dist/openai-review-contract.js +919 -0
  28. package/dist/openai-review-oauth.d.ts +98 -0
  29. package/dist/openai-review-oauth.js +136 -0
  30. package/dist/prompts/register-all.d.ts +2 -0
  31. package/dist/prompts/register-all.js +2 -0
  32. package/dist/public-capability-contract.d.ts +39 -0
  33. package/dist/public-capability-contract.js +186 -0
  34. package/dist/redaction.d.ts +10 -0
  35. package/dist/redaction.js +74 -0
  36. package/dist/resources/register-all.js +6 -12
  37. package/dist/server-composition.d.ts +16 -0
  38. package/dist/server-composition.js +44 -0
  39. package/dist/tool-exposure.d.ts +25 -22
  40. package/dist/tool-exposure.js +199 -77
  41. package/dist/tools/accountingClose.d.ts +14 -5
  42. package/dist/tools/accountingClose.js +41 -23
  43. package/dist/tools/backend-availability.d.ts +3 -3
  44. package/dist/tools/backend-availability.js +3 -3
  45. package/dist/tools/clients.js +9 -9
  46. package/dist/tools/crm.js +22 -21
  47. package/dist/tools/deposits.js +30 -11
  48. package/dist/tools/einvoice.d.ts +1 -1
  49. package/dist/tools/expenses.d.ts +16 -0
  50. package/dist/tools/expenses.js +47 -27
  51. package/dist/tools/hr.d.ts +2 -3
  52. package/dist/tools/hr.js +8 -9
  53. package/dist/tools/intelligence.js +95 -4
  54. package/dist/tools/invoices.js +150 -47
  55. package/dist/tools/payroll.d.ts +5 -8
  56. package/dist/tools/payroll.js +19 -32
  57. package/dist/tools/permissions.d.ts +4 -7
  58. package/dist/tools/permissions.js +11 -14
  59. package/dist/tools/products.js +6 -6
  60. package/dist/tools/quotes.js +109 -24
  61. package/dist/tools/shared.d.ts +1431 -75
  62. package/dist/tools/shared.js +386 -76
  63. package/dist/tools/team.js +18 -14
  64. package/dist/tools/vendors.js +9 -9
  65. package/dist/tools/webhooks.js +35 -30
  66. package/dist/types.d.ts +28 -4
  67. package/docs/agent-onboarding.json +381 -0
  68. package/package.json +32 -7
@@ -6,8 +6,9 @@
6
6
  *
7
7
  * Design:
8
8
  * - Fail-open: any Langfuse error logs a warning and lets the tool proceed.
9
- * - PII: tool input content is passed as-is (business data is fine to trace).
10
- * userId / apiKey metadata are hashed with a simple SHA-256 fingerprint.
9
+ * - Data minimization: only bounded operational facts cross the telemetry
10
+ * boundary. Tool input/output, arbitrary error text, and user/workspace
11
+ * identity are never serialized.
11
12
  * - Fire-and-forget: traces are sent via waitUntil (Workers) or unref'd promise
12
13
  * (Node.js) so they never block tool responses.
13
14
  *
@@ -18,7 +19,53 @@
18
19
  *
19
20
  * Docs: https://langfuse.com/docs/api/reference/overview
20
21
  */
21
- import { redactClone, redactText } from "./redaction.js";
22
+ import { log } from "./logger.js";
23
+ const CANONICAL_LANGFUSE_ORIGIN = "https://langfuse.frihet.io";
24
+ /**
25
+ * Langfuse receives a Basic Authorization header, so its authority is exact.
26
+ * The current product contract documents one hosted origin; arbitrary Frihet
27
+ * subdomains and self-hosted overrides are intentionally not trusted here.
28
+ */
29
+ export function normalizeLangfuseBaseUrl(value) {
30
+ if (value !== value.trim()) {
31
+ throw new Error("LANGFUSE_BASE_URL must not contain surrounding whitespace");
32
+ }
33
+ let parsed;
34
+ try {
35
+ parsed = new URL(value);
36
+ }
37
+ catch {
38
+ throw new Error("LANGFUSE_BASE_URL must be a valid URL");
39
+ }
40
+ if (parsed.protocol !== "https:")
41
+ throw new Error("LANGFUSE_BASE_URL must use HTTPS");
42
+ if (parsed.username || parsed.password) {
43
+ throw new Error("LANGFUSE_BASE_URL must not contain URL credentials");
44
+ }
45
+ if (parsed.port)
46
+ throw new Error("LANGFUSE_BASE_URL must use the default HTTPS port");
47
+ if (parsed.search || parsed.hash) {
48
+ throw new Error("LANGFUSE_BASE_URL must not contain a query or fragment");
49
+ }
50
+ if (parsed.pathname !== "/")
51
+ throw new Error("LANGFUSE_BASE_URL must use the origin root");
52
+ if (parsed.hostname.toLowerCase() !== "langfuse.frihet.io") {
53
+ throw new Error("LANGFUSE_BASE_URL hostname is not trusted");
54
+ }
55
+ return CANONICAL_LANGFUSE_ORIGIN;
56
+ }
57
+ function buildConfig(publicKey, secretKey, baseUrl) {
58
+ if (!publicKey || !secretKey || !baseUrl)
59
+ return null;
60
+ try {
61
+ return { publicKey, secretKey, baseUrl: normalizeLangfuseBaseUrl(baseUrl) };
62
+ }
63
+ catch {
64
+ // Telemetry is optional and fail-open for tools. An invalid authority
65
+ // disables telemetry rather than risking credentials or blocking MCP calls.
66
+ return null;
67
+ }
68
+ }
22
69
  function getConfig() {
23
70
  let publicKey;
24
71
  let secretKey;
@@ -29,44 +76,22 @@ function getConfig() {
29
76
  secretKey = process.env.LANGFUSE_SECRET_KEY;
30
77
  baseUrl = process.env.LANGFUSE_BASE_URL;
31
78
  }
32
- if (!publicKey || !secretKey || !baseUrl)
33
- return null;
34
- return { publicKey, secretKey, baseUrl: baseUrl.replace(/\/$/, "") };
79
+ return buildConfig(publicKey, secretKey, baseUrl);
35
80
  }
36
81
  // ── Worker env injection (for Cloudflare Workers) ───────────────────────────
37
- let workerEnv = null;
82
+ // `undefined` means no Worker-specific decision was made, so Node.js may read
83
+ // process.env. `null` is an explicit Worker-side opt-out and must never fall
84
+ // back to process.env (Workers using nodejs_compat expose that global too).
85
+ let workerEnv;
38
86
  /**
39
87
  * Called once from FrihetMCP.init() in the Worker to inject env vars.
40
88
  * Not needed in Node.js stdio mode (reads from process.env directly).
41
89
  */
42
90
  export function initLangfuse(config) {
43
- if (config.publicKey && config.secretKey && config.baseUrl) {
44
- workerEnv = {
45
- publicKey: config.publicKey,
46
- secretKey: config.secretKey,
47
- baseUrl: config.baseUrl.replace(/\/$/, ""),
48
- };
49
- }
91
+ workerEnv = buildConfig(config.publicKey, config.secretKey, config.baseUrl);
50
92
  }
51
93
  function resolveConfig() {
52
- return workerEnv ?? getConfig();
53
- }
54
- // ── PII helpers ──────────────────────────────────────────────────────────────
55
- /**
56
- * One-way fingerprint for PII values (apiKey, userId, email).
57
- * Uses Web Crypto API (available in both Node.js ≥18 and Workers).
58
- */
59
- async function hashPii(value) {
60
- try {
61
- const encoder = new TextEncoder();
62
- const data = encoder.encode(value);
63
- const hashBuffer = await crypto.subtle.digest("SHA-256", data);
64
- const hashArray = Array.from(new Uint8Array(hashBuffer));
65
- return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("").slice(0, 16);
66
- }
67
- catch {
68
- return "[hash-error]";
69
- }
94
+ return workerEnv === undefined ? getConfig() : workerEnv;
70
95
  }
71
96
  // ── ID generation ────────────────────────────────────────────────────────────
72
97
  function newId() {
@@ -100,17 +125,11 @@ export function inspectStubMarker(output) {
100
125
  candidates.push(sc);
101
126
  for (const obj of candidates) {
102
127
  if (obj["_stub"] === true || obj["_notImplemented"] === true || obj["_unavailable"] === true) {
103
- return {
104
- plannedEndpoint: typeof obj["_plannedEndpoint"] === "string" ? obj["_plannedEndpoint"] : undefined,
105
- note: typeof obj["_note"] === "string" ? obj["_note"] : undefined,
106
- };
128
+ return { stub: true };
107
129
  }
108
130
  // A bare _plannedEndpoint (without an explicit flag) is also a stub signal.
109
131
  if (typeof obj["_plannedEndpoint"] === "string") {
110
- return {
111
- plannedEndpoint: obj["_plannedEndpoint"],
112
- note: typeof obj["_note"] === "string" ? obj["_note"] : undefined,
113
- };
132
+ return { stub: true };
114
133
  }
115
134
  }
116
135
  return null;
@@ -120,6 +139,7 @@ async function sendBatch(config, batch) {
120
139
  const credentials = btoa(`${config.publicKey}:${config.secretKey}`);
121
140
  const resp = await fetch(`${config.baseUrl}/api/public/ingestion`, {
122
141
  method: "POST",
142
+ redirect: "error",
123
143
  headers: {
124
144
  "Content-Type": "application/json",
125
145
  "Authorization": `Basic ${credentials}`,
@@ -128,21 +148,25 @@ async function sendBatch(config, batch) {
128
148
  signal: AbortSignal.timeout(5000),
129
149
  });
130
150
  if (!resp.ok) {
131
- // Log but don't throw — fail-open
132
- const body = await resp.text().catch(() => "");
133
- console.error(JSON.stringify({
134
- service: "frihet-mcp",
151
+ // Never read/log the provider response body. Status is sufficient to
152
+ // correlate an operational delivery failure without importing arbitrary
153
+ // third-party text into telemetry.
154
+ log({
135
155
  level: "warn",
136
- message: `Langfuse ingestion failed: ${resp.status} ${body.slice(0, 200)}`,
156
+ message: "Langfuse ingestion request failed",
137
157
  operation: "langfuse_send",
138
- timestamp: new Date().toISOString(),
139
- }));
158
+ error: {
159
+ message: "MCP operation failed",
160
+ code: "telemetry_http_error",
161
+ statusCode: resp.status,
162
+ },
163
+ });
140
164
  }
141
165
  }
142
166
  // Module-level context set once per session (Workers: per DO init, Node.js: startup)
143
167
  let _sessionContext = {};
144
168
  /**
145
- * Set session-level context (client identity, MCP version).
169
+ * Set session-level protocol context. Client identity is intentionally absent.
146
170
  * Call once from server init; applies to all subsequent traces.
147
171
  */
148
172
  export function setTraceContext(ctx) {
@@ -151,71 +175,69 @@ export function setTraceContext(ctx) {
151
175
  /**
152
176
  * Builds the Langfuse trace+span ingestion batch for a single tool call.
153
177
  *
154
- * CRITICAL (Trust): `input` and `output` are passed through {@link redactClone}
155
- * before they enter the payload, so government IDs (taxId/NIF/CIF), banking
156
- * identifiers (IBAN), webhook signing secrets, and auth tokens NEVER reach the
157
- * external Langfuse service — in ANY profile mode. The tool-call output handed
158
- * to the user is redacted later (OpenAI mode only); this redacts the trace copy
159
- * unconditionally. Exported so a test can assert no sensitive value survives.
178
+ * CRITICAL (Trust): this builder is an allowlist. It has no input/output or
179
+ * identity fields and ignores any extra runtime properties a caller supplies.
180
+ * Exported so tests can assert the exact operational shape.
160
181
  */
161
182
  export function buildTracePayload(p) {
162
- const { toolName, input, output, isError, errorMessage, startTime, endTime, traceId, spanId, userIdHashed, clientName, mcpVersion, stub, } = p;
183
+ const { toolName, isError, errorClass, errorCode, statusCode, startTime, endTime, traceId, spanId, mcpVersion, stub, } = p;
163
184
  const stubbed = stub !== null;
164
185
  // success: false whenever the call threw OR returned a fabricated stub body.
165
186
  const success = !isError && !stubbed;
166
- const stubNote = stub?.note ??
167
- (stub?.plannedEndpoint
168
- ? `Backend endpoint not yet available: ${stub.plannedEndpoint}`
169
- : undefined);
170
- // ── Redact BEFORE the payload is built (never mutates the live response) ──
171
- const safeArgs = redactClone(input);
172
- const safeError = errorMessage ? redactText(errorMessage) : errorMessage;
173
- const safeOutput = isError ? { error: safeError } : redactClone(output);
187
+ const safeToolName = toolNameToken(toolName) ?? "unknown_tool";
188
+ const safeMcpVersion = mcpVersion === "mcp/1.0" ? mcpVersion : undefined;
189
+ const safeErrorClass = TELEMETRY_ERROR_CLASSES.has(errorClass ?? "")
190
+ ? errorClass
191
+ : undefined;
192
+ const safeErrorCode = TELEMETRY_ERROR_CODES.has(errorCode ?? "")
193
+ ? errorCode
194
+ : undefined;
195
+ const safeStatusCode = typeof statusCode === "number"
196
+ && Number.isInteger(statusCode)
197
+ && statusCode >= 0
198
+ && statusCode <= 599
199
+ ? statusCode
200
+ : undefined;
201
+ const errorFacts = {
202
+ ...(safeErrorClass ? { errorClass: safeErrorClass } : {}),
203
+ ...(safeErrorCode ? { errorCode: safeErrorCode } : {}),
204
+ ...(safeStatusCode !== undefined ? { statusCode: safeStatusCode } : {}),
205
+ };
174
206
  const traceBody = {
175
207
  id: traceId,
176
208
  name: "mcp_request",
177
209
  timestamp: startTime.toISOString(),
178
- input: { tool: toolName, args: safeArgs },
179
- output: safeOutput,
180
210
  metadata: {
181
- tool: toolName,
182
- clientName,
183
- mcpVersion,
211
+ tool: safeToolName,
212
+ ...(safeMcpVersion ? { mcpVersion: safeMcpVersion } : {}),
184
213
  // FALSE on a thrown error AND on a fabricated-stub fallback.
185
214
  success,
186
- ...(stubbed
187
- ? {
188
- stub: true,
189
- ...(stub?.plannedEndpoint ? { plannedEndpoint: stub.plannedEndpoint } : {}),
190
- ...(stubNote ? { note: stubNote } : {}),
191
- }
192
- : {}),
215
+ ...(stubbed ? { stub: true } : {}),
216
+ ...errorFacts,
193
217
  },
194
- tags: [`mcp.tool.${toolName}`, ...(stubbed ? ["mcp.stub"] : [])],
195
- ...(userIdHashed ? { userId: userIdHashed } : {}),
218
+ tags: [`mcp.tool.${safeToolName}`, ...(stubbed ? ["mcp.stub"] : [])],
196
219
  };
197
220
  const spanBody = {
198
221
  id: spanId,
199
222
  traceId,
200
- name: `tool.${toolName}`,
223
+ name: `tool.${safeToolName}`,
201
224
  startTime: startTime.toISOString(),
202
225
  endTime: endTime.toISOString(),
203
- input: safeArgs,
204
- output: safeOutput,
205
226
  metadata: {
206
227
  durationMs: endTime.getTime() - startTime.getTime(),
207
- clientName,
208
- mcpVersion,
228
+ ...(safeMcpVersion ? { mcpVersion: safeMcpVersion } : {}),
229
+ success,
209
230
  ...(stubbed ? { stub: true } : {}),
231
+ ...errorFacts,
210
232
  },
211
233
  // ERROR on a thrown error; WARNING on a fabricated-stub fallback (the call
212
234
  // "succeeded" mechanically but returned no real backend data); DEFAULT only
213
235
  // on a genuine success.
214
236
  level: isError ? "ERROR" : stubbed ? "WARNING" : "DEFAULT",
215
- ...(isError && safeError
216
- ? { statusMessage: safeError }
217
- : stubbed && stubNote
218
- ? { statusMessage: stubNote }
237
+ ...(isError
238
+ ? { statusMessage: safeErrorCode ?? safeErrorClass ?? "operation_failed" }
239
+ : stubbed
240
+ ? { statusMessage: "stub_response" }
219
241
  : {}),
220
242
  };
221
243
  return {
@@ -225,6 +247,72 @@ export function buildTracePayload(p) {
225
247
  ],
226
248
  };
227
249
  }
250
+ const TOOL_NAME = /^[a-z][a-z0-9_]{0,63}$/u;
251
+ const TELEMETRY_ERROR_CLASSES = new Set([
252
+ "handled_mcp_error",
253
+ "request_timeout",
254
+ "abort_error",
255
+ "api_error",
256
+ "telemetry_error",
257
+ "unknown_error",
258
+ ]);
259
+ const TELEMETRY_ERROR_CODES = new Set([
260
+ "tool_error",
261
+ "request_timeout",
262
+ "unauthorized",
263
+ "forbidden",
264
+ "not_found",
265
+ "rate_limited",
266
+ "client_error",
267
+ "backend_error",
268
+ "telemetry_http_error",
269
+ "telemetry_error",
270
+ "unknown_error",
271
+ ]);
272
+ function toolNameToken(value) {
273
+ return typeof value === "string" && TOOL_NAME.test(value) ? value : undefined;
274
+ }
275
+ function statusErrorCode(statusCode) {
276
+ if (typeof statusCode !== "number" || !Number.isInteger(statusCode))
277
+ return undefined;
278
+ if (statusCode === 401)
279
+ return "unauthorized";
280
+ if (statusCode === 403)
281
+ return "forbidden";
282
+ if (statusCode === 404)
283
+ return "not_found";
284
+ if (statusCode === 408)
285
+ return "request_timeout";
286
+ if (statusCode === 429)
287
+ return "rate_limited";
288
+ if (statusCode >= 400 && statusCode < 500)
289
+ return "client_error";
290
+ if (statusCode >= 500 && statusCode <= 599)
291
+ return "backend_error";
292
+ return undefined;
293
+ }
294
+ function operationalErrorFacts(error) {
295
+ if (!error || typeof error !== "object")
296
+ return { errorClass: "unknown_error" };
297
+ const record = error;
298
+ const statusCode = typeof record.statusCode === "number" ? record.statusCode : undefined;
299
+ const name = record.name;
300
+ const errorClass = name === "TimeoutError"
301
+ ? "request_timeout"
302
+ : name === "AbortError"
303
+ ? "abort_error"
304
+ : statusErrorCode(statusCode)
305
+ ? "api_error"
306
+ : "unknown_error";
307
+ return {
308
+ errorClass,
309
+ errorCode: statusErrorCode(statusCode) ?? (errorClass === "request_timeout" ? "request_timeout" : "unknown_error"),
310
+ statusCode,
311
+ };
312
+ }
313
+ function isHandledMcpError(output) {
314
+ return !!output && typeof output === "object" && output.isError === true;
315
+ }
228
316
  /**
229
317
  * Wraps a tool handler fn and sends a Langfuse trace+span for the call.
230
318
  *
@@ -236,7 +324,7 @@ export function buildTracePayload(p) {
236
324
  * @param fn Async tool handler to wrap
237
325
  * @returns Result of fn
238
326
  */
239
- export async function traceMCPTool(toolName, input, fn) {
327
+ export async function traceMCPTool(toolName, _input, fn) {
240
328
  const config = resolveConfig();
241
329
  // No config — pass through silently
242
330
  if (!config) {
@@ -247,16 +335,20 @@ export async function traceMCPTool(toolName, input, fn) {
247
335
  const startTime = new Date();
248
336
  let result;
249
337
  let isError = false;
250
- let errorMessage;
338
+ let errorFacts = {};
251
339
  let output;
252
340
  try {
253
341
  result = await fn();
254
342
  output = result;
343
+ if (isHandledMcpError(result)) {
344
+ isError = true;
345
+ errorFacts = { errorClass: "handled_mcp_error", errorCode: "tool_error" };
346
+ }
255
347
  return result;
256
348
  }
257
349
  catch (err) {
258
350
  isError = true;
259
- errorMessage = err instanceof Error ? err.message : String(err);
351
+ errorFacts = operationalErrorFacts(err);
260
352
  throw err;
261
353
  }
262
354
  finally {
@@ -274,37 +366,32 @@ export async function traceMCPTool(toolName, input, fn) {
274
366
  // Fire-and-forget — build and send async, never awaited
275
367
  void (async () => {
276
368
  try {
277
- // Hash PII fields
278
- const userIdRaw = _sessionContext.userId;
279
- const userIdHashed = userIdRaw ? await hashPii(userIdRaw) : undefined;
280
- // buildTracePayload redacts input/output (taxId/secret/IBAN/tokens) so
281
- // the external Langfuse service never stores cleartext PII or credentials.
282
369
  const batch = buildTracePayload({
283
370
  toolName,
284
- input,
285
- output,
286
371
  isError,
287
- errorMessage,
372
+ ...errorFacts,
288
373
  startTime,
289
374
  endTime,
290
375
  traceId,
291
376
  spanId,
292
- userIdHashed,
293
- clientName: _sessionContext.clientName,
294
377
  mcpVersion: _sessionContext.mcpVersion,
295
378
  stub,
296
379
  });
297
380
  await sendBatch(config, batch);
298
381
  }
299
382
  catch (langfuseErr) {
300
- // Fail-open: log warn only
301
- console.error(JSON.stringify({
302
- service: "frihet-mcp",
383
+ // Fail-open, but never serialize the provider/runtime exception text.
384
+ const facts = operationalErrorFacts(langfuseErr);
385
+ log({
303
386
  level: "warn",
304
- message: `Langfuse trace failed (non-blocking): ${langfuseErr instanceof Error ? langfuseErr.message : String(langfuseErr)}`,
387
+ message: "Langfuse trace failed (non-blocking)",
305
388
  operation: "langfuse_trace",
306
- timestamp: new Date().toISOString(),
307
- }));
389
+ error: {
390
+ message: "MCP operation failed",
391
+ code: "telemetry_error",
392
+ statusCode: facts.statusCode,
393
+ },
394
+ });
308
395
  }
309
396
  })();
310
397
  }
@@ -15,11 +15,31 @@
15
15
  * The full MCP server (157 business tools + MCP extras) remains available for Claude, Cursor,
16
16
  * Windsurf, Cline, Codex, and all other MCP clients.
17
17
  *
18
- * OpenAI-safe mode: 53 reviewed business tools, 0 prompts, 0 resources, 0 government IDs in I/O.
18
+ * OpenAI-safe mode: 33 reviewed business tools, 0 prompts, 0 resources, and no
19
+ * dedicated government-ID fields in I/O.
19
20
  * The full MCP surface remains available outside FRIHET_OPENAI_MODE.
20
21
  *
21
22
  * @see https://developers.openai.com/apps-sdk/app-submission-guidelines
22
23
  */
24
+ /** OAuth requirement advertised on every reviewed business tool. */
25
+ export declare const OPENAI_OAUTH_SECURITY_SCHEMES: readonly [{
26
+ readonly type: "oauth2";
27
+ readonly scopes: readonly ["frihet:workspace.manage"];
28
+ }];
29
+ export declare const OPENAI_WORKSPACE_WEBHOOK_EVENT_TOOLS: ReadonlySet<string>;
30
+ export declare const OPENAI_REVIEW_CONFIRM_REQUIRED_TOOLS: ReadonlySet<string>;
31
+ /** Strictly necessary row fields for reviewed collection/search responses. */
32
+ export declare const OPENAI_REVIEW_LIST_OUTPUT_FIELDS: Readonly<Record<string, readonly string[]>>;
33
+ export declare const OPENAI_REVIEW_LIST_LIMIT_MAX = 50;
34
+ export declare const OPENAI_REVIEW_OFFSET_MAX = 10000;
35
+ export declare const OPENAI_REVIEW_PAGINATION_DEFAULT = 20;
36
+ export declare const OPENAI_REVIEW_DOCUMENT_LINE_ITEM_MAX = 100;
37
+ export declare const OPENAI_REVIEW_BUSINESS_CONTEXT_TOP_CLIENTS_MAX = 5;
38
+ export declare const OPENAI_REVIEW_FREE_TEXT_WARNING = "Do not include passwords, credentials, payment-card data, health data, or official/government identifiers in this free-text field / No incluyas contrasenas, credenciales, datos de tarjetas, datos de salud ni identificadores oficiales en este campo de texto libre";
39
+ /** Dot paths whose persisted or query text needs an explicit model-facing warning. */
40
+ export declare const OPENAI_REVIEW_FREE_TEXT_WARNING_PATHS: Readonly<Record<string, readonly string[]>>;
41
+ export declare const OPENAI_REVIEW_PAGINATION_LIMITS: Readonly<Record<string, number>>;
42
+ export declare const OPENAI_REVIEW_TEXT_INPUT_LIMITS: Readonly<Record<string, Readonly<Record<string, number>>>>;
23
43
  /**
24
44
  * Content-Security-Policy for the OpenAI-safe MCP endpoint.
25
45
  * OpenAI requires CSP specifying the exact domains the app fetches from.
@@ -47,22 +67,14 @@ export declare function applyOpenAIProfile(server: any): void;
47
67
  export declare const OPENAI_EXCLUDED_COUNT: number;
48
68
  /** Number of tools explicitly allowed in OpenAI mode. */
49
69
  export declare const OPENAI_ALLOWED_TOOL_COUNT: number;
70
+ /** The exact 33-tool allow-list reviewed for the OpenAI connector. */
71
+ export declare const OPENAI_REVIEWED_TOOL_ALLOWLIST: ReadonlySet<string>;
50
72
  /**
51
- * The reviewed OpenAI tool allow-list (the 53 business tools), exposed read-only
52
- * so the grouped tool-exposure profile can pin its catalog to EXACTLY this set
53
- * when the two profiles are composed on openai-mcp.frihet.io. The progressive-
54
- * disclosure meta-tools (search_tools / describe_tool / list_tool_groups) must
55
- * never surface a tool outside this allow-list; passing it as the grouped
56
- * `allowlist` enforces that statically. This is the SAME object used to gate
57
- * registerTool, so the two can never drift apart.
58
- *
59
- * NOTE: this is the 53 BUSINESS tools only. It deliberately does NOT contain the
60
- * 3 grouped meta-tools — those are registered directly on the real server
61
- * (bypassing the OpenAI gate) by applyToolExposureProfile when it runs first, so
62
- * OPENAI_ALLOWED_TOOL_COUNT (and every advertised "53 reviewed tools" doc) stays
63
- * correct while the live tools/list still materialises 53 + 3 = 56 tools.
73
+ * Backwards-compatible helper for tests and gates that capture the exact
74
+ * ChatGPT review profile. The reviewed host deliberately uses full tool
75
+ * descriptions and exposes no grouped discovery meta-tools.
64
76
  */
65
- export declare const OPENAI_REVIEWED_TOOL_ALLOWLIST: ReadonlySet<string>;
77
+ export declare function applyOpenAIReviewProfiles(server: any): void;
66
78
  /** Number of resources excluded in OpenAI mode (for logging). */
67
79
  export declare const OPENAI_EXCLUDED_RESOURCE_COUNT: number;
68
80
  //# sourceMappingURL=openai-profile.d.ts.map