MCP server for travel-related tools including weather forecasts, place recommendations, task management, email checking, WhatsApp messaging, and itinerary creation
The server defines 9 tools with a mix of quality. All tools have descriptions and visible schema registrations via the @app.tool() decorator pattern. However, several critical issues limit the overall quality: (1) Tool naming is inconsistent, some follow verb-noun convention well (check_important_emails, create_task, get_tasks) but others are awkwardly named (send_whatsapp should be open_whatsapp_chat or send_whatsapp_message; create_itinerary lacks detail about what it actually generates). (2) Descriptions are verbose and include redundant structure (e.g., check_important_emails repeats 'Args:' and 'Returns:' in the docstring after the tool framework should handle that). (3) Parameter descriptions are present but sometimes lack constraint detail, e.g., priority in create_task says '1 - 4' but does not mention what happens if an out-of-range value is passed. (4) Output schemas are documented in descriptions but not formalized in a structured return-type annotation; the framework infers them from the function signature. (5) Error handling is minimal, most tools do not describe what happens on failure or how to recover. (6) The email tool returns complex nested structures (attachments, meeting_details) that could overwhelm context; no pagination or result limiting is documented. (7) Todoist task tools accept project_name as a string enum (Meetings|Shopping|Reminders) but do not validate this at the parameter level, the validation happens inside the tool, forcing error messages to be unstructured.
Scan Gmail for important messages over flexible time or label filters, including any raw attachments and parsed .ics meeting-invite details. Args: limit (int?, default=None) – max number of emails to fetch days (int?, default=None) – look back N days (e.g. 2 for today+1 prior) start_date (str?, default=None) – ISO date "YYYY-MM-DD" to begin range end_date (str?, default=None) – ISO date "YYYY-MM-DD" to end range labels (List[str]?, default=None) – Gmail labels/folders to include Returns: List of dicts, each with keys: • categories List[str] – which of ["meeting","verification","job","subscription","delivery"] • subject str • from str – the From: header • datetime str (ISO) • body str – plain-text body • has_attachments bool • attachments List[{ content_type: str, filename: str, content: str # base-64/text/hex-decoded payload }] • links List[str] – all HTTP/HTTPS URLs in subject+body • tracking_numbers List[str] – any 7+ digit sequences • meeting_details List[{ organizer_cn: str, # from the ICS meeting_url: str, # folded Teams URL location: str, summary: str, description: str, meeting_start: str, # "YYYYMMDDTHHMMSS" This is the start time of the meeting meeting_end: str, # "YYYYMMDDTHHMMSS" This is the end time of the meeting timezone: str, }] (If no attachments or no ICS blocks are found, "attachments" or "meeting_details" will simply be empty lists.)
Creates a travel itinerary for a given location (incomplete in source, definition truncated)
Create a new task in Todoist under a predefined project. Description: Adds a task with the given title and optional details to your Todoist account. The task will be placed into one of the three predefined projects. Args: content (str): A short title or summary of the task (e.g. "Buy groceries"). project_name (str): Name of the project to add the task into. **Must be one of**: - "Meetings" - "Shopping" - "Reminders" description (Optional[str]): (Optional) A longer description or notes for the task. due_string (Optional[str]): (Optional) A human-readable due date (e.g. "today 5pm", "next Monday"). priority (Optional[int]): (Optional) Integer 1–4 where 4 is highest urgency. labels (Optional[List[str]]): (Optional) List of label names to attach to the task. Returns: Dict[str, Any]: The newly created task object returned by the Todoist API, including fields such as: - "id" (int): Unique task identifier. - "content" (str): The title you provided. - "project_id" (str): The internal project ID. - ...and other metadata (due, priority, etc.).
Missing input validation at the schema level for enum-like constraints. create_task and get_tasks both accept project_name as a string with a fixed set of allowed values ('Meetings', 'Shopping', 'Reminders'), but this constraint is only documented in the description, not declared as a JSON Schema enum. This forces runtime validation and unstructured error messages, degrading LLM recovery guidance.
Destructive tools (delete_existing_task) lack confirmation/dry-run mechanism. The tool permanently deletes a task but offers no preview, confirmation step, or undo guidance. Agents can trigger irreversible actions without safeguards.
No pagination or result limits on tools that can return large datasets. check_important_emails documents complex nested return types (emails with attachments, ICS meeting details) but provides no limit parameter, pageCursor, or result-count guidance. A user with hundreds of emails could trigger a massive context-overflow response. get_places similarly lacks a limit parameter despite saying 'up to N'.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-22 | D | 51 | 2026-07-28+ | v2 |
| 2026-03-09 | F | 41 | - | v1 |
Delete a Todoist task, by ID or by searching its content. Description: Removes a task from your Todoist account. Identify the task either by its numeric ID or by providing a substring query that matches the task content; the first match is deleted. Args: task_id (Optional[int]): (Optional) The numeric Todoist task ID. query (Optional[str]): (Optional) Substring to search within task content if you don't know the ID. Returns: bool: True if deletion succeeded, False otherwise. Raises: ValueError: If neither `task_id` nor a matching `query` is found.
Description: Retrieves up to N top tourist attractions for the given location, including a brief description and estimated time/distance to visit. Input: - location (str): The name of the city or place to search attractions for (e.g., "Kakinada"). Output: Returns a list of up to `limit` dictionaries, each containing: - name (str): The attraction's name. - description (str): A short summary of the place. - time_required (str): Estimated visit duration or distance from center (e.g., "2 to 3 hours", "1 km from city center").
List active Todoist tasks, optionally filtered by project. Description: Retrieves all active tasks from your Todoist account. If you specify a project name, only tasks in that project are returned. Args: project_name (Optional[str]): (Optional) Name of one of the predefined projects to filter by: - "Meetings" - "Shopping" - "Reminders" If omitted or None, returns tasks from all three projects. Returns: List[Dict[str, Any]]: A list of task dictionaries, each containing: - "id" (int): Unique task ID. - "content" (str): Task title. - "project" (str): Project name (one of the three). - "description" (str): Task notes (may be empty). - "due" (str): Human-readable due date string (may be empty). - "priority" (int): 1–4 priority level. - "labels" (List[str]): Any attached labels.
Description: Fetches the daily weather forecast for the next 5 days at the specified location. Input: - location (str): The name of the city or place to retrieve weather for (e.g., "Lonavala"). Output: Returns a list of 5 dictionaries, one per day. Each dictionary includes: - date (str): Forecast date in "YYYY-MM-DD" format. - feels_like (float): "Feels like" temperature in °C. - temp_min (float): Minimum daily temperature in °C. - temp_max (float): Maximum daily temperature in °C. - wind_speed (float): Wind speed in m/s. - humidity (int): Relative humidity percentage. - rain_mm (float): Rain volume in mm (0.0 if none). - snow_mm (float): Snow volume in mm (0.0 if none). - clouds_pct (int): Cloud cover percentage. - sunrise (str): Local sunrise time "HH:MM". - sunset (str): Local sunset time "HH:MM". - description (str): Short text weather description (e.g., "light rain").
Looks up contact, builds the WhatsApp click-to-chat URL, opens it automatically in the default browser, and returns the URL.
Update a Todoist task's fields, by ID or by searching its content. Description: Modifies one or more properties of an existing task. You must identify the task either by its numeric ID or by providing a substring query that matches the task's content; the first match is used. Args: task_id (Optional[int]): (Optional) The Todoist task's numeric ID. If provided, `query` is ignored. query (Optional[str]): (Optional) Substring to search within task content if you don't know the ID. new_content (Optional[str]): (Optional) New title for the task. description (Optional[str]): (Optional) New notes or description. due_string (Optional[str]): (Optional) New human-readable due date string. priority (Optional[int]): (Optional) New priority level (1–4). labels (Optional[List[str]]): (Optional) Replacement list of label names (empty list clears labels). Returns: Dict[str, Any]: On success, returns: {"status": "success", "task_id": <int>} Raises: ValueError: If neither `task_id` nor a matching `query` is found, or if no fields are provided to update.
Naming ambiguity: send_whatsapp opens a browser URL; it does not actually send a message via the WhatsApp API. The description clarifies this, but the name is misleading. Should be renamed to 'open_whatsapp_chat' or 'create_whatsapp_url' to match the actual behavior.
Incomplete tool definition for create_itinerary. The description is truncated in the source code, making it impossible to verify the tool's full contract. Without a complete description, LLMs cannot determine when or why to select this tool.
Error handling is minimal or absent across all tools. None of the documented errors provide recovery guidance (e.g., 'If the contact is not found, try search_contacts() first'). Error categories (retryable vs. user-fixable vs. fatal) are not declared.
Parameter descriptions lack specificity on constraints and validation rules. For example, priority in create_task says '1 - 4' but does not explain what happens if an LLM passes 5 or 0. The validation is runtime, forcing unstructured error responses.
Mutual exclusivity between parameters is not formalized. update_existing_task accepts both task_id and query, with task_id taking precedence if both are provided. This undocumented fallback can cause silent misuse (agent intends to update by query but accidentally passes an ID, and the wrong task is modified).
Output schema for check_important_emails is complex and potentially overwhelming. The nested structure includes attachments, meeting_details with multiple fields each, and links. No guidance on typical payload size, typical number of emails per request, or how LLMs should handle large responses.