diff --git a/.gitignore b/.gitignore index c18dd8d..dfe812b 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ __pycache__/ +**/__pycache__ diff --git a/_prow_mcp_server/Containerfile b/_prow_mcp_server/Containerfile index 219cf76..1223f11 100644 --- a/_prow_mcp_server/Containerfile +++ b/_prow_mcp_server/Containerfile @@ -9,4 +9,6 @@ COPY mcp_server.py ./ COPY drain.py ./ COPY drain3.ini ./ +EXPOSE 9000 + CMD ["python", "mcp_server.py"] diff --git a/_prow_mcp_server/mcp.json b/_prow_mcp_server/mcp.json index d4126e0..77d8305 100644 --- a/_prow_mcp_server/mcp.json +++ b/_prow_mcp_server/mcp.json @@ -10,7 +10,7 @@ "localhost/mcp-server:latest" ], "env": { - "MCP_TRANSPORT": "stdio" + "MCP_TRANSPORT": "streamable-http" } } } diff --git a/_prow_mcp_server/mcp_server.py b/_prow_mcp_server/mcp_server.py index 965fb6f..38527c6 100644 --- a/_prow_mcp_server/mcp_server.py +++ b/_prow_mcp_server/mcp_server.py @@ -2,18 +2,139 @@ import asyncio from typing import Any, Optional, Dict from dateutil.parser import parse as parse_date +import re from drain import DrainExtractor import httpx -from mcp.server.fastmcp import FastMCP +from fastmcp import FastMCP -mcp = FastMCP("prow-mcp-server") +mcp = FastMCP(name="prow-mcp-server", stateless_http=True, host="0.0.0.0", port=9000) GCS_URL = "https://gcsweb-ci.apps.ci.l2s4.p1.openshiftapps.com/gcs/test-platform-results/logs" _drain_extractor: Optional['DrainExtractor'] = None +def extract_installation_info(log_content: str) -> Dict[str, Any]: + """Extract installation information from build-log.txt.""" + install_info = { + "installer_version": None, + "installer_commit": None, + "release_image": None, + "instance_types": {}, + "install_duration": None, + "architecture": None, + "cluster_config": {}, + "install_success": False + } + + # Extract openshift-install version and commit (can be on separate lines) + version_patterns = [ + r'openshift-install v([^\s"]+)', + r'"openshift-install v([^\s"]+)"' + ] + + for pattern in version_patterns: + version_match = re.search(pattern, log_content) + if version_match: + install_info["installer_version"] = version_match.group(1) + break + + # Extract commit (separate pattern) + commit_patterns = [ + r'built from commit ([a-f0-9]+)', + r'"built from commit ([a-f0-9]+)"' + ] + + for pattern in commit_patterns: + commit_match = re.search(pattern, log_content) + if commit_match: + install_info["installer_commit"] = commit_match.group(1) + break + + # Extract release image + release_patterns = [ + r'Installing from release ([^\s]+)', + r'release image "([^"]+)"', + r'RELEASE_IMAGE_LATEST for release image "([^"]+)"' + ] + for pattern in release_patterns: + release_match = re.search(pattern, log_content) + if release_match: + install_info["release_image"] = release_match.group(1) + break + + # Extract instance types from install-config.yaml section + # Look for compute and controlPlane sections + compute_type_pattern = r'compute:.*?type:\s*([^\s\n]+)' + control_type_pattern = r'controlPlane:.*?type:\s*([^\s\n]+)' + + compute_match = re.search(compute_type_pattern, log_content, re.DOTALL) + if compute_match: + install_info["instance_types"]["compute"] = compute_match.group(1) + + control_match = re.search(control_type_pattern, log_content, re.DOTALL) + if control_match: + install_info["instance_types"]["control_plane"] = control_match.group(1) + + # Extract architecture + arch_pattern = r'architecture:\s*([^\s\n]+)' + arch_match = re.search(arch_pattern, log_content) + if arch_match: + install_info["architecture"] = arch_match.group(1) + + # Extract cluster configuration details + # Replicas + compute_replicas_pattern = r'compute:.*?replicas:\s*(\d+)' + control_replicas_pattern = r'controlPlane:.*?replicas:\s*(\d+)' + + compute_replicas_match = re.search(compute_replicas_pattern, log_content, re.DOTALL) + if compute_replicas_match: + install_info["cluster_config"]["compute_replicas"] = int(compute_replicas_match.group(1)) + + control_replicas_match = re.search(control_replicas_pattern, log_content, re.DOTALL) + if control_replicas_match: + install_info["cluster_config"]["control_replicas"] = int(control_replicas_match.group(1)) + + # Network type + network_pattern = r'networkType:\s*([^\s\n]+)' + network_match = re.search(network_pattern, log_content) + if network_match: + install_info["cluster_config"]["network_type"] = network_match.group(1) + + # Platform and region + platform_pattern = r'platform:\s*([^\s\n]+):' + region_pattern = r'region:\s*([^\s\n]+)' + + platform_match = re.search(platform_pattern, log_content) + if platform_match: + install_info["cluster_config"]["platform"] = platform_match.group(1) + + region_match = re.search(region_pattern, log_content) + if region_match: + install_info["cluster_config"]["region"] = region_match.group(1) + + # Extract install duration (clean up quotes) + duration_patterns = [ + r'Time elapsed:\s*([^\n"]+)', + r'Install complete!.*?Time elapsed:\s*([^\n"]+)' + ] + + for pattern in duration_patterns: + duration_match = re.search(pattern, log_content, re.DOTALL) + if duration_match: + duration = duration_match.group(1).strip().strip('"') + install_info["install_duration"] = duration + break + + # Check if installation was successful + if "Install complete!" in log_content: + install_info["install_success"] = True + elif "level=error" in log_content or "FATAL" in log_content: + install_info["install_success"] = False + + return install_info + async def make_request( url: str, method: str = "GET", data: dict[str, Any] = None ) -> dict[str, Any] | None: @@ -64,10 +185,7 @@ async def get_job_metadata(job_name: str, build_id: str) -> dict: for arg in args: if arg.startswith("--target="): test_name=arg.replace("--target=","") - - - - + return {"status": status, "build_id": build_id, "job_name": job_name, "test_name": test_name} except Exception as e: @@ -247,5 +365,6 @@ async def get_install_logs(job_name: str, build_id: str, test_name: str): # print(result) if __name__ == "__main__": -# asyncio.run(main()) - mcp.run(transport=os.environ.get("MCP_TRANSPORT", "stdio")) + # mcp.streamable_http_app() + mcp.run(transport="streamable-http", host="0.0.0.0", port=9000) + diff --git a/_prow_mcp_server/requirements.txt b/_prow_mcp_server/requirements.txt index 7255e85..a7afa72 100644 --- a/_prow_mcp_server/requirements.txt +++ b/_prow_mcp_server/requirements.txt @@ -1,4 +1,5 @@ httpx -fastmcp +fastmcp>=2.10.6 +mcp>=1.8.0 python-dateutil drain3 \ No newline at end of file diff --git a/quick-start-containers.sh b/quick-start-containers.sh index 2099880..9362b9a 100755 --- a/quick-start-containers.sh +++ b/quick-start-containers.sh @@ -15,10 +15,13 @@ NC='\033[0m' # No Color # Configuration OLLAMA_CONTAINER="ollama" AGENT_CONTAINER="ci-analysis-agent" +MCP_SERVER_CONTAINER="mcp-server" +MCP_SERVER_IMAGE="localhost/mcp-server-template:latest" OLLAMA_VOLUME="ollama-data" OLLAMA_MODEL="qwen3:4b" AGENT_PORT="8000" OLLAMA_PORT="11434" +MCP_SERVER_PORT="9000" USE_GPU="auto" # auto, nvidia, amd, none # Function to print colored output @@ -135,6 +138,11 @@ cleanup_existing() { podman rm "$AGENT_CONTAINER" 2>/dev/null || true fi + if podman container exists "$MCP_SERVER_CONTAINER" 2>/dev/null; then + podman stop "$MCP_SERVER_CONTAINER" 2>/dev/null || true + podman rm "$MCP_SERVER_CONTAINER" 2>/dev/null || true + fi + print_success "Cleanup completed" } @@ -208,6 +216,60 @@ pull_model() { podman exec "$OLLAMA_CONTAINER" ollama list } +# Function to build MCP server image +build_mcp_server() { + print_status "Building MCP server container image..." + + # Check if _prow_mcp_server directory exists + if [ ! -d "_prow_mcp_server" ]; then + print_error "MCP server directory '_prow_mcp_server' not found" + print_error "Please ensure you're running this script from the ci_analysis_agent directory" + exit 1 + fi + + # Build the MCP server image + podman build -t "$MCP_SERVER_IMAGE" _prow_mcp_server/ + + print_success "MCP server image built successfully" + + # Verify image was created + if podman image exists "$MCP_SERVER_IMAGE" 2>/dev/null; then + print_success "MCP server image '$MCP_SERVER_IMAGE' is available" + else + print_error "Failed to build MCP server image" + exit 1 + fi +} + +# Function to start MCP server +start_mcp_server() { + print_status "Starting MCP server container..." + podman run -d \ + --name "$MCP_SERVER_CONTAINER" \ + -p "$MCP_SERVER_PORT:$MCP_SERVER_PORT" \ + -e MCP_TRANSPORT=http=stream \ + -e MCP_PORT="$MCP_SERVER_PORT" \ + "$MCP_SERVER_IMAGE" + + print_success "MCP server container started" + + # Wait for MCP server to be ready + print_status "Waiting for MCP server to be ready..." + sleep 5 + + # Check if MCP server is responding + for i in {1..15}; do + if curl -s -f "http://localhost:$MCP_SERVER_PORT/" >/dev/null 2>&1; then + print_success "MCP server is ready" + break + fi + if [ $i -eq 15 ]; then + print_warning "MCP server may not be fully ready yet" + fi + sleep 2 + done +} + # Function to build and start CI Analysis Agent start_agent() { print_status "Building CI Analysis Agent container..." @@ -218,6 +280,7 @@ start_agent() { --name "$AGENT_CONTAINER" \ --network host \ -e OLLAMA_API_BASE="http://localhost:$OLLAMA_PORT" \ + -e MCP_SERVER_URL="http://localhost:$MCP_SERVER_PORT" \ -e LOG_LEVEL=INFO \ ci-analysis-agent:latest @@ -243,6 +306,13 @@ verify_deployment() { return 1 fi + if podman ps | grep -q "$MCP_SERVER_CONTAINER"; then + print_success "MCP server container is running" + else + print_error "MCP server container is not running" + return 1 + fi + # Check if services are responding for i in {1..30}; do if curl -s -f "http://localhost:$AGENT_PORT/" >/dev/null 2>&1; then @@ -285,20 +355,33 @@ stop_containers() { print_warning "Ollama container does not exist" fi + if podman container exists "$MCP_SERVER_CONTAINER" 2>/dev/null; then + if podman ps | grep -q "$MCP_SERVER_CONTAINER"; then + print_status "Stopping MCP server container..." + podman stop "$MCP_SERVER_CONTAINER" + print_success "MCP server container stopped" + else + print_warning "MCP server container is not running" + fi + else + print_warning "MCP server container does not exist" + fi + echo "" echo "=================================================================" echo " 🛑 CONTAINERS STOPPED 🛑" echo "=================================================================" echo "" echo "📊 Container Status:" - podman ps -a --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" | grep -E "$OLLAMA_CONTAINER|$AGENT_CONTAINER" || echo " No containers found" + podman ps -a --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" | grep -E "$OLLAMA_CONTAINER|$AGENT_CONTAINER|$MCP_SERVER_CONTAINER" || echo " No containers found" echo "" echo "🎯 Quick Commands:" - echo " • Start containers: podman start $OLLAMA_CONTAINER $AGENT_CONTAINER" + echo " • Start containers: podman start $OLLAMA_CONTAINER $MCP_SERVER_CONTAINER $AGENT_CONTAINER" echo " • Clean up all: $0 --clean-all" echo " • Remove volumes: $0 --remove-volumes" echo " • Remove images: $0 --remove-images" echo " • Check logs: podman logs $AGENT_CONTAINER" + echo " • Check MCP logs: podman logs $MCP_SERVER_CONTAINER" echo " • Restart deployment: $0" echo "=================================================================" } @@ -313,7 +396,7 @@ clean_all() { # Stop containers first print_status "Stopping containers..." - podman stop "$OLLAMA_CONTAINER" "$AGENT_CONTAINER" 2>/dev/null || true + podman stop "$OLLAMA_CONTAINER" "$AGENT_CONTAINER" "$MCP_SERVER_CONTAINER" 2>/dev/null || true # Remove containers print_status "Removing containers..." @@ -327,6 +410,11 @@ clean_all() { print_success "Removed Ollama container" fi + if podman container exists "$MCP_SERVER_CONTAINER" 2>/dev/null; then + podman rm -f "$MCP_SERVER_CONTAINER" 2>/dev/null || true + print_success "Removed MCP server container" + fi + # Remove pods if requested if [ "$remove_pods" = true ]; then print_status "Removing pods..." @@ -371,6 +459,12 @@ clean_all() { print_success "Removed image: ci-analysis-agent:latest" fi + # Remove MCP server image + if podman image exists "$MCP_SERVER_IMAGE" 2>/dev/null; then + podman rmi -f "$MCP_SERVER_IMAGE" 2>/dev/null || true + print_success "Removed image: $MCP_SERVER_IMAGE" + fi + # Remove Ollama image if podman image exists "ollama/ollama:latest" 2>/dev/null; then podman rmi -f "ollama/ollama:latest" 2>/dev/null || true @@ -378,8 +472,8 @@ clean_all() { fi # Remove any other related images - for image in $(podman images --format "{{.Repository}}:{{.Tag}}" 2>/dev/null | grep -E "ci-analysis|ollama" || true); do - if [ -n "$image" ] && [ "$image" != "ollama/ollama:latest" ] && [ "$image" != "ci-analysis-agent:latest" ]; then + for image in $(podman images --format "{{.Repository}}:{{.Tag}}" 2>/dev/null | grep -E "ci-analysis|ollama|mcp-server" || true); do + if [ -n "$image" ] && [ "$image" != "ollama/ollama:latest" ] && [ "$image" != "ci-analysis-agent:latest" ] && [ "$image" != "$MCP_SERVER_IMAGE" ]; then podman rmi -f "$image" 2>/dev/null || true print_success "Removed image: $image" fi @@ -394,13 +488,13 @@ clean_all() { echo "📊 Remaining Resources:" echo "" echo "Containers:" - podman ps -a --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" | grep -E "$OLLAMA_CONTAINER|$AGENT_CONTAINER|ci-analysis" || echo " No related containers found" + podman ps -a --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" | grep -E "$OLLAMA_CONTAINER|$AGENT_CONTAINER|$MCP_SERVER_CONTAINER|ci-analysis" || echo " No related containers found" echo "" echo "Volumes:" podman volume ls --format "table {{.Name}}\t{{.Driver}}" | grep -E "ollama|ci-analysis" || echo " No related volumes found" echo "" echo "Images:" - podman images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}" | grep -E "ollama|ci-analysis" || echo " No related images found" + podman images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}" | grep -E "ollama|ci-analysis|mcp-server" || echo " No related images found" echo "" echo "🎯 Next Steps:" echo " • Fresh deployment: $0" @@ -420,6 +514,7 @@ show_status() { echo "" echo "🌐 Web Interface: http://localhost:$AGENT_PORT" echo "🤖 Ollama API: http://localhost:$OLLAMA_PORT" + echo "🔧 MCP Server: http://localhost:$MCP_SERVER_PORT" # Show GPU status case "$gpu_type" in @@ -441,11 +536,22 @@ show_status() { echo "💾 Volume Status:" podman volume ls | grep "$OLLAMA_VOLUME" || echo " No volumes found" echo "" + echo "🖼️ Images Status:" + echo " CI Agent: $(podman image exists ci-analysis-agent:latest 2>/dev/null && echo "✅ Built" || echo "❌ Missing")" + echo " MCP Server: $(podman image exists "$MCP_SERVER_IMAGE" 2>/dev/null && echo "✅ Built" || echo "❌ Missing")" + echo " Ollama: $(podman image exists ollama/ollama:latest 2>/dev/null && echo "✅ Available" || echo "❌ Missing")" + echo "" + echo "🔄 Service Status:" + echo " CI Agent: $(podman ps | grep -q "$AGENT_CONTAINER" && echo "🟢 Running" || echo "🔴 Stopped")" + echo " MCP Server: $(podman ps | grep -q "$MCP_SERVER_CONTAINER" && echo "🟢 Running" || echo "🔴 Stopped")" + echo " Ollama: $(podman ps | grep -q "$OLLAMA_CONTAINER" && echo "🟢 Running" || echo "🔴 Stopped")" + echo "" echo "🎯 Quick Commands:" echo " • View logs: podman logs -f $AGENT_CONTAINER" + echo " • View MCP logs: podman logs -f $MCP_SERVER_CONTAINER" echo " • Check Ollama models: podman exec $OLLAMA_CONTAINER ollama list" echo " • Stop containers: $0 --stop" - echo " • Start containers: podman start $OLLAMA_CONTAINER $AGENT_CONTAINER" + echo " • Start containers: podman start $OLLAMA_CONTAINER $MCP_SERVER_CONTAINER $AGENT_CONTAINER" echo " • Clean up all: $0 --clean-all" echo " • Remove volumes: $0 --remove-volumes" echo " • Remove images: $0 --remove-images" @@ -466,6 +572,11 @@ show_status() { show_help() { echo "CI Analysis Agent - Quick Start with Containers" echo "" + echo "This script sets up the complete CI Analysis Agent stack including:" + echo " • Ollama LLM server with model management" + echo " • Prow MCP server for CI job analysis" + echo " • CI Analysis Agent with web interface" + echo "" echo "Usage: $0 [OPTIONS]" echo "" echo "Options:" @@ -615,6 +726,9 @@ main() { pull_model fi + build_mcp_server + start_mcp_server + start_agent verify_deployment show_status "$gpu_type" diff --git a/requirements.txt b/requirements.txt index 58c916e..c69d948 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,7 @@ -google-adk>=1.5.0 +google-adk>=1.8.0 litellm>=1.74.0 drain3>=0.9.0 google-cloud-storage>=2.10.0 python-dotenv>=1.0.0 -httpx>=0.24.0 \ No newline at end of file +httpx>=0.24.0 +mcp>=1.8.0 \ No newline at end of file diff --git a/sub_agents/__init__.py b/sub_agents/__init__.py index 3869c16..939a3bb 100644 --- a/sub_agents/__init__.py +++ b/sub_agents/__init__.py @@ -3,6 +3,7 @@ This directory contains specialized sub-agents for different analysis tasks. This is not a standalone agent, but a package containing other agents. """ +from . import installation_analyst # This directory is not an agent itself, but contains sub-agents # Individual agents are in subdirectories: diff --git a/sub_agents/installation_analyst/__init__.py b/sub_agents/installation_analyst/__init__.py index a77ef8f..fb49250 100644 --- a/sub_agents/installation_analyst/__init__.py +++ b/sub_agents/installation_analyst/__init__.py @@ -1,3 +1,3 @@ from .agent import installation_analyst_agent -__all__ = ["installation_analyst_agent"] \ No newline at end of file +__all__ = ["installation_analyst_agent", "root_agent"] \ No newline at end of file diff --git a/sub_agents/installation_analyst/agent.py b/sub_agents/installation_analyst/agent.py index 92e13ed..c186b53 100644 --- a/sub_agents/installation_analyst/agent.py +++ b/sub_agents/installation_analyst/agent.py @@ -3,15 +3,9 @@ from google.adk import Agent from google.adk.models.lite_llm import LiteLlm from . import prompt - -import asyncio -import httpx -import threading -import concurrent.futures +from google.adk.tools.mcp_tool.mcp_toolset import MCPToolset, StreamableHTTPConnectionParams import re -from typing import Dict, Any, Optional - -GCS_URL = "https://gcsweb-ci.apps.ci.l2s4.p1.openshiftapps.com/gcs/test-platform-results/logs" +from typing import Dict, Any MODEL = LiteLlm(model="ollama_chat/qwen3:4b") @@ -135,200 +129,18 @@ def extract_installation_info(log_content: str) -> Dict[str, Any]: return install_info -# Prow tool functions for installation analysis -async def get_job_metadata_async(job_name: str, build_id: str) -> Dict[str, Any]: - """Get the metadata and status for a specific Prow job name and build id.""" - url = f"{GCS_URL}/{job_name}/{build_id}/prowjob.json" - try: - async with httpx.AsyncClient() as client: - response = await client.get(url) - response.raise_for_status() - data = response.json() - - if not data: - return {"error": "No response from Prow API"} - - job_spec = data.get("spec", {}) - job_status = data.get("status", {}) - - build_id_from_status = job_status.get("build_id") - status = job_status.get("state") - args = job_spec.get("pod_spec", {}).get("containers", [])[0].get("args", []) - test_name = "" - for arg in args: - if arg.startswith("--target="): - test_name = arg.replace("--target=", "") - - return { - "status": status, - "build_id": build_id_from_status, - "job_name": job_name, - "test_name": test_name - } - - except Exception as e: - return {"error": f"Failed to fetch job info: {str(e)}"} - -async def get_install_logs_async(job_name: str, build_id: str) -> str: - """Get installation logs from build-log.txt in installation directories.""" - # Extract job short name from full job name - job_parts = job_name.split('-') - if len(job_parts) >= 8: - job_short_name = '-'.join(job_parts[7:]) # Everything after the 7th part - else: - job_short_name = job_name.split('-')[-1] # Fallback to last part - - # Try both possible installation directory patterns - install_dirs = [ - f"artifacts/{job_short_name}/ipi-install-install", - f"artifacts/{job_short_name}/ipi-install-install-stableinitial" - ] - - base_url = f"{GCS_URL}/{job_name}/{build_id}" - - async with httpx.AsyncClient() as client: - for install_dir in install_dirs: - try: - # Get the build-log.txt from this installation directory - log_url = f"{base_url}/{install_dir}/build-log.txt" - - response = await client.get(log_url) - response.raise_for_status() - - log_content = response.text - - # Check if we got HTML instead of log content - if log_content.strip().startswith('') or log_content.strip().startswith(' 40: - result += "--- Last 20 lines ---\n" - result += '\n'.join(lines[-20:]) + "\n\n" - - # Add full log content - result += f"📋 FULL INSTALLATION LOG:\n{log_content}" - - return result - - except httpx.HTTPError: - continue # Try next directory pattern - except Exception as e: - continue # Try next directory pattern - - # If no logs found, return error message with helpful details - return f"""❌ INSTALLATION ANALYSIS FAILED - -Could not find installation logs for job: {job_name} -Build ID: {build_id} - -🔍 DEBUGGING INFO: -- Job short name extracted: {job_short_name} -- Base URL: {base_url} -- Tried directories: {', '.join(install_dirs)} - -🔗 Manual check: {base_url}/ - -⚠️ POSSIBLE CAUSES: -1. Build ID might be invalid or logs not yet available -2. Job might not have installation logs (e.g., upgrade-only jobs) -3. Directory structure might be different for this job type -4. Logs might be in a different location - -💡 SUGGESTIONS: -1. Verify the Prow job URL is correct -2. Check if the job has completed successfully -3. Try browsing the base URL manually to see available directories -4. Use a different job that includes installation steps""" - -def run_async_in_thread(coro): - """Run async function in a thread to avoid event loop conflicts.""" - - def run_in_thread(): - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) - try: - return loop.run_until_complete(coro) - finally: - loop.close() - - with concurrent.futures.ThreadPoolExecutor() as executor: - future = executor.submit(run_in_thread) - return future.result() - -def get_job_metadata_tool(job_name: str, build_id: str): - """Get metadata and status for a specific Prow job name and build ID.""" - return run_async_in_thread(get_job_metadata_async(job_name, build_id)) - -def get_install_logs_tool(job_name: str, build_id: str): - """Get installation logs from build-log.txt in installation directories with detailed analysis.""" - return run_async_in_thread(get_install_logs_async(job_name, build_id)) - installation_analyst_agent = Agent( model=MODEL, name="installation_analyst_agent", instruction=prompt.INSTALLATION_SPECIALIST_PROMPT, output_key="installation_analysis_output", - tools=[ - get_job_metadata_tool, - get_install_logs_tool, + tools=[ MCPToolset( + connection_params=StreamableHTTPConnectionParams( + url="http://127.0.0.1:9000/mcp", + tool_filter=['get_install_logs', 'get_job_metadata'], + ) + ), ], -) \ No newline at end of file +) + +root_agent = installation_analyst_agent \ No newline at end of file diff --git a/sub_agents/installation_analyst/prompt.py b/sub_agents/installation_analyst/prompt.py index b67416b..7e1afae 100644 --- a/sub_agents/installation_analyst/prompt.py +++ b/sub_agents/installation_analyst/prompt.py @@ -45,12 +45,12 @@ def get_user_prompt(): - Log analysis for troubleshooting Available tools: -- get_job_metadata: Get basic job information and metadata +- get_job_metadata: Get basic job information and metadata, including the job name, build id, and test name - get_install_logs: Fetch and analyze build-log.txt with structured information extraction ANALYSIS WORKFLOW: -1. Start with job metadata to understand the test context -2. Fetch installation logs from build-log.txt which automatically extracts: +1. Always start calling get_job_metadata to get the test name and understand the test context +2. Fetch installation logs from build-log.txt by calling get_install_logs with test_name, job_name and build_id. This automatically extracts: - Installer binary version and commit - Instance types and cluster configuration - Installation duration and success status