@frihet/mcp-server 1.16.6 → 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 +488 -56
  13. package/dist/demo-client.d.ts +16 -13
  14. package/dist/demo-client.js +171 -17
  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 +25 -21
  25. package/dist/openai-profile.js +1166 -156
  26. package/dist/openai-review-contract.d.ts +15 -6
  27. package/dist/openai-review-contract.js +715 -41
  28. package/dist/openai-review-oauth.d.ts +47 -0
  29. package/dist/openai-review-oauth.js +73 -3
  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 +2 -2
  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 +30 -7
package/dist/logger.d.ts CHANGED
@@ -37,10 +37,10 @@ export declare function logToolCall(tool: string, startTime: number, success: bo
37
37
  /**
38
38
  * Log an outbound API call with timing.
39
39
  */
40
- export declare function logApiCall(method: string, path: string, statusCode: number, durationMs: number): void;
40
+ export declare function logApiCall(method: string, _path: string, statusCode: number, durationMs: number): void;
41
41
  /**
42
42
  * Log a rate-limit retry.
43
43
  */
44
- export declare function logRetry(method: string, path: string, retryCount: number, delayMs: number): void;
44
+ export declare function logRetry(method: string, _path: string, retryCount: number, delayMs: number): void;
45
45
  export {};
46
46
  //# sourceMappingURL=logger.d.ts.map
package/dist/logger.js CHANGED
@@ -6,6 +6,107 @@
6
6
  *
7
7
  * Set FRIHET_MCP_DEBUG=1 to enable debug-level logs.
8
8
  */
9
+ const OPERATION_MESSAGES = {
10
+ startup: "MCP startup event",
11
+ session_init: "MCP session event",
12
+ tool_call: "MCP tool call completed",
13
+ tool_error: "MCP tool error",
14
+ api_call: "MCP API call completed",
15
+ api_retry: "MCP API retry scheduled",
16
+ http_request: "MCP HTTP request completed",
17
+ oauth_authorize: "MCP OAuth authorization event",
18
+ oauth_callback: "MCP OAuth callback event",
19
+ shutdown_metrics: "MCP shutdown event",
20
+ langfuse_send: "MCP telemetry delivery event",
21
+ langfuse_trace: "MCP telemetry trace event",
22
+ };
23
+ const TOOL_NAME = /^[a-z][a-z0-9_]{0,63}$/u;
24
+ const PACKAGE_VERSION = /^\d{1,3}\.\d{1,3}\.\d{1,3}$/u;
25
+ const SAFE_METHODS = new Set(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
26
+ const SAFE_TRANSPORTS = new Set(["stdio", "remote"]);
27
+ const SAFE_MCP_VERSIONS = new Set(["mcp/1.0"]);
28
+ const LOCAL_ERROR_CODES = new Set([
29
+ "tool_error",
30
+ "telemetry_http_error",
31
+ "telemetry_error",
32
+ "startup_error",
33
+ "unknown_error",
34
+ ]);
35
+ function toolNameToken(value) {
36
+ return typeof value === "string" && TOOL_NAME.test(value) ? value : undefined;
37
+ }
38
+ function operationalErrorCode(value, statusCode) {
39
+ if (statusCode === 401)
40
+ return "unauthorized";
41
+ if (statusCode === 403)
42
+ return "forbidden";
43
+ if (statusCode === 404)
44
+ return "not_found";
45
+ if (statusCode === 408)
46
+ return "request_timeout";
47
+ if (statusCode === 429)
48
+ return "rate_limited";
49
+ if (statusCode !== undefined && statusCode >= 400 && statusCode < 500)
50
+ return "client_error";
51
+ if (statusCode !== undefined && statusCode >= 500)
52
+ return "backend_error";
53
+ return typeof value === "string" && LOCAL_ERROR_CODES.has(value) ? value : "unknown_error";
54
+ }
55
+ function safeInteger(value, min, max) {
56
+ return typeof value === "number" && Number.isInteger(value) && value >= min && value <= max
57
+ ? value
58
+ : undefined;
59
+ }
60
+ /**
61
+ * Central operational metadata allowlist.
62
+ *
63
+ * Callers may pass arbitrary objects for backwards compatibility, but only
64
+ * bounded protocol/runtime facts can cross the logging boundary. Dynamic
65
+ * paths, identifiers, user agents, emails, provider bodies, and nested objects
66
+ * are intentionally discarded.
67
+ */
68
+ function sanitizeMetadata(metadata) {
69
+ if (!metadata)
70
+ return undefined;
71
+ const safe = {};
72
+ const version = typeof metadata.version === "string" && PACKAGE_VERSION.test(metadata.version)
73
+ ? metadata.version
74
+ : undefined;
75
+ const transport = typeof metadata.transport === "string" && SAFE_TRANSPORTS.has(metadata.transport)
76
+ ? metadata.transport
77
+ : undefined;
78
+ const mcpVersion = typeof metadata.mcpVersion === "string" && SAFE_MCP_VERSIONS.has(metadata.mcpVersion)
79
+ ? metadata.mcpVersion
80
+ : undefined;
81
+ const method = typeof metadata.method === "string" && SAFE_METHODS.has(metadata.method)
82
+ ? metadata.method
83
+ : undefined;
84
+ const statusCode = safeInteger(metadata.statusCode, 0, 599);
85
+ const retryCount = safeInteger(metadata.retryCount, 0, 100);
86
+ const delayMs = safeInteger(metadata.delayMs, 0, 86_400_000);
87
+ const uptime = safeInteger(metadata.uptime, 0, Number.MAX_SAFE_INTEGER);
88
+ if (version)
89
+ safe.version = version;
90
+ if (transport)
91
+ safe.transport = transport;
92
+ if (mcpVersion)
93
+ safe.mcpVersion = mcpVersion;
94
+ if (method)
95
+ safe.method = method;
96
+ if (statusCode !== undefined)
97
+ safe.statusCode = statusCode;
98
+ if (retryCount !== undefined)
99
+ safe.retryCount = retryCount;
100
+ if (delayMs !== undefined)
101
+ safe.delayMs = delayMs;
102
+ if (uptime !== undefined)
103
+ safe.uptime = uptime;
104
+ if (typeof metadata.success === "boolean")
105
+ safe.success = metadata.success;
106
+ if (typeof metadata.stub === "boolean")
107
+ safe.stub = metadata.stub;
108
+ return Object.keys(safe).length > 0 ? safe : undefined;
109
+ }
9
110
  /**
10
111
  * Returns true if debug logging is enabled.
11
112
  * Checks env var in Node.js; always false in Workers unless overridden.
@@ -25,10 +126,41 @@ export function log(entry) {
25
126
  if (entry.level === "debug" && !isDebugEnabled()) {
26
127
  return;
27
128
  }
129
+ const operation = typeof entry.operation === "string" && Object.hasOwn(OPERATION_MESSAGES, entry.operation)
130
+ ? entry.operation
131
+ : undefined;
132
+ const tool = toolNameToken(entry.tool);
133
+ const durationMs = typeof entry.durationMs === "number"
134
+ && Number.isFinite(entry.durationMs)
135
+ && entry.durationMs >= 0
136
+ && entry.durationMs <= 86_400_000
137
+ ? Math.round(entry.durationMs)
138
+ : undefined;
139
+ const errorStatus = safeInteger(entry.error?.statusCode, 0, 599);
140
+ const errorCode = operationalErrorCode(entry.error?.code, errorStatus);
141
+ const metadata = sanitizeMetadata(entry.metadata);
28
142
  const full = {
29
- ...entry,
143
+ level: entry.level,
144
+ message: operation === "tool_call" && entry.level === "error"
145
+ ? "MCP tool call failed"
146
+ : operation
147
+ ? (OPERATION_MESSAGES[operation] ?? "MCP operational event")
148
+ : "MCP operational event",
30
149
  service: "frihet-mcp",
31
150
  timestamp: new Date().toISOString(),
151
+ ...(tool ? { tool } : {}),
152
+ ...(operation ? { operation } : {}),
153
+ ...(durationMs !== undefined ? { durationMs } : {}),
154
+ ...(entry.error
155
+ ? {
156
+ error: {
157
+ message: "MCP operation failed",
158
+ code: errorCode,
159
+ ...(errorStatus !== undefined ? { statusCode: errorStatus } : {}),
160
+ },
161
+ }
162
+ : {}),
163
+ ...(metadata ? { metadata } : {}),
32
164
  };
33
165
  // Remove undefined fields for cleaner output
34
166
  const cleaned = JSON.stringify(full, (_key, value) => value === undefined ? undefined : value);
@@ -47,7 +179,7 @@ export function logToolCall(tool, startTime, success, error) {
47
179
  operation: "tool_call",
48
180
  durationMs,
49
181
  error: {
50
- message: error.message,
182
+ message: "MCP operation failed",
51
183
  code: error.errorCode ?? error.name,
52
184
  statusCode: error.statusCode,
53
185
  },
@@ -66,25 +198,25 @@ export function logToolCall(tool, startTime, success, error) {
66
198
  /**
67
199
  * Log an outbound API call with timing.
68
200
  */
69
- export function logApiCall(method, path, statusCode, durationMs) {
201
+ export function logApiCall(method, _path, statusCode, durationMs) {
70
202
  const level = statusCode >= 500 ? "error" : statusCode >= 400 ? "warn" : "info";
71
203
  log({
72
204
  level,
73
- message: `${method} ${path} ${statusCode} ${durationMs}ms`,
205
+ message: "MCP API call completed",
74
206
  operation: "api_call",
75
207
  durationMs,
76
- metadata: { method, path, statusCode },
208
+ metadata: { method, statusCode },
77
209
  });
78
210
  }
79
211
  /**
80
212
  * Log a rate-limit retry.
81
213
  */
82
- export function logRetry(method, path, retryCount, delayMs) {
214
+ export function logRetry(method, _path, retryCount, delayMs) {
83
215
  log({
84
216
  level: "warn",
85
- message: `Rate limited, retrying ${method} ${path} (attempt ${retryCount + 1}, delay ${delayMs}ms)`,
217
+ message: "MCP API retry scheduled",
86
218
  operation: "api_retry",
87
- metadata: { method, path, retryCount: retryCount + 1, delayMs },
219
+ metadata: { method, retryCount: retryCount + 1, delayMs },
88
220
  });
89
221
  }
90
222
  //# sourceMappingURL=logger.js.map
@@ -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,6 +19,12 @@
18
19
  *
19
20
  * Docs: https://langfuse.com/docs/api/reference/overview
20
21
  */
22
+ /**
23
+ * Langfuse receives a Basic Authorization header, so its authority is exact.
24
+ * The current product contract documents one hosted origin; arbitrary Frihet
25
+ * subdomains and self-hosted overrides are intentionally not trusted here.
26
+ */
27
+ export declare function normalizeLangfuseBaseUrl(value: string): string;
21
28
  /**
22
29
  * Called once from FrihetMCP.init() in the Worker to inject env vars.
23
30
  * Not needed in Node.js stdio mode (reads from process.env directly).
@@ -33,8 +40,6 @@ interface LangfuseSpanBody {
33
40
  name: string;
34
41
  startTime: string;
35
42
  endTime: string;
36
- input?: unknown;
37
- output?: unknown;
38
43
  metadata?: Record<string, unknown>;
39
44
  level?: "DEFAULT" | "DEBUG" | "WARNING" | "ERROR";
40
45
  statusMessage?: string;
@@ -43,11 +48,8 @@ interface LangfuseTraceBody {
43
48
  id: string;
44
49
  name: string;
45
50
  timestamp: string;
46
- input?: unknown;
47
- output?: unknown;
48
51
  metadata?: Record<string, unknown>;
49
52
  tags?: string[];
50
- userId?: string;
51
53
  }
52
54
  interface IngestionBatch {
53
55
  batch: Array<{
@@ -58,8 +60,7 @@ interface IngestionBatch {
58
60
  }>;
59
61
  }
60
62
  interface StubMarker {
61
- plannedEndpoint?: string;
62
- note?: string;
63
+ stub: true;
63
64
  }
64
65
  /**
65
66
  * Inspect a resolved tool output for stub / not-implemented / unavailable
@@ -79,33 +80,24 @@ interface StubMarker {
79
80
  */
80
81
  export declare function inspectStubMarker(output: unknown): StubMarker | null;
81
82
  interface TraceContext {
82
- /** User-Agent from MCP client or other client identifier */
83
- clientName?: string;
84
83
  /** MCP protocol version */
85
84
  mcpVersion?: string;
86
- /** Frihet workspace/user ID — will be hashed */
87
- userId?: string;
88
85
  }
89
86
  /**
90
- * Set session-level context (client identity, MCP version).
87
+ * Set session-level protocol context. Client identity is intentionally absent.
91
88
  * Call once from server init; applies to all subsequent traces.
92
89
  */
93
90
  export declare function setTraceContext(ctx: TraceContext): void;
94
91
  interface TracePayloadParams {
95
92
  toolName: string;
96
- /** Raw tool input args (redacted before serialization). */
97
- input: unknown;
98
- /** Raw tool output (redacted before serialization); ignored when isError. */
99
- output: unknown;
100
93
  isError: boolean;
101
- errorMessage?: string;
94
+ errorClass?: string;
95
+ errorCode?: string;
96
+ statusCode?: number;
102
97
  startTime: Date;
103
98
  endTime: Date;
104
99
  traceId: string;
105
100
  spanId: string;
106
- /** Already-hashed user id, or undefined. */
107
- userIdHashed?: string;
108
- clientName?: string;
109
101
  mcpVersion?: string;
110
102
  /** Fabricated-stub marker, or null on a genuine result. */
111
103
  stub: StubMarker | null;
@@ -113,12 +105,9 @@ interface TracePayloadParams {
113
105
  /**
114
106
  * Builds the Langfuse trace+span ingestion batch for a single tool call.
115
107
  *
116
- * CRITICAL (Trust): `input` and `output` are passed through {@link redactClone}
117
- * before they enter the payload, so government IDs (taxId/NIF/CIF), banking
118
- * identifiers (IBAN), webhook signing secrets, and auth tokens NEVER reach the
119
- * external Langfuse service — in ANY profile mode. The tool-call output handed
120
- * to the user is redacted later (OpenAI mode only); this redacts the trace copy
121
- * unconditionally. Exported so a test can assert no sensitive value survives.
108
+ * CRITICAL (Trust): this builder is an allowlist. It has no input/output or
109
+ * identity fields and ignores any extra runtime properties a caller supplies.
110
+ * Exported so tests can assert the exact operational shape.
122
111
  */
123
112
  export declare function buildTracePayload(p: TracePayloadParams): IngestionBatch;
124
113
  /**
@@ -132,6 +121,6 @@ export declare function buildTracePayload(p: TracePayloadParams): IngestionBatch
132
121
  * @param fn Async tool handler to wrap
133
122
  * @returns Result of fn
134
123
  */
135
- export declare function traceMCPTool<T>(toolName: string, input: unknown, fn: () => Promise<T>): Promise<T>;
124
+ export declare function traceMCPTool<T>(toolName: string, _input: unknown, fn: () => Promise<T>): Promise<T>;
136
125
  export {};
137
126
  //# sourceMappingURL=observability.d.ts.map