diff --git a/mcpgateway/admin.py b/mcpgateway/admin.py
index 2f6dfe3b1..3355784a1 100644
--- a/mcpgateway/admin.py
+++ b/mcpgateway/admin.py
@@ -1368,7 +1368,7 @@ async def admin_add_server(request: Request, db: Session = Depends(get_db), user
>>> server_service.register_server = original_register_server
"""
form = await request.form()
- # root_path = request.scope.get("root_path", "")
+ # root_path = settings.app_root_path
# is_inactive_checked = form.get("is_inactive_checked", "false")
# Parse tags from comma-separated string
@@ -1800,7 +1800,7 @@ async def admin_toggle_server(
LOGGER.error(f"Error toggling server status: {e}")
error_message = "Error toggling server status. Please try again."
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
# Build redirect URL with error message if present
if error_message:
@@ -1900,7 +1900,7 @@ async def admin_delete_server(server_id: str, request: Request, db: Session = De
LOGGER.error(f"Error deleting server: {e}")
error_message = "Failed to delete server. Please try again."
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
# Build redirect URL with error message if present
if error_message:
@@ -2320,7 +2320,7 @@ async def admin_toggle_gateway(
LOGGER.error(f"Error toggling gateway status: {e}")
error_message = "Failed to toggle gateway status. Please try again."
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
# Build redirect URL with error message if present
if error_message:
@@ -2970,8 +2970,8 @@ async def admin_login_page(request: Request) -> Response:
"""
# Check if email auth is enabled
if not getattr(settings, "email_auth_enabled", False):
- root_path = request.scope.get("root_path", "")
- return RedirectResponse(url=f"{root_path}/admin", status_code=303)
+ root_path = settings.app_root_path
+ return RedirectResponse(url=f"{root_path}/admin/", status_code=303)
root_path = settings.app_root_path
@@ -3026,8 +3026,8 @@ async def admin_login_handler(request: Request, db: Session = Depends(get_db)) -
True
"""
if not getattr(settings, "email_auth_enabled", False):
- root_path = request.scope.get("root_path", "")
- return RedirectResponse(url=f"{root_path}/admin", status_code=303)
+ root_path = settings.app_root_path
+ return RedirectResponse(url=f"{root_path}/admin/", status_code=303)
try:
form = await request.form()
@@ -3037,7 +3037,7 @@ async def admin_login_handler(request: Request, db: Session = Depends(get_db)) -
password = password_val if isinstance(password_val, str) else None
if not email or not password:
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
return RedirectResponse(url=f"{root_path}/admin/login?error=missing_fields", status_code=303)
# Authenticate using the email auth service
@@ -3051,7 +3051,7 @@ async def admin_login_handler(request: Request, db: Session = Depends(get_db)) -
if not user:
LOGGER.warning(f"Authentication failed for {email} - user is None")
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
return RedirectResponse(url=f"{root_path}/admin/login?error=invalid_credentials", status_code=303)
# Check if password change is required OR if user is using default password
@@ -3075,7 +3075,7 @@ async def admin_login_handler(request: Request, db: Session = Depends(get_db)) -
token, _ = await create_access_token(user)
# Create redirect response to password change page
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
response = RedirectResponse(url=f"{root_path}/admin/change-password-required", status_code=303)
# Set JWT token as secure cookie for the password change process
@@ -3087,8 +3087,8 @@ async def admin_login_handler(request: Request, db: Session = Depends(get_db)) -
token, _ = await create_access_token(user) # expires_seconds not needed here
# Create redirect response
- root_path = request.scope.get("root_path", "")
- response = RedirectResponse(url=f"{root_path}/admin", status_code=303)
+ root_path = settings.app_root_path
+ response = RedirectResponse(url=f"{root_path}/admin/", status_code=303)
# Set JWT token as secure cookie
set_auth_cookie(response, token, remember_me=False)
@@ -3102,12 +3102,12 @@ async def admin_login_handler(request: Request, db: Session = Depends(get_db)) -
if settings.secure_cookies and settings.environment == "development":
LOGGER.warning("Login failed - set SECURE_COOKIES to false in config for HTTP development")
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
return RedirectResponse(url=f"{root_path}/admin/login?error=invalid_credentials", status_code=303)
except Exception as e:
LOGGER.error(f"Login handler error: {e}")
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
return RedirectResponse(url=f"{root_path}/admin/login?error=server_error", status_code=303)
@@ -3144,7 +3144,7 @@ async def admin_logout(request: Request) -> RedirectResponse:
True
"""
LOGGER.info("Admin user logging out")
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
# Create redirect response to login page
response = RedirectResponse(url=f"{root_path}/admin/login", status_code=303)
@@ -3190,11 +3190,11 @@ async def change_password_required_page(request: Request) -> HTMLResponse:
True
"""
if not getattr(settings, "email_auth_enabled", False):
- root_path = request.scope.get("root_path", "")
- return RedirectResponse(url=f"{root_path}/admin", status_code=303)
+ root_path = settings.app_root_path
+ return RedirectResponse(url=f"{root_path}/admin/", status_code=303)
# Get root path for template
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
return request.app.state.templates.TemplateResponse(
"change-password-required.html",
@@ -3254,8 +3254,8 @@ async def change_password_required_handler(request: Request, db: Session = Depen
True
"""
if not getattr(settings, "email_auth_enabled", False):
- root_path = request.scope.get("root_path", "")
- return RedirectResponse(url=f"{root_path}/admin", status_code=303)
+ root_path = settings.app_root_path
+ return RedirectResponse(url=f"{root_path}/admin/", status_code=303)
try:
form = await request.form()
@@ -3268,18 +3268,18 @@ async def change_password_required_handler(request: Request, db: Session = Depen
confirm_password = confirm_password_val if isinstance(confirm_password_val, str) else None
if not all([current_password, new_password, confirm_password]):
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
return RedirectResponse(url=f"{root_path}/admin/change-password-required?error=missing_fields", status_code=303)
if new_password != confirm_password:
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
return RedirectResponse(url=f"{root_path}/admin/change-password-required?error=mismatch", status_code=303)
# Get user from JWT token in cookie
try:
jwt_token = request.cookies.get("jwt_token")
if not jwt_token:
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
return RedirectResponse(url=f"{root_path}/admin/login?error=session_expired", status_code=303)
# Authenticate using the token
@@ -3289,11 +3289,11 @@ async def change_password_required_handler(request: Request, db: Session = Depen
current_user = await get_current_user(credentials, request=request)
if not current_user:
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
return RedirectResponse(url=f"{root_path}/admin/login?error=session_expired", status_code=303)
except Exception as e:
LOGGER.error(f"Authentication error: {e}")
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
return RedirectResponse(url=f"{root_path}/admin/login?error=session_expired", status_code=303)
# Authenticate using the email auth service
@@ -3314,8 +3314,8 @@ async def change_password_required_handler(request: Request, db: Session = Depen
token, _ = await create_access_token(current_user)
# Create redirect response to admin panel
- root_path = request.scope.get("root_path", "")
- response = RedirectResponse(url=f"{root_path}/admin", status_code=303)
+ root_path = settings.app_root_path
+ response = RedirectResponse(url=f"{root_path}/admin/", status_code=303)
# Update JWT token cookie
set_auth_cookie(response, token, remember_me=False)
@@ -3323,24 +3323,24 @@ async def change_password_required_handler(request: Request, db: Session = Depen
LOGGER.info(f"User {current_user.email} successfully changed their expired password")
return response
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
return RedirectResponse(url=f"{root_path}/admin/change-password-required?error=change_failed", status_code=303)
except AuthenticationError:
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
return RedirectResponse(url=f"{root_path}/admin/change-password-required?error=invalid_password", status_code=303)
except PasswordValidationError as e:
LOGGER.warning(f"Password validation failed for {current_user.email}: {e}")
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
return RedirectResponse(url=f"{root_path}/admin/change-password-required?error=weak_password", status_code=303)
except Exception as e:
LOGGER.error(f"Password change failed for {current_user.email}: {e}", exc_info=True)
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
return RedirectResponse(url=f"{root_path}/admin/change-password-required?error=server_error", status_code=303)
except Exception as e:
LOGGER.error(f"Password change handler error: {e}")
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
return RedirectResponse(url=f"{root_path}/admin/change-password-required?error=server_error", status_code=303)
@@ -3558,7 +3558,7 @@ async def admin_list_teams(
if not current_user:
return HTMLResponse(content='
', status_code=200)
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
if unified:
# Generate unified team view
@@ -3646,7 +3646,7 @@ async def admin_create_team(
try:
# Get root path for URL construction
- root_path = request.scope.get("root_path", "") if request else ""
+ root_path = settings.app_root_path if request else ""
form = await request.form()
name = form.get("name")
@@ -3744,7 +3744,7 @@ async def admin_view_team_members(
try:
# Get root_path from request
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
# Get current user context for logging and authorization
user_email = get_user_email(user)
@@ -3985,7 +3985,7 @@ async def admin_get_team_edit(
try:
# Get root path for URL construction
- root_path = _request.scope.get("root_path", "") if _request else ""
+ root_path = _settings.app_root_path if _request else ""
team_service = TeamManagementService(db)
team = await team_service.get_team_by_id(team_id)
@@ -4060,7 +4060,7 @@ async def admin_update_team(
Response: Result of team update operation
"""
# Ensure root_path is available for URL construction in all branches
- root_path = request.scope.get("root_path", "") if request else ""
+ root_path = settings.app_root_path if request else ""
if not settings.email_auth_enabled:
return HTMLResponse(content='Email authentication is disabled
', status_code=403)
@@ -4663,7 +4663,7 @@ async def admin_list_join_requests(
try:
team_service = TeamManagementService(db)
user_email = get_user_email(user)
- request.scope.get("root_path", "")
+ settings.app_root_path
# Get team and verify ownership
team = await team_service.get_team_by_id(team_id)
@@ -4870,7 +4870,7 @@ async def admin_list_users(
return HTMLResponse(content='Email authentication is disabled. User management requires email auth.
', status_code=200)
# Get root_path from request
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
# First-Party
@@ -5003,7 +5003,7 @@ async def admin_create_user(
"""
try:
# Get root path for URL construction
- root_path = request.scope.get("root_path", "") if request else ""
+ root_path = settings.app_root_path if request else ""
form = await request.form()
@@ -5088,7 +5088,7 @@ async def admin_get_user_edit(
try:
# Get root path for URL construction
- root_path = _request.scope.get("root_path", "") if _request else ""
+ root_path = _settings.app_root_path if _request else ""
# First-Party
@@ -5419,7 +5419,7 @@ async def admin_activate_user(
try:
# Get root path for URL construction
- root_path = _request.scope.get("root_path", "") if _request else ""
+ root_path = _settings.app_root_path if _request else ""
# First-Party
@@ -5486,7 +5486,7 @@ async def admin_deactivate_user(
try:
# Get root path for URL construction
- root_path = _request.scope.get("root_path", "") if _request else ""
+ root_path = _settings.app_root_path if _request else ""
# First-Party
@@ -5638,7 +5638,7 @@ async def admin_force_password_change(
try:
# Get root path for URL construction
- root_path = _request.scope.get("root_path", "") if _request else ""
+ root_path = _settings.app_root_path if _request else ""
auth_service = EmailAuthService(db)
@@ -5901,7 +5901,7 @@ async def admin_tools_partial_html(
"hx_target": "#tools-table-body",
"hx_indicator": "#tools-loading",
"query_params": query_params_dict,
- "root_path": request.scope.get("root_path", ""),
+ "root_path": settings.app_root_path,
},
)
@@ -5913,7 +5913,7 @@ async def admin_tools_partial_html(
"request": request,
"data": data,
"pagination": pagination.model_dump(),
- "root_path": request.scope.get("root_path", ""),
+ "root_path": settings.app_root_path,
"gateway_id": gateway_id,
},
)
@@ -5926,7 +5926,7 @@ async def admin_tools_partial_html(
"data": data,
"pagination": pagination.model_dump(),
"links": links.model_dump() if links else None,
- "root_path": request.scope.get("root_path", ""),
+ "root_path": settings.app_root_path,
"include_inactive": include_inactive,
},
)
@@ -6228,7 +6228,7 @@ async def admin_prompts_partial_html(
"hx_target": "#prompts-table-body",
"hx_indicator": "#prompts-loading",
"query_params": query_params,
- "root_path": request.scope.get("root_path", ""),
+ "root_path": settings.app_root_path,
},
)
@@ -6239,7 +6239,7 @@ async def admin_prompts_partial_html(
"request": request,
"data": data,
"pagination": pagination.model_dump(),
- "root_path": request.scope.get("root_path", ""),
+ "root_path": settings.app_root_path,
"gateway_id": gateway_id,
},
)
@@ -6251,7 +6251,7 @@ async def admin_prompts_partial_html(
"data": data,
"pagination": pagination.model_dump(),
"links": links.model_dump() if links else None,
- "root_path": request.scope.get("root_path", ""),
+ "root_path": settings.app_root_path,
"include_inactive": include_inactive,
},
)
@@ -6390,7 +6390,7 @@ async def admin_resources_partial_html(
"hx_target": "#resources-table-body",
"hx_indicator": "#resources-loading",
"query_params": query_params,
- "root_path": request.scope.get("root_path", ""),
+ "root_path": settings.app_root_path,
},
)
@@ -6401,7 +6401,7 @@ async def admin_resources_partial_html(
"request": request,
"data": data,
"pagination": pagination.model_dump(),
- "root_path": request.scope.get("root_path", ""),
+ "root_path": settings.app_root_path,
"gateway_id": gateway_id,
},
)
@@ -6413,7 +6413,7 @@ async def admin_resources_partial_html(
"data": data,
"pagination": pagination.model_dump(),
"links": links.model_dump() if links else None,
- "root_path": request.scope.get("root_path", ""),
+ "root_path": settings.app_root_path,
"include_inactive": include_inactive,
},
)
@@ -7398,7 +7398,7 @@ async def admin_delete_tool(tool_id: str, request: Request, db: Session = Depend
LOGGER.error(f"Error deleting tool: {e}")
error_message = "Failed to delete tool. Please try again."
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
# Build redirect URL with error message if present
if error_message:
@@ -7524,7 +7524,7 @@ async def admin_toggle_tool(
LOGGER.error(f"Error toggling tool status: {e}")
error_message = "Failed to toggle tool status. Please try again."
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
# Build redirect URL with error message if present
if error_message:
@@ -8318,7 +8318,7 @@ async def admin_delete_gateway(gateway_id: str, request: Request, db: Session =
form = await request.form()
is_inactive_checked = str(form.get("is_inactive_checked", "false"))
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
# Build redirect URL with error message if present
if error_message:
@@ -8866,7 +8866,7 @@ async def admin_delete_resource(resource_id: str, request: Request, db: Session
except Exception as e:
LOGGER.error(f"Error deleting resource: {e}")
error_message = "Failed to delete resource. Please try again."
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
# Build redirect URL with error message if present
if error_message:
@@ -8991,7 +8991,7 @@ async def admin_toggle_resource(
LOGGER.error(f"Error toggling resource status: {e}")
error_message = "Failed to toggle resource status. Please try again."
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
# Build redirect URL with error message if present
if error_message:
@@ -9427,7 +9427,7 @@ async def admin_delete_prompt(prompt_id: str, request: Request, db: Session = De
except Exception as e:
LOGGER.error(f"Error deleting prompt: {e}")
error_message = "Failed to delete prompt. Please try again."
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
# Build redirect URL with error message if present
if error_message:
@@ -9552,7 +9552,7 @@ async def admin_toggle_prompt(
LOGGER.error(f"Error toggling prompt status: {e}")
error_message = "Failed to toggle prompt status. Please try again."
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
# Build redirect URL with error message if present
if error_message:
@@ -9617,7 +9617,7 @@ async def admin_add_root(request: Request, user=Depends(get_current_user_with_pe
if isinstance(name_value, str):
name = name_value
await root_service.add_root(uri, name)
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
return RedirectResponse(f"{root_path}/admin#roots", status_code=303)
@@ -9678,7 +9678,7 @@ async def admin_delete_root(uri: str, request: Request, user=Depends(get_current
LOGGER.debug(f"User {get_user_email(user)} is deleting root URI {uri}")
await root_service.remove_root(uri)
form = await request.form()
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
is_inactive_checked: str = str(form.get("is_inactive_checked", "false"))
if is_inactive_checked.lower() == "true":
return RedirectResponse(f"{root_path}/admin/?include_inactive=true#roots", status_code=303)
@@ -9842,7 +9842,7 @@ async def admin_metrics_partial_html(
"entity_type": entity_type,
"data": data,
"pagination": pagination.model_dump(),
- "root_path": request.scope.get("root_path", ""),
+ "root_path": settings.app_root_path,
},
)
@@ -12127,7 +12127,7 @@ async def admin_toggle_a2a_agent(
HTTPException: If A2A features are disabled
"""
if not a2a_service or not settings.mcpgateway_a2a_enabled:
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
return RedirectResponse(f"{root_path}/admin#a2a-agents", status_code=303)
error_message = None
@@ -12139,7 +12139,7 @@ async def admin_toggle_a2a_agent(
user_email = get_user_email(user)
await a2a_service.toggle_agent_status(db, agent_id, activate, user_email=user_email)
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
return RedirectResponse(f"{root_path}/admin#a2a-agents", status_code=303)
except PermissionError as e:
@@ -12147,14 +12147,14 @@ async def admin_toggle_a2a_agent(
error_message = str(e)
except A2AAgentNotFoundError as e:
LOGGER.error(f"A2A agent toggle failed - not found: {e}")
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
error_message = "A2A agent not found."
except Exception as e:
LOGGER.error(f"Error toggling A2A agent: {e}")
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
error_message = "Failed to toggle status of A2A agent. Please try again."
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
# Build redirect URL with error message if present
if error_message:
@@ -12186,7 +12186,7 @@ async def admin_delete_a2a_agent(
HTTPException: If A2A features are disabled
"""
if not a2a_service or not settings.mcpgateway_a2a_enabled:
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
return RedirectResponse(f"{root_path}/admin#a2a-agents", status_code=303)
form = await request.form()
@@ -12205,7 +12205,7 @@ async def admin_delete_a2a_agent(
LOGGER.error(f"Error deleting A2A agent: {e}")
error_message = "Failed to delete A2A agent. Please try again."
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
# Build redirect URL with error message if present
if error_message:
@@ -12793,7 +12793,7 @@ async def get_plugins_partial(request: Request, db: Session = Depends(get_db), u
stats = await plugin_service.get_plugin_statistics()
# Prepare context for template
- context = {"request": request, "plugins": plugins, "stats": stats, "plugins_enabled": plugin_manager is not None, "root_path": request.scope.get("root_path", "")}
+ context = {"request": request, "plugins": plugins, "stats": stats, "plugins_enabled": plugin_manager is not None, "root_path": settings.app_root_path}
# Render the partial template
return request.app.state.templates.TemplateResponse("plugins_partial.html", context)
@@ -13203,7 +13203,7 @@ async def catalog_partial(
if not settings.mcpgateway_catalog_enabled:
raise HTTPException(status_code=404, detail="Catalog feature is disabled")
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
# Calculate pagination
page_size = settings.mcpgateway_catalog_page_size
@@ -13321,7 +13321,7 @@ async def get_system_stats(
{
"request": request,
"stats": stats,
- "root_path": request.scope.get("root_path", ""),
+ "root_path": settings.app_root_path,
"db_metrics_recording_enabled": settings.db_metrics_recording_enabled,
},
)
@@ -13455,7 +13455,7 @@ async def get_maintenance_partial(
if not is_admin:
raise HTTPException(status_code=403, detail="Platform administrator access required")
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
# Build payload with settings for the template
payload = {
@@ -13488,7 +13488,7 @@ async def get_observability_partial(request: Request, _user=Depends(get_current_
Returns:
HTMLResponse: Rendered observability dashboard template
"""
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
return request.app.state.templates.TemplateResponse("observability_partial.html", {"request": request, "root_path": root_path})
@@ -13503,7 +13503,7 @@ async def get_observability_metrics_partial(request: Request, _user=Depends(get_
Returns:
HTMLResponse: Rendered metrics dashboard template
"""
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
return request.app.state.templates.TemplateResponse("observability_metrics.html", {"request": request, "root_path": root_path})
@@ -13638,7 +13638,7 @@ async def get_observability_traces(
# Get traces ordered by most recent
traces = query.order_by(ObservabilityTrace.start_time.desc()).limit(limit).all()
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
return request.app.state.templates.TemplateResponse("observability_traces_list.html", {"request": request, "traces": traces, "root_path": root_path})
finally:
# Ensure close() always runs even if commit() fails
@@ -13670,7 +13670,7 @@ async def get_observability_trace_detail(request: Request, trace_id: str, _user=
if not trace:
raise HTTPException(status_code=404, detail="Trace not found")
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
return request.app.state.templates.TemplateResponse("observability_trace_detail.html", {"request": request, "trace": trace, "root_path": root_path})
finally:
# Ensure close() always runs even if commit() fails
@@ -15027,7 +15027,7 @@ async def get_tools_partial(
Returns:
HTMLResponse: Rendered tool metrics dashboard partial
"""
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
return request.app.state.templates.TemplateResponse(
"observability_tools.html",
{
@@ -15277,7 +15277,7 @@ async def get_prompts_partial(
Returns:
HTMLResponse: Rendered prompt metrics dashboard partial
"""
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
return request.app.state.templates.TemplateResponse(
"observability_prompts.html",
{
@@ -15527,7 +15527,7 @@ async def get_resources_partial(
Returns:
HTMLResponse: Rendered resource metrics dashboard partial
"""
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
return request.app.state.templates.TemplateResponse(
"observability_resources.html",
{
@@ -15586,7 +15586,7 @@ async def get_performance_stats(
worker["create_time"] = worker["create_time"].isoformat()
if request.headers.get("hx-request"):
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
return request.app.state.templates.TemplateResponse(
"performance_partial.html",
{
diff --git a/mcpgateway/routers/llm_admin_router.py b/mcpgateway/routers/llm_admin_router.py
index 66ffda796..07844d9ae 100644
--- a/mcpgateway/routers/llm_admin_router.py
+++ b/mcpgateway/routers/llm_admin_router.py
@@ -16,6 +16,7 @@
from fastapi.responses import HTMLResponse
# First-Party
+from mcpgateway.config import settings
from mcpgateway.db import LLMProviderType
from mcpgateway.middleware.rbac import get_current_user_with_permissions, require_permission
from mcpgateway.services.llm_provider_service import (
@@ -106,7 +107,7 @@ async def get_providers_partial(
"providers": provider_data,
"provider_types": LLMProviderType.get_all_types(),
"pagination": pagination,
- "root_path": request.scope.get("root_path", ""),
+ "root_path": settings.app_root_path,
},
)
@@ -203,7 +204,7 @@ async def get_models_partial(
"providers": provider_options,
"selected_provider_id": provider_id,
"pagination": pagination,
- "root_path": request.scope.get("root_path", ""),
+ "root_path": settings.app_root_path,
},
)
@@ -251,7 +252,7 @@ async def toggle_provider_html(
"health_status": provider.health_status,
"model_count": len(provider.models),
},
- "root_path": request.scope.get("root_path", ""),
+ "root_path": settings.app_root_path,
},
)
except LLMProviderNotFoundError as e:
@@ -370,7 +371,7 @@ async def toggle_model_html(
"enabled": model.enabled,
"deprecated": model.deprecated,
},
- "root_path": request.scope.get("root_path", ""),
+ "root_path": settings.app_root_path,
},
)
except LLMModelNotFoundError as e:
@@ -466,7 +467,7 @@ async def get_api_info_partial(
"models": model_data,
"stats": stats,
"llmchat_enabled": settings.llmchat_enabled,
- "root_path": request.scope.get("root_path", ""),
+ "root_path": settings.app_root_path,
},
)
diff --git a/mcpgateway/routers/oauth_router.py b/mcpgateway/routers/oauth_router.py
index 5180a3959..480264954 100644
--- a/mcpgateway/routers/oauth_router.py
+++ b/mcpgateway/routers/oauth_router.py
@@ -209,7 +209,7 @@ async def oauth_callback(
try:
# Get root path for URL construction
- root_path = request.scope.get("root_path", "") if request else ""
+ root_path = settings.app_root_path if request else ""
# Extract gateway_id from state parameter
# Try new base64-encoded JSON format first
diff --git a/mcpgateway/routers/sso.py b/mcpgateway/routers/sso.py
index 3e0d42c29..8f63f2bda 100644
--- a/mcpgateway/routers/sso.py
+++ b/mcpgateway/routers/sso.py
@@ -200,7 +200,7 @@ async def handle_sso_callback(
raise HTTPException(status_code=404, detail="SSO authentication is disabled")
# Get root path for URL construction
- root_path = request.scope.get("root_path", "") if request else ""
+ root_path = settings.app_root_path if request else ""
sso_service = SSOService(db)
@@ -226,7 +226,7 @@ async def handle_sso_callback(
# Third-Party
from fastapi.responses import RedirectResponse
- redirect_response = RedirectResponse(url=f"{root_path}/admin", status_code=302)
+ redirect_response = RedirectResponse(url=f"{root_path}/admin/", status_code=302)
# Set secure HTTP-only cookie using the same method as email auth
# First-Party
diff --git a/mcpgateway/utils/verify_credentials.py b/mcpgateway/utils/verify_credentials.py
index cfc9a12dc..7de78cceb 100644
--- a/mcpgateway/utils/verify_credentials.py
+++ b/mcpgateway/utils/verify_credentials.py
@@ -822,7 +822,7 @@ async def require_admin_auth(
accept_header = request.headers.get("accept", "")
if "text/html" in accept_header:
# Redirect browser to login page with error
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
raise HTTPException(status_code=status.HTTP_302_FOUND, detail="Admin privileges required", headers={"Location": f"{root_path}/admin/login?error=admin_required"})
else:
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Admin privileges required")
@@ -839,7 +839,7 @@ async def require_admin_auth(
# For 401, check if we should redirect browser users
accept_header = request.headers.get("accept", "")
if "text/html" in accept_header:
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
raise HTTPException(status_code=status.HTTP_302_FOUND, detail="Authentication required", headers={"Location": f"{root_path}/admin/login"})
# If JWT auth fails, fall back to basic auth for backward compatibility
except Exception:
@@ -862,7 +862,7 @@ async def require_admin_auth(
accept_header = request.headers.get("accept", "")
is_htmx = request.headers.get("hx-request") == "true"
if "text/html" in accept_header or is_htmx:
- root_path = request.scope.get("root_path", "")
+ root_path = settings.app_root_path
raise HTTPException(status_code=status.HTTP_302_FOUND, detail="Authentication required", headers={"Location": f"{root_path}/admin/login"})
else:
raise HTTPException(