@@ -430,3 +430,128 @@ def clear_agent_tools_cache(self) -> None:
430
430
>>> tools_v2 = client.agent_tools() # Regenerates from latest definitions
431
431
"""
432
432
self ._function_builder .clear_cache ()
433
+
434
+ def _get_server_health (self , server_name : str | None = None ) -> list [dict ] | dict :
435
+ """Get health information for one or all MCP servers."""
436
+ try :
437
+ if server_name :
438
+ url = f"{ self ._endpoint } /api/v1/health/servers/{ server_name } "
439
+ else :
440
+ url = f"{ self ._endpoint } /api/v1/health/servers"
441
+ response = self ._session .get (url , timeout = 5 )
442
+ response .raise_for_status ()
443
+ data = response .json ()
444
+ return data if server_name else data .get ("servers" , [])
445
+ except requests .exceptions .ConnectionError as e :
446
+ raise ConnectionError (f"Cannot connect to mcpd daemon at { self ._endpoint } : { e } " ) from e
447
+ except requests .exceptions .Timeout as e :
448
+ operation = f"get health of { server_name } " if server_name else "get health of all servers"
449
+ raise TimeoutError ("Request timed out after 5 seconds" , operation = operation , timeout = 5 ) from e
450
+ except requests .exceptions .HTTPError as e :
451
+ if e .response .status_code == 401 :
452
+ msg = (
453
+ f"Authentication failed when accessing server '{ server_name } ': { e } "
454
+ if server_name
455
+ else f"Authentication failed: { e } "
456
+ )
457
+ raise AuthenticationError (msg ) from e
458
+ elif e .response .status_code == 404 :
459
+ assert server_name is not None
460
+ raise ServerNotFoundError (f"Server '{ server_name } ' not found" , server_name = server_name ) from e
461
+ else :
462
+ msg = (
463
+ f"Error retrieving health status for server '{ server_name } ': { e } "
464
+ if server_name
465
+ else f"Error retrieving health status for all servers: { e } "
466
+ )
467
+ raise McpdError (msg ) from e
468
+ except requests .exceptions .RequestException as e :
469
+ msg = (
470
+ f"Error retrieving health status for server '{ server_name } ': { e } "
471
+ if server_name
472
+ else f"Error retrieving health status for all servers: { e } "
473
+ )
474
+ raise McpdError (msg ) from e
475
+
476
+ def server_health (self , server_name : str | None = None ) -> dict [str , dict ] | dict :
477
+ """Retrieve health information from one or all MCP servers.
478
+
479
+ Args:
480
+ server_name: Optional name of a specific server to query. If None,
481
+ retrieves health information from all available servers.
482
+
483
+ Returns:
484
+ - If server_name is provided: The health information for that server.
485
+ - If server_name is None: A dictionary mapping server names to their health information.
486
+
487
+ Server health is a dictionary that contains:
488
+ - '$schema': A URL to the JSON schema
489
+ - 'name': The server identifier
490
+ - 'status': The current health status of the server ('ok', 'timeout', 'unreachable', 'unknown')
491
+ - 'latency': The latency of the server in milliseconds (optional)
492
+ - 'lastChecked': Time when ping was last attempted (optional)
493
+ - 'lastSuccessful': Time of the most recent successful ping (optional)
494
+
495
+ Raises:
496
+ ConnectionError: If unable to connect to the mcpd daemon.
497
+ TimeoutError: If requests to the daemon time out.
498
+ AuthenticationError: If API key authentication fails.
499
+ ServerNotFoundError: If the specified server doesn't exist (when server_name provided).
500
+ McpdError: For other daemon errors or API issues.
501
+
502
+ Examples:
503
+ >>> client = McpdClient(api_endpoint="http://localhost:8090")
504
+ >>>
505
+ >>> # Get health for a specific server
506
+ >>> health_info = client.server_health(server_name="time")
507
+ >>> print(health_info["status"])
508
+ 'ok'
509
+ >>>
510
+ >>> # Get health for all servers
511
+ >>> all_health = client.server_health()
512
+ >>> for server, health in all_health.items():
513
+ ... print(f"{server}: {health['status']}")
514
+ fetch: ok
515
+ time: ok
516
+ """
517
+ if server_name :
518
+ return self ._get_server_health (server_name )
519
+
520
+ try :
521
+ all_health = self ._get_server_health ()
522
+ all_health_by_server = {}
523
+ for health in all_health :
524
+ all_health_by_server [health ["name" ]] = health
525
+ return all_health_by_server
526
+ except McpdError as e :
527
+ raise McpdError (f"Could not retrieve all health information: { e } " ) from e
528
+
529
+ def is_server_healthy (self , server_name : str ) -> bool :
530
+ """Check if the specified MCP server is healthy.
531
+
532
+ This method queries the server's health status and determines whether the server is healthy
533
+ and therefore can handle requests or not. It's useful for validating an MCP server is ready
534
+ before attempting to call one of its tools.
535
+
536
+ Args:
537
+ server_name: The name of the MCP server to check.
538
+
539
+ Returns:
540
+ True if the server is healthy, False otherwise.
541
+ Returns False if the server doesn't exist, is unreachable, or if any
542
+ other error occurs during the check.
543
+
544
+ Example:
545
+ >>> client = McpdClient(api_endpoint="http://localhost:8090")
546
+ >>>
547
+ >>> # Check before calling
548
+ >>> if client.is_server_healthy("time"):
549
+ ... result = client.call.time.get_current_time(timezone="UTC")
550
+ ... else:
551
+ ... print("The server is not ready to accept requests yet.")
552
+ """
553
+ try :
554
+ health = self .server_health (server_name = server_name )
555
+ return health .get ("status" , None ) == "ok"
556
+ except McpdError :
557
+ return False
0 commit comments