@frihet/mcp-server 2.0.1 → 2.0.2

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.
@@ -26,6 +26,10 @@ export declare const OPENAI_OAUTH_SECURITY_SCHEMES: readonly [{
26
26
  readonly type: "oauth2";
27
27
  readonly scopes: readonly string[];
28
28
  }];
29
+ export declare const OPENAI_103_READ_ONLY_CANDIDATE_TOOLS: ReadonlySet<string>;
30
+ export interface ApplyOpenAIProfileOptions {
31
+ candidateVersion?: "1.0.2" | "1.0.3";
32
+ }
29
33
  export declare const OPENAI_WORKSPACE_WEBHOOK_EVENT_TOOLS: ReadonlySet<string>;
30
34
  export declare const OPENAI_REVIEW_CONFIRM_REQUIRED_TOOLS: ReadonlySet<string>;
31
35
  /** Strictly necessary row fields for reviewed collection/search responses. */
@@ -62,7 +66,7 @@ export declare const OPENAI_CSP: string;
62
66
  * registerAllResources(server, client);
63
67
  * ```
64
68
  */
65
- export declare function applyOpenAIProfile(server: any): void;
69
+ export declare function applyOpenAIProfile(server: any, options?: ApplyOpenAIProfileOptions): void;
66
70
  /** Number of tools excluded in OpenAI mode (for logging). */
67
71
  export declare const OPENAI_EXCLUDED_COUNT: number;
68
72
  /** Number of tools explicitly allowed in OpenAI mode. */
@@ -429,6 +429,12 @@ function installOpenAIToolsListSecurityProjection(server) {
429
429
  });
430
430
  };
431
431
  }
432
+ export const OPENAI_103_READ_ONLY_CANDIDATE_TOOLS = new Set([
433
+ "get_daily_operational_brief",
434
+ "get_business_period_summary",
435
+ "search_clients",
436
+ "search_vendors",
437
+ ]);
432
438
  export const OPENAI_WORKSPACE_WEBHOOK_EVENT_TOOLS = new Set([
433
439
  "create_invoice",
434
440
  "create_expense",
@@ -1167,15 +1173,21 @@ export const OPENAI_CSP = "default-src 'self'; " +
1167
1173
  * ```
1168
1174
  */
1169
1175
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
1170
- export function applyOpenAIProfile(server) {
1176
+ export function applyOpenAIProfile(server, options) {
1171
1177
  installOpenAIToolsListSecurityProjection(server);
1172
1178
  const fieldsToRedact = PROFILE.redactOutputFields;
1179
+ const activeIncludeTools = new Set(PROFILE.includeTools);
1180
+ if (options?.candidateVersion === "1.0.3") {
1181
+ for (const tool of OPENAI_103_READ_ONLY_CANDIDATE_TOOLS) {
1182
+ activeIncludeTools.add(tool);
1183
+ }
1184
+ }
1173
1185
  /* ── Intercept registerTool ─────────────────────────────────────── */
1174
1186
  const originalRegisterTool = server.registerTool.bind(server);
1175
1187
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
1176
1188
  server.registerTool = (name, config, handler) => {
1177
1189
  // 0. Keep the public ChatGPT app to the reviewed core tool surface
1178
- if (!PROFILE.includeTools.has(name))
1190
+ if (!activeIncludeTools.has(name))
1179
1191
  return;
1180
1192
  // 1. Skip excluded tools entirely
1181
1193
  if (PROFILE.excludeTools.has(name))
@@ -657,7 +657,7 @@ export function assertOpenAIReviewContract(actual, expected) {
657
657
  const outputRequired = outputSchema?.required;
658
658
  const success = jsonObject(outputProperties?.success);
659
659
  const result = jsonObject(outputProperties?.result);
660
- const branches = result?.anyOf;
660
+ const branches = result?.oneOf;
661
661
  const branchObjects = Array.isArray(branches)
662
662
  ? branches.map((branch) => jsonObject(branch))
663
663
  : [];
@@ -687,6 +687,7 @@ export function assertOpenAIReviewContract(actual, expected) {
687
687
  outputRequired.length !== 3 ||
688
688
  success?.const !== true ||
689
689
  !result ||
690
+ Object.keys(result).length !== 1 ||
690
691
  branchObjects.length !== 2 ||
691
692
  branchObjects.some((branch) => !branch) ||
692
693
  branchByOutcome.size !== 2 ||
@@ -3,6 +3,8 @@
3
3
  * generated public-surface contract. Transport/auth setup stays with each host;
4
4
  * the exposed MCP operations/resources/prompts are composed here exactly once.
5
5
  */
6
+ import { z } from "zod/v4";
7
+ import { READ_STAMP } from "./demo-fixtures.js";
6
8
  import { applyPublicCapabilityTruth, buildLocalDiscoveryCapability, CAPABILITY_META_KEY, } from "./capability-truth.js";
7
9
  import { applyFiscalAliases } from "./fiscal-aliases.js";
8
10
  import { applyClaudeCandidateProfile, CLAUDE_CANDIDATE_TOOLS, CLAUDE_GROUP_METADATA } from "./claude-profile.js";
@@ -43,6 +45,38 @@ export function registerMcpSurface(server, client, options) {
43
45
  clientIsDemo = false;
44
46
  }
45
47
  const isDemoMode = options.demoMode === true || clientIsDemo;
48
+ if (isDemoMode) {
49
+ // Install closest to the SDK so profile projection/redaction runs first.
50
+ // Only trusted server/client configuration enables this boundary; neither
51
+ // input arguments nor response metadata can switch a live server to demo.
52
+ const registerTool = server.registerTool.bind(server);
53
+ server.registerTool = (name, config, handler) => {
54
+ const schema = config.outputSchema;
55
+ if (!(schema instanceof z.ZodObject)
56
+ || !["data", "total", "limit", "offset"].every((key) => key in schema.shape)) {
57
+ return registerTool(name, config, handler);
58
+ }
59
+ return registerTool(name, {
60
+ ...config,
61
+ outputSchema: schema.extend({
62
+ _demo: z.literal(true),
63
+ _demoNotice: z.literal(READ_STAMP._demoNotice),
64
+ }),
65
+ }, (async (args, extra) => {
66
+ const result = await handler(args, extra);
67
+ if (result.isError || !result.structuredContent)
68
+ return result;
69
+ const structuredContent = { ...result.structuredContent, ...READ_STAMP };
70
+ return {
71
+ ...result,
72
+ structuredContent,
73
+ // JSON mirrors structuredContent for hosts that only read text.
74
+ // Keep existing summaries/hints too; no warning is silently dropped.
75
+ content: [{ type: "text", text: JSON.stringify(structuredContent, null, 2) }, ...result.content],
76
+ };
77
+ }));
78
+ };
79
+ }
46
80
  if (options.groupedMode) {
47
81
  if (options.openaiMode) {
48
82
  applyOpenAIReviewProfiles(server);
@@ -113,6 +113,7 @@ export const FILE_TO_GROUP = {
113
113
  deposits: "invoicing",
114
114
  expenses: "expenses",
115
115
  vendors: "expenses",
116
+ purchaseOrders: "expenses",
116
117
  fiscal: "fiscal",
117
118
  igic: "fiscal",
118
119
  impuesto_sociedades: "fiscal",
@@ -177,7 +178,7 @@ export function groupForTool(name) {
177
178
  return "fiscal";
178
179
  if (/(invoice|quote|credit_note|late_fee|recurring|deposit)/.test(n))
179
180
  return "invoicing";
180
- if (/(expense|vendor)/.test(n))
181
+ if (/(expense|vendor|purchase_order)/.test(n))
181
182
  return "expenses";
182
183
  if (/(bank|transaction)/.test(n))
183
184
  return "banking";
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Purchase-order read tools for the Frihet MCP server.
3
+ *
4
+ * READ-ONLY surface for the /v1/purchase-orders family (ADR 0027 S2, server
5
+ * authority shipped in Frihet-ERP #2359). The single-object read returns the
6
+ * stored document as it lives in the workspace; list returns the same shape
7
+ * paged by status and vendorId, ordered by createdAt desc.
8
+ *
9
+ * Scope semantics: list_purchase_orders and get_purchase_order both classify
10
+ * as `read` under the family router (functions/src/publicApi/apiKeyScopes.ts
11
+ * → requiredPurchaseOrderApiAccess), so a restricted API key needs at least
12
+ * one of `*`, `read`, `purchase-orders:*` or `purchase-orders:read`.
13
+ *
14
+ * WRITE tools (create draft, send, confirm, cancel, receive, reverse-receipt,
15
+ * bill) are deliberately NOT exposed here: they move money or stock and need
16
+ * human-authority semantics (draft → show → confirm) that this surface does not
17
+ * define. No write route is called from this file or from the client methods.
18
+ *
19
+ * NOT IN SCOPE for this surface (correction to a prior brief):
20
+ * - Physical count truth (#2355): no callable, no API route. Not designed.
21
+ * - Inventory balances (/v1/inventory/balances): ADR 0027 S4, not landed.
22
+ */
23
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
24
+ import type { IFrihetClient } from "../client-interface.js";
25
+ /**
26
+ * Read tools for purchase orders. Tool names follow the canonical
27
+ * snake_case convention used across the catalog.
28
+ */
29
+ export declare function registerPurchaseOrderTools(server: McpServer, client: IFrihetClient): void;
30
+ //# sourceMappingURL=purchaseOrders.d.ts.map
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Purchase-order read tools for the Frihet MCP server.
3
+ *
4
+ * READ-ONLY surface for the /v1/purchase-orders family (ADR 0027 S2, server
5
+ * authority shipped in Frihet-ERP #2359). The single-object read returns the
6
+ * stored document as it lives in the workspace; list returns the same shape
7
+ * paged by status and vendorId, ordered by createdAt desc.
8
+ *
9
+ * Scope semantics: list_purchase_orders and get_purchase_order both classify
10
+ * as `read` under the family router (functions/src/publicApi/apiKeyScopes.ts
11
+ * → requiredPurchaseOrderApiAccess), so a restricted API key needs at least
12
+ * one of `*`, `read`, `purchase-orders:*` or `purchase-orders:read`.
13
+ *
14
+ * WRITE tools (create draft, send, confirm, cancel, receive, reverse-receipt,
15
+ * bill) are deliberately NOT exposed here: they move money or stock and need
16
+ * human-authority semantics (draft → show → confirm) that this surface does not
17
+ * define. No write route is called from this file or from the client methods.
18
+ *
19
+ * NOT IN SCOPE for this surface (correction to a prior brief):
20
+ * - Physical count truth (#2355): no callable, no API route. Not designed.
21
+ * - Inventory balances (/v1/inventory/balances): ADR 0027 S4, not landed.
22
+ */
23
+ import { z } from "zod/v4";
24
+ import { withToolLogging, formatPaginatedResponse, formatRecord, listContent, getContent, READ_ONLY_ANNOTATIONS, paginatedOutput, purchaseOrderItemOutput, } from "./shared.js";
25
+ /**
26
+ * Read tools for purchase orders. Tool names follow the canonical
27
+ * snake_case convention used across the catalog.
28
+ */
29
+ export function registerPurchaseOrderTools(server, client) {
30
+ // -- list_purchase_orders --
31
+ server.registerTool("list_purchase_orders", {
32
+ title: "List Purchase Orders",
33
+ description: "List purchase orders in the workspace with optional filters. " +
34
+ "Returns one entry per PO with vendor, status, line items and totals. " +
35
+ "Statuses reflect the server-owned lifecycle (ADR 0027 S2): " +
36
+ "draft → sent → confirmed → received → billed, with cancel as a terminal branch. " +
37
+ "/ Lista pedidos de compra del workspace con filtros opcionales. " +
38
+ "Devuelve un registro por pedido con proveedor, estado, lineas y totales. " +
39
+ "Los estados reflejan el ciclo de vida del servidor (ADR 0027 S2): " +
40
+ "borrador → enviado → confirmado → recibido → facturado, con cancelado como rama terminal.",
41
+ annotations: READ_ONLY_ANNOTATIONS,
42
+ inputSchema: {
43
+ status: z
44
+ .string()
45
+ .optional()
46
+ .describe("Filter by PO status (draft, sent, confirmed, received, billed, cancelled) / " +
47
+ "Filtro por estado del pedido"),
48
+ vendorId: z
49
+ .string()
50
+ .trim()
51
+ .min(1)
52
+ .max(128)
53
+ .optional()
54
+ .describe("Filter by vendor ID / Filtro por ID de proveedor"),
55
+ limit: z
56
+ .number()
57
+ .int()
58
+ .min(1)
59
+ .max(100)
60
+ .optional()
61
+ .describe("Max results (1-100) / Resultados maximos"),
62
+ offset: z
63
+ .number()
64
+ .int()
65
+ .min(0)
66
+ .optional()
67
+ .describe("Offset / Desplazamiento"),
68
+ },
69
+ outputSchema: paginatedOutput(purchaseOrderItemOutput),
70
+ }, async ({ status, vendorId, limit, offset }) => withToolLogging("list_purchase_orders", async () => {
71
+ const result = await client.listPurchaseOrders({ status, vendorId, limit, offset });
72
+ return {
73
+ content: [listContent(formatPaginatedResponse("purchase_orders", result))],
74
+ structuredContent: result,
75
+ };
76
+ }));
77
+ // -- get_purchase_order --
78
+ server.registerTool("get_purchase_order", {
79
+ title: "Get Purchase Order",
80
+ description: "Get a single purchase order by its ID. Returns the full document: " +
81
+ "vendor, line items, totals, status, issue/expected dates and currency. " +
82
+ "/ Obtiene un pedido de compra por su ID. Devuelve el documento completo: " +
83
+ "proveedor, lineas, totales, estado, fechas de emision y esperada y moneda.",
84
+ annotations: READ_ONLY_ANNOTATIONS,
85
+ inputSchema: {
86
+ id: z
87
+ .string()
88
+ .trim()
89
+ .min(1)
90
+ .max(128)
91
+ .describe("Purchase-order ID / ID del pedido de compra"),
92
+ },
93
+ outputSchema: purchaseOrderItemOutput,
94
+ }, async ({ id }) => withToolLogging("get_purchase_order", async () => {
95
+ const result = await client.getPurchaseOrder(id);
96
+ return {
97
+ content: [getContent(formatRecord("PurchaseOrder", result))],
98
+ structuredContent: result,
99
+ };
100
+ }));
101
+ }
102
+ //# sourceMappingURL=purchaseOrders.js.map
@@ -15,6 +15,7 @@ import { registerClientTools } from "./clients.js";
15
15
  import { registerProductTools } from "./products.js";
16
16
  import { registerQuoteTools } from "./quotes.js";
17
17
  import { registerVendorTools } from "./vendors.js";
18
+ import { registerPurchaseOrderTools } from "./purchaseOrders.js";
18
19
  import { registerWebhookTools } from "./webhooks.js";
19
20
  import { registerIntelligenceTools } from "./intelligence.js";
20
21
  import { registerCrmTools } from "./crm.js";
@@ -89,6 +90,7 @@ export function registerAllTools(server, client) {
89
90
  registerProductTools(server, client);
90
91
  registerQuoteTools(server, client);
91
92
  registerVendorTools(server, client);
93
+ registerPurchaseOrderTools(server, client);
92
94
  registerWebhookTools(server, client);
93
95
  registerDepositTools(server, client);
94
96
  registerEInvoiceTools(server, client);
@@ -247,6 +247,48 @@ export declare const vendorItemOutput: z.ZodObject<{
247
247
  createdAt: z.ZodOptional<z.ZodString>;
248
248
  updatedAt: z.ZodOptional<z.ZodString>;
249
249
  }, z.core.$loose>;
250
+ /**
251
+ * Purchase-order output (ADR 0027 S2, server-owned lifecycle).
252
+ *
253
+ * Fields mirror the live API shape returned by /v1/purchase-orders GETs:
254
+ * - `status` ∈ {draft, sent, confirmed, received, billed, cancelled}
255
+ * - `items[].taxRate` is optional because per-line tax rates may override
256
+ * the document tax rate; the family router accepts it as a string-coerced
257
+ * number in the request, so the response can carry it.
258
+ * - `currency` is an ISO 4217 code (e.g. "EUR"). It is the same code the
259
+ * server stamps on the stored doc.
260
+ * - `createdAt`/`updatedAt` are ISO 8601; `issueDate`/`expectedDate` are
261
+ * YYYY-MM-DD.
262
+ *
263
+ * `.passthrough()` preserves receipts (`receipts[]`) and the linked expense
264
+ * id (`billedExpenseId`) once the order moves past `billed`, plus any
265
+ * additional fields the server adds in later S-stages without breaking
266
+ * downstream consumers.
267
+ */
268
+ export declare const purchaseOrderItemOutput: z.ZodObject<{
269
+ id: z.ZodString;
270
+ poNumber: z.ZodOptional<z.ZodString>;
271
+ vendorId: z.ZodOptional<z.ZodString>;
272
+ vendorName: z.ZodOptional<z.ZodString>;
273
+ status: z.ZodOptional<z.ZodString>;
274
+ items: z.ZodOptional<z.ZodArray<z.ZodObject<{
275
+ description: z.ZodString;
276
+ quantity: z.ZodNumber;
277
+ unitPrice: z.ZodNumber;
278
+ productId: z.ZodOptional<z.ZodString>;
279
+ taxRate: z.ZodOptional<z.ZodNumber>;
280
+ }, z.core.$loose>>>;
281
+ issueDate: z.ZodOptional<z.ZodString>;
282
+ expectedDate: z.ZodOptional<z.ZodString>;
283
+ taxRate: z.ZodOptional<z.ZodNumber>;
284
+ subtotal: z.ZodOptional<z.ZodNumber>;
285
+ tax: z.ZodOptional<z.ZodNumber>;
286
+ total: z.ZodOptional<z.ZodNumber>;
287
+ currency: z.ZodOptional<z.ZodString>;
288
+ notes: z.ZodOptional<z.ZodString>;
289
+ createdAt: z.ZodOptional<z.ZodString>;
290
+ updatedAt: z.ZodOptional<z.ZodString>;
291
+ }, z.core.$loose>;
250
292
  export declare const webhookItemOutput: z.ZodObject<{
251
293
  id: z.ZodString;
252
294
  userId: z.ZodOptional<z.ZodString>;
@@ -555,6 +555,52 @@ export const vendorItemOutput = z.object({
555
555
  createdAt: z.string().optional(),
556
556
  updatedAt: z.string().optional(),
557
557
  }).passthrough();
558
+ /**
559
+ * Purchase-order output (ADR 0027 S2, server-owned lifecycle).
560
+ *
561
+ * Fields mirror the live API shape returned by /v1/purchase-orders GETs:
562
+ * - `status` ∈ {draft, sent, confirmed, received, billed, cancelled}
563
+ * - `items[].taxRate` is optional because per-line tax rates may override
564
+ * the document tax rate; the family router accepts it as a string-coerced
565
+ * number in the request, so the response can carry it.
566
+ * - `currency` is an ISO 4217 code (e.g. "EUR"). It is the same code the
567
+ * server stamps on the stored doc.
568
+ * - `createdAt`/`updatedAt` are ISO 8601; `issueDate`/`expectedDate` are
569
+ * YYYY-MM-DD.
570
+ *
571
+ * `.passthrough()` preserves receipts (`receipts[]`) and the linked expense
572
+ * id (`billedExpenseId`) once the order moves past `billed`, plus any
573
+ * additional fields the server adds in later S-stages without breaking
574
+ * downstream consumers.
575
+ */
576
+ export const purchaseOrderItemOutput = z.object({
577
+ id: z.string(),
578
+ poNumber: z.string().optional(),
579
+ vendorId: z.string().optional(),
580
+ vendorName: z.string().optional(),
581
+ status: z.string().optional(),
582
+ items: z
583
+ .array(z
584
+ .object({
585
+ description: z.string(),
586
+ quantity: z.number(),
587
+ unitPrice: z.number(),
588
+ productId: z.string().optional(),
589
+ taxRate: z.number().optional(),
590
+ })
591
+ .passthrough())
592
+ .optional(),
593
+ issueDate: z.string().optional(),
594
+ expectedDate: z.string().optional(),
595
+ taxRate: z.number().optional(),
596
+ subtotal: z.number().optional(),
597
+ tax: z.number().optional(),
598
+ total: z.number().optional(),
599
+ currency: z.string().optional(),
600
+ notes: z.string().optional(),
601
+ createdAt: z.string().optional(),
602
+ updatedAt: z.string().optional(),
603
+ }).passthrough();
558
604
  export const webhookItemOutput = z.object({
559
605
  id: z.string(),
560
606
  userId: z.string().optional(),
@@ -68,8 +68,8 @@
68
68
  "FRIHET_TOOL_MODE": "grouped"
69
69
  },
70
70
  "counts": {
71
- "toolNames": 161,
72
- "readOnly": 81,
71
+ "toolNames": 163,
72
+ "readOnly": 83,
73
73
  "writesFrihet": 80,
74
74
  "externalInteraction": 37,
75
75
  "destructive": 60,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@frihet/mcp-server",
3
- "version": "2.0.1",
4
- "description": "AI-native MCP server for Frihet ERP — 156 canonical operations for invoicing, expenses, CRM, banking, POS and ES/EU fiscal workflows, with conservative capability and side-effect metadata.",
3
+ "version": "2.0.2",
4
+ "description": "AI-native MCP server for Frihet ERP — 158 canonical operations for invoicing, expenses, CRM, banking, POS and ES/EU fiscal workflows, with conservative capability and side-effect metadata.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "bin": {
@@ -9,7 +9,7 @@
9
9
  },
10
10
  "scripts": {
11
11
  "build": "tsc",
12
- "test": "npm run build && node --test dist/__tests__/resources-eu-catalog.test.js && node --test dist/__tests__/resources-p1-contract.test.js dist/__tests__/openai-profile.test.js dist/__tests__/openai-client-tax-id-contract.test.js dist/__tests__/tool-exposure.test.js dist/__tests__/fiscal-aliases.test.js dist/__tests__/einvoice-tools.test.js dist/__tests__/einvoice-day4-tools.test.js dist/__tests__/expenses-tools.test.js dist/__tests__/stay-tools.test.js dist/__tests__/pos-tools.test.js dist/__tests__/kitchen-tools.test.js dist/__tests__/banking-tools.test.js dist/__tests__/banking-client-contract.test.js dist/__tests__/pagination-cursor-param.test.js dist/__tests__/fiscal-tools.test.js dist/__tests__/fiscal-period-contract.test.js dist/__tests__/time-tools.test.js dist/__tests__/deposits-tools.test.js dist/__tests__/recurring-tools.test.js dist/__tests__/team-tools.test.js dist/__tests__/team-tools-real-sdk.test.js dist/__tests__/d4b-hr-payroll-onboarding-tools.test.js dist/__tests__/hr-payroll-period-contract.test.js dist/__tests__/webhook-crm-reviewed-contract.test.js dist/__tests__/audit-server-version.test.js dist/__tests__/search-tools.test.js dist/__tests__/openai-full-compose.test.js dist/__tests__/openai-review-descriptor.test.js dist/__tests__/directory-profile-policy.test.js dist/__tests__/claude-profile.test.js dist/__tests__/claude-output-privacy.test.js dist/__tests__/claude-write-route-contract.test.js dist/__tests__/contract.test.js dist/__tests__/observability-redaction.test.js dist/__tests__/security-containment-gate0.test.js dist/__tests__/intelligence-duplicate-invoice.test.js dist/__tests__/get-envelope-unwrap-regression.test.js dist/__tests__/binary-content-types.test.js dist/__tests__/api-error-remediation.test.js dist/__tests__/mutation-unwrap-and-schema-regression.test.js dist/__tests__/schema-envelope-guard.test.js dist/__tests__/paginated-strict-output.test.js dist/__tests__/paginated-metadata-contract.test.js dist/__tests__/demo-mode.test.js dist/__tests__/idempotency-key-contract.test.js dist/__tests__/retry-after-contract.test.js dist/__tests__/source-header-contract.test.js dist/__tests__/no-provisioning-credential-to-public-api.test.js dist/__tests__/truth-in-descriptions.test.js dist/__tests__/mcp-baseline-mutation.test.js dist/__tests__/forms-elicitation.test.js dist/__tests__/agent-onboarding.test.js scripts/__tests__/openai-worker-review-wire.test.mjs scripts/__tests__/sync-openapi-retry.test.mjs scripts/__tests__/conformance-provenance.test.mjs scripts/__tests__/conformance-phase0.test.mjs scripts/__tests__/published-artifact-drift.test.mjs scripts/__tests__/cross-surface-authority.test.mjs scripts/__tests__/release-workflow-contract.test.mjs scripts/__tests__/openai-wrangler-resolution.test.mjs scripts/__tests__/audit-mcp-refs-detector.test.mjs scripts/__tests__/openai-public-surface.test.mjs scripts/__tests__/claude-plugin-package.test.mjs scripts/__tests__/claude-hosted-release.test.mjs scripts/__tests__/claude-hosted-acceptance.test.mjs scripts/__tests__/claude-oauth-provider.test.mjs scripts/__tests__/audit-mcp-refs-published-sot.test.mjs scripts/__tests__/claude-bootstrap-secrets.test.mjs scripts/__tests__/no-public-leak.test.mjs",
12
+ "test": "npm run build && node --test dist/__tests__/resources-eu-catalog.test.js && node --test dist/__tests__/resources-p1-contract.test.js dist/__tests__/openai-profile.test.js dist/__tests__/openai-client-tax-id-contract.test.js dist/__tests__/tool-exposure.test.js dist/__tests__/fiscal-aliases.test.js dist/__tests__/einvoice-tools.test.js dist/__tests__/einvoice-day4-tools.test.js dist/__tests__/expenses-tools.test.js dist/__tests__/stay-tools.test.js dist/__tests__/pos-tools.test.js dist/__tests__/kitchen-tools.test.js dist/__tests__/banking-tools.test.js dist/__tests__/banking-client-contract.test.js dist/__tests__/pagination-cursor-param.test.js dist/__tests__/fiscal-tools.test.js dist/__tests__/fiscal-period-contract.test.js dist/__tests__/time-tools.test.js dist/__tests__/deposits-tools.test.js dist/__tests__/recurring-tools.test.js dist/__tests__/team-tools.test.js dist/__tests__/team-tools-real-sdk.test.js dist/__tests__/d4b-hr-payroll-onboarding-tools.test.js dist/__tests__/hr-payroll-period-contract.test.js dist/__tests__/webhook-crm-reviewed-contract.test.js dist/__tests__/audit-server-version.test.js dist/__tests__/search-tools.test.js dist/__tests__/openai-full-compose.test.js dist/__tests__/openai-review-descriptor.test.js dist/__tests__/directory-profile-policy.test.js dist/__tests__/claude-profile.test.js dist/__tests__/claude-output-privacy.test.js dist/__tests__/claude-write-route-contract.test.js dist/__tests__/contract.test.js dist/__tests__/observability-redaction.test.js dist/__tests__/security-containment-gate0.test.js dist/__tests__/intelligence-duplicate-invoice.test.js dist/__tests__/get-envelope-unwrap-regression.test.js dist/__tests__/binary-content-types.test.js dist/__tests__/api-error-remediation.test.js dist/__tests__/mutation-unwrap-and-schema-regression.test.js dist/__tests__/schema-envelope-guard.test.js dist/__tests__/paginated-strict-output.test.js dist/__tests__/paginated-metadata-contract.test.js dist/__tests__/purchase-orders-tools.test.js dist/__tests__/purchase-orders-client-contract.test.js dist/__tests__/demo-mode.test.js dist/__tests__/idempotency-key-contract.test.js dist/__tests__/retry-after-contract.test.js dist/__tests__/source-header-contract.test.js dist/__tests__/no-provisioning-credential-to-public-api.test.js dist/__tests__/truth-in-descriptions.test.js dist/__tests__/mcp-baseline-mutation.test.js dist/__tests__/forms-elicitation.test.js dist/__tests__/agent-onboarding.test.js scripts/__tests__/openai-worker-review-wire.test.mjs scripts/__tests__/sync-openapi-retry.test.mjs scripts/__tests__/conformance-provenance.test.mjs scripts/__tests__/conformance-phase0.test.mjs scripts/__tests__/published-artifact-drift.test.mjs scripts/__tests__/cross-surface-authority.test.mjs scripts/__tests__/release-workflow-contract.test.mjs scripts/__tests__/openai-wrangler-resolution.test.mjs scripts/__tests__/audit-mcp-refs-detector.test.mjs scripts/__tests__/openai-public-surface.test.mjs scripts/__tests__/claude-plugin-package.test.mjs scripts/__tests__/claude-hosted-release.test.mjs scripts/__tests__/claude-hosted-acceptance.test.mjs scripts/__tests__/claude-oauth-provider.test.mjs scripts/__tests__/audit-mcp-refs-published-sot.test.mjs scripts/__tests__/claude-bootstrap-secrets.test.mjs scripts/__tests__/no-public-leak.test.mjs",
13
13
  "canary:mcp": "npm run build && node scripts/canary-mcp.mjs",
14
14
  "canary:mcp:check": "npm run build && node scripts/canary-mcp.mjs --check",
15
15
  "canary:mcp:snapshot": "npm run build && node scripts/canary-mcp.mjs --snapshot",
@@ -121,17 +121,17 @@
121
121
  "LICENSE"
122
122
  ],
123
123
  "dependencies": {
124
- "@modelcontextprotocol/sdk": "1.31.0"
124
+ "@modelcontextprotocol/sdk": "1.32.2"
125
125
  },
126
126
  "overrides": {
127
- "hono": ">=4.13.13",
128
- "ip-address": ">=10.7.3",
127
+ "hono": ">=4.13.16 <5",
128
+ "ip-address": ">=10.7.6 <11",
129
129
  "proxy-addr": ">=2.0.8",
130
- "qs": ">=6.15.4",
130
+ "qs": ">=6.17.1 <7",
131
131
  "@ai-sdk/provider-utils": ">=4.0.33 <5"
132
132
  },
133
133
  "peerDependencies": {
134
- "zod": ">=3.25.1"
134
+ "zod": "^4.6.6"
135
135
  },
136
136
  "peerDependenciesMeta": {
137
137
  "zod": {
@@ -142,6 +142,6 @@
142
142
  "@types/node": "^22.0.0",
143
143
  "typescript": "^5.7.0",
144
144
  "ajv": "8.18.0",
145
- "zod": "^3.25.1"
145
+ "zod": "^4.6.7"
146
146
  }
147
147
  }