Skip to main content
Glama
oauth.py9.94 kB
# # MCP Foxxy Bridge - OAuth Management Commands # # Copyright (C) 2024 Billy Bryant # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <https://www.gnu.org/licenses/>. # """OAuth authentication management CLI commands.""" import argparse import json import logging from pathlib import Path from types import SimpleNamespace from typing import Any import aiohttp from rich.console import Console from mcp_foxxy_bridge.cli.api_client import get_api_client_from_config from mcp_foxxy_bridge.cli.formatters import OAuthFormatter from mcp_foxxy_bridge.config.config_loader import load_bridge_config_from_file async def handle_oauth_status( args: Any, config_path: Path, config_dir: Path, console: Console, logger: logging.Logger, ) -> None: """Handle OAuth authentication status command from Click CLI. Args: args: Click command arguments containing server name and format options config_path: Path to the configuration file config_dir: Configuration directory (unused) console: Rich console for output logger: Logger for error reporting Shows OAuth authentication status for specified server or all servers. """ # Convert to argparse-style namespace for compatibility argparse_args = argparse.Namespace( oauth_command="status", name=getattr(args, "name", None), format=getattr(args, "format", "table") ) await _oauth_status(argparse_args, config_path, console, logger) async def handle_oauth_command( args: argparse.Namespace, config_path: Path, config_dir: Path, console: Console, logger: logging.Logger, ) -> None: """Handle OAuth authentication management commands. Args: args: Command line arguments with oauth_command subcommand config_path: Path to the configuration file config_dir: Configuration directory (unused) console: Rich console for output logger: Logger for error reporting Routes to appropriate OAuth subcommand handler (status, login, logout). """ # Check if no subcommand was provided if not hasattr(args, "oauth_command") or args.oauth_command is None: console.print("[yellow]Usage: foxxy-bridge oauth <command>[/yellow]") console.print("Available commands: status, login, logout") return if args.oauth_command == "status": await _oauth_status(args, config_path, console, logger) elif args.oauth_command == "login": await handle_oauth_login(args, config_path, config_dir, console, logger) elif args.oauth_command == "logout": await handle_oauth_logout(args, config_path, config_dir, console, logger) else: console.print(f"[red]Unknown OAuth command: {args.oauth_command}[/red]") async def _oauth_status( args: argparse.Namespace, config_path: Path, console: Console, logger: logging.Logger, ) -> None: """Display OAuth authentication status for servers. Args: args: Command line arguments with server name and format options config_path: Path to the configuration file console: Rich console for output logger: Logger for error reporting Shows authentication status for a specific server if name provided, otherwise shows status for all OAuth-enabled servers. Supports table and JSON output formats. """ """Show OAuth authentication status.""" try: api_client = get_api_client_from_config(str(config_path), console) if args.name: # Show status for specific server try: oauth_data = await api_client.get_oauth_status(args.name) oauth_status = {args.name: oauth_data} except aiohttp.ClientError as e: console.print(f"[red]Failed to get OAuth status for '{args.name}': {e}[/red]") return else: # Show status for all OAuth-enabled servers # First get list of servers try: servers = await api_client.list_servers() oauth_status = {} for server in servers: server_name = server.get("name") if server_name: try: oauth_data = await api_client.get_oauth_status(server_name) # Only include if it's actually OAuth-enabled (doesn't error) oauth_status[server_name] = oauth_data except aiohttp.ClientError: # Skip servers that don't support OAuth continue except aiohttp.ClientError as e: console.print(f"[red]Failed to get server list: {e}[/red]") return if not oauth_status: console.print("[yellow]No OAuth-enabled servers found[/yellow]") return if args.format == "json": console.print(json.dumps(oauth_status, indent=2)) else: OAuthFormatter.format_oauth_status(oauth_status, console) except Exception as e: console.print(f"[red]Error: {e}[/red]") logger.exception("Failed to get OAuth status") async def handle_oauth_login( args: argparse.Namespace | SimpleNamespace, config_path: Path, config_dir: Path, console: Console, logger: logging.Logger, ) -> None: """Handle OAuth login command to initiate authentication for a server.""" try: # Load bridge config to get bridge URL bridge_config = load_bridge_config_from_file(str(config_path), {}) if not bridge_config or not bridge_config.bridge: console.print("[red]Error: Invalid or missing bridge configuration[/red]") return bridge_host = bridge_config.bridge.host bridge_port = bridge_config.bridge.port # Check if server exists and has OAuth enabled server_config = bridge_config.servers.get(args.name) if not server_config: console.print(f"[red]Server '{args.name}' not found in configuration[/red]") return if not server_config.oauth_config or not server_config.oauth_config.enabled: console.print(f"[red]OAuth is not enabled for server '{args.name}'[/red]") return # Construct OAuth start URL if bridge_host == "0.0.0.0": # noqa: S104 auth_url = f"http://127.0.0.1:{bridge_port}/oauth/{args.name}/start" else: auth_url = f"http://{bridge_host}:{bridge_port}/oauth/{args.name}/start" console.print(f"[blue]Starting OAuth authentication for server '[cyan]{args.name}[/cyan]'...[/blue]") console.print("[green]Open this URL in your browser:[/green]") console.print(f"[bold]{auth_url}[/bold]") # Try to open browser automatically try: import webbrowser webbrowser.open(auth_url) console.print("[green]✓[/green] Browser opened automatically") except Exception: console.print("[yellow]Could not open browser automatically - please open the URL manually[/yellow]") except Exception as e: console.print(f"[red]Error initiating OAuth login: {e}[/red]") logger.exception("Failed to initiate OAuth login") async def handle_oauth_logout( args: argparse.Namespace | SimpleNamespace, config_path: Path, config_dir: Path, console: Console, logger: logging.Logger, ) -> None: """Handle OAuth logout command to clear authentication tokens.""" try: if args.all: # Clear all OAuth tokens from mcp_foxxy_bridge.oauth.utils import clear_all_tokens try: clear_all_tokens() console.print("[green]✓[/green] All OAuth tokens cleared") except Exception as e: console.print(f"[red]Error clearing all tokens: {e}[/red]") else: # Clear tokens for specific server from mcp_foxxy_bridge.oauth.utils import clear_tokens, get_server_url_hash # Load config to get server URL for token lookup bridge_config = load_bridge_config_from_file(str(config_path), {}) if not bridge_config: console.print("[red]Error: Invalid bridge configuration[/red]") return server_config = bridge_config.servers.get(args.name) if not server_config: console.print(f"[red]Server '{args.name}' not found in configuration[/red]") return server_url = getattr(server_config, "url", "") if not server_url: console.print(f"[red]No URL found for server '{args.name}'[/red]") return # Clear tokens for this server server_url_hash = get_server_url_hash(server_url) try: clear_tokens(server_url_hash, args.name) console.print(f"[green]✓[/green] OAuth tokens cleared for server '[cyan]{args.name}[/cyan]'") except Exception as e: console.print(f"[red]Error clearing tokens for '{args.name}': {e}[/red]") except Exception as e: console.print(f"[red]Error: {e}[/red]") logger.exception("Failed to handle OAuth logout")

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/billyjbryant/mcp-foxxy-bridge'

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