diff --git a/src/fastmcp/server/auth/oauth_proxy.py b/src/fastmcp/server/auth/oauth_proxy.py
index 717b6aa01..c525da1af 100644
--- a/src/fastmcp/server/auth/oauth_proxy.py
+++ b/src/fastmcp/server/auth/oauth_proxy.py
@@ -68,9 +68,10 @@ from fastmcp.utilities.logging import get_logger
from fastmcp.utilities.ui import (
BUTTON_STYLES,
DETAIL_BOX_STYLES,
+ DETAILS_STYLES,
INFO_BOX_STYLES,
+ REDIRECT_SECTION_STYLES,
TOOLTIP_STYLES,
- create_detail_box,
create_logo,
create_page,
create_secure_html_response,
@@ -233,16 +234,13 @@ def create_consent_html(
txn_id: str,
csrf_token: str,
client_name: str | None = None,
- title: str = "Authorization Consent",
+ title: str = "Application Access Request",
server_name: str | None = None,
server_icon_url: str | None = None,
server_website_url: str | None = None,
+ client_website_url: str | None = None,
) -> str:
"""Create a styled HTML consent page for OAuth authorization requests."""
- # Format scopes for display
- scopes_display = ", ".join(scopes) if scopes else "None"
-
- # Build warning box with client name if available
import html as html_module
client_display = html_module.escape(client_name or client_id)
@@ -251,29 +249,58 @@ def create_consent_html(
# Make server name a hyperlink if website URL is available
if server_website_url:
website_url_escaped = html_module.escape(server_website_url)
- server_display = f'{server_name_escaped}'
+ server_display = f'{server_name_escaped}'
else:
server_display = server_name_escaped
- warning_box = f"""
-
-
{client_display} is requesting access to {server_display}.
-
Review the details below before approving.
+ # Build intro box with call-to-action
+ intro_box = f"""
+
+
The application {client_display} wants to access the MCP server {server_display}. Please ensure you recognize the callback address below.
"""
- # Build detail box with client information
- detail_rows = []
- if client_name:
- detail_rows.append(("Client Name", client_name))
- detail_rows.extend(
+ # Build redirect URI section (yellow box, centered)
+ redirect_uri_escaped = html_module.escape(redirect_uri)
+ redirect_section = f"""
+
+
Credentials will be sent to:
+
{redirect_uri_escaped}
+
+ """
+
+ # Build advanced details with collapsible section
+ detail_rows = [
+ ("Application Name", html_module.escape(client_name or client_id)),
+ ("Application Website", html_module.escape(client_website_url or "N/A")),
+ ("Application ID", client_id),
+ ("Redirect URI", redirect_uri_escaped),
+ (
+ "Requested Scopes",
+ ", ".join(html_module.escape(s) for s in scopes) if scopes else "None",
+ ),
+ ]
+
+ detail_rows_html = "\n".join(
[
- ("Client ID", client_id),
- ("Redirect URI", redirect_uri),
- ("Requested Scopes", scopes_display),
+ f"""
+
+ """
+ for label, value in detail_rows
]
)
- detail_box = create_detail_box(detail_rows)
+
+ advanced_details = f"""
+
+ Advanced Details
+
+ {detail_rows_html}
+
+
+ """
# Build form with buttons
form = f"""
@@ -281,13 +308,13 @@ def create_consent_html(
-
+
"""
- # Build help link with tooltip
+ # Build help link with tooltip (identical to current implementation)
help_link = """
@@ -312,9 +339,10 @@ def create_consent_html(
content = f"""
{create_logo(icon_url=server_icon_url, alt_text=server_name or "FastMCP")}
-
Authorization Consent
- {warning_box}
- {detail_box}
+ Application Access Request
+ {intro_box}
+ {redirect_section}
+ {advanced_details}
{form}
{help_link}
@@ -322,7 +350,12 @@ def create_consent_html(
# Additional styles needed for this page
additional_styles = (
- INFO_BOX_STYLES + DETAIL_BOX_STYLES + BUTTON_STYLES + TOOLTIP_STYLES
+ INFO_BOX_STYLES
+ + REDIRECT_SECTION_STYLES
+ + DETAILS_STYLES
+ + DETAIL_BOX_STYLES
+ + BUTTON_STYLES
+ + TOOLTIP_STYLES
)
# Need to allow form-action for form submission
diff --git a/src/fastmcp/utilities/ui.py b/src/fastmcp/utilities/ui.py
index 359a1840b..f2e882f4f 100644
--- a/src/fastmcp/utilities/ui.py
+++ b/src/fastmcp/utilities/ui.py
@@ -111,15 +111,23 @@ BUTTON_STYLES = """
# Info box / message box styles
INFO_BOX_STYLES = """
.info-box {
- background: #f9fafb;
- border: 1px solid #e5e7eb;
+ background: #f0f9ff;
+ border: 1px solid #bae6fd;
border-radius: 0.5rem;
- padding: 0.875rem;
- margin: 1.25rem 0;
- font-size: 0.875rem;
- color: #6b7280;
- font-family: 'SF Mono', 'Monaco', 'Consolas', 'Courier New', monospace;
+ padding: 1rem;
+ margin-bottom: 1.5rem;
text-align: left;
+ font-size: 0.9375rem;
+ line-height: 1.5;
+ color: #374151;
+ }
+
+ .info-box p {
+ margin-bottom: 0.5rem;
+ }
+
+ .info-box p:last-child {
+ margin-bottom: 0;
}
.info-box.centered {
@@ -133,10 +141,22 @@ INFO_BOX_STYLES = """
}
.info-box strong {
- color: #111827;
+ color: #0ea5e9;
font-weight: 600;
}
+ .info-box .server-name-link {
+ color: #0ea5e9;
+ text-decoration: underline;
+ font-weight: 600;
+ cursor: pointer;
+ transition: opacity 0.15s;
+ }
+
+ .info-box .server-name-link:hover {
+ opacity: 0.8;
+ }
+
.warning-box {
background: #f0f9ff;
border: 1px solid #bae6fd;
@@ -236,10 +256,11 @@ DETAIL_BOX_STYLES = """
.detail-label {
font-weight: 600;
- min-width: 140px;
+ min-width: 160px;
color: #6b7280;
font-size: 0.875rem;
flex-shrink: 0;
+ padding-right: 1rem;
}
.detail-value {
@@ -252,6 +273,72 @@ DETAIL_BOX_STYLES = """
}
"""
+# Redirect section styles (for OAuth redirect URI box)
+REDIRECT_SECTION_STYLES = """
+ .redirect-section {
+ background: #fffbeb;
+ border: 1px solid #fcd34d;
+ border-radius: 0.5rem;
+ padding: 1rem;
+ margin-bottom: 1.5rem;
+ text-align: left;
+ }
+
+ .redirect-section .label {
+ font-size: 0.875rem;
+ color: #6b7280;
+ font-weight: 600;
+ margin-bottom: 0.5rem;
+ display: block;
+ }
+
+ .redirect-section .value {
+ font-family: 'SF Mono', 'Monaco', 'Consolas', 'Courier New', monospace;
+ font-size: 0.875rem;
+ color: #111827;
+ word-break: break-all;
+ margin-top: 0.25rem;
+ }
+"""
+
+# Collapsible details styles
+DETAILS_STYLES = """
+ details {
+ margin-bottom: 1.5rem;
+ text-align: left;
+ }
+
+ summary {
+ cursor: pointer;
+ font-size: 0.875rem;
+ color: #6b7280;
+ font-weight: 600;
+ list-style: none;
+ padding: 0.5rem;
+ border-radius: 0.25rem;
+ }
+
+ summary:hover {
+ background: #f9fafb;
+ }
+
+ summary::marker {
+ display: none;
+ }
+
+ summary::before {
+ content: "▶";
+ display: inline-block;
+ margin-right: 0.5rem;
+ transition: transform 0.2s;
+ font-size: 0.75rem;
+ }
+
+ details[open] summary::before {
+ transform: rotate(90deg);
+ }
+"""
+
# Helper text styles
HELPER_TEXT_STYLES = """
.close-instruction, .help-text {