Skip to content

Commit e755e9b

Browse files
Vapi Taskerclaude
andcommitted
fix: expose phone number provider field and add outbound call guidance
Customers using create_call with a Vapi-provisioned phone number get a cryptic transport error because Vapi numbers are inbound-only. This change surfaces the provider field so MCP clients can identify which numbers support outbound dialing, and updates tool descriptions to clearly communicate the Twilio/Vonage requirement. Changes: - Add provider field to PhoneNumberOutputSchema and transformer - Update create_call tool description with outbound requirements - Update CallInputSchema.phoneNumberId with provider guidance - Add 6 unit tests covering all three fixes Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent c5daf98 commit e755e9b

5 files changed

Lines changed: 102 additions & 4 deletions

File tree

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import {
2+
transformPhoneNumberOutput,
3+
transformCallInput,
4+
} from '../transformers/index.js';
5+
import { CallInputSchema, PhoneNumberOutputSchema } from '../schemas/index.js';
6+
7+
describe('Outbound call validation and phone number provider exposure', () => {
8+
describe('PhoneNumberOutputSchema includes provider field', () => {
9+
test('schema should have a provider field', () => {
10+
const shape = PhoneNumberOutputSchema.shape;
11+
expect(shape).toHaveProperty('provider');
12+
});
13+
});
14+
15+
describe('transformPhoneNumberOutput exposes provider', () => {
16+
test('should include provider field for a vapi phone number', () => {
17+
const vapiPhoneNumber = {
18+
id: 'pn-123',
19+
name: 'My Vapi Number',
20+
createdAt: '2025-01-01T00:00:00Z',
21+
updatedAt: '2025-01-01T00:00:00Z',
22+
number: '+15551234567',
23+
status: 'active',
24+
provider: 'vapi',
25+
};
26+
27+
const result = transformPhoneNumberOutput(vapiPhoneNumber);
28+
expect(result).toHaveProperty('provider');
29+
expect(result.provider).toBe('vapi');
30+
});
31+
32+
test('should include provider field for a twilio phone number', () => {
33+
const twilioPhoneNumber = {
34+
id: 'pn-456',
35+
name: 'My Twilio Number',
36+
createdAt: '2025-01-01T00:00:00Z',
37+
updatedAt: '2025-01-01T00:00:00Z',
38+
number: '+15559876543',
39+
status: 'active',
40+
provider: 'twilio',
41+
};
42+
43+
const result = transformPhoneNumberOutput(twilioPhoneNumber);
44+
expect(result).toHaveProperty('provider');
45+
expect(result.provider).toBe('twilio');
46+
});
47+
48+
test('should include provider field for a vonage phone number', () => {
49+
const vonagePhoneNumber = {
50+
id: 'pn-789',
51+
name: 'My Vonage Number',
52+
createdAt: '2025-01-01T00:00:00Z',
53+
updatedAt: '2025-01-01T00:00:00Z',
54+
number: '+15555555555',
55+
status: 'active',
56+
provider: 'vonage',
57+
};
58+
59+
const result = transformPhoneNumberOutput(vonagePhoneNumber);
60+
expect(result).toHaveProperty('provider');
61+
expect(result.provider).toBe('vonage');
62+
});
63+
64+
test('should default provider to "unknown" when not present on source', () => {
65+
const phoneNumberNoProvider = {
66+
id: 'pn-000',
67+
name: 'Legacy Number',
68+
createdAt: '2025-01-01T00:00:00Z',
69+
updatedAt: '2025-01-01T00:00:00Z',
70+
number: '+15550000000',
71+
status: 'active',
72+
};
73+
74+
const result = transformPhoneNumberOutput(phoneNumberNoProvider);
75+
expect(result).toHaveProperty('provider');
76+
expect(result.provider).toBe('unknown');
77+
});
78+
});
79+
80+
describe('CallInputSchema.phoneNumberId has outbound guidance in description', () => {
81+
test('phoneNumberId description should mention Twilio or Vonage for outbound', () => {
82+
const phoneNumberIdField = CallInputSchema.shape.phoneNumberId;
83+
const description = phoneNumberIdField.description;
84+
expect(description).toBeDefined();
85+
expect(description!.toLowerCase()).toContain('twilio');
86+
expect(description!.toLowerCase()).toContain('vonage');
87+
expect(description!.toLowerCase()).toContain('outbound');
88+
});
89+
});
90+
});

src/schemas/index.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,9 @@ export const CallInputSchema = z.object({
261261
phoneNumberId: z
262262
.string()
263263
.optional()
264-
.describe('ID of the phone number to use for the call'),
264+
.describe(
265+
'ID of the phone number to use for the call. For outbound calls, this must be a Twilio or Vonage imported number. Vapi-provisioned numbers are inbound-only and cannot dial outbound. Use list_phone_numbers to check the provider field.'
266+
),
265267
customer: z
266268
.object({
267269
number: z.string().describe('Customer phone number'),
@@ -314,6 +316,11 @@ export const PhoneNumberOutputSchema = BaseResponseSchema.extend({
314316
name: z.string().optional(),
315317
phoneNumber: z.string(),
316318
status: z.string(),
319+
provider: z
320+
.string()
321+
.describe(
322+
'Phone number provider (e.g. "vapi", "twilio", "vonage"). Vapi numbers are inbound-only. Twilio and Vonage numbers support outbound dialing.'
323+
),
317324
capabilities: z
318325
.object({
319326
sms: z.boolean().optional(),

src/tools/call.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export const registerCallTools = (
2424

2525
server.tool(
2626
'create_call',
27-
'Creates a outbound call',
27+
'Creates an outbound call. Important: outbound calls require a Twilio or Vonage imported phone number. Vapi-provisioned numbers are inbound-only and will fail with a transport error if used for outbound dialing.',
2828
CallInputSchema.shape,
2929
createToolHandler(async (data) => {
3030
const createCallDto = transformCallInput(data);

src/transformers/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@ export function transformPhoneNumberOutput(
240240
updatedAt: phoneNumber.updatedAt,
241241
phoneNumber: phoneNumber.number,
242242
status: phoneNumber.status,
243+
provider: phoneNumber.provider || 'unknown',
243244
};
244245
}
245246

0 commit comments

Comments
 (0)