({
modalState,
setModalState,
server,
onServerSaved,
}: MCPServerModalProps)
| 28 | } |
| 29 | |
| 30 | export default function MCPServerModal({ |
| 31 | modalState, |
| 32 | setModalState, |
| 33 | server, |
| 34 | onServerSaved, |
| 35 | }: MCPServerModalProps) { |
| 36 | const { t } = useTranslation(); |
| 37 | const token = useSelector(selectToken); |
| 38 | const recentEvents = useSelector(selectRecentEvents); |
| 39 | |
| 40 | const authTypes = [ |
| 41 | { label: t('settings.tools.mcp.authTypes.none'), value: 'none' }, |
| 42 | { label: t('settings.tools.mcp.authTypes.apiKey'), value: 'api_key' }, |
| 43 | { label: t('settings.tools.mcp.authTypes.bearer'), value: 'bearer' }, |
| 44 | { label: t('settings.tools.mcp.authTypes.oauth'), value: 'oauth' }, |
| 45 | // { label: t('settings.tools.mcp.authTypes.basic'), value: 'basic' }, |
| 46 | ]; |
| 47 | |
| 48 | const [formData, setFormData] = useState({ |
| 49 | name: server?.displayName || t('settings.tools.mcp.defaultServerName'), |
| 50 | server_url: server?.server_url || '', |
| 51 | auth_type: server?.auth_type || 'none', |
| 52 | api_key: '', |
| 53 | header_name: server?.api_key_header || 'X-API-Key', |
| 54 | bearer_token: '', |
| 55 | username: '', |
| 56 | password: '', |
| 57 | timeout: server?.timeout || 30, |
| 58 | oauth_scopes: server?.oauth_scopes || '', |
| 59 | oauth_task_id: '', |
| 60 | }); |
| 61 | |
| 62 | const [loading, setLoading] = useState(false); |
| 63 | const [testing, setTesting] = useState(false); |
| 64 | const [testResult, setTestResult] = useState<{ |
| 65 | success: boolean; |
| 66 | message: string; |
| 67 | status?: string; |
| 68 | authorization_url?: string; |
| 69 | tools?: { name: string; description?: string }[]; |
| 70 | tools_count?: number; |
| 71 | } | null>(null); |
| 72 | const [discoveredTools, setDiscoveredTools] = useState< |
| 73 | { name: string; description?: string }[] |
| 74 | >([]); |
| 75 | const [errors, setErrors] = useState<{ [key: string]: string }>({}); |
| 76 | const oauthPopupRef = useRef<Window | null>(null); |
| 77 | // Set after ``test_mcp_connection`` returns ``task_id``. The SSE |
| 78 | // effect filters ``recentEvents`` to envelopes matching this id and |
| 79 | // drives the OAuth UI (popup open / completion / failure) from the |
| 80 | // push stream rather than polling the legacy status endpoint. |
| 81 | const [oauthTaskId, setOauthTaskId] = useState<string | null>(null); |
| 82 | // Highest event id we have already reacted to for this taskId. Each |
| 83 | // mcp.oauth.* envelope must fire its side-effect once; without this |
| 84 | // any later re-render that re-evaluates ``recentEvents`` would |
| 85 | // re-open the popup or re-fire onComplete. |
| 86 | const handledEventIdsRef = useRef<Set<string>>(new Set()); |
| 87 | // Holds the ``testConnection`` ``onComplete`` for the current |
nothing calls this directly
no test coverage detected