mirror of
https://github.com/nocodb/nocodb.git
synced 2026-05-01 05:26:53 +00:00
250 lines
6.8 KiB
TypeScript
250 lines
6.8 KiB
TypeScript
import { type MCPTokenType } from 'nocodb-sdk'
|
|
import dayjs from 'dayjs'
|
|
|
|
export type MCPTokenExtendedType = MCPTokenType & {
|
|
isNew?: boolean
|
|
loading?: boolean
|
|
error?: boolean
|
|
created_display_name?: string
|
|
workspace?: { title?: string }
|
|
base?: { title?: string }
|
|
}
|
|
|
|
export const useMcpSettings = createSharedComposable(() => {
|
|
const { $api } = useNuxtApp()
|
|
|
|
const { t } = useI18n()
|
|
|
|
const basesStore = useBases()
|
|
|
|
const { activeWorkspaceId, activeWorkspace } = storeToRefs(useWorkspace())
|
|
|
|
const { activeProjectId, openedProject } = storeToRefs(basesStore)
|
|
|
|
const { basesUser } = storeToRefs(basesStore)
|
|
|
|
const baseUsers = computed(() => (activeProjectId.value ? basesUser.value.get(activeProjectId.value) || [] : []))
|
|
|
|
const mcpTokens = ref<MCPTokenExtendedType[]>([] as MCPTokenExtendedType[])
|
|
|
|
const accountMcpTokens = ref<MCPTokenExtendedType[]>([])
|
|
|
|
const newMcpTokenTitle = ref('')
|
|
|
|
const isCreatingMcpToken = ref(false)
|
|
|
|
const isUnsavedMCPTokenPending = computed(() => mcpTokens.value.some((t) => t.isNew))
|
|
|
|
const listMcpTokens = async () => {
|
|
try {
|
|
const response = await $api.internal.getOperation(activeWorkspaceId.value, activeProjectId.value, {
|
|
operation: 'mcpList',
|
|
})
|
|
|
|
if (response && Array.isArray(response)) {
|
|
mcpTokens.value = response.map((token: MCPTokenType) => {
|
|
const user = baseUsers.value.find((u) => u.id === token.fk_user_id)
|
|
return {
|
|
...token,
|
|
isNew: false,
|
|
created_display_name: user?.display_name ?? user?.email ?? '',
|
|
}
|
|
})
|
|
}
|
|
} catch (error: any) {
|
|
message.error(await extractSdkResponseErrorMsg(error))
|
|
console.error(error)
|
|
}
|
|
}
|
|
|
|
const cancelNewMcpToken = () => {
|
|
mcpTokens.value = mcpTokens.value.filter((t) => !t.isNew)
|
|
}
|
|
|
|
const createMcpToken = async (token: Partial<MCPTokenExtendedType>) => {
|
|
if (!activeProjectId.value) return
|
|
|
|
try {
|
|
isCreatingMcpToken.value = true
|
|
|
|
// trim title before saving
|
|
if (newMcpTokenTitle.value) {
|
|
newMcpTokenTitle.value = newMcpTokenTitle.value.trim()
|
|
}
|
|
|
|
const response = await $api.internal.postOperation(
|
|
activeWorkspaceId.value,
|
|
activeProjectId.value,
|
|
{
|
|
operation: 'mcpCreate',
|
|
},
|
|
{
|
|
...token,
|
|
title: newMcpTokenTitle.value,
|
|
},
|
|
)
|
|
|
|
if (response) {
|
|
// Replace the new token placeholder with the created token
|
|
const index = mcpTokens.value.findIndex((t) => t.isNew)
|
|
if (index !== -1) {
|
|
const createdToken = response
|
|
const user = baseUsers.value.find((u) => u.id === createdToken.fk_user_id)
|
|
mcpTokens.value[index] = {
|
|
...createdToken,
|
|
isNew: false,
|
|
created_display_name: user?.display_name ?? user?.email ?? '',
|
|
}
|
|
}
|
|
}
|
|
|
|
message.success(t('msg.success.mcpTokenCreated'))
|
|
isCreatingMcpToken.value = false
|
|
return response
|
|
} catch (error: any) {
|
|
isCreatingMcpToken.value = false
|
|
message.error(await extractSdkResponseErrorMsg(error))
|
|
console.error(error)
|
|
}
|
|
}
|
|
|
|
const updateMcpToken = async (token: MCPTokenExtendedType, isAccountLevel = false) => {
|
|
const workspaceId = isAccountLevel ? token.fk_workspace_id : activeWorkspaceId.value
|
|
const baseId = isAccountLevel ? token.base_id : activeProjectId.value
|
|
|
|
if (!workspaceId || !baseId) return
|
|
|
|
const tokenList = isAccountLevel ? accountMcpTokens : mcpTokens
|
|
|
|
try {
|
|
token.loading = true
|
|
|
|
const res = await $api.internal.postOperation(
|
|
workspaceId,
|
|
baseId,
|
|
{
|
|
operation: 'mcpUpdate',
|
|
},
|
|
{
|
|
tokenId: token.id,
|
|
},
|
|
)
|
|
|
|
token.loading = false
|
|
message.success(t('msg.success.mcpTokenUpdated'))
|
|
|
|
// Update the token in the appropriate list
|
|
if (isAccountLevel) {
|
|
const index = tokenList.value.findIndex((t) => t.id === token.id)
|
|
if (index !== -1 && res) {
|
|
tokenList.value[index] = { ...tokenList.value[index], ...res }
|
|
}
|
|
}
|
|
|
|
return res
|
|
} catch (error: any) {
|
|
message.error(await extractSdkResponseErrorMsg(error))
|
|
token.loading = false
|
|
console.error(error)
|
|
}
|
|
}
|
|
|
|
const deleteMcpToken = async (token: MCPTokenExtendedType, isAccountLevel = false) => {
|
|
const workspaceId = isAccountLevel ? token.fk_workspace_id : activeWorkspaceId.value
|
|
const baseId = isAccountLevel ? token.base_id : activeProjectId.value
|
|
|
|
if (!workspaceId || !baseId) return
|
|
|
|
try {
|
|
const tokenList = isAccountLevel ? accountMcpTokens : mcpTokens
|
|
|
|
// Add loading state
|
|
const tokenToDelete = tokenList.value.find((t) => t.id === token.id)
|
|
if (tokenToDelete) {
|
|
tokenToDelete.loading = true
|
|
}
|
|
|
|
const response = await $api.internal.postOperation(
|
|
workspaceId,
|
|
baseId,
|
|
{
|
|
operation: 'mcpDelete',
|
|
},
|
|
{
|
|
tokenId: token.id,
|
|
},
|
|
)
|
|
|
|
// Only remove from the list if successful
|
|
if (response) {
|
|
tokenList.value = tokenList.value.filter((t) => t.id !== token.id)
|
|
message.success(t('msg.success.mcpTokenDeleted'))
|
|
} else {
|
|
if (tokenToDelete) {
|
|
tokenToDelete.loading = false
|
|
}
|
|
message.error(t('msg.error.failedToDeleteMcpToken'))
|
|
}
|
|
} catch (error: any) {
|
|
const tokenList = isAccountLevel ? accountMcpTokens.value : mcpTokens.value
|
|
const tokenToDelete = tokenList.find((t) => t.id === token.id)
|
|
if (tokenToDelete) {
|
|
tokenToDelete.loading = false
|
|
}
|
|
|
|
message.error(await extractSdkResponseErrorMsg(error))
|
|
console.error('Error deleting MCP token:', error)
|
|
}
|
|
}
|
|
|
|
const addNewMcpToken = () => {
|
|
newMcpTokenTitle.value = `${openedProject.value?.title || 'Base'}(${activeWorkspace.value?.title}) : ${dayjs().format(
|
|
'D MMMM YYYY, h:mm A',
|
|
)}`
|
|
|
|
mcpTokens.value = [
|
|
{
|
|
title: newMcpTokenTitle.value,
|
|
isNew: true,
|
|
},
|
|
...mcpTokens.value,
|
|
]
|
|
}
|
|
|
|
const listAccountMcpTokens = async () => {
|
|
try {
|
|
const response = await $api.internal.getOperation(NO_SCOPE, NO_SCOPE, {
|
|
operation: 'mcpRootList',
|
|
})
|
|
|
|
if (response && Array.isArray(response)) {
|
|
accountMcpTokens.value = response.map((token: any) => ({
|
|
...token,
|
|
isNew: false,
|
|
}))
|
|
}
|
|
return accountMcpTokens.value
|
|
} catch (error: any) {
|
|
message.error(await extractSdkResponseErrorMsg(error))
|
|
console.error(error)
|
|
return []
|
|
}
|
|
}
|
|
|
|
return {
|
|
mcpTokens,
|
|
createMcpToken,
|
|
listMcpTokens,
|
|
updateMcpToken,
|
|
deleteMcpToken,
|
|
isUnsavedMCPTokenPending,
|
|
cancelNewMcpToken,
|
|
addNewMcpToken,
|
|
isCreatingMcpToken,
|
|
newMcpTokenTitle,
|
|
// Account-level
|
|
accountMcpTokens,
|
|
listAccountMcpTokens,
|
|
}
|
|
})
|