@opencx/mcp 1.15.13 → 1.15.15

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=phone-tools.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"phone-tools.spec.d.ts","sourceRoot":"","sources":["../src/phone-tools.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,156 @@
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) {
6
+ return new Response(JSON.stringify(body), { status, statusText: 'OK' });
7
+ }
8
+ function getRequestUrl(call) {
9
+ const arg = call[0];
10
+ return arg instanceof Request ? arg.url : String(arg);
11
+ }
12
+ function getRequestMethod(call) {
13
+ const arg = call[0];
14
+ if (arg instanceof Request)
15
+ return arg.method;
16
+ return call[1].method ?? 'GET';
17
+ }
18
+ async function getRequestBody(call) {
19
+ const arg = call[0];
20
+ if (arg instanceof Request) {
21
+ const text = await arg.clone().text();
22
+ return text ? JSON.parse(text) : undefined;
23
+ }
24
+ const body = call[1].body;
25
+ return body ? JSON.parse(body) : undefined;
26
+ }
27
+ const agentFixture = {
28
+ id: 'agent-1',
29
+ name: 'Agent',
30
+ model: 'oppie-vox-livekit',
31
+ actionIds: [],
32
+ transfer_destination_ids: [],
33
+ };
34
+ describe('phone tools', () => {
35
+ let client;
36
+ let cleanup;
37
+ let fetchSpy;
38
+ beforeEach(async () => {
39
+ fetchSpy = vi.fn();
40
+ vi.stubGlobal('fetch', fetchSpy);
41
+ const server = createServer('test-api-key', 'https://api.test.com');
42
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
43
+ await server.connect(serverTransport);
44
+ client = new Client({ name: 'test-client', version: '1.0.0' });
45
+ await client.connect(clientTransport);
46
+ cleanup = async () => {
47
+ await client.close();
48
+ await server.close();
49
+ };
50
+ });
51
+ afterEach(async () => {
52
+ await cleanup();
53
+ vi.restoreAllMocks();
54
+ });
55
+ it('list_transfer_destinations reads GET /phone/transfer-destinations', async () => {
56
+ fetchSpy.mockResolvedValueOnce(mockResponse([{ id: 'dest-1', name: 'Support', type: 'phone', hasSipPassword: false }]));
57
+ const result = await client.callTool({ name: 'list_transfer_destinations', arguments: {} });
58
+ expect(result.isError).toBeFalsy();
59
+ const call = fetchSpy.mock.calls[0];
60
+ expect(getRequestUrl(call)).toBe('https://api.test.com/phone/transfer-destinations');
61
+ expect(getRequestMethod(call)).toBe('GET');
62
+ expect(JSON.stringify(result.content)).toContain('dest-1');
63
+ });
64
+ it('create_phone_agent forwards actions, destinations and voice tuning fields verbatim', async () => {
65
+ fetchSpy.mockResolvedValueOnce(mockResponse(agentFixture));
66
+ const result = await client.callTool({
67
+ name: 'create_phone_agent',
68
+ arguments: {
69
+ name: 'Agent',
70
+ actionIds: ['act-1'],
71
+ transfer_destination_ids: ['dest-1'],
72
+ failover_transfer_destination_id: 'dest-1',
73
+ tts_speed_pct: 83,
74
+ stt_keyterms: ['OpenCX'],
75
+ recording_enabled: true,
76
+ },
77
+ });
78
+ expect(result.isError).toBeFalsy();
79
+ const call = fetchSpy.mock.calls[0];
80
+ expect(getRequestUrl(call)).toBe('https://api.test.com/phone');
81
+ expect(getRequestMethod(call)).toBe('POST');
82
+ expect(await getRequestBody(call)).toEqual({
83
+ name: 'Agent',
84
+ actionIds: ['act-1'],
85
+ transfer_destination_ids: ['dest-1'],
86
+ failover_transfer_destination_id: 'dest-1',
87
+ tts_speed_pct: 83,
88
+ stt_keyterms: ['OpenCX'],
89
+ recording_enabled: true,
90
+ });
91
+ });
92
+ it('create/update no longer expose the legacy handoff_phone_number / use_org_knowledgebase inputs', async () => {
93
+ const { tools } = await client.listTools();
94
+ for (const name of ['create_phone_agent', 'update_phone_agent']) {
95
+ const tool = tools.find((t) => t.name === name);
96
+ expect(tool, name).toBeDefined();
97
+ const properties = Object.keys(tool?.inputSchema.properties ?? {});
98
+ expect(properties).not.toContain('handoff_phone_number');
99
+ expect(properties).not.toContain('use_org_knowledgebase');
100
+ expect(properties).toEqual(expect.arrayContaining(['actionIds', 'transfer_destination_ids', 'tts_speed_pct']));
101
+ }
102
+ });
103
+ it('update_phone_agent PATCHes only the supplied fields', async () => {
104
+ fetchSpy.mockResolvedValueOnce(mockResponse(agentFixture));
105
+ await client.callTool({
106
+ name: 'update_phone_agent',
107
+ arguments: { phone_agent_id: 'agent-1', transfer_destination_ids: [] },
108
+ });
109
+ const call = fetchSpy.mock.calls[0];
110
+ expect(getRequestUrl(call)).toBe('https://api.test.com/phone/agent-1');
111
+ expect(getRequestMethod(call)).toBe('PATCH');
112
+ expect(await getRequestBody(call)).toEqual({ transfer_destination_ids: [] });
113
+ });
114
+ it('make_outbound_call maps session_custom_data / extra_instructions onto the API body', async () => {
115
+ fetchSpy.mockResolvedValueOnce(mockResponse({ sessionId: 's-1', callId: 'c-1' }));
116
+ const result = await client.callTool({
117
+ name: 'make_outbound_call',
118
+ arguments: {
119
+ phone_agent_id: 'agent-1',
120
+ phone_number: '+15551234567',
121
+ session_custom_data: { npsScore: 3, business: 'Cafe', owner: true },
122
+ extra_instructions: 'Ask about the last order.',
123
+ },
124
+ });
125
+ expect(result.isError).toBeFalsy();
126
+ const call = fetchSpy.mock.calls[0];
127
+ expect(getRequestUrl(call)).toBe('https://api.test.com/phone/outbound-call');
128
+ expect(await getRequestBody(call)).toEqual({
129
+ phoneAgentId: 'agent-1',
130
+ contact: { phoneNumber: '+15551234567' },
131
+ sessionCustomData: { npsScore: 3, business: 'Cafe', owner: true },
132
+ extraInstructions: 'Ask about the last order.',
133
+ });
134
+ });
135
+ it('make_outbound_call omits the optional extras when not provided', async () => {
136
+ fetchSpy.mockResolvedValueOnce(mockResponse({ sessionId: 's-1', callId: 'c-1' }));
137
+ await client.callTool({
138
+ name: 'make_outbound_call',
139
+ arguments: { phone_agent_id: 'agent-1', contact_id: 'contact-1' },
140
+ });
141
+ const call = fetchSpy.mock.calls[0];
142
+ expect(await getRequestBody(call)).toEqual({
143
+ phoneAgentId: 'agent-1',
144
+ contact: { id: 'contact-1' },
145
+ });
146
+ });
147
+ it('make_outbound_call rejects a call with neither contact nor number without hitting the API', async () => {
148
+ const result = await client.callTool({
149
+ name: 'make_outbound_call',
150
+ arguments: { phone_agent_id: 'agent-1' },
151
+ });
152
+ expect(result.isError).toBe(true);
153
+ expect(fetchSpy).not.toHaveBeenCalled();
154
+ });
155
+ });
156
+ //# sourceMappingURL=phone-tools.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"phone-tools.spec.js","sourceRoot":"","sources":["../src/phone-tools.spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=salesforce-ai-reply-templates.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"salesforce-ai-reply-templates.spec.d.ts","sourceRoot":"","sources":["../src/salesforce-ai-reply-templates.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,163 @@
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) {
6
+ return new Response(JSON.stringify(body), { status, statusText: 'OK' });
7
+ }
8
+ function getRequestUrl(call) {
9
+ const arg = call[0];
10
+ return arg instanceof Request ? arg.url : String(arg);
11
+ }
12
+ function getRequestMethod(call) {
13
+ const arg = call[0];
14
+ if (arg instanceof Request)
15
+ return arg.method;
16
+ return call[1].method ?? 'GET';
17
+ }
18
+ async function getRequestBody(call) {
19
+ const arg = call[0];
20
+ if (arg instanceof Request) {
21
+ const text = await arg.clone().text();
22
+ return text ? JSON.parse(text) : undefined;
23
+ }
24
+ const body = call[1].body;
25
+ return body ? JSON.parse(body) : undefined;
26
+ }
27
+ const templateFixture = {
28
+ id: '6f1d2c3b-0000-4000-8000-000000000001',
29
+ name: 'German (Sixt-style)',
30
+ template_html: '<p>Hallo {{contact.first_name}},</p><p>{{content}}</p><p>{{sf_case_Regards__c}}</p>',
31
+ is_default: true,
32
+ rules: null,
33
+ created_at: '2026-08-22T10:00:00.000Z',
34
+ updated_at: '2026-08-22T10:00:00.000Z',
35
+ };
36
+ describe('salesforce AI reply template tools', () => {
37
+ let client;
38
+ let cleanup;
39
+ let fetchSpy;
40
+ beforeEach(async () => {
41
+ fetchSpy = vi.fn();
42
+ vi.stubGlobal('fetch', fetchSpy);
43
+ const server = createServer('test-api-key', 'https://api.test.com');
44
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
45
+ await server.connect(serverTransport);
46
+ client = new Client({ name: 'test-client', version: '1.0.0' });
47
+ await client.connect(clientTransport);
48
+ cleanup = async () => {
49
+ await client.close();
50
+ await server.close();
51
+ };
52
+ });
53
+ afterEach(async () => {
54
+ await cleanup();
55
+ vi.restoreAllMocks();
56
+ });
57
+ it('list_salesforce_ai_reply_templates reads GET /salesforce/ai-reply-templates', async () => {
58
+ fetchSpy.mockResolvedValueOnce(mockResponse({ templates: [templateFixture] }));
59
+ const result = await client.callTool({
60
+ name: 'list_salesforce_ai_reply_templates',
61
+ arguments: {},
62
+ });
63
+ expect(result.isError).toBeFalsy();
64
+ const call = fetchSpy.mock.calls[0];
65
+ expect(getRequestUrl(call)).toBe('https://api.test.com/salesforce/ai-reply-templates');
66
+ expect(getRequestMethod(call)).toBe('GET');
67
+ expect(JSON.stringify(result.content)).toContain('German (Sixt-style)');
68
+ });
69
+ it('create_salesforce_ai_reply_template posts name, template_html, is_default and rules verbatim', async () => {
70
+ fetchSpy.mockResolvedValueOnce(mockResponse(templateFixture, 201));
71
+ const rules = {
72
+ conditions: [{ type: 'attribute', key: 'sf_case_Language__c', op: 'eq', value: 'de' }],
73
+ };
74
+ const result = await client.callTool({
75
+ name: 'create_salesforce_ai_reply_template',
76
+ arguments: {
77
+ name: 'German (Sixt-style)',
78
+ template_html: templateFixture.template_html,
79
+ is_default: true,
80
+ rules,
81
+ },
82
+ });
83
+ expect(result.isError).toBeFalsy();
84
+ const call = fetchSpy.mock.calls[0];
85
+ expect(getRequestUrl(call)).toBe('https://api.test.com/salesforce/ai-reply-templates');
86
+ expect(getRequestMethod(call)).toBe('POST');
87
+ expect(await getRequestBody(call)).toEqual({
88
+ name: 'German (Sixt-style)',
89
+ template_html: templateFixture.template_html,
90
+ is_default: true,
91
+ rules,
92
+ });
93
+ });
94
+ it('create omits optional fields the caller did not pass (the server auto-defaults the first template)', async () => {
95
+ fetchSpy.mockResolvedValueOnce(mockResponse(templateFixture, 201));
96
+ await client.callTool({
97
+ name: 'create_salesforce_ai_reply_template',
98
+ arguments: { name: 'Plain', template_html: '<p>Kind regards</p>' },
99
+ });
100
+ const call = fetchSpy.mock.calls[0];
101
+ expect(await getRequestBody(call)).toEqual({
102
+ name: 'Plain',
103
+ template_html: '<p>Kind regards</p>',
104
+ });
105
+ });
106
+ it('update_salesforce_ai_reply_template PATCHes only the provided fields, null clears rules', async () => {
107
+ fetchSpy.mockResolvedValueOnce(mockResponse({ ...templateFixture, name: 'Renamed' }));
108
+ const result = await client.callTool({
109
+ name: 'update_salesforce_ai_reply_template',
110
+ arguments: { id: templateFixture.id, name: 'Renamed', rules: null },
111
+ });
112
+ expect(result.isError).toBeFalsy();
113
+ const call = fetchSpy.mock.calls[0];
114
+ expect(getRequestUrl(call)).toBe(`https://api.test.com/salesforce/ai-reply-templates/${templateFixture.id}`);
115
+ expect(getRequestMethod(call)).toBe('PATCH');
116
+ expect(await getRequestBody(call)).toEqual({ name: 'Renamed', rules: null });
117
+ });
118
+ it('delete_salesforce_ai_reply_template DELETEs by id', async () => {
119
+ fetchSpy.mockResolvedValueOnce(new Response(null, { status: 204 }));
120
+ const result = await client.callTool({
121
+ name: 'delete_salesforce_ai_reply_template',
122
+ arguments: { id: templateFixture.id },
123
+ });
124
+ expect(result.isError).toBeFalsy();
125
+ const call = fetchSpy.mock.calls[0];
126
+ expect(getRequestUrl(call)).toBe(`https://api.test.com/salesforce/ai-reply-templates/${templateFixture.id}`);
127
+ expect(getRequestMethod(call)).toBe('DELETE');
128
+ expect(JSON.stringify(result.content)).toContain(templateFixture.id);
129
+ });
130
+ it('preview_salesforce_ai_reply_template posts the template and the optional session number', async () => {
131
+ fetchSpy.mockResolvedValueOnce(mockResponse({
132
+ html: '<p>Hallo Alex,</p>',
133
+ text: 'Hallo Alex,',
134
+ session_number: 42,
135
+ missing_variables: ['sf_case_Missing__c'],
136
+ }));
137
+ const result = await client.callTool({
138
+ name: 'preview_salesforce_ai_reply_template',
139
+ arguments: { template_html: templateFixture.template_html, session_number: 42 },
140
+ });
141
+ expect(result.isError).toBeFalsy();
142
+ const call = fetchSpy.mock.calls[0];
143
+ expect(getRequestUrl(call)).toBe('https://api.test.com/salesforce/ai-reply-templates/preview');
144
+ expect(getRequestMethod(call)).toBe('POST');
145
+ expect(await getRequestBody(call)).toEqual({
146
+ template_html: templateFixture.template_html,
147
+ session_number: 42,
148
+ });
149
+ expect(JSON.stringify(result.content)).toContain('sf_case_Missing__c');
150
+ });
151
+ it('surfaces the Salesforce-v2-only 400 from the API as a tool error', async () => {
152
+ fetchSpy.mockResolvedValueOnce(mockResponse({
153
+ message: 'AI reply templates are available only to organizations on the Salesforce v2 (Email-to-Case) integration',
154
+ }, 400));
155
+ const result = await client.callTool({
156
+ name: 'list_salesforce_ai_reply_templates',
157
+ arguments: {},
158
+ });
159
+ expect(result.isError).toBe(true);
160
+ expect(JSON.stringify(result.content)).toContain('Salesforce v2');
161
+ });
162
+ });
163
+ //# sourceMappingURL=salesforce-ai-reply-templates.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"salesforce-ai-reply-templates.spec.js","sourceRoot":"","sources":["../src/salesforce-ai-reply-templates.spec.ts"],"names":[],"mappings":"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"}