1515import { renderToolInterface } from "./toolDisplay.js" ;
1616import { escapeHtml } from "./sanitize.js" ;
1717
18- let toolDetailsAbortController = null ;
18+ let currentToolsList = [ ] ;
1919
2020/**
21- * Fetches a toolset from the /api/toolset endpoint and initiates creating the tool list.
21+ * Fetches a toolset from the /mcp endpoint and initiates creating the tool list.
2222 * @param {!HTMLElement } secondNavContent The HTML element where the tool list will be rendered.
2323 * @param {!HTMLElement } toolDisplayArea The HTML element where the details of a selected tool will be displayed.
2424 * @param {string } toolsetName The name of the toolset to load (empty string loads all tools).
@@ -27,10 +27,24 @@ let toolDetailsAbortController = null;
2727export async function loadTools ( secondNavContent , toolDisplayArea , toolsetName ) {
2828 secondNavContent . innerHTML = '<p>Fetching tools...</p>' ;
2929 try {
30- const response = await fetch ( `/api/toolset/${ toolsetName } ` ) ;
30+ const url = toolsetName ? `/mcp/${ toolsetName } ` : `/mcp` ;
31+ const response = await fetch ( url , {
32+ method : 'POST' ,
33+ headers : {
34+ 'Content-Type' : 'application/json' ,
35+ 'MCP-Protocol-Version' : '2025-11-25'
36+ } ,
37+ body : JSON . stringify ( {
38+ jsonrpc : "2.0" ,
39+ id : "1" ,
40+ method : "tools/list" ,
41+ } )
42+ } ) ;
43+
3144 if ( ! response . ok ) {
3245 throw new Error ( `HTTP error! status: ${ response . status } ` ) ;
3346 }
47+
3448 const apiResponse = await response . json ( ) ;
3549 renderToolList ( apiResponse , secondNavContent , toolDisplayArea ) ;
3650 } catch ( error ) {
@@ -41,33 +55,38 @@ export async function loadTools(secondNavContent, toolDisplayArea, toolsetName)
4155
4256/**
4357 * Renders the list of tools as buttons within the provided HTML element.
44- * @param {?{tools: ? Object<string,*>} } apiResponse The API response object containing the tools.
58+ * @param {Object } apiResponse The API response object containing the tools.
4559 * @param {!HTMLElement } secondNavContent The HTML element to render the tool list into.
4660 * @param {!HTMLElement } toolDisplayArea The HTML element for displaying tool details (passed to event handlers).
4761 */
4862function renderToolList ( apiResponse , secondNavContent , toolDisplayArea ) {
4963 secondNavContent . innerHTML = '' ;
5064
51- if ( ! apiResponse || typeof apiResponse . tools !== 'object' || apiResponse . tools === null ) {
52- console . error ( 'Error: Expected an object with a "tools" property, but received:' , apiResponse ) ;
65+ if ( apiResponse && apiResponse . error ) {
66+ console . error ( 'MCP API Error:' , apiResponse . error ) ;
67+ secondNavContent . textContent = `Error: ${ apiResponse . error . message || 'Unknown MCP error' } ` ;
68+ return ;
69+ }
70+
71+ if ( ! apiResponse || ! apiResponse . result || ! Array . isArray ( apiResponse . result . tools ) ) {
72+ console . error ( 'Error: Expected a valid MCP response with "result.tools" array, but received:' , apiResponse ) ;
5373 secondNavContent . textContent = 'Error: Invalid response format from toolset API.' ;
5474 return ;
5575 }
5676
57- const toolsObject = apiResponse . tools ;
58- const toolNames = Object . keys ( toolsObject ) ;
77+ currentToolsList = apiResponse . result . tools ;
5978
60- if ( toolNames . length === 0 ) {
79+ if ( currentToolsList . length === 0 ) {
6180 secondNavContent . textContent = 'No tools found.' ;
6281 return ;
6382 }
6483
6584 const ul = document . createElement ( 'ul' ) ;
66- toolNames . forEach ( toolName => {
85+ currentToolsList . forEach ( toolObj => {
6786 const li = document . createElement ( 'li' ) ;
6887 const button = document . createElement ( 'button' ) ;
69- button . textContent = toolName ;
70- button . dataset . toolname = toolName ;
88+ button . textContent = toolObj . name ;
89+ button . dataset . toolname = toolObj . name ;
7190 button . classList . add ( 'tool-button' ) ;
7291 button . addEventListener ( 'click' , ( event ) => handleToolClick ( event , secondNavContent , toolDisplayArea ) ) ;
7392 li . appendChild ( button ) ;
@@ -90,86 +109,81 @@ function handleToolClick(event, secondNavContent, toolDisplayArea) {
90109 currentActive . classList . remove ( 'active' ) ;
91110 }
92111 event . target . classList . add ( 'active' ) ;
93- fetchToolDetails ( toolName , toolDisplayArea ) ;
112+ renderToolDetails ( toolName , toolDisplayArea ) ;
94113 }
95114}
96115
97116/**
98- * Fetches details for a specific tool /api/tool endpoint.
99- * It aborts any previous in-flight request for tool details to stop race condition.
100- * @param {string } toolName The name of the tool to fetch details for.
117+ * Renders details for a specific tool from the cached MCP tools list.
118+ * @param {string } toolName The name of the tool to render details for.
101119 * @param {!HTMLElement } toolDisplayArea The HTML element to display the tool interface in.
102- * @returns {!Promise<void> } A promise that resolves when the tool details are fetched and rendered, or rejects on error.
103120 */
104- async function fetchToolDetails ( toolName , toolDisplayArea ) {
105- if ( toolDetailsAbortController ) {
106- toolDetailsAbortController . abort ( ) ;
107- console . debug ( "Aborted previous tool fetch." ) ;
108- }
121+ function renderToolDetails ( toolName , toolDisplayArea ) {
122+ const toolObject = currentToolsList . find ( t => t . name === toolName ) ;
109123
110- toolDetailsAbortController = new AbortController ( ) ;
111- const signal = toolDetailsAbortController . signal ;
124+ if ( ! toolObject ) {
125+ toolDisplayArea . innerHTML = `<p class="error">Tool "${ escapeHtml ( toolName ) } " data not found.</p>` ;
126+ return ;
127+ }
112128
113- toolDisplayArea . innerHTML = '<p>Loading tool details...</p>' ;
129+ console . debug ( "Rendering tool object: " , toolObject ) ;
114130
115- try {
116- const response = await fetch ( `/api/tool/${ encodeURIComponent ( toolName ) } ` , { signal } ) ;
117- if ( ! response . ok ) {
118- throw new Error ( `HTTP error! status: ${ response . status } ` ) ;
131+ let toolAuthRequired = [ ] ;
132+ let toolAuthParams = { } ;
133+ if ( toolObject . _meta ) {
134+ if ( toolObject . _meta [ "toolbox/authInvoke" ] ) {
135+ toolAuthRequired = toolObject . _meta [ "toolbox/authInvoke" ] ;
119136 }
120- const apiResponse = await response . json ( ) ;
121-
122- if ( ! apiResponse . tools || ! apiResponse . tools [ toolName ] ) {
123- throw new Error ( `Tool "${ toolName } " data not found in API response.` ) ;
137+ if ( toolObject . _meta [ "toolbox/authParam" ] ) {
138+ toolAuthParams = toolObject . _meta [ "toolbox/authParam" ] ;
124139 }
125- const toolObject = apiResponse . tools [ toolName ] ;
126- console . debug ( "Received tool object: " , toolObject )
127-
128- const toolInterfaceData = {
129- id : toolName ,
130- name : toolName ,
131- description : toolObject . description || "No description provided." ,
132- authRequired : toolObject . authRequired || [ ] ,
133- parameters : ( toolObject . parameters || [ ] ) . map ( param => {
134- let inputType = 'text' ;
135- const apiType = param . type ? param . type . toLowerCase ( ) : 'string' ;
136- let valueType = 'string' ;
137- let label = param . description || param . name ;
138-
139- if ( apiType === 'integer' || apiType === 'float' ) {
140- inputType = 'number' ;
141- valueType = 'number' ;
142- } else if ( apiType === 'boolean' ) {
143- inputType = 'checkbox' ;
144- valueType = 'boolean' ;
145- } else if ( apiType === 'array' ) {
146- inputType = 'textarea' ;
147- const itemType = param . items && param . items . type ? param . items . type . toLowerCase ( ) : 'string' ;
148- valueType = `array<${ itemType } >` ;
149- label += ' (Array)' ;
150- }
151-
152- return {
153- name : param . name ,
154- type : inputType ,
155- valueType : valueType ,
156- label : label ,
157- authServices : param . authServices , // TODO: to be updated when the native endpoint is no longer supported.
158- required : param . required || false ,
159- // defaultValue: param.default, can't do this yet bc tool manifest doesn't have default
160- } ;
161- } )
162- } ;
163-
164- console . debug ( "Transformed toolInterfaceData:" , toolInterfaceData ) ;
140+ }
165141
166- renderToolInterface ( toolInterfaceData , toolDisplayArea ) ;
167- } catch ( error ) {
168- if ( error . name === 'AbortError' ) {
169- console . debug ( "Previous fetch was aborted, expected behavior." ) ;
170- } else {
171- console . error ( `Failed to load details for tool "${ toolName } ":` , error ) ;
172- toolDisplayArea . innerHTML = `<p class="error">Failed to load details for ${ escapeHtml ( toolName ) } . ${ escapeHtml ( error . message ) } </p>` ;
173- }
142+ // Default processing if inputSchema properties are not present
143+ let toolParameters = [ ] ;
144+ if ( toolObject . inputSchema && toolObject . inputSchema . properties ) {
145+ const props = toolObject . inputSchema . properties ;
146+ const requiredFields = toolObject . inputSchema . required || [ ] ;
147+
148+ toolParameters = Object . keys ( props ) . map ( paramName => {
149+ const param = props [ paramName ] ;
150+ let inputType = 'text' ;
151+ const apiType = param . type ? param . type . toLowerCase ( ) : 'string' ;
152+ let valueType = 'string' ;
153+ let label = param . description || paramName ;
154+
155+ if ( apiType === 'integer' || apiType === 'number' ) {
156+ inputType = 'number' ;
157+ valueType = 'number' ;
158+ } else if ( apiType === 'boolean' ) {
159+ inputType = 'checkbox' ;
160+ valueType = 'boolean' ;
161+ } else if ( apiType === 'array' ) {
162+ inputType = 'textarea' ;
163+ const itemType = param . items && param . items . type ? param . items . type . toLowerCase ( ) : 'string' ;
164+ valueType = `array<${ itemType } >` ;
165+ label += ' (Array)' ;
166+ }
167+
168+ return {
169+ name : paramName ,
170+ type : inputType ,
171+ valueType : valueType ,
172+ label : label ,
173+ required : requiredFields . includes ( paramName ) ,
174+ authServices : toolAuthParams [ paramName ] || [ ]
175+ } ;
176+ } ) ;
174177 }
178+
179+ const toolInterfaceData = {
180+ id : toolName ,
181+ name : toolName ,
182+ description : toolObject . description || "No description provided." ,
183+ authRequired : toolAuthRequired ,
184+ parameters : toolParameters
185+ } ;
186+
187+ console . debug ( "Transformed toolInterfaceData:" , toolInterfaceData ) ;
188+ renderToolInterface ( toolInterfaceData , toolDisplayArea ) ;
175189}
0 commit comments