11import asyncio
2- from typing import List , Dict , Any
2+ from typing import Any
3+
34import httpx
45from mcp import ClientSession
5- from mcp .client .streamable_http import streamablehttp_client
66from mcp .client .sse import sse_client
7-
7+ from mcp . client . streamable_http import streamablehttp_client
88
99DEFAULT_HOST = "127.0.0.1"
1010DEFAULT_PORT = 8080
1111
1212
13- async def get_workloads (host : str = DEFAULT_HOST , port : int = DEFAULT_PORT ) -> List [Dict [str , Any ]]:
13+ async def get_workloads (
14+ host : str = DEFAULT_HOST , port : int = DEFAULT_PORT
15+ ) -> list [dict [str , Any ]]:
1416 """
1517 Get list of workloads from ToolHive API.
1618
@@ -46,7 +48,7 @@ async def get_workloads(host: str = DEFAULT_HOST, port: int = DEFAULT_PORT) -> L
4648 return workloads
4749
4850
49- async def list_tools_from_server (workload : Dict [str , Any ]) -> Dict [str , Any ]:
51+ async def list_tools_from_server (workload : dict [str , Any ]) -> dict [str , Any ]:
5052 """List tools from a single MCP server workload"""
5153 name = workload .get ("name" , "unknown" )
5254
@@ -56,7 +58,7 @@ async def list_tools_from_server(workload: Dict[str, Any]) -> Dict[str, Any]:
5658 "list_available_shell_commands" ,
5759 "execute_pipeline" ,
5860 "list_all_tools" ,
59- "get_tool_details"
61+ "get_tool_details" ,
6062 }
6163
6264 try :
@@ -72,15 +74,15 @@ async def list_tools_from_server(workload: Dict[str, Any]) -> Dict[str, Any]:
7274 "workload" : name ,
7375 "status" : "skipped" ,
7476 "tools" : [],
75- "error" : f"Workload status is '{ status } ', not running"
77+ "error" : f"Workload status is '{ status } ', not running" ,
7678 }
7779
7880 if not url :
7981 return {
8082 "workload" : name ,
8183 "status" : "error" ,
8284 "tools" : [],
83- "error" : "No URL provided for workload"
85+ "error" : "No URL provided for workload" ,
8486 }
8587
8688 # Determine which client to use based on proxy_mode or transport_type
@@ -92,10 +94,7 @@ async def list_tools_from_server(workload: Dict[str, Any]) -> Dict[str, Any]:
9294 await session .initialize ()
9395 tools_response = await session .list_tools ()
9496 tools_info = [
95- {
96- "name" : tool .name ,
97- "description" : tool .description or ""
98- }
97+ {"name" : tool .name , "description" : tool .description or "" }
9998 for tool in tools_response .tools
10099 ]
101100
@@ -107,14 +106,14 @@ async def list_tools_from_server(workload: Dict[str, Any]) -> Dict[str, Any]:
107106 "workload" : name ,
108107 "status" : "skipped" ,
109108 "tools" : [],
110- "error" : "Skipped: orchestrator workload (self)"
109+ "error" : "Skipped: orchestrator workload (self)" ,
111110 }
112111
113112 return {
114113 "workload" : name ,
115114 "status" : "success" ,
116115 "tools" : tools_info ,
117- "error" : None
116+ "error" : None ,
118117 }
119118 elif proxy_mode == "streamable-http" or transport_type == "streamable-http" :
120119 # Use streamable HTTP client
@@ -123,10 +122,7 @@ async def list_tools_from_server(workload: Dict[str, Any]) -> Dict[str, Any]:
123122 await session .initialize ()
124123 tools_response = await session .list_tools ()
125124 tools_info = [
126- {
127- "name" : tool .name ,
128- "description" : tool .description or ""
129- }
125+ {"name" : tool .name , "description" : tool .description or "" }
130126 for tool in tools_response .tools
131127 ]
132128
@@ -138,54 +134,46 @@ async def list_tools_from_server(workload: Dict[str, Any]) -> Dict[str, Any]:
138134 "workload" : name ,
139135 "status" : "skipped" ,
140136 "tools" : [],
141- "error" : "Skipped: orchestrator workload (self)"
137+ "error" : "Skipped: orchestrator workload (self)" ,
142138 }
143139
144140 return {
145141 "workload" : name ,
146142 "status" : "success" ,
147143 "tools" : tools_info ,
148- "error" : None
144+ "error" : None ,
149145 }
150146 else :
151147 return {
152148 "workload" : name ,
153149 "status" : "unsupported" ,
154150 "tools" : [],
155- "error" : f"Transport/proxy mode '{ proxy_mode or transport_type } ' not yet supported"
151+ "error" : f"Transport/proxy mode '{ proxy_mode or transport_type } ' not yet supported" ,
156152 }
157153
158154 except Exception as e :
159155 import traceback
156+
160157 error_msg = f"{ str (e )} \n { traceback .format_exc ()} "
161- return {
162- "workload" : name ,
163- "status" : "error" ,
164- "tools" : [],
165- "error" : error_msg
166- }
158+ return {"workload" : name , "status" : "error" , "tools" : [], "error" : error_msg }
167159
168160
169161async def get_tool_details_from_server (
170- workload_name : str ,
171- tool_name : str ,
172- host : str = None ,
173- port : int = None
174- ) -> Dict [str , Any ]:
162+ workload_name : str , tool_name : str , host : str = None , port : int = None
163+ ) -> dict [str , Any ]:
175164 """Get detailed information about a specific tool from a workload"""
176165 # Discover ToolHive if not already done
177166 if host is None or port is None :
178167 from toolhive_client import discover_toolhive
168+
179169 host , port = discover_toolhive (host , port )
180170
181171 # Get workload details
182172 workloads = await get_workloads (host , port )
183173 workload = next ((w for w in workloads if w .get ("name" ) == workload_name ), None )
184174
185175 if not workload :
186- return {
187- "error" : f"Workload '{ workload_name } ' not found"
188- }
176+ return {"error" : f"Workload '{ workload_name } ' not found" }
189177
190178 try :
191179 transport_type = workload .get ("transport_type" , "" )
@@ -201,36 +189,47 @@ async def get_tool_details_from_server(
201189 async with ClientSession (read , write ) as session :
202190 await session .initialize ()
203191 tools_response = await session .list_tools ()
204- tool = next ((t for t in tools_response .tools if t .name == tool_name ), None )
192+ tool = next (
193+ (t for t in tools_response .tools if t .name == tool_name ), None
194+ )
205195
206196 if not tool :
207- return {"error" : f"Tool '{ tool_name } ' not found in workload '{ workload_name } '" }
197+ return {
198+ "error" : f"Tool '{ tool_name } ' not found in workload '{ workload_name } '"
199+ }
208200
209201 return {
210202 "name" : tool .name ,
211203 "description" : tool .description or "" ,
212- "inputSchema" : tool .inputSchema
204+ "inputSchema" : tool .inputSchema ,
213205 }
214206 elif proxy_mode == "streamable-http" or transport_type == "streamable-http" :
215207 async with streamablehttp_client (url ) as (read , write , get_session_id ):
216208 async with ClientSession (read , write ) as session :
217209 await session .initialize ()
218210 tools_response = await session .list_tools ()
219- tool = next ((t for t in tools_response .tools if t .name == tool_name ), None )
211+ tool = next (
212+ (t for t in tools_response .tools if t .name == tool_name ), None
213+ )
220214
221215 if not tool :
222- return {"error" : f"Tool '{ tool_name } ' not found in workload '{ workload_name } '" }
216+ return {
217+ "error" : f"Tool '{ tool_name } ' not found in workload '{ workload_name } '"
218+ }
223219
224220 return {
225221 "name" : tool .name ,
226222 "description" : tool .description or "" ,
227- "inputSchema" : tool .inputSchema
223+ "inputSchema" : tool .inputSchema ,
228224 }
229225 else :
230- return {"error" : f"Transport/proxy mode '{ proxy_mode or transport_type } ' not supported" }
226+ return {
227+ "error" : f"Transport/proxy mode '{ proxy_mode or transport_type } ' not supported"
228+ }
231229
232230 except Exception as e :
233231 import traceback
232+
234233 return {
235234 "error" : f"Failed to get tool details: { str (e )} \n { traceback .format_exc ()} "
236235 }
@@ -239,9 +238,9 @@ async def get_tool_details_from_server(
239238async def call_tool (
240239 workload_name : str ,
241240 tool_name : str ,
242- arguments : Dict [str , Any ],
241+ arguments : dict [str , Any ],
243242 host : str = DEFAULT_HOST ,
244- port : int = DEFAULT_PORT
243+ port : int = DEFAULT_PORT ,
245244) -> Any :
246245 """
247246 Call a tool from a specific MCP server workload.
@@ -253,6 +252,7 @@ async def call_tool(
253252 try :
254253 if host == DEFAULT_HOST and port == DEFAULT_PORT :
255254 from toolhive_client import discover_toolhive
255+
256256 host , port = discover_toolhive (host = None , port = None )
257257 except Exception :
258258 # Fall back to provided/defaults if discovery fails
@@ -271,7 +271,9 @@ async def call_tool(
271271 transport_type = workload .get ("transport_type" , "" )
272272
273273 if status != "running" :
274- raise RuntimeError (f"Workload '{ workload_name } ' is not running (status: { status } )" )
274+ raise RuntimeError (
275+ f"Workload '{ workload_name } ' is not running (status: { status } )"
276+ )
275277
276278 if not url :
277279 raise ValueError (f"No URL provided for workload '{ workload_name } '" )
@@ -290,10 +292,14 @@ async def call_tool(
290292 result = await session .call_tool (tool_name , arguments = arguments )
291293 return result
292294 else :
293- raise ValueError (f"Transport/proxy mode '{ proxy_mode or transport_type } ' not supported" )
295+ raise ValueError (
296+ f"Transport/proxy mode '{ proxy_mode or transport_type } ' not supported"
297+ )
294298
295299
296- async def list_tools (host : str = DEFAULT_HOST , port : int = DEFAULT_PORT ) -> List [Dict [str , Any ]]:
300+ async def list_tools (
301+ host : str = DEFAULT_HOST , port : int = DEFAULT_PORT
302+ ) -> list [dict [str , Any ]]:
297303 """
298304 List tools from all MCP servers running through ToolHive.
299305
@@ -318,22 +324,26 @@ async def list_tools(host: str = DEFAULT_HOST, port: int = DEFAULT_PORT) -> List
318324 processed_results = []
319325 for i , result in enumerate (results ):
320326 if isinstance (result , Exception ):
321- processed_results .append ({
322- "workload" : workloads [i ].get ("name" , f"workload_{ i } " ),
323- "status" : "error" ,
324- "tools" : [],
325- "error" : str (result )
326- })
327+ processed_results .append (
328+ {
329+ "workload" : workloads [i ].get ("name" , f"workload_{ i } " ),
330+ "status" : "error" ,
331+ "tools" : [],
332+ "error" : str (result ),
333+ }
334+ )
327335 else :
328336 processed_results .append (result )
329337
330338 return processed_results
331339
332340 except Exception as e :
333341 # If we can't even get the workload list, return error
334- return [{
335- "workload" : "toolhive" ,
336- "status" : "error" ,
337- "tools" : [],
338- "error" : f"Failed to get workload list: { str (e )} "
339- }]
342+ return [
343+ {
344+ "workload" : "toolhive" ,
345+ "status" : "error" ,
346+ "tools" : [],
347+ "error" : f"Failed to get workload list: { str (e )} " ,
348+ }
349+ ]
0 commit comments