mirror of
https://github.com/makeplane/plane.git
synced 2026-06-14 03:30:00 +00:00
[PAI-1383] fix: mcp error status codes and add structured exception logging (#6866)
* chore: improve MCP error logging and update status codes to 400 for connection and OAuth failures * fix: improve MCP connection and decryption error logging by including stack traces and sanitizing error messages
This commit is contained in:
committed by
GitHub
parent
6f974deaf1
commit
3aa23fa61c
@@ -74,8 +74,8 @@ async def _probe_mcp_async(url, headers):
|
||||
err_msg = _mcp_error_from_exc(exc)
|
||||
if err_msg:
|
||||
return False, {"error": err_msg}
|
||||
logger.warning("Streamable HTTP probe failed for %s: %s: %s", url, type(exc).__name__, exc)
|
||||
return False, {"error": f"Connection failed: {type(exc).__name__}"}
|
||||
logger.warning("Streamable HTTP probe failed for %s", url, exc_info=True)
|
||||
return False, {"error": "Connection failed."}
|
||||
|
||||
|
||||
def test_mcp_connection(url, headers=None):
|
||||
@@ -84,9 +84,9 @@ def test_mcp_connection(url, headers=None):
|
||||
"""
|
||||
try:
|
||||
return async_to_sync(_probe_mcp_async)(url, headers)
|
||||
except Exception as e:
|
||||
logger.exception("Unexpected error testing MCP connection to %s", url)
|
||||
return False, {"error": f"Unexpected error during connection test: {e}"}
|
||||
except Exception:
|
||||
logger.warning("Unexpected error testing MCP connection to %s", url, exc_info=True)
|
||||
return False, {"error": "Unexpected error during connection test."}
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -228,6 +228,7 @@ def discover_oauth_metadata(mcp_url):
|
||||
prm_data = prm_resp.json()
|
||||
break
|
||||
except Exception:
|
||||
logger.debug("PRM fetch failed for %s", prm_url, exc_info=True)
|
||||
continue
|
||||
|
||||
if not prm_data:
|
||||
@@ -253,6 +254,7 @@ def discover_oauth_metadata(mcp_url):
|
||||
as_data = as_resp.json()
|
||||
break
|
||||
except Exception:
|
||||
logger.debug("AS metadata fetch failed for %s", as_url, exc_info=True)
|
||||
continue
|
||||
|
||||
if not as_data:
|
||||
@@ -568,7 +570,7 @@ def decrypt_auth_config(auth_config):
|
||||
try:
|
||||
config[key] = _decrypt_value(config[key])
|
||||
except Exception:
|
||||
logger.warning("Failed to decrypt auth_config key '%s'; value may be unencrypted", key)
|
||||
logger.warning("Failed to decrypt auth_config key '%s'; value may be unencrypted", key, exc_info=True)
|
||||
|
||||
return config
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ def refresh_token_if_expired(credentials, decrypted_config, mcp_url, app_label):
|
||||
credentials.auth_config = encrypt_auth_config(decrypted_config)
|
||||
credentials.save(update_fields=["auth_config"])
|
||||
logger.info("Refreshed OAuth token for MCP app %s", app_label)
|
||||
except ValueError as e:
|
||||
logger.warning("OAuth token refresh failed for MCP app %s: %s", app_label, e)
|
||||
except ValueError:
|
||||
logger.warning("OAuth token refresh failed for MCP app %s", app_label, exc_info=True)
|
||||
|
||||
return decrypted_config
|
||||
|
||||
@@ -279,7 +279,7 @@ class MCPApplicationConnectAPIView(BaseAPIView):
|
||||
if not success:
|
||||
return Response(
|
||||
{"error": data.get("error", "Could not connect to MCP server.")},
|
||||
status=status.HTTP_502_BAD_GATEWAY,
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
save_credentials_and_activate(mcp_app, workspace_id, user_id, auth_config={})
|
||||
@@ -310,7 +310,7 @@ class MCPApplicationConnectAPIView(BaseAPIView):
|
||||
if not success:
|
||||
return Response(
|
||||
{"error": data.get("error", "Could not connect to MCP server.")},
|
||||
status=status.HTTP_502_BAD_GATEWAY,
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
save_credentials_and_activate(mcp_app, workspace_id, user_id, credentials.auth_config)
|
||||
@@ -335,10 +335,11 @@ class MCPApplicationOAuthConnectAPIView(BaseAPIView):
|
||||
|
||||
try:
|
||||
metadata = discover_oauth_metadata(mcp_app.url)
|
||||
except ValueError as e:
|
||||
except ValueError:
|
||||
logger.warning("OAuth metadata discovery failed for MCP app %s", pk, exc_info=True)
|
||||
return Response(
|
||||
{"error": "OAuth discovery failed.", "detail": str(e)},
|
||||
status=status.HTTP_502_BAD_GATEWAY,
|
||||
{"error": "OAuth discovery failed."},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
callback_url = f"{settings.API_URL}/silo/mcp-applications/oauth/callback/"
|
||||
@@ -346,7 +347,7 @@ class MCPApplicationOAuthConnectAPIView(BaseAPIView):
|
||||
if not metadata.get("registration_endpoint"):
|
||||
return Response(
|
||||
{"error": "Server does not support dynamic client registration."},
|
||||
status=status.HTTP_502_BAD_GATEWAY,
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
try:
|
||||
@@ -355,10 +356,11 @@ class MCPApplicationOAuthConnectAPIView(BaseAPIView):
|
||||
redirect_uri=callback_url,
|
||||
client_name="Plane",
|
||||
)
|
||||
except ValueError as e:
|
||||
except ValueError:
|
||||
logger.warning("OAuth DCR failed for MCP app %s", pk, exc_info=True)
|
||||
return Response(
|
||||
{"error": "Dynamic client registration failed.", "detail": str(e)},
|
||||
status=status.HTTP_502_BAD_GATEWAY,
|
||||
{"error": "Dynamic client registration failed."},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
dcr_client_id = dcr_result["client_id"]
|
||||
@@ -471,8 +473,8 @@ class MCPApplicationOAuthCallbackAPIView(BaseAPIView):
|
||||
"token_endpoint_auth_method", "client_secret_post"
|
||||
),
|
||||
)
|
||||
except ValueError as e:
|
||||
logger.error("OAuth token exchange failed for MCP app %s: %s", mcp_app_id, e)
|
||||
except ValueError:
|
||||
logger.error("OAuth token exchange failed for MCP app %s", mcp_app_id, exc_info=True)
|
||||
return HttpResponseRedirect(
|
||||
f"{connectors_url}&status=error&reason=token_exchange_failed"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user