@opencx/mcp 1.11.81 → 1.11.83

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.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=filter-sessions-dates.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter-sessions-dates.spec.d.ts","sourceRoot":"","sources":["../src/filter-sessions-dates.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,89 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
3
+ import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js';
4
+ import { z } from 'zod';
5
+ import { createServer } from './server.js';
6
+ // Prod agents kept sending date-only strings ("2026-06-01") to
7
+ // `filter_sessions`, which the backend rejects with 400 "Invalid ISO
8
+ // datetime" on `filters.date.from/to`. The schema now validates full ISO
9
+ // datetimes at the MCP layer so the failure is immediate and names the
10
+ // expected format instead of surfacing as an opaque API 400.
11
+ describe('filter_sessions date validation', () => {
12
+ let client;
13
+ let cleanup;
14
+ let fetchSpy;
15
+ beforeEach(async () => {
16
+ fetchSpy = vi.fn();
17
+ vi.stubGlobal('fetch', fetchSpy);
18
+ const server = createServer('test-api-key', 'https://api.test.com');
19
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
20
+ await server.connect(serverTransport);
21
+ client = new Client({ name: 'test-client', version: '1.0.0' });
22
+ await client.connect(clientTransport);
23
+ cleanup = async () => {
24
+ await client.close();
25
+ await server.close();
26
+ };
27
+ });
28
+ afterEach(async () => {
29
+ await cleanup();
30
+ vi.unstubAllGlobals();
31
+ });
32
+ it('rejects date-only strings before any API call', async () => {
33
+ const result = await client.callTool({
34
+ name: 'filter_sessions',
35
+ arguments: { date_from: '2026-06-01', date_to: '2026-06-08' },
36
+ });
37
+ expect(result.isError).toBe(true);
38
+ const text = JSON.stringify(result.content);
39
+ expect(text).toContain('Invalid datetime');
40
+ expect(text).toContain('date_from');
41
+ expect(fetchSpy).not.toHaveBeenCalled();
42
+ });
43
+ // The backend DTO validates with bare `.datetime()` (UTC `Z` only), so the
44
+ // MCP schema must reject offsets too — `{ offset: true }` here would pass
45
+ // values the backend still 400s on.
46
+ it('rejects timezone-offset datetimes the backend would 400 on', async () => {
47
+ const result = await client.callTool({
48
+ name: 'filter_sessions',
49
+ arguments: { date_from: '2026-06-01T00:00:00+02:00' },
50
+ });
51
+ expect(result.isError).toBe(true);
52
+ expect(JSON.stringify(result.content)).toContain('Invalid datetime');
53
+ expect(fetchSpy).not.toHaveBeenCalled();
54
+ });
55
+ it('still works with no dates at all (most common call)', async () => {
56
+ fetchSpy.mockResolvedValue(new Response(JSON.stringify({ items: [], total: 0 }), { status: 200, statusText: 'OK' }));
57
+ const result = await client.callTool({ name: 'filter_sessions', arguments: {} });
58
+ expect(result.isError).toBeFalsy();
59
+ expect(fetchSpy).toHaveBeenCalledTimes(1);
60
+ const arg = fetchSpy.mock.calls[0]?.[0];
61
+ if (!(arg instanceof Request))
62
+ throw new Error('expected fetch to receive a Request');
63
+ const body = z
64
+ .object({ filters: z.object({ date: z.unknown().optional() }) })
65
+ .parse(JSON.parse(await arg.clone().text()));
66
+ expect(body.filters.date).toBeUndefined();
67
+ });
68
+ it('accepts full ISO 8601 datetimes and forwards them as the date filter', async () => {
69
+ fetchSpy.mockResolvedValue(new Response(JSON.stringify({ items: [], total: 0 }), { status: 200, statusText: 'OK' }));
70
+ const result = await client.callTool({
71
+ name: 'filter_sessions',
72
+ arguments: { date_from: '2026-06-01T00:00:00Z', date_to: '2026-06-08T23:59:59Z' },
73
+ });
74
+ expect(result.isError).toBeFalsy();
75
+ expect(fetchSpy).toHaveBeenCalledTimes(1);
76
+ // openapi-fetch always passes a fully-built Request as the first arg.
77
+ const arg = fetchSpy.mock.calls[0]?.[0];
78
+ if (!(arg instanceof Request))
79
+ throw new Error('expected fetch to receive a Request');
80
+ const body = z
81
+ .object({ filters: z.object({ date: z.object({ from: z.string(), to: z.string() }) }) })
82
+ .parse(JSON.parse(await arg.clone().text()));
83
+ expect(body.filters.date).toEqual({
84
+ from: '2026-06-01T00:00:00Z',
85
+ to: '2026-06-08T23:59:59Z',
86
+ });
87
+ });
88
+ });
89
+ //# sourceMappingURL=filter-sessions-dates.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter-sessions-dates.spec.js","sourceRoot":"","sources":["../src/filter-sessions-dates.spec.ts"],"names":[],"mappings":"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"}
package/dist/schema.d.ts CHANGED
@@ -1240,6 +1240,26 @@ export interface paths {
1240
1240
  patch?: never;
1241
1241
  trace?: never;
1242
1242
  };
1243
+ "/handoff-analytics/events": {
1244
+ parameters: {
1245
+ query?: never;
1246
+ header?: never;
1247
+ path?: never;
1248
+ cookie?: never;
1249
+ };
1250
+ /**
1251
+ * List handoff events
1252
+ * @description Returns a paginated list of individual AI-to-human handoff events, newest first. Each event includes the AI-written summary and explanation of why it handed off, the handoff reasons, sentiment, and the session ID. Filter by reason (e.g. insufficient_tools) and date range to analyze recurring gaps.
1253
+ */
1254
+ get: operations["listHandoffEvents"];
1255
+ put?: never;
1256
+ post?: never;
1257
+ delete?: never;
1258
+ options?: never;
1259
+ head?: never;
1260
+ patch?: never;
1261
+ trace?: never;
1262
+ };
1243
1263
  "/handoff-analytics": {
1244
1264
  parameters: {
1245
1265
  query?: never;
@@ -6965,7 +6985,7 @@ export interface components {
6965
6985
  /** Format: email */
6966
6986
  email: string;
6967
6987
  avatar_url?: string | null;
6968
- permissions?: ("*" | "sessions:read" | "sessions:write" | "sessions-shared-views:read" | "sessions-shared-views:write" | "contacts:read" | "contacts:write" | "sequences:read" | "sequences:write" | "ai-training:read" | "ai-training:write" | "reports:read" | "reports:write" | "help-center:read" | "help-center:write" | "workflows:read" | "workflows:write" | "actions:read" | "actions:write" | "settings:read" | "settings:write" | "settings-channels:read" | "settings-channels:write" | "settings-helpdesk:read" | "settings-helpdesk:write" | "settings-groups:read" | "settings-groups:write" | "settings-roles:read" | "settings-roles:write" | "settings-macros:read" | "settings-macros:write")[] | null;
6988
+ permissions?: ("*" | "sessions:read" | "sessions:write" | "sessions-shared-views:read" | "sessions-shared-views:write" | "contacts:read" | "contacts:write" | "sequences:read" | "sequences:write" | "ai-training:read" | "ai-training:write" | "reports:read" | "reports:write" | "help-center:read" | "help-center:write" | "workflows:read" | "workflows:write" | "actions:read" | "actions:write" | "settings:read" | "settings:write" | "settings-channels:read" | "settings-channels:write" | "settings-helpdesk:read" | "settings-helpdesk:write" | "settings-groups:read" | "settings-groups:write" | "settings-roles:read" | "settings-roles:write" | "settings-macros:read" | "settings-macros:write" | "settings-skills:read" | "settings-skills:write")[] | null;
6969
6989
  };
6970
6990
  OrgUserDetail: {
6971
6991
  id: number;
@@ -6974,7 +6994,7 @@ export interface components {
6974
6994
  /** Format: email */
6975
6995
  email: string;
6976
6996
  avatar_url?: string | null;
6977
- permissions?: ("*" | "sessions:read" | "sessions:write" | "sessions-shared-views:read" | "sessions-shared-views:write" | "contacts:read" | "contacts:write" | "sequences:read" | "sequences:write" | "ai-training:read" | "ai-training:write" | "reports:read" | "reports:write" | "help-center:read" | "help-center:write" | "workflows:read" | "workflows:write" | "actions:read" | "actions:write" | "settings:read" | "settings:write" | "settings-channels:read" | "settings-channels:write" | "settings-helpdesk:read" | "settings-helpdesk:write" | "settings-groups:read" | "settings-groups:write" | "settings-roles:read" | "settings-roles:write" | "settings-macros:read" | "settings-macros:write")[] | null;
6997
+ permissions?: ("*" | "sessions:read" | "sessions:write" | "sessions-shared-views:read" | "sessions-shared-views:write" | "contacts:read" | "contacts:write" | "sequences:read" | "sequences:write" | "ai-training:read" | "ai-training:write" | "reports:read" | "reports:write" | "help-center:read" | "help-center:write" | "workflows:read" | "workflows:write" | "actions:read" | "actions:write" | "settings:read" | "settings:write" | "settings-channels:read" | "settings-channels:write" | "settings-helpdesk:read" | "settings-helpdesk:write" | "settings-groups:read" | "settings-groups:write" | "settings-roles:read" | "settings-roles:write" | "settings-macros:read" | "settings-macros:write" | "settings-skills:read" | "settings-skills:write")[] | null;
6978
6998
  teams: {
6979
6999
  group: components["schemas"]["GroupDto"];
6980
7000
  is_user_available_on_group: boolean;
@@ -7402,6 +7422,18 @@ export interface components {
7402
7422
  records: unknown | null;
7403
7423
  }[];
7404
7424
  ListEmailTemplatesOutput: components["schemas"]["EmailTemplateDto"][];
7425
+ ListHandoffEventsOutput: {
7426
+ items: {
7427
+ session_id: string;
7428
+ summary: string;
7429
+ explanation: string | null;
7430
+ reasons: ("based_on_instructions" | "instructions_not_clear" | "insufficient_knowledge" | "insufficient_tools" | "prohibited_topic" | "user_requested_human_assistance")[];
7431
+ sentiment: ("angry" | "happy" | "neutral") | null;
7432
+ created_at: string;
7433
+ }[];
7434
+ next: string | null;
7435
+ total: number;
7436
+ };
7405
7437
  ListOfficeHoursOutput: {
7406
7438
  data: components["schemas"]["OfficeHoursPublicResponseDto"][];
7407
7439
  };
@@ -10073,6 +10105,44 @@ export interface operations {
10073
10105
  };
10074
10106
  };
10075
10107
  };
10108
+ listHandoffEvents: {
10109
+ parameters: {
10110
+ query?: {
10111
+ /** @description Pagination cursor from a previous response */
10112
+ cursor?: string;
10113
+ /** @description Only return handoffs that include this reason */
10114
+ reason?: "based_on_instructions" | "instructions_not_clear" | "insufficient_knowledge" | "insufficient_tools" | "prohibited_topic" | "user_requested_human_assistance";
10115
+ /** @description Start date in ISO 8601 format (defaults to 30 days ago) */
10116
+ start_date?: string;
10117
+ /** @description End date in ISO 8601 format (defaults to now) */
10118
+ end_date?: string;
10119
+ };
10120
+ header?: never;
10121
+ path?: never;
10122
+ cookie?: never;
10123
+ };
10124
+ requestBody?: never;
10125
+ responses: {
10126
+ /** @description Default Response */
10127
+ 200: {
10128
+ headers: {
10129
+ [name: string]: unknown;
10130
+ };
10131
+ content: {
10132
+ "application/json": components["schemas"]["ListHandoffEventsOutput"];
10133
+ };
10134
+ };
10135
+ /** @description Internal Server Error */
10136
+ 500: {
10137
+ headers: {
10138
+ [name: string]: unknown;
10139
+ };
10140
+ content: {
10141
+ "application/json": components["schemas"]["ErrorDto"];
10142
+ };
10143
+ };
10144
+ };
10145
+ };
10076
10146
  getHandoffAnalytics: {
10077
10147
  parameters: {
10078
10148
  query?: {