1
- import React , { useState } from 'react' ;
2
- import { useQuery , useMutation } from '@tanstack/react-query' ;
3
- import { DataTable } from '../view_logs/table' ;
4
- import { columns , ToolTestPanel } from './columns' ;
5
- import { MCPTool , MCPToolsViewerProps , CallMCPToolResponse } from './types' ;
6
- import { listMCPTools , callMCPTool } from '../networking' ;
7
1
import MCPServers from './mcp_servers' ;
8
-
9
- // Wrapper to handle the type mismatch between MCPTool and DataTable's expected type
10
- function DataTableWrapper ( {
11
- columns,
12
- data,
13
- isLoading,
14
- } : {
15
- columns : any ;
16
- data : MCPTool [ ] ;
17
- isLoading : boolean ;
18
- } ) {
19
- // Create a dummy renderSubComponent and getRowCanExpand function
20
- const renderSubComponent = ( ) => < div /> ;
21
- const getRowCanExpand = ( ) => false ;
22
-
23
- return (
24
- < DataTable
25
- columns = { columns as any }
26
- data = { data as any }
27
- isLoading = { isLoading }
28
- renderSubComponent = { renderSubComponent }
29
- getRowCanExpand = { getRowCanExpand }
30
- loadingMessage = "🚅 Loading tools..."
31
- noDataMessage = "No tools found"
32
- />
33
- ) ;
34
- }
35
-
36
- const MCPToolsViewer = ( {
37
- serverId,
38
- accessToken,
39
- userRole,
40
- userID,
41
- } : MCPToolsViewerProps ) => {
42
- const [ searchTerm , setSearchTerm ] = useState ( '' ) ;
43
- const [ selectedTool , setSelectedTool ] = useState < MCPTool | null > ( null ) ;
44
- const [ toolResult , setToolResult ] = useState < CallMCPToolResponse | null > ( null ) ;
45
- const [ toolError , setToolError ] = useState < Error | null > ( null ) ;
46
-
47
- // Query to fetch MCP tools
48
- const { data : mcpTools , isLoading : isLoadingTools } = useQuery ( {
49
- queryKey : [ 'mcpTools' ] ,
50
- queryFn : ( ) => {
51
- if ( ! accessToken ) throw new Error ( 'Access Token required' ) ;
52
- return listMCPTools ( accessToken , serverId ) ;
53
- } ,
54
- enabled : ! ! accessToken ,
55
- } ) ;
56
-
57
- // Mutation for calling a tool
58
- const { mutate : executeTool , isPending : isCallingTool } = useMutation ( {
59
- mutationFn : ( args : { tool : MCPTool ; arguments : Record < string , any > } ) => {
60
- if ( ! accessToken ) throw new Error ( 'Access Token required' ) ;
61
- return callMCPTool (
62
- accessToken ,
63
- args . tool . name ,
64
- args . arguments
65
- ) ;
66
- } ,
67
- onSuccess : ( data ) => {
68
- setToolResult ( data ) ;
69
- setToolError ( null ) ;
70
- } ,
71
- onError : ( error : Error ) => {
72
- setToolError ( error ) ;
73
- setToolResult ( null ) ;
74
- } ,
75
- } ) ;
76
-
77
- // Add onToolSelect handler to each tool
78
- const toolsData = React . useMemo ( ( ) => {
79
- if ( ! mcpTools ) return [ ] ;
80
-
81
- return mcpTools . map ( ( tool : MCPTool ) => ( {
82
- ...tool ,
83
- onToolSelect : ( tool : MCPTool ) => {
84
- setSelectedTool ( tool ) ;
85
- setToolResult ( null ) ;
86
- setToolError ( null ) ;
87
- }
88
- } ) ) ;
89
- } , [ mcpTools ] ) ;
90
-
91
- // Filter tools based on search term
92
- const filteredTools = React . useMemo ( ( ) => {
93
- return toolsData . filter ( ( tool : MCPTool ) => {
94
- const searchLower = searchTerm . toLowerCase ( ) ;
95
- return (
96
- tool . name . toLowerCase ( ) . includes ( searchLower ) ||
97
- ( tool . description != null && tool . description . toLowerCase ( ) . includes ( searchLower ) ) ||
98
- tool . mcp_info . server_name . toLowerCase ( ) . includes ( searchLower )
99
- ) ;
100
- } ) ;
101
- } , [ toolsData , searchTerm ] ) ;
102
-
103
- // Handle tool call submission
104
- const handleToolSubmit = ( args : Record < string , any > ) => {
105
- if ( ! selectedTool ) return ;
106
-
107
- executeTool ( {
108
- tool : selectedTool ,
109
- arguments : args ,
110
- } ) ;
111
- } ;
112
-
113
- if ( ! accessToken || ! userRole || ! userID ) {
114
- return < div className = "p-6 text-center text-gray-500" > Missing required authentication parameters.</ div > ;
115
- }
116
-
117
- return (
118
- < div className = "w-full p-6" >
119
- < div className = "flex items-center justify-between mb-4" >
120
- < h1 className = "text-xl font-semibold" > MCP Tools</ h1 >
121
- </ div >
122
-
123
- < div className = "bg-white rounded-lg shadow" >
124
- < div className = "border-b px-6 py-4" >
125
- < div className = "flex items-center justify-between" >
126
- < div className = "relative w-64" >
127
- < input
128
- type = "text"
129
- placeholder = "Search tools..."
130
- className = "w-full px-3 py-2 pl-8 border rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
131
- value = { searchTerm }
132
- onChange = { ( e ) => setSearchTerm ( e . target . value ) }
133
- />
134
- < svg
135
- className = "absolute left-2.5 top-2.5 h-4 w-4 text-gray-500"
136
- fill = "none"
137
- stroke = "currentColor"
138
- viewBox = "0 0 24 24"
139
- >
140
- < path
141
- strokeLinecap = "round"
142
- strokeLinejoin = "round"
143
- strokeWidth = { 2 }
144
- d = "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
145
- />
146
- </ svg >
147
- </ div >
148
- < div className = "text-sm text-gray-500" >
149
- { filteredTools . length } tool{ filteredTools . length !== 1 ? "s" : "" } available
150
- </ div >
151
- </ div >
152
- </ div >
153
-
154
- < DataTableWrapper
155
- columns = { columns }
156
- data = { filteredTools }
157
- isLoading = { isLoadingTools }
158
- />
159
- </ div >
160
-
161
- { /* Tool Test Panel - Show when a tool is selected */ }
162
- { selectedTool && (
163
- < div className = "fixed inset-0 bg-gray-800 bg-opacity-75 flex items-center justify-center z-50 p-4" >
164
- < ToolTestPanel
165
- tool = { selectedTool }
166
- onSubmit = { handleToolSubmit }
167
- isLoading = { isCallingTool }
168
- result = { toolResult }
169
- error = { toolError }
170
- onClose = { ( ) => setSelectedTool ( null ) }
171
- />
172
- </ div >
173
- ) }
174
- </ div >
175
- ) ;
176
- }
2
+ import MCPToolsViewer from './mcp_tools' ;
177
3
178
4
export { MCPToolsViewer , MCPServers } ;
0 commit comments