authorizing redirection

This commit is contained in:
jZonG 2025-05-28 15:35:44 +08:00
parent 2bcfcfabb7
commit 8832f08fed
5 changed files with 72 additions and 21 deletions

View File

@ -34,8 +34,8 @@ const NewMCPCard = ({ handleCreate }: Props) => {
const linkUrl = useMemo(() => {
// TODO help link
if (language.startsWith('zh_'))
return 'https://docs.dify.ai/zh-hans/guides/tools#ru-he-chuang-jian-zi-ding-yi-gong-ju'
return 'https://docs.dify.ai/en/guides/tools#how-to-create-custom-tools'
return 'https://docs.dify.ai/zh-hans/guides/tools/integrate-tool/mcp'
return 'https://docs.dify.ai/en/guides/tools/integrate-tool/mcp'
}, [language])
const [showModal, setShowModal] = useState(false)

View File

@ -1,5 +1,6 @@
'use client'
import React, { useCallback, useState } from 'react'
import React, { useCallback } from 'react'
import { useRouter } from 'next/navigation'
import type { FC } from 'react'
import { useBoolean } from 'ahooks'
import { useTranslation } from 'react-i18next'
@ -41,9 +42,10 @@ const MCPDetailContent: FC<Props> = ({
onHide,
}) => {
const { t } = useTranslation()
const router = useRouter()
const { isCurrentWorkspaceManager } = useAppContext()
const { data, isPending: isGettingTools } = useMCPTools(detail.is_team_authorization ? detail.id : '')
const { data, isFetching: isGettingTools } = useMCPTools(detail.is_team_authorization ? detail.id : '')
const invalidateMCPTools = useInvalidateMCPTools()
const { mutateAsync: updateTools, isPending: isUpdating } = useUpdateMCPTools()
const { mutateAsync: authorizeMcp, isPending: isAuthorizing } = useAuthorizeMCP()
@ -54,6 +56,7 @@ const MCPDetailContent: FC<Props> = ({
return
await updateTools(detail.id)
invalidateMCPTools(detail.id)
onUpdate()
}, [detail, updateTools])
const { mutate: updateMCP } = useUpdateMCP({
@ -85,11 +88,11 @@ const MCPDetailContent: FC<Props> = ({
provider_id: detail.id,
server_url: detail.server_url!,
})
// TODO
if ((res as any)?.result === 'success') {
hideUpdateModal()
onUpdate()
}
if (res.result === 'success')
handleUpdateTools()
else if (res.authorization_url)
router.push(res.authorization_url)
}, [detail, updateMCP, hideUpdateModal, onUpdate])
const handleUpdate = useCallback(async (data: any) => {
@ -117,8 +120,6 @@ const MCPDetailContent: FC<Props> = ({
}
}, [detail, showDeleting, hideDeleting, hideDeleteConfirm, onUpdate])
const [loading, setLoading] = useState(false)
if (!detail)
return null
@ -150,7 +151,6 @@ const MCPDetailContent: FC<Props> = ({
<Button
variant='secondary'
className='w-full'
// onClick={() => setShowSettingAuth(true)}
disabled={!isCurrentWorkspaceManager}
>
<Indicator className='mr-2' color={'green'} />

View File

@ -1,9 +1,16 @@
'use client'
import { useMemo, useState } from 'react'
import { useEffect, useMemo, useState } from 'react'
import { useSearchParams } from 'next/navigation'
import NewMCPCard from './create-card'
import MCPCard from './provider-card'
import MCPDetailPanel from './detail/provider-detail'
import { useAllMCPTools, useAuthorizeMCP, useInvalidateAllMCPTools, useInvalidateMCPTools, useUpdateMCPTools } from '@/service/use-tools'
import {
useAllMCPTools,
useAuthorizeMCP,
useInvalidateMCPTools,
useUpdateMCPAuthorizationToken,
useUpdateMCPTools,
} from '@/service/use-tools'
import type { ToolWithProvider } from '@/app/components/workflow/types'
import cn from '@/utils/classnames'
@ -32,11 +39,15 @@ function renderDefaultCard() {
const MCPList = ({
searchText,
}: Props) => {
const { data: list = [] } = useAllMCPTools()
const invalidateMCPList = useInvalidateAllMCPTools()
const searchParams = useSearchParams()
const authCode = searchParams.get('code') || ''
const providerID = decodeURIComponent(searchParams.get('state') || '').split('provider_id=')[1] || ''
const { data: list = [], refetch } = useAllMCPTools()
const { mutateAsync: authorizeMcp } = useAuthorizeMCP()
const { mutateAsync: updateTools } = useUpdateMCPTools()
const invalidateMCPTools = useInvalidateMCPTools()
const { mutateAsync: updateMCPAuthorizationToken } = useUpdateMCPAuthorizationToken()
const filteredList = useMemo(() => {
return list.filter((collection) => {
@ -53,16 +64,38 @@ const MCPList = ({
}, [list, currentProviderID])
const handleCreate = async (provider: ToolWithProvider) => {
invalidateMCPList()
await refetch() // update list
setCurrentProviderID(provider.id)
await authorizeMcp({
provider_id: provider.id,
server_url: provider.server_url!,
})
await refetch() // update authorization in list
await updateTools(provider.id)
invalidateMCPTools(provider.id)
await refetch() // update tool list in provider list
}
const handleUpdateAuthorization = async (providerID: string, code: string) => {
const targetProvider = list.find(provider => provider.id === providerID)
if (!targetProvider) return
await updateMCPAuthorizationToken({
provider_id: providerID,
server_url: targetProvider.server_url!,
authorization_code: code,
})
await refetch()
setCurrentProviderID(providerID)
await updateTools(providerID)
invalidateMCPTools(providerID)
await refetch()
}
useEffect(() => {
if (authCode && providerID)
handleUpdateAuthorization(providerID, authCode)
}, [authCode, providerID])
return (
<>
<div
@ -78,7 +111,7 @@ const MCPList = ({
data={provider}
currentProvider={currentProvider}
handleSelect={setCurrentProviderID}
onUpdate={() => invalidateMCPList()}
onUpdate={refetch}
/>
))}
{!list.length && renderDefaultCard()}
@ -87,7 +120,7 @@ const MCPList = ({
<MCPDetailPanel
detail={currentProvider}
onHide={() => setCurrentProviderID(undefined)}
onUpdate={() => invalidateMCPList()}
onUpdate={refetch}
/>
)}
</>

View File

@ -1,6 +1,7 @@
'use client'
import { useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useSearchParams } from 'next/navigation'
import type { Collection } from './types'
import Marketplace from './marketplace'
import cn from '@/utils/classnames'
@ -25,8 +26,12 @@ const ProviderList = () => {
const containerRef = useRef<HTMLDivElement>(null)
const { enable_marketplace } = useAppContextSelector(s => s.systemFeatures)
const searchParams = useSearchParams()
const authCode = searchParams.get('code') || ''
const providerID = decodeURIComponent(searchParams.get('state') || '').split('provider_id=')[1] || ''
const [activeTab, setActiveTab] = useTabSearchParams({
defaultTab: 'builtin',
defaultTab: authCode && providerID ? 'mcp' : 'builtin',
})
const options = [
{ value: 'builtin', text: t('tools.type.builtIn') },

View File

@ -141,13 +141,26 @@ export const useAuthorizeMCP = () => {
return useMutation({
mutationKey: [NAME_SPACE, 'authorize-mcp'],
mutationFn: (payload: { provider_id: string; server_url: string }) => {
return post('/workspaces/current/tool-provider/mcp/auth', {
return post<{ result?: string; authorization_url?: string }>('/workspaces/current/tool-provider/mcp/auth', {
body: payload,
})
},
})
}
export const useUpdateMCPAuthorizationToken = () => {
return useMutation({
mutationKey: [NAME_SPACE, 'refresh-mcp-server-code'],
mutationFn: (payload: { provider_id: string; server_url: string; authorization_code: string }) => {
return get<MCPServerDetail>('/workspaces/current/tool-provider/mcp/token', {
params: {
...payload,
},
})
},
})
}
export const useMCPTools = (providerID: string) => {
return useQuery({
enabled: !!providerID,