From 1f0cbfbdc43b9aef00930853e5728f740686b3df Mon Sep 17 00:00:00 2001 From: zxhlyh Date: Mon, 13 Oct 2025 16:15:00 +0800 Subject: [PATCH 1/6] feat: mcp auth --- web/app/components/base/tab-slider/index.tsx | 3 + web/app/components/tools/mcp/modal.tsx | 155 ++++++++++++++----- web/app/components/tools/types.ts | 6 + web/i18n/en-US/tools.ts | 6 + 4 files changed, 133 insertions(+), 37 deletions(-) diff --git a/web/app/components/base/tab-slider/index.tsx b/web/app/components/base/tab-slider/index.tsx index 55c44d5ea8..7c9364baf9 100644 --- a/web/app/components/base/tab-slider/index.tsx +++ b/web/app/components/base/tab-slider/index.tsx @@ -11,12 +11,14 @@ type Option = { type TabSliderProps = { className?: string value: string + itemClassName?: string | ((active: boolean) => string) onChange: (v: string) => void options: Option[] } const TabSlider: FC = ({ className, + itemClassName, value, onChange, options, @@ -58,6 +60,7 @@ const TabSlider: FC = ({ index === activeIndex ? 'text-text-primary' : 'text-text-tertiary', + typeof itemClassName === 'function' ? itemClassName(index === activeIndex) : itemClassName, )} onClick={() => { if (index !== activeIndex) { diff --git a/web/app/components/tools/mcp/modal.tsx b/web/app/components/tools/mcp/modal.tsx index 1d888c57e8..6b4c99e17a 100644 --- a/web/app/components/tools/mcp/modal.tsx +++ b/web/app/components/tools/mcp/modal.tsx @@ -1,5 +1,5 @@ 'use client' -import React, { useRef, useState } from 'react' +import React, { useCallback, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { getDomain } from 'tldts' import { RiCloseLine, RiEditLine } from '@remixicon/react' @@ -19,6 +19,9 @@ import { uploadRemoteFileInfo } from '@/service/common' import cn from '@/utils/classnames' import { useHover } from 'ahooks' import { shouldUseMcpIconForAppIcon } from '@/utils/mcp' +import TabSlider from '@/app/components/base/tab-slider' +import { MCPAuthMethod } from '@/app/components/tools/types' +import Switch from '@/app/components/base/switch' export type DuplicateAppModalProps = { data?: ToolWithProvider @@ -63,6 +66,20 @@ const MCPModal = ({ const { t } = useTranslation() const isCreate = !data + const authMethods = [ + { + text: t('tools.mcp.modal.authentication'), + value: MCPAuthMethod.authentication, + }, + { + text: t('tools.mcp.modal.headers'), + value: MCPAuthMethod.headers, + }, + { + text: t('tools.mcp.modal.configurations'), + value: MCPAuthMethod.configurations, + }, + ] const originalServerUrl = data?.server_url const originalServerID = data?.server_identifier const [url, setUrl] = React.useState(data?.server_url || '') @@ -78,6 +95,10 @@ const MCPModal = ({ const [isFetchingIcon, setIsFetchingIcon] = useState(false) const appIconRef = useRef(null) const isHovering = useHover(appIconRef) + const [authMethod, setAuthMethod] = useState(MCPAuthMethod.authentication) + const [useDynamicClientRegistration, setUseDynamicClientRegistration] = useState(data?.use_dynamic_client_registration || false) + const [clientID, setClientID] = useState(data?.client_id || '') + const [credentials, setCredentials] = useState(data?.credentials || '') // Update states when data changes (for edit mode) React.useEffect(() => { @@ -165,6 +186,10 @@ const MCPModal = ({ onHide() } + const handleAuthMethodChange = useCallback((value: string) => { + setAuthMethod(value as MCPAuthMethod) + }, []) + return ( <> )} -
-
- {t('tools.mcp.modal.timeout')} -
- setMcpTimeout(Number(e.target.value))} - onBlur={e => handleBlur(e.target.value.trim())} - placeholder={t('tools.mcp.modal.timeoutPlaceholder')} - /> -
-
-
- {t('tools.mcp.modal.sseReadTimeout')} -
- setSseReadTimeout(Number(e.target.value))} - onBlur={e => handleBlur(e.target.value.trim())} - placeholder={t('tools.mcp.modal.timeoutPlaceholder')} - /> -
-
-
- {t('tools.mcp.modal.headers')} -
-
{t('tools.mcp.modal.headersTip')}
- 0} - /> -
+ { + return `flex-1 ${isActive && 'text-text-accent-light-mode-only'}` + }} + value={authMethod} + onChange={handleAuthMethodChange} + options={authMethods} + /> + { + authMethod === MCPAuthMethod.authentication && ( + <> +
+
+ + {t('tools.mcp.modal.useDynamicClientRegistration')} +
+
+
+
+ {t('tools.mcp.modal.clientID')} +
+ setClientID(e.target.value)} + onBlur={e => handleBlur(e.target.value.trim())} + placeholder={t('tools.mcp.modal.clientID')} + /> +
+
+
+ {t('tools.mcp.modal.credentials')} +
+ setCredentials(e.target.value)} + onBlur={e => handleBlur(e.target.value.trim())} + placeholder={t('tools.mcp.modal.credentialsPlaceholder')} + /> +
+ + ) + } + { + authMethod === MCPAuthMethod.headers && ( +
+
+ {t('tools.mcp.modal.headers')} +
+
{t('tools.mcp.modal.headersTip')}
+ 0} + /> +
+ ) + } + { + authMethod === MCPAuthMethod.configurations && ( + <> +
+
+ {t('tools.mcp.modal.timeout')} +
+ setMcpTimeout(Number(e.target.value))} + onBlur={e => handleBlur(e.target.value.trim())} + placeholder={t('tools.mcp.modal.timeoutPlaceholder')} + /> +
+
+
+ {t('tools.mcp.modal.sseReadTimeout')} +
+ setSseReadTimeout(Number(e.target.value))} + onBlur={e => handleBlur(e.target.value.trim())} + placeholder={t('tools.mcp.modal.timeoutPlaceholder')} + /> +
+ + ) + }
diff --git a/web/app/components/tools/types.ts b/web/app/components/tools/types.ts index 93b15b0533..858b29ea03 100644 --- a/web/app/components/tools/types.ts +++ b/web/app/components/tools/types.ts @@ -191,3 +191,9 @@ export type MCPServerDetail = { parameters?: Record headers?: Record } + +export enum MCPAuthMethod { + authentication = 'authentication', + headers = 'headers', + configurations = 'configurations', +} diff --git a/web/i18n/en-US/tools.ts b/web/i18n/en-US/tools.ts index 35d5202879..02720ef5bd 100644 --- a/web/i18n/en-US/tools.ts +++ b/web/i18n/en-US/tools.ts @@ -203,6 +203,12 @@ const translation = { timeout: 'Timeout', sseReadTimeout: 'SSE Read Timeout', timeoutPlaceholder: '30', + authentication: 'Authentication', + useDynamicClientRegistration: 'Use Dynamic Client Registration', + clientID: 'Client ID', + credentials: 'Credentials', + credentialsPlaceholder: 'Client secret', + configurations: 'Configurations', }, delete: 'Remove MCP Server', deleteConfirmTitle: 'Would you like to remove {{mcp}}?', From d98f375926c7e6c5ef209376a532fd1ec38ec745 Mon Sep 17 00:00:00 2001 From: zxhlyh Date: Tue, 14 Oct 2025 10:24:04 +0800 Subject: [PATCH 2/6] feat: mcp auth --- .../components/tools/mcp/headers-input.tsx | 2 +- web/app/components/tools/mcp/modal.tsx | 41 ++++++++++++++----- web/app/components/tools/types.ts | 10 +++++ 3 files changed, 41 insertions(+), 12 deletions(-) diff --git a/web/app/components/tools/mcp/headers-input.tsx b/web/app/components/tools/mcp/headers-input.tsx index 81d62993c9..2d7598729b 100644 --- a/web/app/components/tools/mcp/headers-input.tsx +++ b/web/app/components/tools/mcp/headers-input.tsx @@ -113,7 +113,7 @@ const HeadersInput = ({ className='flex-1 rounded-none border-0' readOnly={readonly} /> - {!readonly && headerItems.length > 1 && ( + {!readonly && !!headerItems.length && ( handleRemoveItem(index)} className='mr-2' diff --git a/web/app/components/tools/mcp/modal.tsx b/web/app/components/tools/mcp/modal.tsx index 6b4c99e17a..2286620a2e 100644 --- a/web/app/components/tools/mcp/modal.tsx +++ b/web/app/components/tools/mcp/modal.tsx @@ -33,9 +33,17 @@ export type DuplicateAppModalProps = { icon: string icon_background?: string | null server_identifier: string - timeout: number - sse_read_timeout: number headers?: Record + is_dynamic_registration?: boolean + authentication?: { + client_id?: string + client_secret?: string + grant_type?: string + } + configurations: { + timeout: number + sse_read_timeout: number + } }) => void onHide: () => void } @@ -96,9 +104,9 @@ const MCPModal = ({ const appIconRef = useRef(null) const isHovering = useHover(appIconRef) const [authMethod, setAuthMethod] = useState(MCPAuthMethod.authentication) - const [useDynamicClientRegistration, setUseDynamicClientRegistration] = useState(data?.use_dynamic_client_registration || false) - const [clientID, setClientID] = useState(data?.client_id || '') - const [credentials, setCredentials] = useState(data?.credentials || '') + const [isDynamicRegistration, setIsDynamicRegistration] = useState(data?.is_dynamic_registration || false) + const [clientID, setClientID] = useState(data?.authentication?.client_id || '') + const [credentials, setCredentials] = useState(data?.authentication?.client_secret || '') // Update states when data changes (for edit mode) React.useEffect(() => { @@ -178,9 +186,17 @@ const MCPModal = ({ icon: appIcon.type === 'emoji' ? appIcon.icon : appIcon.fileId, icon_background: appIcon.type === 'emoji' ? appIcon.background : undefined, server_identifier: serverIdentifier.trim(), - timeout: timeout || 30, - sse_read_timeout: sseReadTimeout || 300, headers: Object.keys(headers).length > 0 ? headers : undefined, + is_dynamic_registration: isDynamicRegistration, + authentication: { + client_id: clientID, + client_secret: credentials, + grant_type: 'client_credentials', + }, + configurations: { + timeout: timeout || 30, + sse_read_timeout: sseReadTimeout || 300, + }, }) if(isCreate) onHide() @@ -279,14 +295,15 @@ const MCPModal = ({
{t('tools.mcp.modal.useDynamicClientRegistration')}
-
+
{t('tools.mcp.modal.clientID')}
setClientID(e.target.value)} onBlur={e => handleBlur(e.target.value.trim())} placeholder={t('tools.mcp.modal.clientID')} + disabled={isDynamicRegistration} />
-
+
{t('tools.mcp.modal.credentials')}
setCredentials(e.target.value)} onBlur={e => handleBlur(e.target.value.trim())} placeholder={t('tools.mcp.modal.credentialsPlaceholder')} + disabled={isDynamicRegistration} />
diff --git a/web/app/components/tools/types.ts b/web/app/components/tools/types.ts index 858b29ea03..be54954f0b 100644 --- a/web/app/components/tools/types.ts +++ b/web/app/components/tools/types.ts @@ -64,6 +64,16 @@ export type Collection = { masked_headers?: Record is_authorized?: boolean provider?: string + is_dynamic_registration?: boolean + authentication?: { + client_id?: string + client_secret?: string + grant_type?: string + } + configurations?: { + timeout?: number + sse_read_timeout?: number + } } export type ToolParameter = { From 584921081bf68948cf9816cd4ea9d2bd99c4e0b4 Mon Sep 17 00:00:00 2001 From: zxhlyh Date: Wed, 15 Oct 2025 10:49:54 +0800 Subject: [PATCH 3/6] feat: mcp auth --- .../components/tools/mcp/headers-input.tsx | 56 ++++++++----------- web/app/components/tools/mcp/modal.tsx | 26 +++++---- web/app/components/tools/types.ts | 3 +- web/i18n/zh-Hans/tools.ts | 6 ++ 4 files changed, 46 insertions(+), 45 deletions(-) diff --git a/web/app/components/tools/mcp/headers-input.tsx b/web/app/components/tools/mcp/headers-input.tsx index 2d7598729b..ede5b6cffe 100644 --- a/web/app/components/tools/mcp/headers-input.tsx +++ b/web/app/components/tools/mcp/headers-input.tsx @@ -1,6 +1,7 @@ 'use client' -import React, { useCallback } from 'react' +import React from 'react' import { useTranslation } from 'react-i18next' +import { v4 as uuid } from 'uuid' import { RiAddLine, RiDeleteBinLine } from '@remixicon/react' import Input from '@/app/components/base/input' import Button from '@/app/components/base/button' @@ -8,57 +9,46 @@ import ActionButton from '@/app/components/base/action-button' import cn from '@/utils/classnames' export type HeaderItem = { + id: string key: string value: string } type Props = { - headers: Record - onChange: (headers: Record) => void + headersItems: HeaderItem[] + onChange: (headerItems: HeaderItem[]) => void readonly?: boolean isMasked?: boolean } const HeadersInput = ({ - headers, + headersItems, onChange, readonly = false, isMasked = false, }: Props) => { const { t } = useTranslation() - const headerItems = Object.entries(headers).map(([key, value]) => ({ key, value })) - - const handleItemChange = useCallback((index: number, field: 'key' | 'value', value: string) => { - const newItems = [...headerItems] + const handleItemChange = (index: number, field: 'key' | 'value', value: string) => { + const newItems = [...headersItems] newItems[index] = { ...newItems[index], [field]: value } - const newHeaders = newItems.reduce((acc, item) => { - if (item.key.trim()) - acc[item.key.trim()] = item.value - return acc - }, {} as Record) + onChange(newItems) + } - onChange(newHeaders) - }, [headerItems, onChange]) + const handleRemoveItem = (index: number) => { + const newItems = headersItems.filter((_, i) => i !== index) - const handleRemoveItem = useCallback((index: number) => { - const newItems = headerItems.filter((_, i) => i !== index) - const newHeaders = newItems.reduce((acc, item) => { - if (item.key.trim()) - acc[item.key.trim()] = item.value + onChange(newItems) + } - return acc - }, {} as Record) - onChange(newHeaders) - }, [headerItems, onChange]) + const handleAddItem = () => { + const newItems = [...headersItems, { id: uuid(), key: '', value: '' }] - const handleAddItem = useCallback(() => { - const newHeaders = { ...headers, '': '' } - onChange(newHeaders) - }, [headers, onChange]) + onChange(newItems) + } - if (headerItems.length === 0) { + if (headersItems.length === 0) { return (
@@ -91,10 +81,10 @@ const HeadersInput = ({
{t('tools.mcp.modal.headerKey')}
{t('tools.mcp.modal.headerValue')}
- {headerItems.map((item, index) => ( -
( +
- {!readonly && !!headerItems.length && ( + {!readonly && !!headersItems.length && ( handleRemoveItem(index)} className='mr-2' diff --git a/web/app/components/tools/mcp/modal.tsx b/web/app/components/tools/mcp/modal.tsx index 2286620a2e..00caa695fe 100644 --- a/web/app/components/tools/mcp/modal.tsx +++ b/web/app/components/tools/mcp/modal.tsx @@ -1,6 +1,7 @@ 'use client' import React, { useCallback, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' +import { v4 as uuid } from 'uuid' import { getDomain } from 'tldts' import { RiCloseLine, RiEditLine } from '@remixicon/react' import { Mcp } from '@/app/components/base/icons/src/vender/other' @@ -11,6 +12,7 @@ import Modal from '@/app/components/base/modal' import Button from '@/app/components/base/button' import Input from '@/app/components/base/input' import HeadersInput from './headers-input' +import type { HeaderItem } from './headers-input' import type { AppIconType } from '@/types/app' import type { ToolWithProvider } from '@/app/components/workflow/types' import { noop } from 'lodash-es' @@ -40,7 +42,7 @@ export type DuplicateAppModalProps = { client_secret?: string grant_type?: string } - configurations: { + configuration: { timeout: number sse_read_timeout: number } @@ -97,8 +99,8 @@ const MCPModal = ({ const [serverIdentifier, setServerIdentifier] = React.useState(data?.server_identifier || '') const [timeout, setMcpTimeout] = React.useState(data?.timeout || 30) const [sseReadTimeout, setSseReadTimeout] = React.useState(data?.sse_read_timeout || 300) - const [headers, setHeaders] = React.useState>( - data?.masked_headers || {}, + const [headers, setHeaders] = React.useState( + Object.entries(data?.masked_headers || {}).map(([key, value]) => ({ id: uuid(), key, value })), ) const [isFetchingIcon, setIsFetchingIcon] = useState(false) const appIconRef = useRef(null) @@ -116,7 +118,7 @@ const MCPModal = ({ setServerIdentifier(data.server_identifier || '') setMcpTimeout(data.timeout || 30) setSseReadTimeout(data.sse_read_timeout || 300) - setHeaders(data.masked_headers || {}) + setHeaders(Object.entries(data.masked_headers || {}).map(([key, value]) => ({ id: uuid(), key, value }))) setAppIcon(getIcon(data)) } else { @@ -126,7 +128,7 @@ const MCPModal = ({ setServerIdentifier('') setMcpTimeout(30) setSseReadTimeout(300) - setHeaders({}) + setHeaders([]) setAppIcon(DEFAULT_ICON as AppIconSelection) } }, [data]) @@ -179,6 +181,11 @@ const MCPModal = ({ Toast.notify({ type: 'error', message: 'invalid server identifier' }) return } + const formattedHeaders = headers.reduce((acc, item) => { + if (item.key.trim()) + acc[item.key.trim()] = item.value + return acc + }, {} as Record) await onConfirm({ server_url: originalServerUrl === url ? '[__HIDDEN__]' : url.trim(), name, @@ -186,14 +193,13 @@ const MCPModal = ({ icon: appIcon.type === 'emoji' ? appIcon.icon : appIcon.fileId, icon_background: appIcon.type === 'emoji' ? appIcon.background : undefined, server_identifier: serverIdentifier.trim(), - headers: Object.keys(headers).length > 0 ? headers : undefined, + headers: Object.keys(formattedHeaders).length > 0 ? formattedHeaders : undefined, is_dynamic_registration: isDynamicRegistration, authentication: { client_id: clientID, client_secret: credentials, - grant_type: 'client_credentials', }, - configurations: { + configuration: { timeout: timeout || 30, sse_read_timeout: sseReadTimeout || 300, }, @@ -337,10 +343,10 @@ const MCPModal = ({
{t('tools.mcp.modal.headersTip')}
0} + isMasked={!isCreate && headers.filter(item => item.key.trim()).length > 0} />
) diff --git a/web/app/components/tools/types.ts b/web/app/components/tools/types.ts index be54954f0b..578701b5e7 100644 --- a/web/app/components/tools/types.ts +++ b/web/app/components/tools/types.ts @@ -68,9 +68,8 @@ export type Collection = { authentication?: { client_id?: string client_secret?: string - grant_type?: string } - configurations?: { + configuration?: { timeout?: number sse_read_timeout?: number } diff --git a/web/i18n/zh-Hans/tools.ts b/web/i18n/zh-Hans/tools.ts index e45d396617..ad85aa91fd 100644 --- a/web/i18n/zh-Hans/tools.ts +++ b/web/i18n/zh-Hans/tools.ts @@ -203,6 +203,12 @@ const translation = { timeout: '超时时间', sseReadTimeout: 'SSE 读取超时时间', timeoutPlaceholder: '30', + authentication: '认证', + useDynamicClientRegistration: '使用动态客户端注册', + clientID: '客户端 ID', + credentials: '凭证', + credentialsPlaceholder: '客户端密钥', + configurations: '配置', }, delete: '删除 MCP 服务', deleteConfirmTitle: '你想要删除 {{mcp}} 吗?', From 40af17fdace96192daf076230d7b7ea41da701d8 Mon Sep 17 00:00:00 2001 From: Yansong Zhang <916125788@qq.com> Date: Wed, 15 Oct 2025 10:50:46 +0800 Subject: [PATCH 4/6] add default trial models --- .../feature/hosted_service/__init__.py | 73 +++++++++++++++++-- 1 file changed, 65 insertions(+), 8 deletions(-) diff --git a/api/configs/feature/hosted_service/__init__.py b/api/configs/feature/hosted_service/__init__.py index 6415bd239d..ffbea931da 100644 --- a/api/configs/feature/hosted_service/__init__.py +++ b/api/configs/feature/hosted_service/__init__.py @@ -65,14 +65,46 @@ class HostedOpenAiConfig(BaseSettings): HOSTED_OPENAI_TRIAL_MODELS: str = Field( description="Comma-separated list of available models for trial access", - default="gpt-3.5-turbo," - "gpt-3.5-turbo-1106," - "gpt-3.5-turbo-instruct," + default="gpt-4," + "gpt-4-turbo-preview," + "gpt-4-turbo-2024-04-09," + "gpt-4-1106-preview," + "gpt-4-0125-preview," + "gpt-4-turbo," + "gpt-4.1," + "gpt-4.1-2025-04-14," + "gpt-4.1-mini," + "gpt-4.1-mini-2025-04-14," + "gpt-4.1-nano," + "gpt-4.1-nano-2025-04-14," + "gpt-3.5-turbo," "gpt-3.5-turbo-16k," "gpt-3.5-turbo-16k-0613," + "gpt-3.5-turbo-1106," "gpt-3.5-turbo-0613," "gpt-3.5-turbo-0125," - "text-davinci-003", + "gpt-3.5-turbo-instruct," + "text-davinci-003," + "chatgpt-4o-latest," + "gpt-4o," + "gpt-4o-2024-05-13," + "gpt-4o-2024-08-06," + "gpt-4o-2024-11-20," + "gpt-4o-audio-preview," + "gpt-4o-audio-preview-2025-06-03," + "gpt-4o-mini," + "gpt-4o-mini-2024-07-18," + "o3-mini," + "o3-mini-2025-01-31," + "gpt-5-mini-2025-08-07," + "gpt-5-mini," + "o4-mini," + "o4-mini-2025-04-16," + "gpt-5-chat-latest," + "gpt-5," + "gpt-5-2025-08-07," + "gpt-5-nano," + "gpt-5-nano-2025-08-07", ) HOSTED_OPENAI_PAID_ENABLED: bool = Field( @@ -87,6 +119,13 @@ class HostedOpenAiConfig(BaseSettings): "gpt-4-turbo-2024-04-09," "gpt-4-1106-preview," "gpt-4-0125-preview," + "gpt-4-turbo," + "gpt-4.1," + "gpt-4.1-2025-04-14," + "gpt-4.1-mini," + "gpt-4.1-mini-2025-04-14," + "gpt-4.1-nano," + "gpt-4.1-nano-2025-04-14," "gpt-3.5-turbo," "gpt-3.5-turbo-16k," "gpt-3.5-turbo-16k-0613," @@ -94,7 +133,27 @@ class HostedOpenAiConfig(BaseSettings): "gpt-3.5-turbo-0613," "gpt-3.5-turbo-0125," "gpt-3.5-turbo-instruct," - "text-davinci-003", + "text-davinci-003," + "chatgpt-4o-latest," + "gpt-4o," + "gpt-4o-2024-05-13," + "gpt-4o-2024-08-06," + "gpt-4o-2024-11-20," + "gpt-4o-audio-preview," + "gpt-4o-audio-preview-2025-06-03," + "gpt-4o-mini," + "gpt-4o-mini-2024-07-18," + "o3-mini," + "o3-mini-2025-01-31," + "gpt-5-mini-2025-08-07," + "gpt-5-mini," + "o4-mini," + "o4-mini-2025-04-16," + "gpt-5-chat-latest," + "gpt-5," + "gpt-5-2025-08-07," + "gpt-5-nano," + "gpt-5-nano-2025-08-07", ) @@ -217,7 +276,7 @@ class HostedDeepseekConfig(BaseSettings): HOSTED_DEEPSEEK_PAID_MODELS: str = Field( description="Comma-separated list of available models for paid access", - default="grok-3,grok-3-mini,grok-3-mini-fast", + default="deepseek-chat,deepseek-reasoner", ) @@ -275,7 +334,6 @@ class HostedAnthropicConfig(BaseSettings): HOSTED_ANTHROPIC_TRIAL_MODELS: str = Field( description="Comma-separated list of available models for paid access", default="claude-opus-4-20250514," - "claude-opus-4-20250514," "claude-sonnet-4-20250514," "claude-3-5-haiku-20241022," "claude-3-opus-20240229," @@ -285,7 +343,6 @@ class HostedAnthropicConfig(BaseSettings): HOSTED_ANTHROPIC_PAID_MODELS: str = Field( description="Comma-separated list of available models for paid access", default="claude-opus-4-20250514," - "claude-opus-4-20250514," "claude-sonnet-4-20250514," "claude-3-5-haiku-20241022," "claude-3-opus-20240229," From 23d073f16cd15efbd0fbe1841009a9b369be1305 Mon Sep 17 00:00:00 2001 From: Novice Date: Wed, 15 Oct 2025 10:49:32 +0800 Subject: [PATCH 5/6] fix: mcp update controller missing headers --- api/controllers/console/workspace/tool_providers.py | 1 + 1 file changed, 1 insertion(+) diff --git a/api/controllers/console/workspace/tool_providers.py b/api/controllers/console/workspace/tool_providers.py index 93176ccf16..0d8d943c81 100644 --- a/api/controllers/console/workspace/tool_providers.py +++ b/api/controllers/console/workspace/tool_providers.py @@ -919,6 +919,7 @@ class ToolProviderMCPApi(Resource): parser.add_argument("server_identifier", type=str, required=True, nullable=False, location="json") parser.add_argument("configuration", type=dict, required=False, nullable=True, location="json") parser.add_argument("authentication", type=dict, required=False, nullable=True, location="json") + parser.add_argument("headers", type=dict, required=False, nullable=True, location="json", default={}) args = parser.parse_args() if not is_valid_url(args["server_url"]): From fa1b497e0f4797f48461f79e38efe2ca3455728f Mon Sep 17 00:00:00 2001 From: zxhlyh Date: Wed, 15 Oct 2025 11:30:59 +0800 Subject: [PATCH 6/6] feat: mcp auth --- web/app/components/tools/mcp/modal.tsx | 12 +++++++++--- web/i18n/en-US/tools.ts | 4 ++-- web/i18n/zh-Hans/tools.ts | 4 ++-- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/web/app/components/tools/mcp/modal.tsx b/web/app/components/tools/mcp/modal.tsx index 00caa695fe..987a517ef5 100644 --- a/web/app/components/tools/mcp/modal.tsx +++ b/web/app/components/tools/mcp/modal.tsx @@ -106,7 +106,7 @@ const MCPModal = ({ const appIconRef = useRef(null) const isHovering = useHover(appIconRef) const [authMethod, setAuthMethod] = useState(MCPAuthMethod.authentication) - const [isDynamicRegistration, setIsDynamicRegistration] = useState(data?.is_dynamic_registration || false) + const [isDynamicRegistration, setIsDynamicRegistration] = useState(isCreate ? true : data?.is_dynamic_registration) const [clientID, setClientID] = useState(data?.authentication?.client_id || '') const [credentials, setCredentials] = useState(data?.authentication?.client_secret || '') @@ -120,6 +120,9 @@ const MCPModal = ({ setSseReadTimeout(data.sse_read_timeout || 300) setHeaders(Object.entries(data.masked_headers || {}).map(([key, value]) => ({ id: uuid(), key, value }))) setAppIcon(getIcon(data)) + setIsDynamicRegistration(data.is_dynamic_registration) + setClientID(data.authentication?.client_id || '') + setCredentials(data.authentication?.client_secret || '') } else { // Reset for create mode @@ -130,6 +133,9 @@ const MCPModal = ({ setSseReadTimeout(300) setHeaders([]) setAppIcon(DEFAULT_ICON as AppIconSelection) + setIsDynamicRegistration(true) + setClientID('') + setCredentials('') } }, [data]) @@ -322,13 +328,13 @@ const MCPModal = ({
- {t('tools.mcp.modal.credentials')} + {t('tools.mcp.modal.clientSecret')}
setCredentials(e.target.value)} onBlur={e => handleBlur(e.target.value.trim())} - placeholder={t('tools.mcp.modal.credentialsPlaceholder')} + placeholder={t('tools.mcp.modal.clientSecretPlaceholder')} disabled={isDynamicRegistration} />
diff --git a/web/i18n/en-US/tools.ts b/web/i18n/en-US/tools.ts index 02720ef5bd..ff04a87f6c 100644 --- a/web/i18n/en-US/tools.ts +++ b/web/i18n/en-US/tools.ts @@ -206,8 +206,8 @@ const translation = { authentication: 'Authentication', useDynamicClientRegistration: 'Use Dynamic Client Registration', clientID: 'Client ID', - credentials: 'Credentials', - credentialsPlaceholder: 'Client secret', + clientSecret: 'Client Secret', + clientSecretPlaceholder: 'Client Secret', configurations: 'Configurations', }, delete: 'Remove MCP Server', diff --git a/web/i18n/zh-Hans/tools.ts b/web/i18n/zh-Hans/tools.ts index ad85aa91fd..6c51f991f6 100644 --- a/web/i18n/zh-Hans/tools.ts +++ b/web/i18n/zh-Hans/tools.ts @@ -206,8 +206,8 @@ const translation = { authentication: '认证', useDynamicClientRegistration: '使用动态客户端注册', clientID: '客户端 ID', - credentials: '凭证', - credentialsPlaceholder: '客户端密钥', + clientSecret: '客户端密钥', + clientSecretPlaceholder: '客户端密钥', configurations: '配置', }, delete: '删除 MCP 服务',