From bb747033119b0e7da1bb6133afefe04538b17d77 Mon Sep 17 00:00:00 2001 From: airano Date: Wed, 18 Feb 2026 03:41:04 +0330 Subject: [PATCH] fix(ux): improve new user onboarding experience - Show full temporary API key in logs (not masked) so users can log in - Redirect root URL / to /dashboard (was 404) - Add setup guidance and docs link to login page - Make footer version dynamic (read from pyproject.toml) Co-Authored-By: Claude Opus 4.6 --- core/auth.py | 5 ++++- core/dashboard/routes.py | 16 +++++++++++++++- core/templates/dashboard/login.html | 17 ++++++++++++----- server.py | 13 +++++++++++-- 4 files changed, 42 insertions(+), 9 deletions(-) diff --git a/core/auth.py b/core/auth.py index d3599e9..648d006 100644 --- a/core/auth.py +++ b/core/auth.py @@ -27,11 +27,14 @@ class AuthManager: if not self.master_api_key: # Generate a random key if not provided (dev mode) self.master_api_key = secrets.token_urlsafe(32) + self._is_temporary_key = True logger.warning( "No MASTER_API_KEY environment variable found. " - f"Generated temporary key: {self.master_api_key[:8]}***{self.master_api_key[-4:]} " + f"Generated temporary key: {self.master_api_key} " "(set MASTER_API_KEY in .env for production use)" ) + else: + self._is_temporary_key = False # Project-specific keys (future feature) self.project_keys = {} diff --git a/core/dashboard/routes.py b/core/dashboard/routes.py index d95161b..889776c 100644 --- a/core/dashboard/routes.py +++ b/core/dashboard/routes.py @@ -334,6 +334,7 @@ async def dashboard_login_page(request: Request) -> Response: "t": t, "error": error, "next_url": next_url, + "version": _get_project_version(), }, ) @@ -1984,6 +1985,19 @@ def get_registered_plugins() -> list: return plugins +def _get_project_version() -> str: + """Read version from pyproject.toml.""" + try: + toml_path = os.path.join(os.path.dirname(os.path.dirname(TEMPLATES_DIR)), "pyproject.toml") + with open(toml_path) as f: + for line in f: + if line.strip().startswith("version"): + return line.split("=")[1].strip().strip('"').strip("'") + except Exception: + pass + return "3.0.0" + + def get_about_info() -> dict: """Get about information.""" import sys @@ -1998,7 +2012,7 @@ def get_about_info() -> dict: pass return { - "version": "1.0.0", + "version": _get_project_version(), "mcp_version": "2024-11-05", "python_version": f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}", "tools_count": tools_count, diff --git a/core/templates/dashboard/login.html b/core/templates/dashboard/login.html index ec1dcb1..f5b6ca3 100644 --- a/core/templates/dashboard/login.html +++ b/core/templates/dashboard/login.html @@ -108,13 +108,20 @@ -

+

{% if lang == 'fa' %} - برای دسترسی به داشبورد به Master API Key یا یک API Key با scope=admin نیاز دارید. +

برای دسترسی به داشبورد به Master API Key نیاز دارید.

+

کلید API خود را در متغیر محیطی MASTER_API_KEY تنظیم کنید.

{% else %} - You need a Master API Key or an API Key with scope=admin to access the dashboard. +

Enter your MASTER_API_KEY to access the dashboard.

+

Don't have one? Set it in your .env file, or check the server logs for the temporary key.

{% endif %} -

+

+ + {% if lang == 'fa' %}راهنمای شروع{% else %}Setup Guide{% endif %} + +

+
@@ -131,7 +138,7 @@

- MCP Hub v3.0.0 + MCP Hub v{{ version|default('3.0.0') }}

diff --git a/server.py b/server.py index 056f5a0..07c4bd6 100644 --- a/server.py +++ b/server.py @@ -274,7 +274,10 @@ logger.info("=" * 60) logger.info("MCP Hub Server - Initialized") logger.info("=" * 60) _mk = auth_manager.get_master_key() -logger.info(f"Master API Key: {_mk[:8]}***{_mk[-4:]}") +if auth_manager._is_temporary_key: + logger.info(f"Master API Key (temporary): {_mk}") +else: + logger.info(f"Master API Key: {_mk[:8]}***{_mk[-4:]}") logger.info(f"Discovered {len(project_manager.projects)} per-site project instances (legacy)") logger.info( f"Discovered {site_manager.get_count()} unique sites across {len(plugin_types)} plugin types" @@ -4182,12 +4185,18 @@ def create_multi_endpoint_app(transport: str = "streamable-http"): except Exception as e: logger.warning(f"Error during lifespan cleanup for {name}: {e}") + # Root redirect: / → /dashboard (so users don't see 404) + async def root_redirect(request): + return RedirectResponse(url="/dashboard", status_code=302) + # Build routes # Note: Order matters! More specific routes first routes = [ # Health check Route("/health", health_check, methods=["GET"]), - # Dashboard routes (Phase K.1) + # Root redirect + Route("/", root_redirect, methods=["GET"]), + # Dashboard routes Route("/dashboard/login", dashboard_login_page, methods=["GET"]), Route("/dashboard/login", dashboard_login_submit, methods=["POST"]), Route("/dashboard/logout", dashboard_logout, methods=["GET", "POST"]),