[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:
Akhil Vamshi Konam
2026-04-23 00:03:01 +05:30
committed by GitHub
parent 6f974deaf1
commit 3aa23fa61c
3 changed files with 23 additions and 19 deletions
@@ -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
+2 -2
View File
@@ -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
+13 -11
View File
@@ -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"
)