Skip to main content
Glama

check_auth

Validate your authentication token and retrieve current user information, including status and details.

Instructions

Check if authentication token is valid.

Returns: Auth status and user info

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • The check_auth tool handler: calls RTM auth.checkToken API, returns authenticated status with user info (id, username, fullname) and permissions on success, or not_authenticated status on error.
    @mcp.tool()
    async def check_auth(ctx: Context) -> dict[str, Any]:
        """Check if authentication token is valid.
    
        Returns:
            Auth status and user info
        """
        from ..client import RTMClient
    
        client: RTMClient = await get_client()
    
        try:
            result = await client.check_token()
            auth = result.get("auth", {})
            user = auth.get("user", {})
    
            return build_response(
                data={
                    "status": "authenticated",
                    "user": {
                        "id": user.get("id"),
                        "username": user.get("username"),
                        "fullname": user.get("fullname"),
                    },
                    "permissions": auth.get("perms"),
                },
            )
        except Exception as e:
            return build_response(
                data={
                    "status": "not_authenticated",
                    "error": str(e),
                },
            )
  • The register_utility_tools function registers the check_auth tool (and others) via the @mcp.tool() decorator, binding them to the MCP server.
    def register_utility_tools(mcp: Any, get_client: Any) -> None:
        """Register utility and diagnostic tools."""
    
        @mcp.tool()
        async def test_connection(ctx: Context) -> dict[str, Any]:
            """Test connection to RTM API.
  • The build_response helper formats consistent MCP responses with data and metadata (including fetched_at timestamp), used by check_auth.
    def build_response(
        data: dict[str, Any] | list[Any],
        analysis: dict[str, Any] | None = None,
        transaction_id: str | None = None,
    ) -> dict[str, Any]:
        """Build a consistent response structure.
    
        Args:
            data: The main response data
            analysis: Optional analysis/insights
            transaction_id: Optional transaction ID for undo support
    
        Returns:
            Structured response dict
        """
        response = {
            "data": data,
            "metadata": {
                "fetched_at": datetime.now().isoformat(),
            },
        }
    
        if analysis:
            response["analysis"] = analysis
    
        if transaction_id:
            response["metadata"]["transaction_id"] = transaction_id
    
        return response
    
    
    def _convert_rtm_date(due: str, timezone: str | None) -> str:
        """Convert RTM date (UTC) to user's timezone.
    
        Args:
            due: Date string from RTM (ISO 8601 format, typically with Z suffix)
            timezone: User's IANA timezone (e.g., 'Europe/Warsaw')
    
        Returns:
            ISO 8601 date string in user's timezone, or original if conversion fails
        """
        if not timezone:
            return due
    
        try:
            from zoneinfo import ZoneInfo
    
            # Parse the UTC date from RTM
            due_dt = datetime.fromisoformat(due.replace("Z", "+00:00"))
    
            # Convert to user's timezone
            user_tz = ZoneInfo(timezone)
            due_local = due_dt.astimezone(user_tz)
    
            # Return ISO format in user's timezone
            return due_local.isoformat()
        except Exception:
            # If conversion fails, return original
            return due
    
    
    def format_task(
        task: dict[str, Any], include_ids: bool = True, timezone: str | None = None
    ) -> dict[str, Any]:
        """Format a task for response.
    
        Args:
            task: Raw task data from RTM
            include_ids: Whether to include task IDs
            timezone: User's IANA timezone for date conversion (e.g., 'Europe/Warsaw')
    
        Returns:
            Formatted task dict
        """
        # Convert dates to user's timezone
        due_display = None
        due_raw = task.get("due")
        if due_raw:
            due_display = _convert_rtm_date(due_raw, timezone)
    
        start_display = None
        start_raw = task.get("start")
        if start_raw:
            start_display = _convert_rtm_date(start_raw, timezone)
    
        formatted = {
            "name": task.get("name", ""),
            "priority": _priority_label(task.get("priority", "N")),
            "due": due_display,
            "start": start_display,
            "completed": task.get("completed") or None,
            "tags": task.get("tags", []),
            "url": task.get("url") or None,
            "notes_count": len(task.get("notes", [])),
            "estimate": task.get("estimate") or None,
            "modified": task.get("modified") or None,
        }
    
        if include_ids:
            formatted["id"] = task.get("id")
            formatted["taskseries_id"] = task.get("taskseries_id")
            formatted["list_id"] = task.get("list_id")
    
        return formatted
    
    
    def format_list(lst: dict[str, Any]) -> dict[str, Any]:
        """Format a list for response."""
        return {
            "id": lst.get("id"),
            "name": lst.get("name"),
            "smart": lst.get("smart") == "1",
            "archived": lst.get("archived") == "1",
            "locked": lst.get("locked") == "1",
        }
    
    
    def _priority_label(priority: str) -> str:
        """Convert priority code to label."""
        labels = {
            "1": "high",
            "2": "medium",
            "3": "low",
            "N": "none",
        }
        return labels.get(priority, "none")
    
    
    def priority_to_code(priority: str | int | None) -> str:
        """Convert priority label/number to RTM code."""
        if priority is None:
            return "N"
    
        priority_str = str(priority).lower()
    
        mapping = {
            "high": "1",
            "1": "1",
            "medium": "2",
            "2": "2",
            "low": "3",
            "3": "3",
            "none": "N",
            "0": "N",
            "n": "N",
        }
    
        return mapping.get(priority_str, "N")
    
    
    def parse_tasks_response(result: dict[str, Any]) -> list[dict[str, Any]]:
        """Parse RTM tasks response into flat task list.
    
        RTM returns nested structure:
        tasks.list[].taskseries[].task[]
    
        We flatten this to a simple list with all IDs attached.
        """
        tasks = []
        task_lists = result.get("tasks", {}).get("list", [])
    
        if isinstance(task_lists, dict):
            task_lists = [task_lists]
    
        for tl in task_lists:
            list_id = tl.get("id")
            taskseries_list = tl.get("taskseries", [])
    
            if isinstance(taskseries_list, dict):
                taskseries_list = [taskseries_list]
    
            for ts in taskseries_list:
                task_data = ts.get("task", [])
                if isinstance(task_data, dict):
                    task_data = [task_data]
    
                # Parse tags
                tags_data = ts.get("tags", [])
                if isinstance(tags_data, dict):
                    tags = tags_data.get("tag", [])
                    if isinstance(tags, str):
                        tags = [tags]
                else:
                    tags = []
    
                # Parse notes
                notes_data = ts.get("notes", [])
                notes = []
                if isinstance(notes_data, dict):
                    notes = notes_data.get("note", [])
                    if isinstance(notes, dict):
                        notes = [notes]
    
                for t in task_data:
                    tasks.append(
                        {
                            "id": t.get("id"),
                            "taskseries_id": ts.get("id"),
                            "list_id": list_id,
                            "name": ts.get("name"),
                            "due": t.get("due") or None,
                            "has_due_time": t.get("has_due_time") == "1",
                            "start": t.get("start") or None,
                            "has_start_time": t.get("has_start_time") == "1",
                            "completed": t.get("completed") or None,
                            "deleted": t.get("deleted") or None,
                            "priority": t.get("priority", "N"),
                            "postponed": int(t.get("postponed", 0)),
                            "estimate": t.get("estimate") or None,
                            "tags": tags if tags else [],
                            "notes": notes,
                            "url": ts.get("url") or None,
                            "location_id": ts.get("location_id") or None,
                            "created": ts.get("created") or None,
                            "modified": ts.get("modified") or None,
                        }
                    )
    
        return tasks
    
    
    def parse_lists_response(result: dict[str, Any]) -> list[dict[str, Any]]:
        """Parse RTM lists response."""
        lists = result.get("lists", {}).get("list", [])
        if isinstance(lists, dict):
            lists = [lists]
    
        return [
            {
                "id": lst.get("id"),
                "name": lst.get("name"),
                "deleted": lst.get("deleted") == "1",
                "locked": lst.get("locked") == "1",
                "archived": lst.get("archived") == "1",
                "position": int(lst.get("position", -1)),
                "smart": lst.get("smart") == "1",
                "filter": lst.get("filter"),
                "sort_order": lst.get("sort_order"),
            }
            for lst in lists
        ]
    
    
    def get_transaction_id(result: dict[str, Any]) -> str | None:
        """Extract transaction ID from response for undo support."""
        transaction = result.get("transaction", {})
        return transaction.get("id")
  • The client.check_token method calls the RTM API's rtm.auth.checkToken endpoint.
    async def check_token(self) -> dict[str, Any]:
        """Check if auth token is valid (rtm.auth.checkToken)."""
        return await self.call("rtm.auth.checkToken")
  • Top-level registration call in server.py that triggers register_utility_tools (and thus check_auth).
    # Register all tools
    register_task_tools(mcp, get_client)
    register_list_tools(mcp, get_client)
    register_note_tools(mcp, get_client)
    register_utility_tools(mcp, get_client)
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, but the description clearly indicates a read-only operation that returns auth status and user info. It does not disclose potential side effects or authentication requirements for the token, but for a 0-parameter tool, the behavior is sufficiently transparent.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two short sentences with no redundant information, perfectly concise and front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given zero parameters and a simple output, the description adequately covers the purpose and return values. The presence of an output schema further reduces the need for description detail.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

There are no parameters, so the baseline score is 4. The description does not require parameter explanation, and the schema coverage is 100%.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool checks if an authentication token is valid, specifying a specific verb and resource. It distinguishes from siblings as no other tool performs auth operations.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description does not provide explicit guidance on when to use this tool versus alternatives or context for using it before other operations. For a simple status check, the need is implicit but not articulated.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ljadach/rtm-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server