Skip to main content
Glama

login_to_adeu_cloud

Logs the user into the Adeu Cloud backend via secure browser authentication.

Instructions

Logs the user into the Adeu Cloud backend. Securely opens a browser window for authentication.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
resultYes

Implementation Reference

  • The `login_to_adeu_cloud` tool handler function. Decorated with @tool, it calls DesktopAuthManager.ensure_authenticated() to get an API key, then verifies it with the backend at /api/v1/auth/me, returning the authenticated user's email.
    @tool(
        description="Logs the user into the Adeu Cloud backend. Securely opens a browser window for authentication.",
        annotations={"openWorldHint": True},
    )
    async def login_to_adeu_cloud(ctx: Context) -> str:
        await ctx.info("Initiating cloud authentication workflow")
        try:
            await ctx.debug("Checking DesktopAuthManager for API key")
            api_key = DesktopAuthManager.ensure_authenticated()
            if not api_key:
                await ctx.error("Failed to obtain API key from login flow")
                raise ToolError("Error: Could not obtain API key from the login flow.")
    
            url = f"{BACKEND_URL}/api/v1/auth/me"
            req = urllib.request.Request(
                url,
                headers={
                    "Authorization": f"Bearer {api_key}",
                    "Accept": "application/json",
                },
            )
    
            try:
                await ctx.debug("Verifying token with backend", extra={"url": url})
                with urllib.request.urlopen(req) as response:
                    data = json.loads(response.read().decode("utf-8"))
                    email = data.get("email", "Unknown Email")
    
                    await ctx.info(
                        "Login successful",
                        extra={"email": email},
                    )
                    return f"Login successful! Connected to Adeu Cloud as: {email}."
    
            except urllib.error.HTTPError as e:
                if e.code == 401:
                    await ctx.warning("Session expired or invalid token. Clearing API key.")
                    DesktopAuthManager.clear_api_key()
                    raise ToolError(
                        "Your previous session expired. The stale key has been cleared. "
                        "Please call the `login_to_adeu_cloud` tool ONE MORE TIME to log in fresh."
                    ) from e
                await ctx.error(
                    "HTTP Error verifying login",
                    extra={"status_code": e.code, "reason": e.reason},
                )
                raise ToolError(f"HTTP Error verifying login: {e.code} - {e.reason}") from e
    
        except Exception as e:
            await ctx.error("Exception during login process", extra={"error": str(e)})
            raise ToolError(f"Error during login process: {str(e)}") from e
  • The @tool decorator defines the tool's description ('Logs the user into the Adeu Cloud backend...') and annotation (openWorldHint). No explicit input/output schema is needed beyond the Context parameter.
    @tool(
        description="Logs the user into the Adeu Cloud backend. Securely opens a browser window for authentication.",
        annotations={"openWorldHint": True},
    )
  • The tool is registered via the @tool() decorator from fastmcp, which auto-registers the function as an MCP tool named 'login_to_adeu_cloud' (derived from the function name).
    @tool(
        description="Logs the user into the Adeu Cloud backend. Securely opens a browser window for authentication.",
        annotations={"openWorldHint": True},
    )
  • DesktopAuthManager class handles keychain storage (get/set/clear_api_key), interactive browser-based authentication (authenticate_interactive), and ensure_authenticated which returns existing key or triggers auth flow.
    class DesktopAuthManager:
        """Manages the authentication lifecycle for the local MCP server."""
    
        @staticmethod
        def get_api_key() -> str | None:
            """Retrieve the API key from the OS Keychain."""
            try:
                return keyring.get_password(KEYRING_SERVICE_NAME, KEYRING_ACCOUNT_NAME)
            except Exception as e:
                logger.error(f"Failed to access keychain: {e}")
                return None
    
        @staticmethod
        def set_api_key(api_key: str) -> None:
            """Store the API key securely in the OS Keychain."""
            try:
                keyring.set_password(KEYRING_SERVICE_NAME, KEYRING_ACCOUNT_NAME, api_key)
            except Exception as e:
                logger.error(f"Failed to save to keychain: {e}")
    
        @staticmethod
        def clear_api_key() -> None:
            """Remove the API key from the OS Keychain."""
            try:
                keyring.delete_password(KEYRING_SERVICE_NAME, KEYRING_ACCOUNT_NAME)
            except Exception:
                pass  # Ignore if it doesn't exist
    
        @classmethod
        def authenticate_interactive(cls) -> str:
            """
            Spins up a local server, opens the browser to log in, and waits for the API key.
            Returns the raw API key upon success.
            """
            # Create an ephemeral TCP server (port 0 lets the OS pick a free port)
            with AuthServer(("localhost", 0), AuthCallbackHandler) as httpd:
                # We access index 1 because server_address for IPv4 is a (host, port) tuple
                port = httpd.server_address[1]
    
                # Direct the user to the React UI login page instead of the backend directly
                auth_url = f"{FRONTEND_URL}/login?desktop_port={port}"
    
                logger.info(f"Opening browser for authentication: {auth_url}")
                # Open the user's default web browser
                webbrowser.open(auth_url)
    
                # Block and wait for the callback to hit the local server
                httpd.serve_forever()
    
                api_key = httpd.api_key
                if not api_key:
                    raise RuntimeError("Authentication failed: No API key received.")
    
                # Save the key securely
                cls.set_api_key(api_key)
                logger.info("Successfully stored Adeu API Key in OS Keychain.")
                return api_key
    
        @classmethod
        def ensure_authenticated(cls) -> str:
            """
            Returns the existing API key, or triggers interactive auth if missing.
            """
            api_key = cls.get_api_key()
            if api_key:
                return api_key
    
            logger.info("No API key found in Keychain. Starting interactive authentication...")
            return cls.authenticate_interactive()
  • get_cloud_auth_token() is a dependency helper that checks for an existing API key and raises a ToolError referencing the login_to_adeu_cloud tool if not authenticated.
    def get_cloud_auth_token() -> str:
        """Dependency to enforce cloud authentication before tool execution."""
        api_key = DesktopAuthManager.get_api_key()
        if not api_key:
            raise ToolError(
                "Authentication Required: You are not logged in. "
                "Please call the `login_to_adeu_cloud` tool first to authenticate, "
                "then try this task again."
            )
        return api_key
Behavior4/5

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

Description adds the key side effect of opening a browser window for authentication, which is not covered by the openWorldHint annotation. It does not detail if user interaction is required or if it's blocking.

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 sentences that are front-loaded and concise. Every word adds value with no redundancy.

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

Completeness4/5

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

The description covers purpose and behavior adequately. With 0 parameters and an output schema available, it is fairly complete, though it could mention idempotency or prerequisites like being logged out first.

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?

Input schema has 0 parameters, so no parameter documentation is needed. Description does not need to add param semantics.

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 logs the user into Adeu Cloud backend via secure browser authentication, with specific verb 'logs' and resource 'Adeu Cloud backend'. It distinguishes from the sibling 'logout_of_adeu_cloud'.

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 implies usage for authentication but does not provide explicit guidance on when to use or when not to (e.g., if already logged in). No alternatives are mentioned.

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/dealfluence/adeu'

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