Description
Submitting the "Add New MCP Server or Gateway" form with any authentication type (Bearer, Basic, OAuth 2.0, etc.) fails with:
String should match pattern '^[a-zA-Z_][a-zA-Z0-9_-]*$'
The error gives no field name, making it appear as though the gateway name or URL is invalid when it is not.
Steps to reproduce
- Open the Admin UI → Gateways tab
- Click Add Gateway
- Fill in MCP Server Name and MCP Server URL
- Set Authentication Type to anything other than "Query Parameter (INSECURE)" — e.g. OAuth 2.0 or Bearer Token
- Click Add Gateway
- Observe the error banner above the form
Root cause
HTML layer: The form contains a hidden <div id="auth-query_param-fields-gw"> with an <input name="auth_query_param_key"> inside it. The div is toggled visible/hidden via style.display by handleAuthTypeChange() in auth.js, but the input is never disabled. Browsers submit all enabled inputs regardless of visibility, so auth_query_param_key="" is always included in the POST body.
Server layer: _parse_gateway_data_from_request() in admin.py collects every non-None form field into the data dict:
for key in form.keys():
value = form.get(key)
if value is not None:
data[key] = value
An empty string is not None, so auth_query_param_key: "" reaches GatewayCreate(**data). Pydantic then rejects "" against the field's pattern=r"^[a-zA-Z_][a-zA-Z0-9_\-]*$" constraint and raises a ValidationError.
The gateway update path already handles this correctly (str(form.get("auth_query_param_key", "")) or None), but the create path goes through the shared parser which lacks the same normalization.
Fix (two-layer)
-
admin.py / _parse_gateway_data_from_request — after form parsing, convert empty strings to None for auth_query_param_key and auth_query_param_value before the data reaches Pydantic. Mirrors what the update handler already does; provides server-side defense in depth regardless of frontend state.
-
admin_ui/auth.js / handleAuthTypeChange — disable inputs inside sections being hidden and re-enable them when shown. Stops the spurious empty-string fields from reaching the server in the first place.
Description
Submitting the "Add New MCP Server or Gateway" form with any authentication type (Bearer, Basic, OAuth 2.0, etc.) fails with:
The error gives no field name, making it appear as though the gateway name or URL is invalid when it is not.
Steps to reproduce
Root cause
HTML layer: The form contains a hidden
<div id="auth-query_param-fields-gw">with an<input name="auth_query_param_key">inside it. The div is toggled visible/hidden viastyle.displaybyhandleAuthTypeChange()inauth.js, but the input is neverdisabled. Browsers submit all enabled inputs regardless of visibility, soauth_query_param_key=""is always included in the POST body.Server layer:
_parse_gateway_data_from_request()inadmin.pycollects every non-Noneform field into the data dict:An empty string is not
None, soauth_query_param_key: ""reachesGatewayCreate(**data). Pydantic then rejects""against the field'spattern=r"^[a-zA-Z_][a-zA-Z0-9_\-]*$"constraint and raises aValidationError.The gateway update path already handles this correctly (
str(form.get("auth_query_param_key", "")) or None), but the create path goes through the shared parser which lacks the same normalization.Fix (two-layer)
admin.py/_parse_gateway_data_from_request— after form parsing, convert empty strings toNoneforauth_query_param_keyandauth_query_param_valuebefore the data reaches Pydantic. Mirrors what the update handler already does; provides server-side defense in depth regardless of frontend state.admin_ui/auth.js/handleAuthTypeChange— disable inputs inside sections being hidden and re-enable them when shown. Stops the spurious empty-string fields from reaching the server in the first place.