vapi-mcp-server has 26 tools with visible schemas and descriptions, but critical quality gaps make it unsuitable for production. All tools have descriptions (good baseline), but most descriptions are extremely brief (10-20 chars), violating the 10-1024 char guideline and leaving LLMs unable to distinguish between similar tools. Schemas use Zod and are technically present, but many parameter descriptions are missing or trivial ('Squad members' vs. explaining the data structure). No output schemas are documented. No error handling guidance. No tool annotations (readOnlyHint/destructiveHint). Parameter validation is weak, many use generic `z.record(z.string(), z.any())` which accepts arbitrary payloads with no validation. For example, vapi_create_assistant accepts 'model', 'voice', and 'transcriber' as any object, offering LLMs no guidance on what fields are valid. The tool set lacks composition guidance: an LLM cannot tell from the descriptions whether vapi_create_call requires vapi_buy_phone_number to be called first, or what IDs are needed for chaining. Security is a concern: vapi_import_phone_number exposes 'providerToken' as a parameter, violating secret-injection pattern. Average description length across all tools is ~28 chars, far below the 194-char production baseline.
Descriptions too brief (avg 28 chars vs 194-char production baseline). Examples: 'List all Vapi assistants' (27 chars), 'Get a specific Vapi call' (25 chars), 'Delete a Vapi tool' (18 chars). LLMs cannot distinguish between similar tools or understand WHEN to call them.
Expand every tool description from 20+ chars to 50-150 chars. Answer: WHAT does it do? WHEN should the LLM call it instead of a similar tool? Example: vapi_create_assistant → 'Create a new Vapi assistant with voice, transcriber, and model configuration. Call this to set up a new AI voice agent. Requires at least a name; model, voice, and transcriber configs are optional.'
Add detailed parameter descriptions. For each object parameter (model, voice, transcriber, function, server, customer, data), document the expected fields and their types. Example: 'model: {type: "object", description: "Model configuration with fields: model (string, e.g. \"gpt-4\"), provider (string, e.g. \"openai\")"}'
Document output schemas in tool descriptions. Add a 'Returns' section listing the structure of the response. Example: 'Returns an assistant object with fields: id (string), name (string), model (object), voice (object), createdAt (ISO 8601 string).'
Move providerToken from vapi_import_phone_number parameters to server-side environment variable injection. Update description to: 'Import a phone number from an external provider. The provider token is fetched from server configuration (VAPI_PROVIDER_TOKEN env var).'
Add tool annotations. Register tools with destructiveHint for vapi_delete_*, readOnlyHint for vapi_list_*, and idempotentHint for idempotent operations. Example: server.registerTool('vapi_delete_assistant', {...}, {destructiveHint: true}).
Add error handling with recovery guidance. Catch common API errors and return actionable messages. Example: If vapi_create_assistant fails with 'Invalid model configuration', return 'Invalid model. Check that model.model is a valid provider model name (e.g. gpt-4, claude-opus).'
Parameter descriptions missing or trivial. vapi_create_assistant has 'model: {type: object, description: "Model configuration"}', what fields does Model require? Same for 'voice', 'transcriber'. vapi_create_tool has 'function: z.record(z.string(), z.any())' with description 'Function configuration', LLM has no idea what fields are valid. vapi_update_squad has 'data: {type: object, description: "Update data"}', completely opaque.
No output schemas documented. Tools return JSON.stringify() of raw API responses with no structured field descriptions. LLMs cannot know what fields to expect or extract for downstream tool calls. Example: vapi_create_call returns raw API response, does it include call_id, customer_id, phone_number_id? Downstream vapi_end_call and vapi_get_call need these IDs but documentation gives no guarantee they're present.
providerToken exposed as tool parameter in vapi_import_phone_number. Credentials must NEVER be tool parameters, they end up in agent logs and prompt history. Use server-side secret injection instead (environment variables or vault).
No error handling guidance. Tools return raw API errors with no recovery hints. If vapi_create_assistant fails because 'model configuration is invalid', the LLM has no idea how to recover. No 'retryable vs. fatal' classification. No input validation, vapi_create_call accepts arbitrary 'customer' object, risking API rejection with opaque 400 errors.
No tool annotations. Destructive tools (vapi_delete_assistant, vapi_delete_tool, vapi_delete_squad) lack destructiveHint. vapi_end_call is reversible but unmarked. vapi_list_* tools lack readOnlyHint. LLMs cannot infer safety properties and may incorrectly plan around non-destructive tools.
Generic parameter schemas with z.any(). vapi_create_assistant uses z.record(z.string(), z.any()) for 'model', 'voice', 'transcriber'. vapi_create_tool allows arbitrary 'messages' and 'function' objects. No enum constraints, no string patterns, no length limits. LLMs will hallucinate invalid field names because there are no constraints to guide them.
No tool composition guidance. Unclear if vapi_buy_phone_number must be called before vapi_create_call. No documentation of ID chaining, if vapi_create_assistant returns assistant_id, does vapi_create_call require that exact ID format? No dependency hints like 'Call vapi_list_squads first if you only have a squad name.'
No pagination or result limits documented. vapi_list_assistants, vapi_list_calls, vapi_list_tools, vapi_list_squads may return hundreds of items with no documented limit. Response will blow context window, diluting signal and increasing hallucination. No page/offset/limit parameters visible.
Constrain generic object parameters. Replace z.record(z.string(), z.any()) with explicit field schemas. For vapi_create_assistant, define: model: z.object({model: z.string(), provider: z.string().optional()}).
Add pagination parameters to list_* tools. Example: vapi_list_assistants should accept limit (default 20, max 100) and offset or cursor parameters. Document the limit in the description.
Add tool composition hints. In vapi_create_call description, note: 'Requires an assistantId from vapi_create_assistant and optionally a phoneNumberId from vapi_buy_phone_number.'
Add input validation. Validate required fields, enum constraints, and format constraints before calling the Vapi API. Return clear, actionable error messages for invalid inputs.