This WhatsApp MCP server has significant definition quality gaps. Both tools are explicitly registered with the @mcp.tool() decorator and have visible docstrings, but the definitions fall well short of production standards. Tool descriptions are present but generic (averaging ~50-80 chars, below the 194-char baseline for A+ tools). Input schemas are visible and properly typed (string parameters with descriptions), but parameter descriptions are minimal and lack context about format, constraints, or failure modes. Output schemas are not documented, callers cannot plan downstream actions based on response structure. Error handling returns plain strings without guidance for recovery or classification. No parameter validation, no enum constraints, no actionable error messages. The two tools have highly similar responsibilities (both send messages, one by phone number, one by name) with minimal differentiation in naming, LLMs may conflate them. Overall, this reads as a minimal proof-of-concept rather than a production-grade tool suite.
Tools (2)
send_messagewritesource verified50/100
Sends a message via WhatsApp using the Waha API
send_message_by_namewritesource verified50/100
Sends a text message via WhatsApp to a contact by name
No output schema documented. Tools return plain strings ('Success sending message!', 'Contact X not found...'), but callers cannot parse structured data for downstream actions. Baseline requires every tool to document return types and fields.
Tool descriptions are too short (50-55 chars vs 194-char baseline). Both descriptions omit WHEN to use each tool (vs the other), prerequisites (contacts.json location, Waha API endpoint config), and implications (state modification, no dry-run). LLMs cannot adequately reason about selection or error recovery.
Parameter descriptions lack actionable constraints. 'Phone number of the recipient' does not specify format (E.164? with country code? with +?). 'Name of the contact registered in the system' does not specify match strategy (exact? case-insensitive? fuzzy?). LLMs will guess and cause failures.
send_messagesend_message_by_name
Recommendations
Expand tool descriptions to 100-200 chars. Example for send_message: 'Sends a text message via WhatsApp to a recipient by phone number (E.164 format). Returns success/failure with reason. Use this when you have the exact phone number; use send_message_by_name if you have a contact name from the address book.'
Document output schema for both tools. Example: 'Returns {success: bool, message: str, timestamp: str}' or similar structured object. LLMs need to know what fields are available for parsing.
Add format constraints to parameter descriptions. Example for phone_number: 'Phone number in E.164 format (e.g. +1-555-0123, +44-20-7946-0958). Must include country code.' For name: 'Exact name of a contact in the system. Case-sensitive. If uncertain, call list_contacts() first.'
Consolidate the two send tools into one with a union parameter. Instead of send_message and send_message_by_name, offer send_message(recipient: str, message: str, recipient_type: 'phone'|'contact_name'), this eliminates confusion and reduces the tool set. Or move contact lookup into the send_message implementation (detect if input is a name vs phone).
Implement actionable error messages. Replace 'Error: Bad Request' with 'Invalid phone number: expected E.164 format (e.g. +1-555-0123), got "123-456-7890". Please reformat and retry.' Include recovery hints: 'If you have a contact name instead, use send_message_by_name.'
Add error classification to docstrings. Example: 'Raises: ContactNotFound (user-fixable: call list_contacts() to find the correct name), APITimeout (retryable: backend service may be temporarily unavailable), InvalidFormat (user-fixable: see parameter constraints above).'
Error handling is unguided. Responses like 'Error: Bad Request. Please check the phone number and message format.' appear in code docstrings but are not actionable for LLMs, no recovery path (e.g. 'try search_contacts()'), no error classification (retryable? user-fixable?), no constraint detail ('must be E.164 format').
Two nearly identical tools (send_message, send_message_by_name) risk LLM confusion. Both send messages; they differ only in how the recipient is specified. Naming does not make the distinction obvious enough, and there is no clear guidance in descriptions about when to use each.
No input validation or enum constraints. Phone number parameter accepts free-form strings with no length, format, or regex. Contact name accepts free-form strings. Invalid inputs produce unclear errors ('Bad Request') rather than 'Invalid phone number: got "abc", must be E.164 format (e.g. +1-555-0123)'.
Credentials and config exposed in code. The Waha API endpoint 'http://localhost:3000/api/sendText' and session name 'default' are hardcoded. Should use environment variables or server-side config injection to avoid secrets/config in agent traces.
send_message
Move hardcoded config to environment variables. Replace 'http://localhost:3000' with os.getenv('WAHA_API_URL', 'http://localhost:3000'), and document required env vars in server docstring or README.
Document contacts.json location and format in tool descriptions. Example: 'Requires a contacts.json file (located at {CONTACTS_FILE}) with structure {"contacts": {"John": "+1-555-0123", ...}}.' Include an error message if the file is missing: 'Contacts file not found at {path}. Create it with format {"contacts": {...}} and retry.'
Validate inputs early. Add explicit checks: phone_number must match regex r'^\+\d{1,3}\d{3,14}$' (E.164), name must be non-empty and exist in contacts. Return clear errors: 'Contact "Jhn" not found in address book. Available contacts: John, Jane, Jack. Did you mean "John"?'
Consider adding a list_contacts() discovery tool. Let LLMs see available contact names before calling send_message_by_name. This prevents wasted failed calls and matches the pattern where discovery tools precede action tools.