jan/web-app/src/hooks/useToolApproval.ts
Faisal Amir 057accfb96
enhancement: ux tool call permission dialog and state active (#5157)
* enhancement: mcp toold dialog approval

* enhancement: update mcp tool enable or disable

* chore: add toggle mcl global permission
2025-06-01 23:58:20 +07:00

108 lines
3.1 KiB
TypeScript

import { create } from 'zustand'
import { persist, createJSONStorage } from 'zustand/middleware'
import { localStorageKey } from '@/constants/localStorage'
export type ToolApprovalModalProps = {
toolName: string
threadId: string
onApprove: (allowOnce: boolean) => void
onDeny: () => void
}
type ToolApprovalState = {
// Track approved tools per thread
approvedTools: Record<string, string[]> // threadId -> toolNames[]
// Global MCP permission toggle
allowAllMCPPermissions: boolean
// Modal state
isModalOpen: boolean
modalProps: ToolApprovalModalProps | null
// Actions
approveToolForThread: (threadId: string, toolName: string) => void
isToolApproved: (threadId: string, toolName: string) => boolean
showApprovalModal: (toolName: string, threadId: string) => Promise<boolean>
closeModal: () => void
setModalOpen: (open: boolean) => void
setAllowAllMCPPermissions: (allow: boolean) => void
}
export const useToolApproval = create<ToolApprovalState>()(
persist(
(set, get) => ({
approvedTools: {},
allowAllMCPPermissions: false,
isModalOpen: false,
modalProps: null,
approveToolForThread: (threadId: string, toolName: string) => {
set((state) => ({
approvedTools: {
...state.approvedTools,
[threadId]: [
...(state.approvedTools[threadId] || []),
toolName,
].filter((tool, index, arr) => arr.indexOf(tool) === index), // Remove duplicates
},
}))
},
isToolApproved: (threadId: string, toolName: string) => {
const state = get()
return state.approvedTools[threadId]?.includes(toolName) || false
},
showApprovalModal: (toolName: string, threadId: string) => {
return new Promise<boolean>((resolve) => {
set({
isModalOpen: true,
modalProps: {
toolName,
threadId,
onApprove: (allowOnce: boolean) => {
if (!allowOnce) {
// If not "allow once", add to approved tools for this thread
get().approveToolForThread(threadId, toolName)
}
get().closeModal()
resolve(true)
},
onDeny: () => {
get().closeModal()
resolve(false)
},
},
})
})
},
closeModal: () => {
set({
isModalOpen: false,
modalProps: null,
})
},
setModalOpen: (open: boolean) => {
set({ isModalOpen: open })
if (!open) {
get().closeModal()
}
},
setAllowAllMCPPermissions: (allow: boolean) => {
set({ allowAllMCPPermissions: allow })
},
}),
{
name: localStorageKey.toolApproval,
storage: createJSONStorage(() => localStorage),
// Only persist approved tools and global permission setting, not modal state
partialize: (state) => ({
approvedTools: state.approvedTools,
allowAllMCPPermissions: state.allowAllMCPPermissions,
}),
}
)
)