create_tool
Create a Vapi tool for SMS, call transfers, custom functions, or API requests by specifying its type, name, description, and configuration.
Instructions
Creates a new Vapi tool
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| name | No | Name of the function/tool | |
| description | No | Description of what the function/tool does | |
| sms | No | SMS tool configuration - to send text messages | |
| transferCall | No | Transfer call tool configuration - to transfer calls to destinations | |
| function | No | Custom function tool configuration - for custom server-side functions | |
| apiRequest | No | API Request tool configuration - for HTTP API integration | |
| type | Yes | Type of the tool to create |
Implementation Reference
- src/tools/tool.ts:32-41 (handler)The MCP tool registration for 'create_tool'. Lines 33-34 define the tool name and description. Line 35 sets the input schema from CreateToolInputSchema.shape. Lines 36-40 contain the handler that transforms input, calls vapiClient.tools.create(), and transforms output.
server.tool( 'create_tool', 'Creates a new Vapi tool', CreateToolInputSchema.shape, createToolHandler(async (data) => { const createToolDto = transformToolInput(data); const tool = await vapiClient.tools.create(createToolDto); return transformToolOutput(tool); }) ); - src/schemas/index.ts:367-404 (schema)Input schema for create_tool. BaseToolConfigSchema (lines 367-399) defines common fields (name, description, sms, transferCall, function, apiRequest). CreateToolInputSchema (lines 401-404) extends it with a 'type' enum (sms, transferCall, function, apiRequest).
const BaseToolConfigSchema = z.object({ // Common fields for all tools name: z.string().optional().describe('Name of the function/tool'), description: z.string().optional().describe('Description of what the function/tool does'), // SMS tool configuration sms: z.object({ metadata: z.object({ from: z.string().describe('Phone number to send SMS from (e.g., "+15551234567"). It must be a twilio number in E.164 format.'), }).describe('SMS configuration metadata'), }).optional().describe('SMS tool configuration - to send text messages'), // Transfer call tool configuration transferCall: z.object({ destinations: z.array(TransferCallDestinationSchema).describe('Array of possible transfer destinations'), }).optional().describe('Transfer call tool configuration - to transfer calls to destinations'), // Function tool configuration (custom functions with parameters) function: z.object({ parameters: JsonSchema.describe('JSON schema for function parameters'), server: ServerSchema.describe('Server configuration with URL where the function will be called'), }).optional().describe('Custom function tool configuration - for custom server-side functions'), // API Request tool configuration apiRequest: z.object({ url: z.string().url().describe('URL to make the API request to'), method: z.enum(['GET', 'POST']).default('POST').describe('HTTP method for the API request'), headers: z.record(z.string()).optional().describe('Headers to send with the request (key-value pairs)'), body: JsonSchema.optional().describe('Body schema for the API request in JSON Schema format'), backoffPlan: BackoffPlanSchema.optional().describe('Retry configuration for failed API requests'), timeoutSeconds: z.number().default(20).describe('Request timeout in seconds'), }).optional().describe('API Request tool configuration - for HTTP API integration'), }); export const CreateToolInputSchema = BaseToolConfigSchema.extend({ type: z.enum(['sms', 'transferCall', 'function', 'apiRequest']) .describe('Type of the tool to create'), }); - src/tools/utils.ts:44-72 (helper)The createToolHandler wrapper function. It checks authentication (calls hasValidToken), and if not authenticated, starts auth flow and returns an auth-required response. Otherwise, it executes the provided handler and wraps the result in a success/error response.
export function createToolHandler<T>( handler: (params: T) => Promise<any> ): (params: T) => Promise<ToolResponse> { return async (params: T) => { // Check auth first if (!hasValidToken()) { // Start auth if not already in progress if (!isAuthInProgress()) { try { await startAuthFlow(); } catch (error) { // Ignore - we'll show the auth URL below } } const url = getAuthUrl(); if (url) { return createAuthRequiredResponse(url); } return createErrorResponse('Authentication required. Please use vapi_login tool first.'); } try { const result = await handler(params); return createSuccessResponse(result); } catch (error) { return createErrorResponse(error); } }; } - src/transformers/index.ts:248-312 (helper)The transformToolInput helper that transforms the create_tool input data into the Vapi API DTO format. It handles all four tool types (sms, transferCall, function, apiRequest) with type-specific field mappings.
export function transformToolInput( input: z.infer<typeof CreateToolInputSchema> ): any { let toolDto: any = { type: input.type, }; // Add function definition if name and description are provided if (input.name || input.description) { toolDto.function = { ...(input.name && { name: input.name }), ...(input.description && { description: input.description }), }; } // Handle different tool types using the new nested structure switch (input.type) { case 'sms': if (input.sms?.metadata) { toolDto.metadata = input.sms.metadata; } break; case 'transferCall': if (input.transferCall?.destinations) { toolDto.destinations = input.transferCall.destinations; } break; case 'function': if (input.function?.parameters && input.function?.server) { // For function tools, add parameters to the existing function object if (toolDto.function) { toolDto.function.parameters = input.function.parameters; } else { toolDto.function = { parameters: input.function.parameters, }; } toolDto.server = { url: input.function.server.url, ...(input.function.server.headers && { headers: input.function.server.headers }), }; } break; case 'apiRequest': if (input.apiRequest?.url) { toolDto.url = input.apiRequest.url; toolDto.method = input.apiRequest.method || 'POST'; if (input.apiRequest.headers) toolDto.headers = input.apiRequest.headers; if (input.apiRequest.body) toolDto.body = input.apiRequest.body; if (input.apiRequest.backoffPlan) toolDto.backoffPlan = input.apiRequest.backoffPlan; if (input.apiRequest.timeoutSeconds) toolDto.timeoutSeconds = input.apiRequest.timeoutSeconds; } break; default: throw new Error(`Unsupported tool type: ${(input as any).type}`); } return toolDto; } - src/tools/index.ts:1-15 (registration)The registration module that exports registerAllTools, which calls registerToolTools (among others) to register 'create_tool' and other tool-related MCP tools on the server.
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { VapiClient } from '@vapi-ai/server-sdk'; import { registerAssistantTools } from './assistant.js'; import { registerCallTools } from './call.js'; import { registerPhoneNumberTools } from './phone-number.js'; import { registerToolTools } from './tool.js'; export const registerAllTools = (server: McpServer, vapiClient: VapiClient) => { registerAssistantTools(server, vapiClient); registerCallTools(server, vapiClient); registerPhoneNumberTools(server, vapiClient); registerToolTools(server, vapiClient); };