From 7a2b167805b879009c357d3c7ab8bd8932c22be4 Mon Sep 17 00:00:00 2001 From: Yassin Kortam Date: Mon, 20 Jul 2026 14:49:36 -0700 Subject: [PATCH] feat(ui): add ID-JAG (Okta Cross App Access) auth type to MCP server form Admins could configure an oauth2_id_jag MCP server only through config.yaml or the REST API; the dashboard's Add/Edit MCP Server form had no ID-JAG option, so the leg-2 resource token endpoint and resource indicator were unreachable from the UI. Add an ID-JAG auth type that renders the two-leg fields (IdP token endpoint for leg 1, resource token endpoint for leg 2, client id/secret, audience, resource indicator, scopes), sending the credential-blob fields nested under credentials so the existing backend persists them unchanged. The auth-type Select is switched to virtual={false} so all options render deterministically; the antd virtual list otherwise drops the last option once the list grows, which is both an a11y gap and what broke the option-selection tests. --- .../_components/IdJagFormFields.tsx | 105 ++++++++++++++++++ .../_components/create_mcp_server.test.tsx | 67 +++++++++++ .../_components/create_mcp_server.tsx | 7 +- .../_components/mcp_server_edit.test.tsx | 38 +++++++ .../_components/mcp_server_edit.tsx | 14 ++- .../src/components/mcp_tools/types.tsx | 1 + ui/litellm-dashboard/tsconfig.tsbuildinfo | 2 +- 7 files changed, 230 insertions(+), 4 deletions(-) create mode 100644 ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/IdJagFormFields.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/IdJagFormFields.tsx b/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/IdJagFormFields.tsx new file mode 100644 index 00000000000..0d95c5b426d --- /dev/null +++ b/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/IdJagFormFields.tsx @@ -0,0 +1,105 @@ +import React from "react"; +import { Form, Input, Select, Tooltip } from "antd"; +import { InfoCircleOutlined } from "@ant-design/icons"; + +interface IdJagFormFieldsProps { + isEditing?: boolean; +} + +const fieldClassName = "rounded-lg border-gray-300 focus:border-blue-500 focus:ring-blue-500"; + +const FieldLabel: React.FC<{ label: string; tooltip: string }> = ({ label, tooltip }) => ( + + {label} + + + + +); + +const IdJagFormFields: React.FC = ({ isEditing = false }) => { + const placeholderSuffix = isEditing ? " (leave blank to keep existing)" : ""; + + return ( + <> + + } + name="token_exchange_endpoint" + rules={[{ required: !isEditing, message: "The IdP token endpoint is required for ID-JAG" }]} + > + + + + } + name={["credentials", "id_jag_resource_token_endpoint"]} + rules={[{ required: !isEditing, message: "The resource token endpoint is required for ID-JAG" }]} + > + + + + } + name={["credentials", "client_id"]} + rules={[{ required: !isEditing, message: "Client ID is required for ID-JAG" }]} + > + + + + } + name={["credentials", "client_secret"]} + rules={[{ required: !isEditing, message: "Client Secret is required for ID-JAG" }]} + > + + + + } + name="audience" + > + + + + } + name={["credentials", "id_jag_resource"]} + > + + + } + name={["credentials", "scopes"]} + > + + +