mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-09 03:18:44 +00:00
ui feat fixes
This commit is contained in:
parent
4f12a0f517
commit
9ff9134cf1
4 changed files with 390 additions and 247 deletions
|
|
@ -0,0 +1,151 @@
|
|||
import React from "react";
|
||||
import { Form, Select, Tooltip } from "antd";
|
||||
import { InfoCircleOutlined } from "@ant-design/icons";
|
||||
import { Button, TextInput } from "@tremor/react";
|
||||
import { OAUTH_FLOW } from "./types";
|
||||
|
||||
interface OAuthFlowStatus {
|
||||
startOAuthFlow: () => void;
|
||||
status: string;
|
||||
error: string | null;
|
||||
tokenResponse: { access_token?: string; expires_in?: number } | null;
|
||||
}
|
||||
|
||||
interface OAuthFormFieldsProps {
|
||||
isM2M: boolean;
|
||||
isEditing?: boolean;
|
||||
oauthFlow?: OAuthFlowStatus;
|
||||
initialFlowType?: string;
|
||||
}
|
||||
|
||||
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 }) => (
|
||||
<span className="text-sm font-medium text-gray-700 flex items-center">
|
||||
{label}
|
||||
<Tooltip title={tooltip}>
|
||||
<InfoCircleOutlined className="ml-2 text-blue-400 hover:text-blue-600 cursor-help" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
);
|
||||
|
||||
const OAuthFormFields: React.FC<OAuthFormFieldsProps> = ({
|
||||
isM2M,
|
||||
isEditing = false,
|
||||
oauthFlow,
|
||||
initialFlowType,
|
||||
}) => {
|
||||
const placeholderSuffix = isEditing ? " (leave blank to keep existing)" : "";
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form.Item
|
||||
label={<span className="text-sm font-medium text-gray-700">OAuth Flow Type</span>}
|
||||
name="oauth_flow_type"
|
||||
{...(initialFlowType ? { initialValue: initialFlowType } : {})}
|
||||
>
|
||||
<Select className="rounded-lg" size="large">
|
||||
<Select.Option value={OAUTH_FLOW.INTERACTIVE}>Interactive (PKCE)</Select.Option>
|
||||
<Select.Option value={OAUTH_FLOW.M2M}>Machine-to-Machine (M2M)</Select.Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
{isM2M ? (
|
||||
<>
|
||||
<Form.Item
|
||||
label={<FieldLabel label="Client ID" tooltip="OAuth2 client ID for the client_credentials grant." />}
|
||||
name={["credentials", "client_id"]}
|
||||
rules={[{ required: true, message: "Client ID is required for M2M OAuth" }]}
|
||||
>
|
||||
<TextInput type="password" placeholder={`Enter OAuth client ID${placeholderSuffix}`} className={fieldClassName} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={<FieldLabel label="Client Secret" tooltip="OAuth2 client secret for the client_credentials grant." />}
|
||||
name={["credentials", "client_secret"]}
|
||||
rules={[{ required: true, message: "Client Secret is required for M2M OAuth" }]}
|
||||
>
|
||||
<TextInput type="password" placeholder={`Enter OAuth client secret${placeholderSuffix}`} className={fieldClassName} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={<FieldLabel label="Token URL" tooltip="Token endpoint URL for the client_credentials grant." />}
|
||||
name="token_url"
|
||||
rules={[{ required: true, message: "Token URL is required for M2M OAuth" }]}
|
||||
>
|
||||
<TextInput placeholder="https://auth.example.com/oauth/token" className={fieldClassName} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={<FieldLabel label="Scopes (optional)" tooltip="Optional scopes to request with the client_credentials grant." />}
|
||||
name={["credentials", "scopes"]}
|
||||
>
|
||||
<Select mode="tags" tokenSeparators={[","]} placeholder="Add scopes" className="rounded-lg" size="large" />
|
||||
</Form.Item>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Form.Item
|
||||
label={<FieldLabel label="OAuth Client ID (optional)" tooltip="Provide only if your MCP server cannot handle dynamic client registration." />}
|
||||
name={["credentials", "client_id"]}
|
||||
>
|
||||
<TextInput type="password" placeholder={`Enter OAuth client ID${placeholderSuffix}`} className={fieldClassName} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={<FieldLabel label="OAuth Client Secret (optional)" tooltip="Provide only if your MCP server cannot handle dynamic client registration." />}
|
||||
name={["credentials", "client_secret"]}
|
||||
>
|
||||
<TextInput type="password" placeholder={`Enter OAuth client secret${placeholderSuffix}`} className={fieldClassName} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={<FieldLabel label="OAuth Scopes (optional)" tooltip="Optional scopes requested during token exchange. Separate multiple scopes with enter or commas." />}
|
||||
name={["credentials", "scopes"]}
|
||||
>
|
||||
<Select mode="tags" tokenSeparators={[","]} placeholder="Add scopes" className="rounded-lg" size="large" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={<FieldLabel label="Authorization URL Override (optional)" tooltip="Optional override for the authorization endpoint." />}
|
||||
name="authorization_url"
|
||||
>
|
||||
<TextInput placeholder="https://example.com/oauth/authorize" className={fieldClassName} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={<FieldLabel label="Token URL Override (optional)" tooltip="Optional override for the token endpoint." />}
|
||||
name="token_url"
|
||||
>
|
||||
<TextInput placeholder="https://example.com/oauth/token" className={fieldClassName} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={<FieldLabel label="Registration URL Override (optional)" tooltip="Optional override for the dynamic client registration endpoint." />}
|
||||
name="registration_url"
|
||||
>
|
||||
<TextInput placeholder="https://example.com/oauth/register" className={fieldClassName} />
|
||||
</Form.Item>
|
||||
{oauthFlow && (
|
||||
<div className="rounded-lg border border-dashed border-gray-300 p-4 space-y-2">
|
||||
<p className="text-sm text-gray-600">
|
||||
Use OAuth to fetch a fresh access token and temporarily save it in the session as the authentication value.
|
||||
</p>
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={oauthFlow.startOAuthFlow}
|
||||
disabled={oauthFlow.status === "authorizing" || oauthFlow.status === "exchanging"}
|
||||
>
|
||||
{oauthFlow.status === "authorizing"
|
||||
? "Waiting for authorization..."
|
||||
: oauthFlow.status === "exchanging"
|
||||
? "Exchanging authorization code..."
|
||||
: "Authorize & Fetch Token"}
|
||||
</Button>
|
||||
{oauthFlow.error && <p className="text-sm text-red-500">{oauthFlow.error}</p>}
|
||||
{oauthFlow.status === "success" && oauthFlow.tokenResponse?.access_token && (
|
||||
<p className="text-sm text-green-600">
|
||||
Token fetched. Expires in {oauthFlow.tokenResponse.expires_in ?? "?"} seconds.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default OAuthFormFields;
|
||||
|
|
@ -3,7 +3,8 @@ import { Modal, Tooltip, Form, Select, Input } from "antd";
|
|||
import { InfoCircleOutlined } from "@ant-design/icons";
|
||||
import { Button, TextInput } from "@tremor/react";
|
||||
import { createMCPServer } from "../networking";
|
||||
import { AUTH_TYPE, MCPServer, MCPServerCostInfo } from "./types";
|
||||
import { AUTH_TYPE, OAUTH_FLOW, MCPServer, MCPServerCostInfo } from "./types";
|
||||
import OAuthFormFields from "./OAuthFormFields";
|
||||
import MCPServerCostConfig from "./mcp_server_cost_config";
|
||||
import MCPConnectionStatus from "./mcp_connection_status";
|
||||
import MCPToolConfiguration from "./mcp_tool_configuration";
|
||||
|
|
@ -52,6 +53,8 @@ const CreateMCPServer: React.FC<CreateMCPServerProps> = ({
|
|||
const authType = formValues.auth_type as string | undefined;
|
||||
const shouldShowAuthValueField = authType ? AUTH_TYPES_REQUIRING_AUTH_VALUE.includes(authType) : false;
|
||||
const isOAuthAuthType = authType === AUTH_TYPE.OAUTH2;
|
||||
const isM2MFlow = isOAuthAuthType && formValues.oauth_flow_type === OAUTH_FLOW.M2M;
|
||||
const isInteractiveFlow = isOAuthAuthType && formValues.oauth_flow_type !== OAUTH_FLOW.M2M;
|
||||
|
||||
const persistCreateUiState = () => {
|
||||
if (typeof window === "undefined") {
|
||||
|
|
@ -561,131 +564,16 @@ const CreateMCPServer: React.FC<CreateMCPServerProps> = ({
|
|||
)}
|
||||
|
||||
{transportType !== "stdio" && isOAuthAuthType && (
|
||||
<>
|
||||
<Form.Item
|
||||
label={
|
||||
<span className="text-sm font-medium text-gray-700 flex items-center">
|
||||
OAuth Client ID (optional)
|
||||
<Tooltip title="Provide only if your MCP server cannot handle dynamic client registration.">
|
||||
<InfoCircleOutlined className="ml-2 text-blue-400 hover:text-blue-600 cursor-help" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
name={["credentials", "client_id"]}
|
||||
>
|
||||
<TextInput
|
||||
type="password"
|
||||
placeholder="Enter OAuth client ID"
|
||||
className="rounded-lg border-gray-300 focus:border-blue-500 focus:ring-blue-500"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={
|
||||
<span className="text-sm font-medium text-gray-700 flex items-center">
|
||||
OAuth Client Secret (optional)
|
||||
<Tooltip title="Provide only if your MCP server cannot handle dynamic client registration.">
|
||||
<InfoCircleOutlined className="ml-2 text-blue-400 hover:text-blue-600 cursor-help" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
name={["credentials", "client_secret"]}
|
||||
>
|
||||
<TextInput
|
||||
type="password"
|
||||
placeholder="Enter OAuth client secret"
|
||||
className="rounded-lg border-gray-300 focus:border-blue-500 focus:ring-blue-500"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={
|
||||
<span className="text-sm font-medium text-gray-700 flex items-center">
|
||||
OAuth Scopes (optional)
|
||||
<Tooltip title="Optional scopes requested during token exchange. Separate multiple scopes with enter or commas.">
|
||||
<InfoCircleOutlined className="ml-2 text-blue-400 hover:text-blue-600 cursor-help" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
name={["credentials", "scopes"]}
|
||||
>
|
||||
<Select
|
||||
mode="tags"
|
||||
tokenSeparators={[","]}
|
||||
placeholder="Add scopes"
|
||||
className="rounded-lg"
|
||||
size="large"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={
|
||||
<span className="text-sm font-medium text-gray-700 flex items-center">
|
||||
Authorization URL Override (optional)
|
||||
<Tooltip title="Optional override for the authorization endpoint.">
|
||||
<InfoCircleOutlined className="ml-2 text-blue-400 hover:text-blue-600 cursor-help" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
name="authorization_url"
|
||||
>
|
||||
<TextInput
|
||||
placeholder="https://example.com/oauth/authorize"
|
||||
className="rounded-lg border-gray-300 focus:border-blue-500 focus:ring-blue-500"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={
|
||||
<span className="text-sm font-medium text-gray-700 flex items-center">
|
||||
Token URL Override (optional)
|
||||
<Tooltip title="Optional override for the token endpoint.">
|
||||
<InfoCircleOutlined className="ml-2 text-blue-400 hover:text-blue-600 cursor-help" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
name="token_url"
|
||||
>
|
||||
<TextInput
|
||||
placeholder="https://example.com/oauth/token"
|
||||
className="rounded-lg border-gray-300 focus:border-blue-500 focus:ring-blue-500"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={
|
||||
<span className="text-sm font-medium text-gray-700 flex items-center">
|
||||
Registration URL Override (optional)
|
||||
<Tooltip title="Optional orverride for the dynamic client registration endpoint.">
|
||||
<InfoCircleOutlined className="ml-2 text-blue-400 hover:text-blue-600 cursor-help" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
name="registration_url"
|
||||
>
|
||||
<TextInput
|
||||
placeholder="https://example.com/oauth/register"
|
||||
className="rounded-lg border-gray-300 focus:border-blue-500 focus:ring-blue-500"
|
||||
/>
|
||||
</Form.Item>
|
||||
<div className="rounded-lg border border-dashed border-gray-300 p-4 space-y-2">
|
||||
<p className="text-sm text-gray-600">
|
||||
Use OAuth to fetch a fresh access token and temporarily save it in the session as the authentication value.
|
||||
</p>
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={startOAuthFlow}
|
||||
disabled={oauthStatus === "authorizing" || oauthStatus === "exchanging"}
|
||||
>
|
||||
{oauthStatus === "authorizing"
|
||||
? "Waiting for authorization..."
|
||||
: oauthStatus === "exchanging"
|
||||
? "Exchanging authorization code..."
|
||||
: "Authorize & Fetch Token"}
|
||||
</Button>
|
||||
{oauthError && <p className="text-sm text-red-500">{oauthError}</p>}
|
||||
{oauthStatus === "success" && oauthTokenResponse?.access_token && (
|
||||
<p className="text-sm text-green-600">
|
||||
Token fetched. Expires in {oauthTokenResponse.expires_in ?? "?"} seconds.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
<OAuthFormFields
|
||||
isM2M={isM2MFlow}
|
||||
initialFlowType={OAUTH_FLOW.INTERACTIVE}
|
||||
oauthFlow={{
|
||||
startOAuthFlow,
|
||||
status: oauthStatus,
|
||||
error: oauthError,
|
||||
tokenResponse: oauthTokenResponse,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Stdio Configuration - only show for stdio transport */}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@ import React, { useState, useEffect } from "react";
|
|||
import { Form, Select, Button as AntdButton, Tooltip } from "antd";
|
||||
import { InfoCircleOutlined } from "@ant-design/icons";
|
||||
import { Button, TextInput, TabGroup, TabList, Tab, TabPanels, TabPanel } from "@tremor/react";
|
||||
import { AUTH_TYPE, MCPServer, MCPServerCostInfo } from "./types";
|
||||
import { AUTH_TYPE, OAUTH_FLOW, MCPServer, MCPServerCostInfo } from "./types";
|
||||
import OAuthFormFields from "./OAuthFormFields";
|
||||
import { updateMCPServer, testMCPToolsListRequest } from "../networking";
|
||||
import MCPServerCostConfig from "./mcp_server_cost_config";
|
||||
import MCPPermissionManagement from "./MCPPermissionManagement";
|
||||
|
|
@ -41,7 +42,10 @@ const MCPServerEdit: React.FC<MCPServerEditProps> = ({
|
|||
const authType = Form.useWatch("auth_type", form) as string | undefined;
|
||||
const shouldShowAuthValueField = authType ? AUTH_TYPES_REQUIRING_AUTH_VALUE.includes(authType) : false;
|
||||
const isOAuthAuthType = authType === AUTH_TYPE.OAUTH2;
|
||||
|
||||
const oauthFlowTypeValue = Form.useWatch("oauth_flow_type", form) as string | undefined;
|
||||
const isM2MFlow = isOAuthAuthType && oauthFlowTypeValue === OAUTH_FLOW.M2M;
|
||||
const isInteractiveFlow = isOAuthAuthType && oauthFlowTypeValue !== OAUTH_FLOW.M2M;
|
||||
|
||||
const [oauthAccessToken, setOauthAccessToken] = useState<string | null>(null);
|
||||
|
||||
const persistEditUiState = () => {
|
||||
|
|
@ -128,6 +132,7 @@ const MCPServerEdit: React.FC<MCPServerEditProps> = ({
|
|||
() => ({
|
||||
...mcpServer,
|
||||
static_headers: initialStaticHeaders,
|
||||
oauth_flow_type: mcpServer.token_url ? OAUTH_FLOW.M2M : OAUTH_FLOW.INTERACTIVE,
|
||||
}),
|
||||
[mcpServer, initialStaticHeaders],
|
||||
);
|
||||
|
|
@ -214,7 +219,8 @@ const MCPServerEdit: React.FC<MCPServerEditProps> = ({
|
|||
return;
|
||||
}
|
||||
|
||||
if (mcpServer.auth_type === AUTH_TYPE.OAUTH2 && !oauthAccessToken) {
|
||||
const isM2M = mcpServer.auth_type === AUTH_TYPE.OAUTH2 && !!mcpServer.token_url;
|
||||
if (mcpServer.auth_type === AUTH_TYPE.OAUTH2 && !isM2M && !oauthAccessToken) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -454,126 +460,219 @@ const MCPServerEdit: React.FC<MCPServerEditProps> = ({
|
|||
{isOAuthAuthType && (
|
||||
<>
|
||||
<Form.Item
|
||||
label={
|
||||
<span className="text-sm font-medium text-gray-700 flex items-center">
|
||||
OAuth Client ID (optional)
|
||||
<Tooltip title="Provide only if your MCP server cannot handle dynamic client registration.">
|
||||
<InfoCircleOutlined className="ml-2 text-blue-400 hover:text-blue-600 cursor-help" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
name={["credentials", "client_id"]}
|
||||
label={<span className="text-sm font-medium text-gray-700">OAuth Flow Type</span>}
|
||||
name="oauth_flow_type"
|
||||
>
|
||||
<TextInput
|
||||
type="password"
|
||||
placeholder="Enter OAuth client ID (leave blank to keep existing)"
|
||||
className="rounded-lg border-gray-300 focus:border-blue-500 focus:ring-blue-500"
|
||||
/>
|
||||
<Select className="rounded-lg" size="large">
|
||||
<Select.Option value={OAUTH_FLOW.INTERACTIVE}>Interactive (PKCE)</Select.Option>
|
||||
<Select.Option value={OAUTH_FLOW.M2M}>Machine-to-Machine (M2M)</Select.Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={
|
||||
<span className="text-sm font-medium text-gray-700 flex items-center">
|
||||
OAuth Client Secret (optional)
|
||||
<Tooltip title="Provide only if your MCP server cannot handle dynamic client registration.">
|
||||
<InfoCircleOutlined className="ml-2 text-blue-400 hover:text-blue-600 cursor-help" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
name={["credentials", "client_secret"]}
|
||||
>
|
||||
<TextInput
|
||||
type="password"
|
||||
placeholder="Enter OAuth client secret (leave blank to keep existing)"
|
||||
className="rounded-lg border-gray-300 focus:border-blue-500 focus:ring-blue-500"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={
|
||||
<span className="text-sm font-medium text-gray-700 flex items-center">
|
||||
OAuth Scopes (optional)
|
||||
<Tooltip title="Add scopes to override the default scope list used for this MCP server.">
|
||||
<InfoCircleOutlined className="ml-2 text-blue-400 hover:text-blue-600 cursor-help" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
name={["credentials", "scopes"]}
|
||||
>
|
||||
<Select
|
||||
mode="tags"
|
||||
tokenSeparators={[","]}
|
||||
placeholder="Add scopes"
|
||||
className="rounded-lg"
|
||||
size="large"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={
|
||||
<span className="text-sm font-medium text-gray-700 flex items-center">
|
||||
Authorization URL Override (optional)
|
||||
<Tooltip title="Optional override for the authorization endpoint.">
|
||||
<InfoCircleOutlined className="ml-2 text-blue-400 hover:text-blue-600 cursor-help" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
name="authorization_url"
|
||||
>
|
||||
<TextInput
|
||||
placeholder="https://example.com/oauth/authorize"
|
||||
className="rounded-lg border-gray-300 focus:border-blue-500 focus:ring-blue-500"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={
|
||||
<span className="text-sm font-medium text-gray-700 flex items-center">
|
||||
Token URL Override (optional)
|
||||
<Tooltip title="Optional override for the token endpoint.">
|
||||
<InfoCircleOutlined className="ml-2 text-blue-400 hover:text-blue-600 cursor-help" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
name="token_url"
|
||||
>
|
||||
<TextInput
|
||||
placeholder="https://example.com/oauth/token"
|
||||
className="rounded-lg border-gray-300 focus:border-blue-500 focus:ring-blue-500"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={
|
||||
<span className="text-sm font-medium text-gray-700 flex items-center">
|
||||
Registration URL Override (optional)
|
||||
<Tooltip title="Optional override for the dynamic client registration endpoint.">
|
||||
<InfoCircleOutlined className="ml-2 text-blue-400 hover:text-blue-600 cursor-help" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
name="registration_url"
|
||||
>
|
||||
<TextInput
|
||||
placeholder="https://example.com/oauth/register"
|
||||
className="rounded-lg border-gray-300 focus:border-blue-500 focus:ring-blue-500"
|
||||
/>
|
||||
</Form.Item>
|
||||
<div className="rounded-lg border border-dashed border-gray-300 p-4 space-y-2">
|
||||
<p className="text-sm text-gray-600">Use OAuth to fetch a fresh access token and temporarily save it in the session as the authentication value.</p>
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={startOAuthFlow}
|
||||
disabled={oauthStatus === "authorizing" || oauthStatus === "exchanging"}
|
||||
>
|
||||
{oauthStatus === "authorizing"
|
||||
? "Waiting for authorization..."
|
||||
: oauthStatus === "exchanging"
|
||||
? "Exchanging authorization code..."
|
||||
: "Authorize & Fetch Token"}
|
||||
</Button>
|
||||
{oauthError && <p className="text-sm text-red-500">{oauthError}</p>}
|
||||
{oauthStatus === "success" && oauthTokenResponse?.access_token && (
|
||||
<p className="text-sm text-green-600">
|
||||
Token fetched. Expires in {oauthTokenResponse.expires_in ?? "?"} seconds.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* M2M fields: client_id, client_secret, token_url required; scopes optional */}
|
||||
{isM2MFlow && (
|
||||
<>
|
||||
<Form.Item
|
||||
label={
|
||||
<span className="text-sm font-medium text-gray-700 flex items-center">
|
||||
Client ID
|
||||
<Tooltip title="OAuth2 client ID for the client_credentials grant.">
|
||||
<InfoCircleOutlined className="ml-2 text-blue-400 hover:text-blue-600 cursor-help" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
name={["credentials", "client_id"]}
|
||||
rules={[{ required: true, message: "Client ID is required for M2M OAuth" }]}
|
||||
>
|
||||
<TextInput
|
||||
type="password"
|
||||
placeholder="Enter OAuth client ID (leave blank to keep existing)"
|
||||
className="rounded-lg border-gray-300 focus:border-blue-500 focus:ring-blue-500"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={
|
||||
<span className="text-sm font-medium text-gray-700 flex items-center">
|
||||
Client Secret
|
||||
<Tooltip title="OAuth2 client secret for the client_credentials grant.">
|
||||
<InfoCircleOutlined className="ml-2 text-blue-400 hover:text-blue-600 cursor-help" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
name={["credentials", "client_secret"]}
|
||||
rules={[{ required: true, message: "Client Secret is required for M2M OAuth" }]}
|
||||
>
|
||||
<TextInput
|
||||
type="password"
|
||||
placeholder="Enter OAuth client secret (leave blank to keep existing)"
|
||||
className="rounded-lg border-gray-300 focus:border-blue-500 focus:ring-blue-500"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={
|
||||
<span className="text-sm font-medium text-gray-700 flex items-center">
|
||||
Token URL
|
||||
<Tooltip title="Token endpoint URL for the client_credentials grant.">
|
||||
<InfoCircleOutlined className="ml-2 text-blue-400 hover:text-blue-600 cursor-help" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
name="token_url"
|
||||
rules={[{ required: true, message: "Token URL is required for M2M OAuth" }]}
|
||||
>
|
||||
<TextInput
|
||||
placeholder="https://auth.example.com/oauth/token"
|
||||
className="rounded-lg border-gray-300 focus:border-blue-500 focus:ring-blue-500"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={
|
||||
<span className="text-sm font-medium text-gray-700 flex items-center">
|
||||
Scopes (optional)
|
||||
<Tooltip title="Optional scopes to request with the client_credentials grant.">
|
||||
<InfoCircleOutlined className="ml-2 text-blue-400 hover:text-blue-600 cursor-help" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
name={["credentials", "scopes"]}
|
||||
>
|
||||
<Select
|
||||
mode="tags"
|
||||
tokenSeparators={[","]}
|
||||
placeholder="Add scopes"
|
||||
className="rounded-lg"
|
||||
size="large"
|
||||
/>
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Interactive (PKCE) fields: all optional, plus authorize button */}
|
||||
{isInteractiveFlow && (
|
||||
<>
|
||||
<Form.Item
|
||||
label={
|
||||
<span className="text-sm font-medium text-gray-700 flex items-center">
|
||||
OAuth Client ID (optional)
|
||||
<Tooltip title="Provide only if your MCP server cannot handle dynamic client registration.">
|
||||
<InfoCircleOutlined className="ml-2 text-blue-400 hover:text-blue-600 cursor-help" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
name={["credentials", "client_id"]}
|
||||
>
|
||||
<TextInput
|
||||
type="password"
|
||||
placeholder="Enter OAuth client ID (leave blank to keep existing)"
|
||||
className="rounded-lg border-gray-300 focus:border-blue-500 focus:ring-blue-500"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={
|
||||
<span className="text-sm font-medium text-gray-700 flex items-center">
|
||||
OAuth Client Secret (optional)
|
||||
<Tooltip title="Provide only if your MCP server cannot handle dynamic client registration.">
|
||||
<InfoCircleOutlined className="ml-2 text-blue-400 hover:text-blue-600 cursor-help" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
name={["credentials", "client_secret"]}
|
||||
>
|
||||
<TextInput
|
||||
type="password"
|
||||
placeholder="Enter OAuth client secret (leave blank to keep existing)"
|
||||
className="rounded-lg border-gray-300 focus:border-blue-500 focus:ring-blue-500"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={
|
||||
<span className="text-sm font-medium text-gray-700 flex items-center">
|
||||
OAuth Scopes (optional)
|
||||
<Tooltip title="Add scopes to override the default scope list used for this MCP server.">
|
||||
<InfoCircleOutlined className="ml-2 text-blue-400 hover:text-blue-600 cursor-help" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
name={["credentials", "scopes"]}
|
||||
>
|
||||
<Select
|
||||
mode="tags"
|
||||
tokenSeparators={[","]}
|
||||
placeholder="Add scopes"
|
||||
className="rounded-lg"
|
||||
size="large"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={
|
||||
<span className="text-sm font-medium text-gray-700 flex items-center">
|
||||
Authorization URL Override (optional)
|
||||
<Tooltip title="Optional override for the authorization endpoint.">
|
||||
<InfoCircleOutlined className="ml-2 text-blue-400 hover:text-blue-600 cursor-help" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
name="authorization_url"
|
||||
>
|
||||
<TextInput
|
||||
placeholder="https://example.com/oauth/authorize"
|
||||
className="rounded-lg border-gray-300 focus:border-blue-500 focus:ring-blue-500"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={
|
||||
<span className="text-sm font-medium text-gray-700 flex items-center">
|
||||
Token URL Override (optional)
|
||||
<Tooltip title="Optional override for the token endpoint.">
|
||||
<InfoCircleOutlined className="ml-2 text-blue-400 hover:text-blue-600 cursor-help" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
name="token_url"
|
||||
>
|
||||
<TextInput
|
||||
placeholder="https://example.com/oauth/token"
|
||||
className="rounded-lg border-gray-300 focus:border-blue-500 focus:ring-blue-500"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={
|
||||
<span className="text-sm font-medium text-gray-700 flex items-center">
|
||||
Registration URL Override (optional)
|
||||
<Tooltip title="Optional override for the dynamic client registration endpoint.">
|
||||
<InfoCircleOutlined className="ml-2 text-blue-400 hover:text-blue-600 cursor-help" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
name="registration_url"
|
||||
>
|
||||
<TextInput
|
||||
placeholder="https://example.com/oauth/register"
|
||||
className="rounded-lg border-gray-300 focus:border-blue-500 focus:ring-blue-500"
|
||||
/>
|
||||
</Form.Item>
|
||||
<div className="rounded-lg border border-dashed border-gray-300 p-4 space-y-2">
|
||||
<p className="text-sm text-gray-600">Use OAuth to fetch a fresh access token and temporarily save it in the session as the authentication value.</p>
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={startOAuthFlow}
|
||||
disabled={oauthStatus === "authorizing" || oauthStatus === "exchanging"}
|
||||
>
|
||||
{oauthStatus === "authorizing"
|
||||
? "Waiting for authorization..."
|
||||
: oauthStatus === "exchanging"
|
||||
? "Exchanging authorization code..."
|
||||
: "Authorize & Fetch Token"}
|
||||
</Button>
|
||||
{oauthError && <p className="text-sm text-red-500">{oauthError}</p>}
|
||||
{oauthStatus === "success" && oauthTokenResponse?.access_token && (
|
||||
<p className="text-sm text-green-600">
|
||||
Token fetched. Expires in {oauthTokenResponse.expires_in ?? "?"} seconds.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,11 @@ export const AUTH_TYPE = {
|
|||
OAUTH2: "oauth2",
|
||||
};
|
||||
|
||||
export const OAUTH_FLOW = {
|
||||
INTERACTIVE: "interactive",
|
||||
M2M: "m2m",
|
||||
};
|
||||
|
||||
export const TRANSPORT = {
|
||||
SSE: "sse",
|
||||
HTTP: "http",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue