@@ -173,87 +173,124 @@ async def _init_config_async(self, config: Dict[str, Any]) -> List[Fn]:
173173 """Async implementation of MCP configuration initialization."""
174174 tools : List [Fn ] = []
175175 mcp_servers = config ['mcpServers' ]
176+ successful_connections = 0
177+ failed_connections = []
176178
177179 for server_name , server_config in mcp_servers .items ():
178- client = MCPClient ()
179-
180- # Connect to the MCP server
181- await client .connect_server (server_name , server_config )
182-
183- # Generate unique client ID
184- client_id = f"{ server_name } _{ uuid .uuid4 ()} "
185- client .client_id = client_id
186- self .clients [client_id ] = client
187-
188- # Convert MCP tools to Fn objects
189- for mcp_tool in client .tools :
190- # Create tool parameters schema
191- parameters = mcp_tool .inputSchema
192- if 'required' not in parameters :
193- parameters ['required' ] = []
180+ try :
181+ client = MCPClient ()
194182
195- # Ensure schema has required fields
196- required_fields = {'type' , 'properties' , 'required' }
197- missing_fields = required_fields - parameters .keys ()
198- if missing_fields :
199- raise ValueError (f'Missing required schema fields: { missing_fields } ' )
200-
201- # Clean up parameters to only include standard fields
202- cleaned_parameters = {
203- 'type' : parameters ['type' ],
204- 'properties' : parameters ['properties' ],
205- 'required' : parameters ['required' ]
206- }
183+ # Connect to the MCP server
184+ await client .connect_server (server_name , server_config )
207185
208- # Create tool name and Fn object
209- tool_name = f"{ server_name } -{ mcp_tool .name } "
210- fn_obj = self ._create_mcp_tool_fn (
211- name = tool_name ,
212- client_id = client_id ,
213- mcp_tool_name = mcp_tool .name ,
214- description = mcp_tool .description ,
215- parameters = cleaned_parameters
216- )
217- tools .append (fn_obj )
218-
219- # Add resource tools if available
220- if client .resources :
221- # List resources tool
222- list_resources_name = f"{ server_name } -list_resources"
223- list_resources_fn = self ._create_mcp_tool_fn (
224- name = list_resources_name ,
225- client_id = client_id ,
226- mcp_tool_name = 'list_resources' ,
227- description = (
228- 'List available resources from the MCP server. '
229- 'Resources represent data sources that can be used as context.'
230- ),
231- parameters = {'type' : 'object' , 'properties' : {}, 'required' : []}
232- )
233- tools .append (list_resources_fn )
234-
235- # Read resource tool
236- read_resource_name = f"{ server_name } -read_resource"
237- read_resource_fn = self ._create_mcp_tool_fn (
238- name = read_resource_name ,
239- client_id = client_id ,
240- mcp_tool_name = 'read_resource' ,
241- description = (
242- 'Read a specific resource by URI. '
243- 'Use list_resources first to discover available URIs.'
244- ),
245- parameters = {
246- 'type' : 'object' ,
247- 'properties' : {
248- 'uri' : {
249- 'type' : 'string' ,
250- 'description' : 'The URI of the resource to read'
251- }
252- },
253- 'required' : ['uri' ]
186+ # Generate unique client ID
187+ client_id = f"{ server_name } _{ uuid .uuid4 ()} "
188+ client .client_id = client_id
189+ self .clients [client_id ] = client
190+ successful_connections += 1
191+
192+ # Convert MCP tools to Fn objects
193+ for mcp_tool in client .tools :
194+ # Create tool parameters schema
195+ parameters = mcp_tool .inputSchema
196+ if 'required' not in parameters :
197+ parameters ['required' ] = []
198+
199+ # Ensure schema has required fields
200+ required_fields = {'type' , 'properties' , 'required' }
201+ missing_fields = required_fields - parameters .keys ()
202+ if missing_fields :
203+ raise ValueError (f'Missing required schema fields: { missing_fields } ' )
204+
205+ # Clean up parameters to only include standard fields
206+ cleaned_parameters = {
207+ 'type' : parameters ['type' ],
208+ 'properties' : parameters ['properties' ],
209+ 'required' : parameters ['required' ]
254210 }
255- )
256- tools .append (read_resource_fn )
211+
212+ # Create tool name and Fn object
213+ tool_name = f"{ server_name } -{ mcp_tool .name } "
214+ fn_obj = self ._create_mcp_tool_fn (
215+ name = tool_name ,
216+ client_id = client_id ,
217+ mcp_tool_name = mcp_tool .name ,
218+ description = mcp_tool .description ,
219+ parameters = cleaned_parameters
220+ )
221+ tools .append (fn_obj )
222+
223+ # Add resource tools if available
224+ if client .resources :
225+ # List resources tool
226+ list_resources_name = f"{ server_name } -list_resources"
227+ list_resources_fn = self ._create_mcp_tool_fn (
228+ name = list_resources_name ,
229+ client_id = client_id ,
230+ mcp_tool_name = 'list_resources' ,
231+ description = (
232+ 'List available resources from the MCP server. '
233+ 'Resources represent data sources that can be used as context.'
234+ ),
235+ parameters = {'type' : 'object' , 'properties' : {}, 'required' : []}
236+ )
237+ tools .append (list_resources_fn )
238+
239+ # Read resource tool
240+ read_resource_name = f"{ server_name } -read_resource"
241+ read_resource_fn = self ._create_mcp_tool_fn (
242+ name = read_resource_name ,
243+ client_id = client_id ,
244+ mcp_tool_name = 'read_resource' ,
245+ description = (
246+ 'Read a specific resource by URI. '
247+ 'Use list_resources first to discover available URIs.'
248+ ),
249+ parameters = {
250+ 'type' : 'object' ,
251+ 'properties' : {
252+ 'uri' : {
253+ 'type' : 'string' ,
254+ 'description' : 'The URI of the resource to read'
255+ }
256+ },
257+ 'required' : ['uri' ]
258+ }
259+ )
260+ tools .append (read_resource_fn )
261+
262+ except Exception as e :
263+ # Log the failed connection but continue with other servers
264+ failed_connections .append ((server_name , str (e )))
265+ continue
266+
267+ # If no servers connected successfully, raise an error with helpful details
268+ if successful_connections == 0 :
269+ error_details = []
270+ for server_name , error in failed_connections :
271+ error_details .append (f" - { server_name } : { error } " )
272+
273+ error_msg = f"Failed to connect to any MCP servers:\n " + "\n " .join (error_details )
274+
275+ # Provide helpful suggestions based on common errors
276+ if any ("No such file or directory" in error for _ , error in failed_connections ):
277+ error_msg += "\n \n Common solutions:"
278+ if any ("uvx" in error for _ , error in failed_connections ):
279+ error_msg += "\n - Install uvx: pip install uvx"
280+ if any ("npx" in error for _ , error in failed_connections ):
281+ error_msg += "\n - Install Node.js and npm"
282+ error_msg += "\n - Check that MCP server commands are in your PATH"
283+
284+ raise HAIError (error_msg )
285+
286+ # If some servers failed but others succeeded, just warn
287+ if failed_connections :
288+ import warnings
289+ failed_names = [name for name , _ in failed_connections ]
290+ warnings .warn (
291+ f"Some MCP servers failed to connect: { ', ' .join (failed_names )} . "
292+ f"Continuing with { successful_connections } successful connection(s)."
293+ )
257294
258295 return tools
259296
0 commit comments