@opencx/mcp 1.15.12 → 1.15.13

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=add-session-comment.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add-session-comment.spec.d.ts","sourceRoot":"","sources":["../src/add-session-comment.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,108 @@
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 { createServer } from './server.js';
5
+ function mockResponse(body, status = 200, statusText = 'OK') {
6
+ return new Response(JSON.stringify(body), { status, statusText });
7
+ }
8
+ function getRequestUrl(call) {
9
+ const arg = call[0];
10
+ if (arg instanceof Request)
11
+ return arg.url;
12
+ return String(arg);
13
+ }
14
+ function getRequestMethod(call) {
15
+ const arg = call[0];
16
+ if (arg instanceof Request)
17
+ return arg.method;
18
+ const opts = call[1];
19
+ return opts.method ?? 'GET';
20
+ }
21
+ async function getRequestBody(call) {
22
+ const arg = call[0];
23
+ if (arg instanceof Request) {
24
+ const text = await arg.clone().text();
25
+ return text ? JSON.parse(text) : undefined;
26
+ }
27
+ const opts = call[1];
28
+ return opts.body ? JSON.parse(opts.body) : undefined;
29
+ }
30
+ describe('add_session_comment tool', () => {
31
+ let client;
32
+ let cleanup;
33
+ let fetchSpy;
34
+ beforeEach(async () => {
35
+ fetchSpy = vi.fn();
36
+ vi.stubGlobal('fetch', fetchSpy);
37
+ const server = createServer('test-api-key', 'https://api.test.com');
38
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
39
+ await server.connect(serverTransport);
40
+ client = new Client({ name: 'test-client', version: '1.0.0' });
41
+ await client.connect(clientTransport);
42
+ cleanup = async () => {
43
+ await client.close();
44
+ await server.close();
45
+ };
46
+ });
47
+ afterEach(async () => {
48
+ await cleanup();
49
+ vi.unstubAllGlobals();
50
+ });
51
+ it('sends thread_id when provided (threaded comment)', async () => {
52
+ const created = { uuid: 'comment-uuid', comment_thread_id: 'thread-uuid' };
53
+ fetchSpy.mockResolvedValueOnce(mockResponse(created, 201, 'Created'));
54
+ await client.callTool({
55
+ name: 'add_session_comment',
56
+ arguments: {
57
+ session_id: 'session-1',
58
+ thread_id: '4f9df1e2-95d7-4b83-a9be-7d2f0a2f7f11',
59
+ content: 'threaded note',
60
+ agent_id: 42,
61
+ },
62
+ });
63
+ expect(fetchSpy).toHaveBeenCalledOnce();
64
+ const call = fetchSpy.mock.calls[0];
65
+ expect(getRequestUrl(call)).toBe('https://api.test.com/chat/sessions/session-1/comments');
66
+ expect(getRequestMethod(call)).toBe('POST');
67
+ expect(await getRequestBody(call)).toEqual({
68
+ thread_id: '4f9df1e2-95d7-4b83-a9be-7d2f0a2f7f11',
69
+ content: 'threaded note',
70
+ agent_id: 42,
71
+ });
72
+ });
73
+ it('omits thread_id entirely when not provided (session-level note)', async () => {
74
+ const created = { uuid: 'comment-uuid', comment_thread_id: null };
75
+ fetchSpy.mockResolvedValueOnce(mockResponse(created, 201, 'Created'));
76
+ await client.callTool({
77
+ name: 'add_session_comment',
78
+ arguments: {
79
+ session_id: 'session-1',
80
+ content: 'session-level note',
81
+ agent_id: 42,
82
+ },
83
+ });
84
+ expect(fetchSpy).toHaveBeenCalledOnce();
85
+ const call = fetchSpy.mock.calls[0];
86
+ expect(getRequestUrl(call)).toBe('https://api.test.com/chat/sessions/session-1/comments');
87
+ expect(getRequestMethod(call)).toBe('POST');
88
+ const body = await getRequestBody(call);
89
+ expect(body).toEqual({ content: 'session-level note', agent_id: 42 });
90
+ // The key must be ABSENT, not undefined/null — the public API treats a
91
+ // missing thread_id as "session-level note".
92
+ expect(Object.keys(body)).not.toContain('thread_id');
93
+ });
94
+ it('rejects a non-uuid thread_id at the tool input layer', async () => {
95
+ const result = await client.callTool({
96
+ name: 'add_session_comment',
97
+ arguments: {
98
+ session_id: 'session-1',
99
+ thread_id: 'not-a-uuid',
100
+ content: 'bad thread',
101
+ agent_id: 42,
102
+ },
103
+ });
104
+ expect(result.isError).toBe(true);
105
+ expect(fetchSpy).not.toHaveBeenCalled();
106
+ });
107
+ });
108
+ //# sourceMappingURL=add-session-comment.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add-session-comment.spec.js","sourceRoot":"","sources":["../src/add-session-comment.spec.ts"],"names":[],"mappings":"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"}
package/dist/schema.d.ts CHANGED
@@ -502,7 +502,7 @@ export interface paths {
502
502
  put?: never;
503
503
  /**
504
504
  * Create a session comment
505
- * @description Creates an internal human-agent comment on an existing message thread in the chat session.
505
+ * @description Creates an internal note on the chat session, visible to agents only. Pass thread_id to attach it to a message; omit it for a session-level note.
506
506
  */
507
507
  post: operations["createChatSessionComment"];
508
508
  delete?: never;
@@ -603,7 +603,7 @@ export interface paths {
603
603
  put?: never;
604
604
  /**
605
605
  * Link many contacts to companies
606
- * @description Links up to 100 contacts in one call. Failures are isolated: a contact that cannot be linked returns an error in its own result entry and does not affect the others, so the call succeeds even when some links do not. Results come back in request order.
606
+ * @description Links up to 100 contacts in one call. Name each contact by `contact_id`, `contact_email`, or `contact_phone` — exactly one — so a sync can link straight from the identifiers it already holds. Each result carries the resolved `contact_id`. Failures are isolated: a contact that cannot be linked returns an error in its own result entry and does not affect the others, so the call succeeds even when some links do not. Results come back in request order.
607
607
  */
608
608
  post: operations["bulkLinkContactsToCompanies"];
609
609
  delete?: never;
@@ -5150,6 +5150,34 @@ export interface components {
5150
5150
  }[];
5151
5151
  })[];
5152
5152
  };
5153
+ UpsertCompanyDto: {
5154
+ external_id: string;
5155
+ name?: string;
5156
+ custom_data?: {
5157
+ [key: string]: string | number | boolean | null;
5158
+ };
5159
+ };
5160
+ BulkLinkContactsDto: {
5161
+ links: {
5162
+ /**
5163
+ * Format: uuid
5164
+ * @description The OpenCX contact id, if you already hold it
5165
+ */
5166
+ contact_id?: string;
5167
+ /**
5168
+ * Format: email
5169
+ * @description Look the contact up by email instead of id
5170
+ */
5171
+ contact_email?: string;
5172
+ /** @description Look the contact up by phone instead of id. `+15551234567` and `15551234567` both match */
5173
+ contact_phone?: string;
5174
+ /** @description The company to link, by the same `external_id` you upserted it with */
5175
+ external_id: string;
5176
+ }[];
5177
+ };
5178
+ LinkContactCompanyDto: {
5179
+ external_id: string;
5180
+ };
5153
5181
  MakeOutboundCallDto: {
5154
5182
  phoneAgentId: string;
5155
5183
  contact: {
@@ -5782,9 +5810,9 @@ export interface components {
5782
5810
  CreateSessionCommentInput: {
5783
5811
  /**
5784
5812
  * Format: uuid
5785
- * @description Message UUID to comment on
5813
+ * @description Message UUID to attach the comment to. Omit to create a session-level note (not attached to any message).
5786
5814
  */
5787
- thread_id: string;
5815
+ thread_id?: string;
5788
5816
  /** @description Comment text content */
5789
5817
  content: string;
5790
5818
  /** @description Agent user ID that authors the comment */
@@ -5858,23 +5886,6 @@ export interface components {
5858
5886
  */
5859
5887
  limit: number;
5860
5888
  };
5861
- UpsertCompanyDto: {
5862
- external_id: string;
5863
- name?: string;
5864
- custom_data?: {
5865
- [key: string]: string | number | boolean | null;
5866
- };
5867
- };
5868
- BulkLinkContactsDto: {
5869
- links: {
5870
- /** Format: uuid */
5871
- contact_id: string;
5872
- external_id: string;
5873
- }[];
5874
- };
5875
- LinkContactCompanyDto: {
5876
- external_id: string;
5877
- };
5878
5889
  UpdatePublicKnowledgebaseItem: {
5879
5890
  /** @description Contact segment ids this item should be restricted to. Pass [] to make it available to all contacts. */
5880
5891
  restricted_to_segments?: string[];
@@ -6636,6 +6647,7 @@ export interface components {
6636
6647
  })[];
6637
6648
  })[] | null;
6638
6649
  rejected_reason?: string | null;
6650
+ message_send_ttl_seconds?: number | null;
6639
6651
  };
6640
6652
  PhoneCallEventDto: {
6641
6653
  /** Format: uuid */
@@ -6743,7 +6755,7 @@ export interface components {
6743
6755
  /** @constant */
6744
6756
  type: "response_skipped";
6745
6757
  /** @enum {string} */
6746
- reason?: "empty_response" | "potential_answers_dead_end" | "silent_handoff" | "superseded_by_newer_run" | "flagged_as_spam";
6758
+ reason?: "empty_response" | "potential_answers_dead_end" | "silent_handoff" | "assist_human_handling_recommended" | "superseded_by_newer_run" | "flagged_as_spam";
6747
6759
  reasoning?: ({
6748
6760
  /** @constant */
6749
6761
  type: "reasoning";
@@ -7279,6 +7291,63 @@ export interface components {
7279
7291
  customDataKeys?: string[];
7280
7292
  }[];
7281
7293
  };
7294
+ Company: {
7295
+ /** Format: uuid */
7296
+ id: string;
7297
+ /** @description The organization's own key for this company */
7298
+ external_id: string;
7299
+ name: string;
7300
+ custom_data: {
7301
+ [key: string]: string | number | boolean;
7302
+ } | null;
7303
+ /** @description ISO 8601 timestamp of when the company was created */
7304
+ created_at: string;
7305
+ /** @description ISO 8601 timestamp of when the company was last updated */
7306
+ updated_at: string;
7307
+ };
7308
+ ListCompaniesOutput: {
7309
+ data: components["schemas"]["Company"][];
7310
+ total: number;
7311
+ page: number;
7312
+ limit: number;
7313
+ };
7314
+ GetContactCompaniesOutput: {
7315
+ /** @description Empty when the contact belongs to no company */
7316
+ companies: components["schemas"]["Company"][];
7317
+ };
7318
+ LinkContactCompanyOutput: {
7319
+ /**
7320
+ * @description `unchanged` means the contact was already in this company.
7321
+ * @enum {string}
7322
+ */
7323
+ outcome: "linked" | "unchanged";
7324
+ company: components["schemas"]["Company"];
7325
+ };
7326
+ BulkLinkContactsOutput: {
7327
+ /** @description One entry per requested link, in request order */
7328
+ results: {
7329
+ /** @description The resolved contact id. Null when the identifier did not resolve. */
7330
+ contact_id: string | null;
7331
+ external_id: string;
7332
+ outcome: ("linked" | "unchanged") | null;
7333
+ /** @description Null when this contact linked successfully */
7334
+ error: string | null;
7335
+ company: components["schemas"]["Company"] | null;
7336
+ }[];
7337
+ summary: {
7338
+ total: number;
7339
+ linked: number;
7340
+ unchanged: number;
7341
+ failed: number;
7342
+ };
7343
+ };
7344
+ UnlinkContactCompanyOutput: {
7345
+ /**
7346
+ * @description `unchanged` means the contact was not in that company.
7347
+ * @enum {string}
7348
+ */
7349
+ outcome: "unlinked" | "unchanged";
7350
+ };
7282
7351
  PhoneAgentDto: {
7283
7352
  id: string;
7284
7353
  org_id: string;
@@ -9230,62 +9299,6 @@ export interface components {
9230
9299
  };
9231
9300
  };
9232
9301
  };
9233
- Company: {
9234
- /** Format: uuid */
9235
- id: string;
9236
- /** @description The organization's own key for this company */
9237
- external_id: string;
9238
- name: string;
9239
- custom_data: {
9240
- [key: string]: string | number | boolean;
9241
- } | null;
9242
- /** @description ISO 8601 timestamp of when the company was created */
9243
- created_at: string;
9244
- /** @description ISO 8601 timestamp of when the company was last updated */
9245
- updated_at: string;
9246
- };
9247
- ListCompaniesOutput: {
9248
- data: components["schemas"]["Company"][];
9249
- total: number;
9250
- page: number;
9251
- limit: number;
9252
- };
9253
- GetContactCompaniesOutput: {
9254
- /** @description Empty when the contact belongs to no company */
9255
- companies: components["schemas"]["Company"][];
9256
- };
9257
- LinkContactCompanyOutput: {
9258
- /**
9259
- * @description `unchanged` means the contact was already in this company.
9260
- * @enum {string}
9261
- */
9262
- outcome: "linked" | "unchanged";
9263
- company: components["schemas"]["Company"];
9264
- };
9265
- BulkLinkContactsOutput: {
9266
- /** @description One entry per requested link, in request order */
9267
- results: {
9268
- contact_id: string;
9269
- external_id: string;
9270
- outcome: ("linked" | "unchanged") | null;
9271
- /** @description Null when this contact linked successfully */
9272
- error: string | null;
9273
- company: components["schemas"]["Company"] | null;
9274
- }[];
9275
- summary: {
9276
- total: number;
9277
- linked: number;
9278
- unchanged: number;
9279
- failed: number;
9280
- };
9281
- };
9282
- UnlinkContactCompanyOutput: {
9283
- /**
9284
- * @description `unchanged` means the contact was not in that company.
9285
- * @enum {string}
9286
- */
9287
- outcome: "unlinked" | "unchanged";
9288
- };
9289
9302
  ContactSegment: {
9290
9303
  id: string;
9291
9304
  name: string;