22 * Hook for discovering and managing MCP tools
33 *
44 * This hook provides a unified interface for accessing MCP tools
5- * alongside regular platform tools in the tool-input component
5+ * using TanStack Query for optimal caching and performance
66 */
77
88import type React from 'react'
9- import { useCallback , useEffect , useMemo , useRef , useState } from 'react'
9+ import { useCallback , useMemo } from 'react'
10+ import { useQueryClient } from '@tanstack/react-query'
1011import { WrenchIcon } from 'lucide-react'
1112import { createLogger } from '@/lib/logs/console/logger'
12- import type { McpTool } from '@/lib/mcp/types'
1313import { createMcpToolId } from '@/lib/mcp/utils'
14- import { useMcpServers } from '@/hooks/queries/mcp'
14+ import { mcpKeys , useMcpToolsQuery } from '@/hooks/queries/mcp'
1515
1616const logger = createLogger ( 'useMcpTools' )
1717
@@ -37,81 +37,39 @@ export interface UseMcpToolsResult {
3737}
3838
3939export function useMcpTools ( workspaceId : string ) : UseMcpToolsResult {
40- const [ mcpTools , setMcpTools ] = useState < McpToolForUI [ ] > ( [ ] )
41- const [ isLoading , setIsLoading ] = useState ( false )
42- const [ error , setError ] = useState < string | null > ( null )
43-
44- const { data : servers = [ ] } = useMcpServers ( workspaceId )
45-
46- // Track the last fingerprint
47- const lastProcessedFingerprintRef = useRef < string > ( '' )
48-
49- // Create a stable server fingerprint
50- const serversFingerprint = useMemo ( ( ) => {
51- return servers
52- . filter ( ( s ) => s . enabled && ! s . deletedAt )
53- . map ( ( s ) => ` ${ s . id } - ${ s . enabled } - ${ s . updatedAt } ` )
54- . sort ( )
55- . join ( '|' )
56- } , [ servers ] )
40+ const queryClient = useQueryClient ( )
41+
42+ const { data : mcpToolsData = [ ] , isLoading , error : queryError } = useMcpToolsQuery ( workspaceId )
43+
44+ const mcpTools = useMemo < McpToolForUI [ ] > ( ( ) => {
45+ return mcpToolsData . map ( ( tool ) => ( {
46+ id : createMcpToolId ( tool . serverId , tool . name ) ,
47+ name : tool . name ,
48+ description : tool . description ,
49+ serverId : tool . serverId ,
50+ serverName : tool . serverName ,
51+ type : 'mcp' as const ,
52+ inputSchema : tool . inputSchema ,
53+ bgColor : '#6366F1' ,
54+ icon : WrenchIcon ,
55+ } ) )
56+ } , [ mcpToolsData ] )
5757
5858 const refreshTools = useCallback (
5959 async ( forceRefresh = false ) => {
60- // Skip if no workspaceId (e.g., on template preview pages)
6160 if ( ! workspaceId ) {
62- setMcpTools ( [ ] )
63- setIsLoading ( false )
61+ logger . warn ( 'Cannot refresh tools: no workspaceId provided' )
6462 return
6563 }
6664
67- setIsLoading ( true )
68- setError ( null )
69-
70- try {
71- logger . info ( 'Discovering MCP tools' , { forceRefresh, workspaceId } )
72-
73- const response = await fetch (
74- `/api/mcp/tools/discover?workspaceId=${ workspaceId } &refresh=${ forceRefresh } `
75- )
76-
77- if ( ! response . ok ) {
78- throw new Error ( `Failed to discover MCP tools: ${ response . status } ${ response . statusText } ` )
79- }
80-
81- const data = await response . json ( )
82-
83- if ( ! data . success ) {
84- throw new Error ( data . error || 'Failed to discover MCP tools' )
85- }
86-
87- const tools = data . data . tools || [ ]
88- const transformedTools = tools . map ( ( tool : McpTool ) => ( {
89- id : createMcpToolId ( tool . serverId , tool . name ) ,
90- name : tool . name ,
91- description : tool . description ,
92- serverId : tool . serverId ,
93- serverName : tool . serverName ,
94- type : 'mcp' as const ,
95- inputSchema : tool . inputSchema ,
96- bgColor : '#6366F1' ,
97- icon : WrenchIcon ,
98- } ) )
99-
100- setMcpTools ( transformedTools )
101-
102- logger . info (
103- `Discovered ${ transformedTools . length } MCP tools from ${ data . data . byServer ? Object . keys ( data . data . byServer ) . length : 0 } servers`
104- )
105- } catch ( err ) {
106- const errorMessage = err instanceof Error ? err . message : 'Failed to discover MCP tools'
107- logger . error ( 'Error discovering MCP tools:' , err )
108- setError ( errorMessage )
109- setMcpTools ( [ ] )
110- } finally {
111- setIsLoading ( false )
112- }
65+ logger . info ( 'Refreshing MCP tools' , { forceRefresh, workspaceId } )
66+
67+ await queryClient . invalidateQueries ( {
68+ queryKey : mcpKeys . tools ( workspaceId ) ,
69+ refetchType : forceRefresh ? 'active' : 'all' ,
70+ } )
11371 } ,
114- [ workspaceId ]
72+ [ workspaceId , queryClient ]
11573 )
11674
11775 const getToolById = useCallback (
@@ -128,41 +86,10 @@ export function useMcpTools(workspaceId: string): UseMcpToolsResult {
12886 [ mcpTools ]
12987 )
13088
131- useEffect ( ( ) => {
132- refreshTools ( )
133- } , [ refreshTools ] )
134-
135- // Refresh tools when servers change
136- useEffect ( ( ) => {
137- if ( ! serversFingerprint || serversFingerprint === lastProcessedFingerprintRef . current ) return
138-
139- logger . info ( 'Active servers changed, refreshing MCP tools' , {
140- serverCount : servers . filter ( ( s ) => s . enabled && ! s . deletedAt ) . length ,
141- fingerprint : serversFingerprint ,
142- } )
143-
144- lastProcessedFingerprintRef . current = serversFingerprint
145- refreshTools ( )
146- } , [ serversFingerprint , refreshTools ] )
147-
148- // Auto-refresh every 5 minutes
149- useEffect ( ( ) => {
150- const interval = setInterval (
151- ( ) => {
152- if ( ! isLoading ) {
153- refreshTools ( )
154- }
155- } ,
156- 5 * 60 * 1000
157- )
158-
159- return ( ) => clearInterval ( interval )
160- } , [ refreshTools ] )
161-
16289 return {
16390 mcpTools,
16491 isLoading,
165- error,
92+ error : queryError instanceof Error ? queryError . message : null ,
16693 refreshTools,
16794 getToolById,
16895 getToolsByServer,
0 commit comments