mirror of
https://github.com/mcp-use/mcp-use.git
synced 2026-09-14 18:06:01 +08:00
30b23271d5
* fix(python): respect configured inspector path * fix(python): treat inspector path as a base prefix * fix(python): format inspector fixes for ruff * fix(ci): run conformance with node 22 * fix(ci): fail conformance runs with empty results * chore(ci): drop inline conformance result validation * docs(python): clarify inspector path behavior
88 lines
3.5 KiB
Python
88 lines
3.5 KiB
Python
import logging
|
|
import os
|
|
from urllib.parse import urlencode
|
|
|
|
import httpx
|
|
from starlette.requests import Request
|
|
from starlette.responses import HTMLResponse, RedirectResponse, Response
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# Override with INSPECTOR_CDN_BASE_URL (e.g. http://localhost:2967) for E2E or local dev
|
|
INSPECTOR_CDN_BASE_URL = os.environ.get(
|
|
"INSPECTOR_CDN_BASE_URL",
|
|
"https://unpkg.com/@mcp-use/inspector@latest/dist/web",
|
|
).rstrip("/")
|
|
INDEX_URL = f"{INSPECTOR_CDN_BASE_URL}/index.html"
|
|
|
|
|
|
async def _inspector_index(
|
|
request: Request,
|
|
mcp_path: str = "/mcp",
|
|
inspector_path: str = "/inspector",
|
|
):
|
|
"""Serve the inspector index.html file with autoconnect parameter."""
|
|
# Get the server URL from the request
|
|
server_url = f"{request.url.scheme}://{request.url.netloc}{mcp_path}"
|
|
|
|
# Check if server or autoConnect parameter is already present
|
|
server_param = request.query_params.get("server")
|
|
autoconnect_param = request.query_params.get("autoConnect")
|
|
|
|
if not server_param and not autoconnect_param:
|
|
# Redirect to add the autoConnect parameter (note: capital C)
|
|
autoconnect_url = (
|
|
f"{request.url.scheme}://{request.url.netloc}{inspector_path}?{urlencode({'autoConnect': server_url})}"
|
|
)
|
|
return RedirectResponse(url=autoconnect_url, status_code=302)
|
|
|
|
# Fetch the CDN file
|
|
try:
|
|
async with httpx.AsyncClient(timeout=10.0) as client:
|
|
response = await client.get(INDEX_URL, follow_redirects=True)
|
|
if response.status_code == 200:
|
|
html = response.text.replace('src="/inspector/assets/', f'src="{INSPECTOR_CDN_BASE_URL}/assets/')
|
|
html = html.replace('href="/inspector/assets/', f'href="{INSPECTOR_CDN_BASE_URL}/assets/')
|
|
return HTMLResponse(html)
|
|
else:
|
|
logger.warning(
|
|
f"Failed to fetch inspector from CDN: {INDEX_URL} returned status {response.status_code}"
|
|
)
|
|
except Exception as e:
|
|
logger.exception(f"Failed to fetch inspector from CDN: {INDEX_URL} - {e}")
|
|
|
|
# CDN failed - return error page (no redirect to avoid loop)
|
|
return HTMLResponse(
|
|
content=f"""
|
|
<html>
|
|
<head><title>Inspector Unavailable</title></head>
|
|
<body style="font-family: sans-serif; padding: 2rem;">
|
|
<h1>Inspector CDN Unavailable</h1>
|
|
<p>Could not load the inspector from CDN: <code>{INDEX_URL}</code></p>
|
|
<p>The <code>index.html</code> file may be missing from the npm package.</p>
|
|
<p style="margin-top: 2rem;">Server URL: <code>{server_url}</code></p>
|
|
</body>
|
|
</html>
|
|
""",
|
|
status_code=503,
|
|
)
|
|
|
|
|
|
async def _inspector_static(request: Request):
|
|
"""Serve static files from the CDN."""
|
|
path = request.path_params.get("path", "")
|
|
cdn_url = f"{INSPECTOR_CDN_BASE_URL}/{path}"
|
|
try:
|
|
async with httpx.AsyncClient(timeout=10.0) as client:
|
|
response = await client.get(cdn_url, follow_redirects=True)
|
|
if response.status_code == 200:
|
|
return Response(content=response.content, media_type=response.headers.get("Content-Type", "text/plain"))
|
|
else:
|
|
logger.warning(
|
|
f"Failed to fetch static file from CDN: {cdn_url} returned status {response.status_code}"
|
|
)
|
|
except Exception as e:
|
|
logger.exception(f"Failed to fetch static file from CDN: {cdn_url} - {e}")
|
|
|
|
return HTMLResponse("File not found", status_code=404)
|