From b59ad212f439e51087bc8c71ea53dd8af47ae59a Mon Sep 17 00:00:00 2001 From: tin-berri Date: Fri, 3 Jul 2026 10:25:58 -0700 Subject: [PATCH] feat(ui): add token endpoint auth method selector to MCP OAuth forms (#31739) PR #31635 added a per-server token_endpoint_auth_method (client_secret_basic or client_secret_post) for upstream OAuth token endpoints, but it could only be set by editing the stored credentials JSON. This surfaces it in the dashboard as an optional selector directly under the Token URL field, in both the create form (OAuthFormFields, M2M and interactive flows) and the edit form. The field binds to credentials.token_endpoint_auth_method, which the backend already reads; the value is sent only when chosen, so leaving it blank keeps the existing setting and preserves the client_secret_post default. --- .../mcp_tools/OAuthFormFields.test.tsx | 41 ++++++ .../components/mcp_tools/OAuthFormFields.tsx | 3 + .../TokenEndpointAuthMethodField.tsx | 38 ++++++ .../mcp_tools/create_mcp_server.test.tsx | 118 ++++++++++++------ .../mcp_tools/mcp_server_edit.test.tsx | 63 ++++++++++ .../components/mcp_tools/mcp_server_edit.tsx | 2 + .../src/components/mcp_tools/testUtils.ts | 27 ++++ 7 files changed, 253 insertions(+), 39 deletions(-) create mode 100644 ui/litellm-dashboard/src/components/mcp_tools/TokenEndpointAuthMethodField.tsx create mode 100644 ui/litellm-dashboard/src/components/mcp_tools/testUtils.ts diff --git a/ui/litellm-dashboard/src/components/mcp_tools/OAuthFormFields.test.tsx b/ui/litellm-dashboard/src/components/mcp_tools/OAuthFormFields.test.tsx index 888f3066252..02b7e3af09c 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/OAuthFormFields.test.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/OAuthFormFields.test.tsx @@ -91,6 +91,47 @@ describe("OAuthFormFields", () => { }); }); + describe("token endpoint auth method selector", () => { + it("renders directly below the Token URL field in interactive mode", () => { + render( + + + , + ); + const tokenUrlLabel = screen.getByText("Token URL (optional)"); + const authMethodLabel = screen.getByText("Token Endpoint Auth Method (optional)"); + expect(tokenUrlLabel.compareDocumentPosition(authMethodLabel) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy(); + }); + + it("renders directly below the Token URL field in M2M mode", () => { + render( + + + , + ); + const tokenUrlLabel = screen.getByText("Token URL"); + const authMethodLabel = screen.getByText("Token Endpoint Auth Method (optional)"); + expect(tokenUrlLabel.compareDocumentPosition(authMethodLabel) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy(); + }); + + it("offers client_secret_basic and client_secret_post options", async () => { + render( + + + , + ); + const authMethodLabel = screen.getByText("Token Endpoint Auth Method (optional)"); + const selector = authMethodLabel.closest(".ant-form-item")!.querySelector(".ant-select-selector")!; + await act(async () => { + fireEvent.mouseDown(selector); + }); + await waitFor(() => { + expect(screen.getByText("Client Secret Basic")).toBeInTheDocument(); + expect(screen.getByText("Client Secret Post")).toBeInTheDocument(); + }); + }); + }); + // ── token_validation_json inline JSON validator ────────────────────────────── describe("token_validation_json validation", () => { diff --git a/ui/litellm-dashboard/src/components/mcp_tools/OAuthFormFields.tsx b/ui/litellm-dashboard/src/components/mcp_tools/OAuthFormFields.tsx index 27d90d5ae1a..93afefe4358 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/OAuthFormFields.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/OAuthFormFields.tsx @@ -3,6 +3,7 @@ import { Form, Input, InputNumber, Select, Tooltip } from "antd"; import { InfoCircleOutlined } from "@ant-design/icons"; import { Button, TextInput } from "@tremor/react"; import { OAUTH_FLOW } from "./types"; +import TokenEndpointAuthMethodField from "./TokenEndpointAuthMethodField"; interface OAuthFlowStatus { startOAuthFlow: () => void; @@ -101,6 +102,7 @@ const OAuthFormFields: React.FC = ({ > + = ({ > + = ({ isEditing = false }) => ( + + Token Endpoint Auth Method (optional) + + + + + } + name={["credentials", "token_endpoint_auth_method"]} + > +