works with /mcp/ in both mcp inspector and vscode
This commit is contained in:
@@ -24,61 +24,61 @@ logger = logging.getLogger(__name__)
|
||||
class VSCodePathNormalizationMiddleware(BaseHTTPMiddleware):
|
||||
"""
|
||||
ASGI middleware to normalize VS Code's OAuth discovery paths.
|
||||
|
||||
|
||||
VS Code's MCP client requests OAuth discovery endpoints with a /mcp prefix:
|
||||
- /mcp/.well-known/oauth-protected-resource
|
||||
- /mcp/.well-known/oauth-authorization-server
|
||||
- /mcp/.well-known/oauth-authorization-server
|
||||
- /mcp/.well-known/oauth-client
|
||||
|
||||
|
||||
This middleware transparently rewrites these paths to their canonical locations
|
||||
without requiring HTTP redirects, improving performance and maintaining
|
||||
OAuth 2.1 compliance.
|
||||
"""
|
||||
|
||||
|
||||
def __init__(self, app, debug: bool = False):
|
||||
super().__init__(app)
|
||||
self.debug = debug
|
||||
logger.info("VSCode path normalization middleware initialized")
|
||||
|
||||
|
||||
async def dispatch(self, request: Request, call_next: Callable) -> Response:
|
||||
"""
|
||||
Process request and normalize VS Code paths transparently.
|
||||
|
||||
|
||||
Args:
|
||||
request: The incoming HTTP request
|
||||
call_next: The next middleware/handler in the stack
|
||||
|
||||
|
||||
Returns:
|
||||
Response from the normalized request
|
||||
"""
|
||||
original_path = request.url.path
|
||||
|
||||
|
||||
# Check if this is a VS Code OAuth discovery request
|
||||
if self._should_normalize_path(original_path):
|
||||
# Normalize the path in-place
|
||||
normalized_path = self._normalize_vscode_path(original_path)
|
||||
|
||||
|
||||
# Modify the request scope to use the normalized path
|
||||
request.scope["path"] = normalized_path
|
||||
request.scope["raw_path"] = normalized_path.encode("utf-8")
|
||||
|
||||
|
||||
# Log the path normalization for debugging
|
||||
if self.debug or self._is_vscode_client(request):
|
||||
logger.info(f"VS Code path normalization: {original_path} → {normalized_path}")
|
||||
user_agent = request.headers.get("user-agent", "unknown")
|
||||
logger.debug(f"User agent: {user_agent}")
|
||||
|
||||
|
||||
# Continue with the request (using normalized path if modified)
|
||||
response = await call_next(request)
|
||||
return response
|
||||
|
||||
|
||||
def _should_normalize_path(self, path: str) -> bool:
|
||||
"""
|
||||
Determine if a path should be normalized for VS Code compatibility.
|
||||
|
||||
|
||||
Args:
|
||||
path: The request path to check
|
||||
|
||||
|
||||
Returns:
|
||||
True if the path needs normalization, False otherwise
|
||||
"""
|
||||
@@ -86,18 +86,18 @@ class VSCodePathNormalizationMiddleware(BaseHTTPMiddleware):
|
||||
path.startswith("/mcp/.well-known/") and
|
||||
path in [
|
||||
"/mcp/.well-known/oauth-protected-resource",
|
||||
"/mcp/.well-known/oauth-authorization-server",
|
||||
"/mcp/.well-known/oauth-authorization-server",
|
||||
"/mcp/.well-known/oauth-client"
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def _normalize_vscode_path(self, path: str) -> str:
|
||||
"""
|
||||
Normalize a VS Code path to its canonical OAuth 2.1 location.
|
||||
|
||||
|
||||
Args:
|
||||
path: The VS Code path to normalize
|
||||
|
||||
|
||||
Returns:
|
||||
The canonical OAuth 2.1 path
|
||||
"""
|
||||
@@ -105,14 +105,14 @@ class VSCodePathNormalizationMiddleware(BaseHTTPMiddleware):
|
||||
# Remove the /mcp prefix to get canonical path
|
||||
return path.replace("/mcp", "", 1)
|
||||
return path
|
||||
|
||||
|
||||
def _is_vscode_client(self, request: Request) -> bool:
|
||||
"""
|
||||
Detect if the request is from VS Code's MCP client.
|
||||
|
||||
|
||||
Args:
|
||||
request: The HTTP request to analyze
|
||||
|
||||
|
||||
Returns:
|
||||
True if the request appears to be from VS Code, False otherwise
|
||||
"""
|
||||
@@ -123,13 +123,13 @@ class VSCodePathNormalizationMiddleware(BaseHTTPMiddleware):
|
||||
def is_vscode_client(request: Request) -> bool:
|
||||
"""
|
||||
Utility function to detect VS Code MCP client requests.
|
||||
|
||||
|
||||
This function can be used by other components that need to adapt
|
||||
behavior for VS Code clients.
|
||||
|
||||
|
||||
Args:
|
||||
request: The HTTP request to analyze
|
||||
|
||||
|
||||
Returns:
|
||||
True if the request appears to be from VS Code, False otherwise
|
||||
"""
|
||||
@@ -140,16 +140,16 @@ def is_vscode_client(request: Request) -> bool:
|
||||
def get_vscode_client_info(request: Request) -> Optional[dict]:
|
||||
"""
|
||||
Extract VS Code client information from request headers.
|
||||
|
||||
|
||||
Args:
|
||||
request: The HTTP request to analyze
|
||||
|
||||
|
||||
Returns:
|
||||
Dictionary with client info if VS Code detected, None otherwise
|
||||
"""
|
||||
if not is_vscode_client(request):
|
||||
return None
|
||||
|
||||
|
||||
user_agent = request.headers.get("user-agent", "")
|
||||
return {
|
||||
"is_vscode": True,
|
||||
|
||||
Reference in New Issue
Block a user