@@ -104,6 +104,28 @@ def is_valid_server_url_format(server_url: str) -> bool:
104104 return parsed .scheme in {"http" , "https" } and bool (parsed .netloc )
105105
106106
107+ class LogEntry (BaseModel ):
108+ """Represents a single log entry."""
109+ timestamp : Optional [str ] = None
110+ level : Optional [str ] = None
111+ message : Optional [str ] = None
112+ # Allow additional fields that might be present
113+
114+ class Config :
115+ extra = "allow"
116+
117+
118+ class GetAppLogsResponse (BaseModel ):
119+ """Response from get_app_logs API endpoint."""
120+ logEntries : Optional [List [LogEntry ]] = []
121+ log_entries : Optional [List [LogEntry ]] = []
122+
123+ @property
124+ def log_entries_list (self ) -> List [LogEntry ]:
125+ """Get log entries regardless of field name format."""
126+ return self .logEntries or self .log_entries or []
127+
128+
107129class MCPAppClient (APIClient ):
108130 """Client for interacting with the MCP App API service over HTTP."""
109131
@@ -586,3 +608,62 @@ async def can_delete_app_configuration(self, app_config_id: str) -> bool:
586608 resource_name = f"MCP_APP_CONFIG:{ app_config_id } " ,
587609 action = "MANAGE:MCP_APP_CONFIG" ,
588610 )
611+
612+ async def get_app_logs (
613+ self ,
614+ app_id : Optional [str ] = None ,
615+ app_configuration_id : Optional [str ] = None ,
616+ since : Optional [str ] = None ,
617+ limit : Optional [int ] = None ,
618+ order_by : Optional [str ] = None ,
619+ order : Optional [str ] = None ,
620+ ) -> GetAppLogsResponse :
621+ """Get logs for an MCP App or App Configuration via the API.
622+
623+ Args:
624+ app_id: The UUID of the app to get logs for (mutually exclusive with app_configuration_id)
625+ app_configuration_id: The UUID of the app configuration to get logs for (mutually exclusive with app_id)
626+ since: Time filter for logs (e.g., "1h", "24h", "7d")
627+ limit: Maximum number of log entries to return
628+ order_by: Field to order by ("LOG_ORDER_BY_TIMESTAMP" or "LOG_ORDER_BY_LEVEL")
629+ order: Log ordering direction ("LOG_ORDER_ASC" or "LOG_ORDER_DESC")
630+
631+ Returns:
632+ GetAppLogsResponse: The retrieved log entries
633+
634+ Raises:
635+ ValueError: If neither or both app_id and app_configuration_id are provided, or if IDs are invalid
636+ httpx.HTTPStatusError: If the API returns an error (e.g., 404, 403)
637+ httpx.HTTPError: If the request fails
638+ """
639+ # Validate inputs
640+ if not app_id and not app_configuration_id :
641+ raise ValueError ("Either app_id or app_configuration_id must be provided" )
642+ if app_id and app_configuration_id :
643+ raise ValueError ("Only one of app_id or app_configuration_id can be provided" )
644+
645+ if app_id and not is_valid_app_id_format (app_id ):
646+ raise ValueError (f"Invalid app ID format: { app_id } " )
647+ if app_configuration_id and not is_valid_app_config_id_format (app_configuration_id ):
648+ raise ValueError (f"Invalid app configuration ID format: { app_configuration_id } " )
649+
650+ # Prepare request payload
651+ payload = {}
652+ if app_id :
653+ payload ["app_id" ] = app_id
654+ if app_configuration_id :
655+ payload ["app_configuration_id" ] = app_configuration_id
656+ if since :
657+ payload ["since" ] = since
658+ if limit :
659+ payload ["limit" ] = limit
660+ if order_by :
661+ payload ["order_by" ] = order_by
662+ if order :
663+ payload ["order" ] = order
664+
665+ response = await self .post ("/mcp_app/get_app_logs" , payload )
666+
667+ # Parse the response
668+ data = response .json ()
669+ return GetAppLogsResponse (** data )
0 commit comments