diff --git a/.gitignore b/.gitignore
index 8d83d79..27906c8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,6 +9,7 @@
*.user
*.userosscache
*.sln.docstates
+.env
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
diff --git a/README.md b/README.md
index 33ec5c3..f642029 100644
--- a/README.md
+++ b/README.md
@@ -237,11 +237,37 @@ eBPF.
az login
```
-> **Note**: The AKS-MCP binary will be automatically downloaded when using the 1-Click Installation buttons below.
-
### VS Code with GitHub Copilot (Recommended)
-#### ๐ Quick Setup Guide
+#### ๐ One-Click Installation with the AKS Extension
+
+The easiest way to get started with AKS-MCP is through the **Azure Kubernetes Service Extension for VS Code**.
+
+#### Step 1: Install the AKS Extension
+
+1. Open VS Code and go to Extensions (Ctrl+Shift+X).
+1. Search for "Azure Kubernetes Service".
+1. Install the official Microsoft AKS extension.
+
+#### Step 2: Launch the AKS MCP Server
+
+1. Open the **Command Palette** (`Cmd+Shift+P` on macOS / `Ctrl+Shift+P` on Windows/Linux).
+2. Search and run: **AKS: Setup MCP Server**.
+
+Upon successful installation, the server will now be visible in **MCP: List Servers** (via Command Palette). From there, you can start the MCP server or view its status.
+
+#### Step 3: Start Using AKS-MCP
+
+Once started, the MCP server will appear in the **Copilot Chat: Configure Tools** dropdown under `MCP Server: AKS MCP`, ready to enhance contextual prompts based on your AKS environment. By default, all AKS MCP server tools are enabled. You can review the list of available tools and disable any that are not required for your specific scenario.
+
+Try a prompt like *"List all my AKS clusters"*, which will start using tools from the AKS MCP server.
+
+> **๐ก Benefits**: The AKS extension handles binary downloads, updates, and configuration automatically, ensuring you always have the latest version with optimal settings.
+
+### Alternative Installation Methods
+
+
+Manual Binary Installation
#### Step 1: Download the Binary
@@ -341,8 +367,13 @@ For a persistent configuration that works across all your VS Code workspaces, ad
**Note**: Ensure you have authenticated with Azure CLI (`az login`) for the server to access your Azure resources.
+
+
### Other MCP-Compatible Clients
+
+Docker and Custom Client Installation
+
For other MCP-compatible AI clients like [Claude Desktop](https://claude.ai/), configure the server in your MCP configuration:
```json
@@ -358,12 +389,7 @@ For other MCP-compatible AI clients like [Claude Desktop](https://claude.ai/), c
}
```
-### โ๏ธ Advanced Installation Scenarios (Optional)
-
-
-Docker containers, custom MCP clients, and manual install options
-
-### ๐ Docker Installation
+#### ๐ Docker Installation
For containerized deployment, you can run AKS-MCP server using the official Docker image:
diff --git a/docs/labs/.env.example b/docs/labs/.env.example
new file mode 100644
index 0000000..7cc8d33
--- /dev/null
+++ b/docs/labs/.env.example
@@ -0,0 +1,17 @@
+# Azure OpenAI Configuration
+AZURE_OPENAI_ENDPOINT=https://your-resource.openai.azure.com/
+AZURE_OPENAI_API_KEY=your-api-key-here
+AZURE_OPENAI_DEPLOYMENT_NAME=gpt-4
+
+# Or use OpenAI directly
+# OPENAI_API_KEY=your-openai-api-key-here
+
+# Azure Authentication (optional - if not using az login)
+# AZURE_TENANT_ID=your-tenant-id
+# AZURE_CLIENT_ID=your-client-id
+# AZURE_CLIENT_SECRET=your-client-secret
+# AZURE_SUBSCRIPTION_ID=your-subscription-id
+
+# AKS Cluster Details (for the examples)
+# CLUSTER_NAME=your-cluster-name
+# RESOURCE_GROUP=your-resource-group
\ No newline at end of file
diff --git a/docs/labs/.gitignore b/docs/labs/.gitignore
new file mode 100644
index 0000000..5897dcb
--- /dev/null
+++ b/docs/labs/.gitignore
@@ -0,0 +1,35 @@
+# Environment variables
+.env
+.env.local
+
+# Jupyter
+.ipynb_checkpoints/
+*.ipynb_checkpoints
+
+# Python
+__pycache__/
+*.py[cod]
+*$py.class
+*.so
+.Python
+venv/
+env/
+ENV/
+
+# IDE
+.vscode/
+.idea/
+*.swp
+*.swo
+*~
+
+# OS
+.DS_Store
+Thumbs.db
+
+# Logs
+*.log
+
+# Temporary files
+*.tmp
+*.temp
\ No newline at end of file
diff --git a/docs/labs/README.md b/docs/labs/README.md
new file mode 100644
index 0000000..87b0877
--- /dev/null
+++ b/docs/labs/README.md
@@ -0,0 +1,193 @@
+# AKS MCP Transport Lab Guide
+
+This directory contains tools for lab exercises with the AKS-MCP server using different transport protocols and verbose logging capabilities.
+
+## Overview
+
+The AKS-MCP server supports three transport protocols:
+- **STDIO**: Direct process communication (default)
+- **SSE**: Server-Sent Events over HTTP
+- **Streamable HTTP**: Standard HTTP with streaming support
+
+## Files
+
+- `test_aks_mcp.py` - Multi-transport lab client with comprehensive scenarios
+- `aks-mcp-demo.ipynb` - Jupyter notebook demonstration
+- `TRANSPORT_TESTING.md` - Detailed transport lab documentation
+
+## Prerequisites
+
+- Python 3.11+
+- Go 1.21+ (for building the server)
+- Azure CLI (configured with appropriate permissions)
+- Azure OpenAI or OpenAI API access
+
+## Quick Start
+
+### 1. Build AKS-MCP Server
+```bash
+cd /Users/hieunguyennhu/github/aks-mcp
+make build
+```
+
+### 2. Setup Environment
+```bash
+# Optional: Create virtual environment (recommended)
+python -m venv .venv
+source .venv/bin/activate # On Windows: .venv\Scripts\activate
+
+# Install Python dependencies
+pip install semantic-kernel[mcp] python-dotenv
+# Copy and configure environment variables
+cp .env.example .env
+# Edit .env with your Azure OpenAI settings:
+# AZURE_OPENAI_ENDPOINT=https://your-resource.openai.azure.com/
+# AZURE_OPENAI_API_KEY=your-api-key
+# AZURE_OPENAI_DEPLOYMENT_NAME=gpt-4o
+```
+
+### 3. Lab Exercise: Each Transport
+
+#### STDIO Transport (Default)
+```bash
+# Lab exercise directly - no server needed
+python test_aks_mcp.py
+```
+
+#### SSE Transport
+```bash
+# Terminal 1: Start SSE server with verbose logging
+./aks-mcp --transport sse --port 8000 --access-level admin --verbose
+
+# Terminal 2: Run lab client
+python test_aks_mcp.py --transport sse --host localhost --port 8000
+```
+
+#### Streamable HTTP Transport
+```bash
+# Terminal 1: Start HTTP server with verbose logging
+./aks-mcp --transport streamable-http --port 8000 --access-level admin --verbose
+
+# Terminal 2: Run lab client
+python test_aks_mcp.py --transport streamable-http --host localhost --port 8000
+```
+
+## Verbose Logging
+
+Add `--verbose` or `-v` to any server command to see detailed tool call information:
+
+```bash
+./aks-mcp --transport sse --port 8000 --access-level admin --verbose
+```
+
+**Example verbose output:**
+```
+>>> [az_aks_operations] {"args":"","operation":"list"}
+ Result: 20291 bytes (truncated): [{"aadProfile":{"enableAzureRbac":true...
+
+>>> [az_monitoring] {"cluster_name":"hub","operation":"resource_health","parameters":{"start_time":"2025-01-01T00:00:00Z"}}
+ ERROR: missing or invalid start_time parameter
+```
+
+## Lab Scenarios
+
+The lab client runs 5 comprehensive scenarios:
+
+1. **Cluster Discovery** - Lists all AKS clusters with health status
+2. **Diagnostic Detectors** - Discovers available diagnostic tools
+3. **Kubernetes Workloads** - Analyzes pods, services, deployments
+4. **Fleet Management** - Checks Azure Kubernetes Fleet configuration
+5. **Advisory Recommendations** - Retrieves Azure Advisor suggestions
+
+## Command Reference
+
+### Server Commands
+```bash
+# STDIO (default)
+./aks-mcp --access-level admin --verbose
+
+# SSE on custom port
+./aks-mcp --transport sse --port 9000 --access-level admin --verbose
+
+# HTTP on custom host/port
+./aks-mcp --transport streamable-http --host 0.0.0.0 --port 8080 --access-level admin --verbose
+```
+
+### Client Commands
+```bash
+# Lab exercise with specific transport
+python test_aks_mcp.py --transport
+
+# Custom host/port for HTTP transports
+python test_aks_mcp.py --transport sse --host localhost --port 9000
+
+# Help
+python test_aks_mcp.py --help
+```
+
+## Troubleshooting
+
+### Connection Issues
+1. **Check server is running**: Ensure the server started successfully
+2. **Verify port availability**: `lsof -i :8000`
+3. **Test connectivity**: `curl http://localhost:8000/mcp` (for HTTP transports)
+4. **Check firewall**: Local firewall may block connections
+
+### Transport-Specific Issues
+
+**STDIO**:
+- Ensure `aks-mcp` binary exists and is executable
+- Check Azure credentials are configured
+
+**SSE**:
+- Server endpoint: `http://localhost:8000/sse`
+- Some proxies may interfere with SSE connections
+
+**Streamable HTTP**:
+- Server endpoint: `http://localhost:8000/mcp`
+- May show harmless cleanup warnings on client exit
+
+### Known Issues
+- Streamable HTTP transport shows async cleanup warnings - these are harmless
+- All transports require valid Azure credentials for AKS operations
+
+## Jupyter Notebook
+
+Install Jupyter and required dependencies:
+
+```bash
+# Install Jupyter and notebook dependencies
+pip install jupyter semantic-kernel[mcp] python-dotenv pandas
+
+# Start Jupyter
+jupyter notebook aks-mcp-demo.ipynb
+```
+
+The notebook demonstrates the same functionality with step-by-step execution and detailed output.
+
+## Performance Tips
+
+- Use `--verbose` only during lab exercises - it generates significant log output
+- STDIO transport has the lowest overhead
+- HTTP transports allow multiple concurrent clients
+- Set appropriate `--timeout` values for long-running operations
+
+## Security Notes
+
+- `--access-level admin` provides full cluster access
+- Use `--access-level readonly` for safer lab exercises
+- Never expose HTTP/SSE servers publicly without proper authentication
+- All transports require valid Azure RBAC permissions
+
+## Getting Help
+
+```bash
+# Server help
+./aks-mcp --help
+
+# Client help
+python test_aks_mcp.py --help
+
+# Check available AKS functions
+./aks-mcp --transport stdio --access-level admin | grep "Registering"
+```
\ No newline at end of file
diff --git a/docs/labs/aks-mcp-demo.ipynb b/docs/labs/aks-mcp-demo.ipynb
new file mode 100644
index 0000000..228d0f5
--- /dev/null
+++ b/docs/labs/aks-mcp-demo.ipynb
@@ -0,0 +1,559 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# AKS MCP Demo - Working Version\n",
+ "\n",
+ "Simple, working AKS MCP integration based on your test file."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Install required dependencies (run this first)\n",
+ "# Uncomment and run if packages are not installed\n",
+ "%pip install semantic-kernel[mcp] python-dotenv\n",
+ "\n",
+ "print(\"๐ฆ If you see import errors below, uncomment and run the pip install above\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 66,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "โ
Setup complete\n"
+ ]
+ }
+ ],
+ "source": [
+ "import asyncio\n",
+ "import os\n",
+ "from pathlib import Path\n",
+ "from dotenv import load_dotenv\n",
+ "\n",
+ "load_dotenv()\n",
+ "print(\"โ
Setup complete\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 67,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "โ
Imports successful\n"
+ ]
+ }
+ ],
+ "source": [
+ "from semantic_kernel import Kernel\n",
+ "from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion\n",
+ "from semantic_kernel.connectors.mcp import MCPStdioPlugin\n",
+ "from semantic_kernel.agents import ChatCompletionAgent\n",
+ "from semantic_kernel.contents import ChatHistory\n",
+ "\n",
+ "print(\"โ
Imports successful\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 68,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "โ
Kernel initialized\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Initialize kernel\n",
+ "kernel = Kernel()\n",
+ "\n",
+ "# Configure Azure OpenAI\n",
+ "azure_openai = AzureChatCompletion(\n",
+ " endpoint=os.getenv(\"AZURE_OPENAI_ENDPOINT\"),\n",
+ " api_key=os.getenv(\"AZURE_OPENAI_API_KEY\"),\n",
+ " deployment_name=os.getenv(\"AZURE_OPENAI_DEPLOYMENT_NAME\", \"gpt-4o\"),\n",
+ " api_version=os.getenv(\"AZURE_OPENAI_API_VERSION\", \"2024-08-01-preview\")\n",
+ ")\n",
+ "\n",
+ "kernel.add_service(azure_openai)\n",
+ "print(\"โ
Kernel initialized\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 69,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "โ
MCP connected\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Setup AKS MCP Plugin\n",
+ "aks_mcp_path = \"../../aks-mcp\"\n",
+ "if not Path(aks_mcp_path).exists():\n",
+ " print(f\"โ AKS-MCP not found at {aks_mcp_path}\")\n",
+ " raise FileNotFoundError(\"Build with: make build\")\n",
+ "\n",
+ "mcp_plugin = MCPStdioPlugin(\n",
+ " name=\"AKSMCP\",\n",
+ " command=aks_mcp_path,\n",
+ " args=[\"--transport\", \"stdio\", \"--access-level\", \"admin\"],\n",
+ ")\n",
+ "\n",
+ "await mcp_plugin.connect()\n",
+ "kernel.add_plugin(mcp_plugin, plugin_name=\"akstool\")\n",
+ "print(\"โ
MCP connected\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 70,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "๐ Available functions: 16\n",
+ " โข az_advisor_recommendation\n",
+ " โข az_aks_operations\n",
+ " โข az_fleet\n",
+ " โข az_monitoring\n",
+ " โข az_network_resources\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Show available functions\n",
+ "plugin_functions = kernel.get_plugin(\"akstool\")\n",
+ "print(f\"๐ Available functions: {len(plugin_functions.functions)}\")\n",
+ "for func_name in list(plugin_functions.functions.keys())[:5]:\n",
+ " print(f\" โข {func_name}\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 71,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "โ
Agent created\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Create agent\n",
+ "agent = ChatCompletionAgent(\n",
+ " kernel=kernel,\n",
+ " name=\"AKS_Assistant\",\n",
+ " instructions=\"\"\"You are an expert AKS administrator with access to powerful AKS management tools.\n",
+ "\n",
+ "IMPORTANT: Always use the available akstool functions to gather information instead of asking users for details.\n",
+ "\n",
+ "When asked about clusters, detectors, or recommendations:\n",
+ "1. FIRST use az_aks_operations to discover available clusters and their details\n",
+ "2. THEN use the appropriate tool with the discovered information\n",
+ "3. Provide comprehensive analysis based on the actual data\n",
+ "\n",
+ "Never ask users for cluster names, resource groups, or subscription IDs - discover them using the tools.\"\"\"\n",
+ ")\n",
+ "\n",
+ "print(\"โ
Agent created\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 72,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "โ
Helper function ready\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Simple helper function\n",
+ "async def ask_assistant(question: str):\n",
+ " print(f\"๐ Question: {question}\")\n",
+ " print(\"-\" * 50)\n",
+ " \n",
+ " chat_history = ChatHistory()\n",
+ " chat_history.add_user_message(question)\n",
+ " \n",
+ " async for response in agent.invoke(messages=chat_history):\n",
+ " if response.content:\n",
+ " print(response.content, end=\"\", flush=True)\n",
+ " \n",
+ " print(\"\\n\" + \"=\" * 50)\n",
+ "\n",
+ "print(\"โ
Helper function ready\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 73,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "๐ Question: What AKS clusters do I have? Please provide a comprehensive overview.\n",
+ "--------------------------------------------------\n",
+ "Here is a comprehensive overview of your Azure Kubernetes Service (AKS) clusters:\n",
+ "\n",
+ "### Cluster 1: Hub\n",
+ "- **Name:** hub\n",
+ "- **Resource Group:** FL_hieunhu_testhubfleet1_eastasia\n",
+ "- **Location:** eastasia\n",
+ "- **Kubernetes Version:** 1.33.2\n",
+ "- **Provisioning State:** Succeeded\n",
+ "- **AAD Profile:**\n",
+ " - Enabled Azure RBAC\n",
+ " - Managed AAD\n",
+ "- **API Server Access:**\n",
+ " - Disable Run Command: True\n",
+ "- **Upgrade Profile:** \n",
+ " - Node OS Upgrade Channel: NodeImage\n",
+ " - Upgrade Channel: patch\n",
+ "- **RBAC Enabled:** True\n",
+ "- **DNS Prefix:** testhubfleet1-dns\n",
+ "- **FQDN:** testhubfleet1-dns-dm34i8zt.hcp.eastasia.azmk8s.io\n",
+ "- **Agent Pool:**\n",
+ " - Name: agentpool\n",
+ " - Count: 1\n",
+ " - VM Size: Standard_D4s_v4\n",
+ " - OS Disk Size: 128 GB\n",
+ " - OS Type: Linux\n",
+ " - Current Orchestrator Version: 1.33.2\n",
+ " - Network Profile: None\n",
+ " - Power State: Running\n",
+ "- **Network Profile:**\n",
+ " - Plugin: azure\n",
+ " - Load Balancer SKU: standard\n",
+ " - Network Dataplane: azure\n",
+ " - Network Policy: none\n",
+ " - Service CIDR: 10.0.0.0/16\n",
+ " - DNS Service IP: 10.0.0.10\n",
+ "\n",
+ "### Cluster 2: AKS Keda\n",
+ "- **Name:** akskeda-aks\n",
+ "- **Resource Group:** akskeda-rg\n",
+ "- **Location:** eastasia\n",
+ "- **Kubernetes Version:** 1.32.6\n",
+ "- **Provisioning State:** Succeeded\n",
+ "- **AAD Profile:**\n",
+ " - Enabled Azure RBAC\n",
+ " - Managed AAD\n",
+ "- **API Server Access:**\n",
+ " - Enable Private Cluster: False\n",
+ " - Enable Vnet Integration: True\n",
+ " - Subnet ID: \"\"\n",
+ "- **Addons:**\n",
+ " - Azure Keyvault Secrets Provider: Enabled\n",
+ " - Azure Policy: Enabled\n",
+ " - Azure Monitor: Container Insights Enabled\n",
+ "- **Upgrade Profile:**\n",
+ " - Node OS Upgrade Channel: NodeImage\n",
+ " - Upgrade Channel: stable\n",
+ "- **RBAC Enabled:** True\n",
+ "- **DNS Prefix:** akskeda-ak-akskeda-rg-b9bdc3\n",
+ "- **FQDN:** akskeda-ak-akskeda-rg-b9bdc3-4hoj1fkc.hcp.eastasia.azmk8s.io\n",
+ "- **Agent Pool:**\n",
+ " - Name: nodepool1\n",
+ " - Count: 1\n",
+ " - VM Size: standard_d4lds_v5\n",
+ " - OS Disk Size: 128 GB\n",
+ " - OS Type: Linux\n",
+ " - Current Orchestrator Version: 1.32.6\n",
+ " - Node Taints: [\"CriticalAddonsOnly=true:NoSchedule\"]\n",
+ " - Network Profile: advancedNetworking.\n",
+ " - Power State: Running\n",
+ "- **Network Profile:**\n",
+ " - Plugin: azure (overlay)\n",
+ " - Policy: cilium\n",
+ " - Load Balancer SKU: standard\n",
+ " - Network Dataplane: cilium\n",
+ " - Outbound Type: managedNATGateway\n",
+ " - Pod CIDR: 10.244.0.0/16\n",
+ " - Service CIDR: 10.0.0.0/16\n",
+ " - DNS Service IP: 10.0.0.10\n",
+ "\n",
+ "If you need further details on specific aspects of these clusters, please let me know!\n",
+ "==================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Test: Cluster discovery\n",
+ "await ask_assistant(\"What AKS clusters do I have? Please provide a comprehensive overview.\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 74,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "๐ Question: What diagnostic detectors are available? Discover my clusters first, then list detectors.\n",
+ "--------------------------------------------------\n",
+ "### Available Diagnostic Detectors\n",
+ "\n",
+ "#### Cluster: `hub`\n",
+ "- **Active Live Site Incidents:** Single source of reference for any active live site incidents. \n",
+ " Category: Cluster and Control Plane Availability and Performance\n",
+ "- **Pod CIDR out of IP Ranges:** Checks for cluster nodes that were not able to be assigned an IP range due to the pod CIDR running out of IP ranges. \n",
+ " Category: Node Health\n",
+ "- **AKS Operation Troubleshooter:** Checks for a failing operation ID then displays the summary and customer mitigation for that error type. \n",
+ " Category: Workflow Nodes\n",
+ "- **AKS-managed AAD Integration Issues:** Detects any issues with AKS-managed AAD integration for operator access. \n",
+ " Category: Identity and Security\n",
+ "- **API Server - Resource Intensive Listing:** Detect potential API Server load issues that stem from large and or inefficient listing patterns. \n",
+ " Category: Cluster and Control Plane Availability and Performance\n",
+ "- **API Server Authorized Ranges:** Shows if the cluster is configured to use authorized IP address ranges for limiting access to the API server. \n",
+ " Category: Connectivity Issues\n",
+ "- **Auto-Update Configuration Check:** Checks that the cluster has either the auto-upgrade turned on or has Kured installed. \n",
+ " Category: Best Practices\n",
+ "- **Availability Risk Alerts:** Checks for common issues that can affect the availability of AKS clusters. \n",
+ " Category: Risk Alerts\n",
+ "- **Azure File Creation Issues:** Designed to detect Azure Files creation failures. \n",
+ " Category: Storage\n",
+ "- **Azure KeyVault KMS Integration Issues:** Detects any issues with Azure KeyVault KMS integration for operator access. \n",
+ " Category: Identity and Security\n",
+ "- **Azure Linux Image Version:** Checks the cluster agent pools are using the correct Azure Linux version. \n",
+ " Category: Best Practices\n",
+ "- **Azure Resource Request Throttling:** Shows if the cluster has experienced any Azure Resource Manager (ARM), or Resource Provider (RP) request rate throttling. \n",
+ " Category: Cluster and Control Plane Availability and Performance\n",
+ "- **Best Practices:** Checks that your cluster conforms to the current AKS best practices. \n",
+ " Category: Best Practices\n",
+ "- **Blob Add-On and Blob Open-Source Driver Conflict:** Detects if a cluster has both blob add-on and the open-source blob driver. \n",
+ " Category: Storage\n",
+ "- **Client Service Principal:** Detects if a cluster's service principal is invalid or expired. \n",
+ " Category: Identity and Security\n",
+ "\n",
+ "... and many more.\n",
+ "\n",
+ "#### Cluster: `akskeda-aks`\n",
+ "- **Active Live Site Incidents:** Single source of reference for any active live site incidents. \n",
+ " Category: Cluster and Control Plane Availability and Performance\n",
+ "- **Pod CIDR out of IP Ranges:** Checks for cluster nodes that were not able to be assigned an IP range due to the pod CIDR running out of IP ranges. \n",
+ " Category: Node Health\n",
+ "- **AKS Operation Troubleshooter:** Checks for a failing operation ID then displays the summary and customer mitigation for that error type. \n",
+ " Category: Workflow Nodes\n",
+ "- **AKS-managed AAD Integration Issues:** Detects any issues with AKS-managed AAD integration for operator access. \n",
+ " Category: Identity and Security\n",
+ "- **API Server - Resource Intensive Listing:** Detect potential API Server load issues that stem from large and or inefficient listing patterns. \n",
+ " Category: Cluster and Control Plane Availability and Performance\n",
+ "- **API Server Authorized Ranges:** Shows if the cluster is configured to use authorized IP address ranges for limiting access to the API server. \n",
+ " Category: Connectivity Issues\n",
+ "- **Auto-Update Configuration Check:** Checks that the cluster has either the auto-upgrade turned on or has Kured installed. \n",
+ " Category: Best Practices\n",
+ "- **Availability Risk Alerts:** Checks for common issues that can affect the availability of AKS clusters. \n",
+ " Category: Risk Alerts\n",
+ "- **Azure File Creation Issues:** Designed to detect Azure Files creation failures. \n",
+ " Category: Storage\n",
+ "- **Azure KeyVault KMS Integration Issues:** Detects any issues with Azure KeyVault KMS integration for operator access. \n",
+ " Category: Identity and Security\n",
+ "- **Azure Linux Image Version:** Checks the cluster agent pools are using the correct Azure Linux version. \n",
+ " Category: Best Practices\n",
+ "- **Azure Resource Request Throttling:** Shows if the cluster has experienced any Azure Resource Manager (ARM), or Resource Provider (RP) request rate throttling. \n",
+ " Category: Cluster and Control Plane Availability and Performance\n",
+ "- **Best Practices:** Checks that your cluster conforms to the current AKS best practices. \n",
+ " Category: Best Practices\n",
+ "- **Blob Add-On and Blob Open-Source Driver Conflict:** Detects if a cluster has both blob add-on and the open-source blob driver. \n",
+ " Category: Storage\n",
+ "- **Client Service Principal:** Detects if a cluster's service principal is invalid or expired. \n",
+ " Category: Identity and Security\n",
+ "\n",
+ "... and many more.\n",
+ "\n",
+ "These detectors cover a wide range of categories such as Best Practices, Cluster and Control Plane Availability and Performance, Connectivity Issues, Identity and Security, Node Health, and Storage.\n",
+ "==================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Test: Diagnostic detectors\n",
+ "await ask_assistant(\"What diagnostic detectors are available? Discover my clusters first, then list detectors.\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 75,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "๐ Question: Analyze the workloads in my clusters. Find my clusters first, then check pods and services.\n",
+ "--------------------------------------------------\n",
+ "It appears that there are permission issues with accessing the credentials for your clusters, which is preventing further analysis of the workloads.\n",
+ "\n",
+ "Here is what I have tried so far:\n",
+ "1. Listed the available clusters - successfully identified two clusters: `hub` and `akskeda-aks`.\n",
+ "2. Attempted to retrieve pods and services details from both clusters using different methods, but encountered errors including permission issues when fetching the credentials.\n",
+ "\n",
+ "To successfully analyze workloads in your clusters, please ensure that the client account has the necessary permissions for accessing AKS cluster resources, particularly for listing cluster user credentials and running kubectl commands.\n",
+ "\n",
+ "If you need further assistance or have specific actions that should be directed differently, let me know!\n",
+ "==================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Test: Workload analysis\n",
+ "await ask_assistant(\"Analyze the workloads in my clusters. Find my clusters first, then check pods and services.\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 76,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "๐ Question: Tell me about the network configuration and suggest improvements.\n",
+ "--------------------------------------------------\n",
+ "Based on the network configurations of the two AKS clusters (\"hub\" in \"FL_hieunhu_testhubfleet1_eastasia\" and \"akskeda-aks\" in \"akskeda-rg\"), here is the detailed information and some recommendations for improvement:\n",
+ "\n",
+ "### Network Configuration Details:\n",
+ "\n",
+ "#### Cluster: \"hub\"\n",
+ "\n",
+ "1. **Load Balancer**:\n",
+ " - **Backend Address Pools**: Utilizes two backend pools for outbound and cluster services.\n",
+ " - **Frontend IP Configurations**: Configured with a dynamic IP allocation and a standard public IP.\n",
+ " - **Outbound Rules**: Configured with an outbound rule enabling outbound traffic with TCP reset and specific idle timeout.\n",
+ "\n",
+ "2. **Network Security Group (NSG)**:\n",
+ " - **Default Security Rules**: Includes standard rules allowing VNet traffic, Azure Load Balancer traffic, and denying all inbound/outbound traffic except for Internet outbound.\n",
+ " - **Custom Rules**: No additional custom rules found.\n",
+ "\n",
+ "3. **Virtual Network (VNet) and Subnet**:\n",
+ " - **VNet**: Configured with an address space of `10.224.0.0/12`.\n",
+ " - **Subnet**: Configured with a subnet `aks-subnet` with an address prefix of `10.224.0.0/16`. The subnet is associated with the appropriate NSG and contains multiple IP configurations.\n",
+ "\n",
+ "#### Cluster: \"akskeda-aks\"\n",
+ "\n",
+ "1. **Load Balancer**:\n",
+ " - **Backend Address Pools**: Utilizes backend pools for balancing various backend IP configurations.\n",
+ " - **Frontend IP Configurations**: Configured with dynamic IP allocation and public IP addresses.\n",
+ " - **Inbound Rules**: Several load balancing rules for ports 80, 443, 7473, 7474, and 7687. Probes configured for health checks.\n",
+ "\n",
+ "2. **Network Security Group (NSG)**:\n",
+ " - **Default Security Rules**: Includes standard rules similar to the \"hub\" cluster.\n",
+ " - **Custom Rules**: Custom rules to allow specific inbound traffic from the Internet to the load balancer and respective ports.\n",
+ "\n",
+ "3. **Virtual Network (VNet) and Subnet**:\n",
+ " - **VNet**: Configured with an address space of `10.224.0.0/12`.\n",
+ " - **Subnets**: Includes subnets `aks-subnet` and `aks-apiserver-subnet`, both associated with appropriate NSGs and link services.\n",
+ " - **NAT Gateway**: Configured in `aks-subnet` for outbound connectivity.\n",
+ "\n",
+ "### Recommendations for Improvement:\n",
+ "\n",
+ "1. **Enable Network Policies**:\n",
+ " - Implement network policies to control traffic flow between pods and services within the cluster. This can enhance security by ensuring that only authorized traffic is allowed.\n",
+ "\n",
+ "2. **IP Ranges for API Server Access**:\n",
+ " - Define `authorizedIpRanges` in the `apiServerAccessProfile` to restrict access to the API Server, ensuring that only trusted IP addresses can reach the Kubernetes API.\n",
+ "\n",
+ "3. **Utilize Private Endpoints**:\n",
+ " - Consider configuring private endpoints for your API Server and other critical services. This can limit exposure to the public internet and enhance the security of your cluster.\n",
+ "\n",
+ "4. **Dedicated Route Table**:\n",
+ " - Implement a dedicated route table with specific routing rules to manage traffic flows within the VNet more effectively. This can be beneficial if you plan to use advanced networking features or integrate with other Azure services.\n",
+ "\n",
+ "5. **Review NSG Rules**:\n",
+ " - Regularly review and update NSG rules to ensure they adhere to the principle of least privilege. Remove or tighten any rules that allow unnecessary traffic.\n",
+ "\n",
+ "6. **Advanced Load Balancing**:\n",
+ " - Use multiple standard load balancers and customize idle timeouts and protocols based on the specific needs of your applications.\n",
+ "\n",
+ "7. **Network Monitoring**:\n",
+ " - Enable and configure Azure Network Watcher to monitor and diagnose network issues. This can provide insights into network performance and help detect and resolve issues quickly.\n",
+ "\n",
+ "By implementing these recommendations, you can enhance the security, performance, and reliability of your AKS clusters.\n",
+ "==================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Custom question\n",
+ "await ask_assistant(\"Tell me about the network configuration and suggest improvements.\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 77,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "โ ๏ธ Cleanup warning: Attempted to exit cancel scope in a different task than it was entered in\n",
+ "โ
Demo complete\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Cleanup\n",
+ "try:\n",
+ " await mcp_plugin.close()\n",
+ " print(\"โ
MCP plugin disconnected\")\n",
+ "except Exception as e:\n",
+ " print(f\"โ ๏ธ Cleanup warning: {e}\")\n",
+ " \n",
+ "print(\"โ
Demo complete\")"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "name": "python",
+ "version": "3.8.0"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/docs/labs/test_aks_mcp.py b/docs/labs/test_aks_mcp.py
new file mode 100755
index 0000000..5872469
--- /dev/null
+++ b/docs/labs/test_aks_mcp.py
@@ -0,0 +1,272 @@
+#!/usr/bin/env python3
+"""
+Test AKS-MCP integration with Semantic Kernel ChatCompletionAgent
+
+Supports multiple transport types:
+- stdio: Direct process communication (default)
+- sse: Server-Sent Events HTTP transport
+- streamable-http: Streamable HTTP transport
+
+Usage:
+ # Default stdio transport
+ python test_aks_mcp.py
+
+ # SSE transport
+ python test_aks_mcp.py --transport sse --host localhost --port 8000
+
+ # Streamable HTTP transport
+ python test_aks_mcp.py --transport streamable-http --host localhost --port 8000
+
+Prerequisites:
+- For stdio: Build aks-mcp binary with 'make build'
+- For SSE/HTTP: Start AKS-MCP server with appropriate transport
+ Example: ./aks-mcp --transport sse --port 8000
+"""
+
+import asyncio
+import os
+import sys
+from pathlib import Path
+from dotenv import load_dotenv
+
+# Load environment variables
+load_dotenv()
+
+async def test_aks_mcp(transport="stdio", host="localhost", port=8000):
+ """Test AKS-MCP with Semantic Kernel ChatCompletionAgent
+
+ Args:
+ transport: Transport type - "stdio", "sse", or "streamable-http"
+ host: Host for HTTP/SSE connections (default: localhost)
+ port: Port for HTTP/SSE connections (default: 8000)
+ """
+ try:
+ # Import Semantic Kernel components
+ from semantic_kernel import Kernel
+ from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion
+ from semantic_kernel.connectors.mcp import MCPStdioPlugin, MCPSsePlugin, MCPStreamableHttpPlugin
+ from semantic_kernel.agents import ChatCompletionAgent
+
+ print("โ
Imports successful")
+
+ # Initialize kernel
+ kernel = Kernel()
+
+ # Configure Azure OpenAI
+ azure_openai = AzureChatCompletion(
+ endpoint=os.getenv("AZURE_OPENAI_ENDPOINT"),
+ api_key=os.getenv("AZURE_OPENAI_API_KEY"),
+ deployment_name=os.getenv("AZURE_OPENAI_DEPLOYMENT_NAME", "gpt-4o"),
+ api_version=os.getenv("AZURE_OPENAI_API_VERSION", "2024-08-01-preview")
+ )
+
+ kernel.add_service(azure_openai)
+ print("โ
Kernel initialized with Azure OpenAI")
+
+ # Create AKS-MCP plugin based on transport type
+ print(f"๐ง Using transport: {transport}")
+
+ if transport == "stdio":
+ aks_mcp_path = "/Users/hieunguyennhu/github/aks-mcp/aks-mcp"
+ if not Path(aks_mcp_path).exists():
+ print(f"โ AKS-MCP not found at {aks_mcp_path}")
+ print("๐ก Build it with: cd /Users/hieunguyennhu/github/aks-mcp && make build")
+ return False
+
+ mcp_plugin = MCPStdioPlugin(
+ name="AKSMCP",
+ command=aks_mcp_path,
+ args=["--transport", "stdio", "--access-level", "admin"],
+ )
+
+ elif transport == "sse":
+ print(f"๐ Connecting to SSE server at {host}:{port}")
+ print(f"๐ก URL: http://{host}:{port}/sse")
+ mcp_plugin = MCPSsePlugin(
+ name="AKSMCP",
+ url=f"http://{host}:{port}/sse",
+ )
+
+ elif transport == "streamable-http":
+ print(f"๐ Connecting to HTTP server at {host}:{port}")
+ print(f"๐ก URL: http://{host}:{port}/mcp")
+ mcp_plugin = MCPStreamableHttpPlugin(
+ name="AKSMCP",
+ url=f"http://{host}:{port}/mcp",
+ )
+
+ else:
+ print(f"โ Unsupported transport: {transport}")
+ print("๐ก Supported transports: stdio, sse, streamable-http")
+ return False
+
+ # Connect to MCP server
+ await mcp_plugin.connect()
+ print(f"โ
MCP plugin connected via {transport}")
+
+ # Add plugin to kernel
+ kernel.add_plugin(mcp_plugin, plugin_name="akstool")
+ print("โ
MCP plugin added to kernel")
+
+ # Get plugin functions from kernel
+ plugin_functions = kernel.get_plugin("akstool")
+ print(f"๐ Available functions: {len(plugin_functions.functions)}")
+ for func_name in list(plugin_functions.functions.keys())[:3]:
+ print(f" โข {func_name}")
+
+ # Create ChatCompletionAgent
+ agent = ChatCompletionAgent(
+ kernel=kernel,
+ name="AKS_Assistant", # Use valid name pattern (no spaces)
+ instructions="""You are an expert AKS administrator with access to powerful AKS management tools.
+
+IMPORTANT: Always use the available akstool functions to gather information instead of asking users for details.
+
+Your capabilities include:
+- az_aks_operations: List and analyze AKS clusters (use this to discover cluster names, resource groups, subscription IDs)
+- list_detectors: List available diagnostic detectors
+- run_detector: Execute specific diagnostic checks
+- kubectl_*: Execute kubectl commands on clusters
+- az_fleet: Manage Azure Kubernetes Fleet
+- az_advisor_recommendation: Get Azure Advisor recommendations
+- az_monitoring: Monitor cluster health
+
+When asked about clusters, detectors, or recommendations:
+1. FIRST use az_aks_operations to discover available clusters and their details
+2. THEN use the appropriate tool with the discovered information
+3. Provide comprehensive analysis based on the actual data
+
+Never ask users for cluster names, resource groups, or subscription IDs - discover them using the tools."""
+ )
+
+ print("๐ค ChatCompletionAgent created")
+
+ # Test the agent with multiple scenarios
+ print("\n๐งช Testing agent with comprehensive scenarios...")
+
+ from semantic_kernel.contents import ChatHistory
+
+ # Test scenarios to showcase AKS MCP capabilities
+ test_scenarios = [
+ {
+ "name": "Cluster Discovery",
+ "question": "What AKS clusters do I have? Please provide a comprehensive overview including health status.",
+ "expected": "Should use az_aks_operations to list all clusters"
+ },
+ {
+ "name": "Diagnostic Detectors Discovery",
+ "question": "Discover what diagnostic detectors are available for my AKS clusters. Use the tools to find my clusters first, then list the detectors.",
+ "expected": "Should use az_aks_operations then list_detectors"
+ },
+ {
+ "name": "Kubernetes Workloads Analysis",
+ "question": "Analyze the Kubernetes workloads running in my clusters. Discover my clusters first, then check what pods, services, and deployments are running.",
+ "expected": "Should use az_aks_operations then kubectl commands"
+ },
+ {
+ "name": "Fleet Management Check",
+ "question": "Check my Azure Kubernetes Fleet configuration and resources using the available tools.",
+ "expected": "Should use az_fleet functionality"
+ },
+ {
+ "name": "Advisory Recommendations Analysis",
+ "question": "Find Azure Advisor recommendations for my environment. Use the tools to discover my current subscription and resource details first.",
+ "expected": "Should use az_aks_operations then az_advisor_recommendation"
+ }
+ ]
+
+ for i, scenario in enumerate(test_scenarios, 1):
+ print(f"\n{'='*60}")
+ print(f"๐ฏ Scenario {i}: {scenario['name']}")
+ print(f"โ Question: {scenario['question']}")
+ print(f"๐ก Expected: {scenario['expected']}")
+ print("="*60)
+
+ chat_history = ChatHistory()
+ chat_history.add_user_message(scenario['question'])
+
+ print("๐ค Agent thinking and using tools...\n")
+
+ response_text = ""
+ async for response in agent.invoke(messages=chat_history):
+ content = str(response.content or "")
+ response_text += content
+ print(content, end="", flush=True)
+
+ print(f"\n\nโ
Scenario {i} completed")
+ print(f"๐ Response length: {len(response_text)} characters")
+
+ # Brief pause between scenarios
+ import asyncio
+ await asyncio.sleep(1)
+
+ # Cleanup
+ try:
+ await mcp_plugin.close()
+ print("\nโ
MCP plugin disconnected")
+ except Exception as cleanup_error:
+ # Known issue with streamable-http transport cleanup
+ if "cancel scope" in str(cleanup_error) or "Session terminated" in str(cleanup_error):
+ print("โ ๏ธ Known cleanup issue with streamable-http transport (harmless)")
+ else:
+ print(f"โ ๏ธ Cleanup warning: {cleanup_error}")
+
+ print("\nโ
Test completed successfully!")
+ return True
+
+ except Exception as e:
+ # Handle known streamable-http cleanup issues
+ if ("cancel scope" in str(e) or "Session terminated" in str(e) or
+ "streamablehttp_client" in str(e) or "GeneratorExit" in str(e)):
+ print(f"\nโ ๏ธ Known streamable-http transport cleanup issue (test likely succeeded)")
+ print("๐ก This is a harmless MCP library cleanup warning")
+ return True
+
+ print(f"\nโ Error: {e}")
+
+ if "opentelemetry" in str(e):
+ print("๐ก Try: pip install 'semantic-kernel[mcp]'")
+ elif "Failed to connect" in str(e) or "Connection" in str(e):
+ print("\n๐ก Connection failed. Please ensure:")
+ if transport == "sse":
+ print(" 1. AKS-MCP server is running with SSE transport:")
+ print(f" ./aks-mcp --transport sse --port {port} --access-level admin")
+ print(f" 2. Server is accessible at http://{host}:{port}/sse")
+ elif transport == "streamable-http":
+ print(" 1. AKS-MCP server is running with HTTP transport:")
+ print(f" ./aks-mcp --transport streamable-http --port {port} --access-level admin")
+ print(f" 2. Server is accessible at http://{host}:{port}")
+ print(" 3. Check firewall/network settings")
+
+ return False
+
+async def main():
+ # Check prerequisites
+ if not Path(".env").exists():
+ print("โ ๏ธ No .env file found")
+ print("๐ก Copy .env.example to .env and configure Azure OpenAI settings")
+ return False
+
+ # Parse command line arguments for transport options
+ import argparse
+ parser = argparse.ArgumentParser(description="Test AKS-MCP with different transports")
+ parser.add_argument("--transport", choices=["stdio", "sse", "streamable-http"],
+ default="stdio", help="Transport type (default: stdio)")
+ parser.add_argument("--host", default="localhost",
+ help="Host for HTTP/SSE connections (default: localhost)")
+ parser.add_argument("--port", type=int, default=8000,
+ help="Port for HTTP/SSE connections (default: 8000)")
+
+ args = parser.parse_args()
+
+ print(f"๐ Starting AKS-MCP test with {args.transport} transport")
+ if args.transport != "stdio":
+ print(f"๐ Server: {args.host}:{args.port}")
+
+ # Run test with specified transport
+ success = await test_aks_mcp(args.transport, args.host, args.port)
+ return success
+
+if __name__ == "__main__":
+ success = asyncio.run(main())
+ sys.exit(0 if success else 1)
\ No newline at end of file
diff --git a/go.mod b/go.mod
index 76912b3..68eb59e 100644
--- a/go.mod
+++ b/go.mod
@@ -5,13 +5,13 @@ go 1.24.0
toolchain go1.24.5
require (
- github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.1
+ github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.2
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.2.1
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v2 v2.4.0
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/monitor/armmonitor v0.11.0
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v2 v2.2.1
- github.com/Azure/mcp-kubernetes v0.0.5
+ github.com/Azure/mcp-kubernetes v0.0.6
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/inspektor-gadget/inspektor-gadget v0.42.0
github.com/mark3labs/mcp-go v0.36.0
@@ -22,7 +22,7 @@ require (
)
require (
- github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect
+ github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 // indirect
github.com/bahlo/generic-list-go v0.2.0 // indirect
@@ -86,14 +86,14 @@ require (
go.uber.org/multierr v1.11.0 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
go.yaml.in/yaml/v3 v3.0.3 // indirect
- golang.org/x/crypto v0.39.0 // indirect
+ golang.org/x/crypto v0.40.0 // indirect
golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329 // indirect
- golang.org/x/net v0.41.0 // indirect
+ golang.org/x/net v0.42.0 // indirect
golang.org/x/oauth2 v0.30.0 // indirect
- golang.org/x/sync v0.15.0 // indirect
- golang.org/x/sys v0.33.0 // indirect
- golang.org/x/term v0.32.0 // indirect
- golang.org/x/text v0.26.0 // indirect
+ golang.org/x/sync v0.16.0 // indirect
+ golang.org/x/sys v0.34.0 // indirect
+ golang.org/x/term v0.33.0 // indirect
+ golang.org/x/text v0.27.0 // indirect
golang.org/x/time v0.11.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect
google.golang.org/grpc v1.73.0 // indirect
diff --git a/go.sum b/go.sum
index 520999d..afc7dea 100644
--- a/go.sum
+++ b/go.sum
@@ -1,11 +1,11 @@
-github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.1 h1:Wc1ml6QlJs2BHQ/9Bqu1jiyggbsSjramq2oUmp5WeIo=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.1/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.2 h1:Hr5FTipp7SL07o2FvoVOX9HRiRH3CR3Mj8pxqCcdD5A=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.2/go.mod h1:QyVsSSN64v5TGltphKLQ2sQxe4OBQg0J1eKRcVBnfgE=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 h1:B+blDbyVIG3WaikNxPnhPiJ1MThR03b3vKGtER95TP4=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1/go.mod h1:JdM5psgjfBf5fo2uWOZhflPWyDBZ/O/CNAH9CtsuZE4=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8=
-github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4=
-github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1/go.mod h1:j2chePtV91HrC22tGoRX3sGY42uF13WzmmV80/OdVAA=
+github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA=
+github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.2.1 h1:UPeCRD+XY7QlaGQte2EVI2iOcWvUYA2XY8w5T/8v0NQ=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.2.1/go.mod h1:oGV6NlB0cvi1ZbYRR2UN44QHxWFyGk+iylgD0qaMXjA=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v2 v2.4.0 h1:1u/K2BFv0MwkG6he8RYuUcbbeK22rkoZbg4lKa/msZU=
@@ -26,6 +26,8 @@ github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEK
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/Azure/mcp-kubernetes v0.0.5 h1:TME3Yjjn0bTdR5FeiGCeGcX3fNkWy4i2Cx7F+2qR+OA=
github.com/Azure/mcp-kubernetes v0.0.5/go.mod h1:qDusmS5NdOyEDCWJZNvIXciwzpLRTmul7tOpRkQGMUs=
+github.com/Azure/mcp-kubernetes v0.0.6 h1:DsLQy/LgU3/znNaxZrss7uQWHmilWRovdF6pIS2BpTw=
+github.com/Azure/mcp-kubernetes v0.0.6/go.mod h1:qDusmS5NdOyEDCWJZNvIXciwzpLRTmul7tOpRkQGMUs=
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM=
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE=
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs=
@@ -239,8 +241,8 @@ go.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
-golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
+golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
+golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329 h1:9kj3STMvgqy3YA4VQXBrN7925ICMxD5wzMRcgA30588=
golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@@ -249,37 +251,37 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
-golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
+golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
+golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
-golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
+golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
+golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
-golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
-golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
-golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
+golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
+golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
+golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
-golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
+golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
+golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
-golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
+golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
+golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/internal/config/config.go b/internal/config/config.go
index 0b68ca7..9207668 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -28,6 +28,9 @@ type ConfigData struct {
AdditionalTools map[string]bool
// Comma-separated list of allowed Kubernetes namespaces
AllowNamespaces string
+
+ // Verbose logging
+ Verbose bool
}
// NewConfig creates and returns a new configuration instance
@@ -60,6 +63,9 @@ func (cfg *ConfigData) ParseFlags() {
flag.StringVar(&cfg.AllowNamespaces, "allow-namespaces", "",
"Comma-separated list of allowed Kubernetes namespaces (empty means all namespaces)")
+ // Logging settings
+ flag.BoolVarP(&cfg.Verbose, "verbose", "v", false, "Enable verbose logging")
+
flag.Parse()
// Update security config
diff --git a/internal/tools/handler.go b/internal/tools/handler.go
index 96b09f9..e92b213 100644
--- a/internal/tools/handler.go
+++ b/internal/tools/handler.go
@@ -2,20 +2,52 @@ package tools
import (
"context"
+ "encoding/json"
"fmt"
+ "log"
"github.com/Azure/aks-mcp/internal/config"
"github.com/mark3labs/mcp-go/mcp"
)
+// logToolCall logs the start of a tool call
+func logToolCall(toolName string, arguments interface{}) {
+ // Try to format as JSON for better readability
+ if jsonBytes, err := json.Marshal(arguments); err == nil {
+ log.Printf("\n>>> [%s] %s", toolName, string(jsonBytes))
+ } else {
+ log.Printf("\n>>> [%s] %v", toolName, arguments)
+ }
+}
+
+// logToolResult logs the result or error of a tool call
+func logToolResult(toolName string, result string, err error) {
+ if err != nil {
+ log.Printf("\n<<< [%s] ERROR: %v", toolName, err)
+ } else if len(result) > 500 {
+ log.Printf("\n<<< [%s] Result: %d bytes (truncated): %.500s...", toolName, len(result), result)
+ } else {
+ log.Printf("\n<<< [%s] Result: %s", toolName, result)
+ }
+}
+
// CreateToolHandler creates an adapter that converts CommandExecutor to the format expected by MCP server
func CreateToolHandler(executor CommandExecutor, cfg *config.ConfigData) func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
return func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+ if cfg.Verbose {
+ logToolCall(req.Params.Name, req.Params.Arguments)
+ }
+
args, ok := req.Params.Arguments.(map[string]interface{})
if !ok {
return mcp.NewToolResultError("arguments must be a map[string]interface{}, got " + fmt.Sprintf("%T", req.Params.Arguments)), nil
}
result, err := executor.Execute(args, cfg)
+
+ if cfg.Verbose {
+ logToolResult(req.Params.Name, result, err)
+ }
+
if err != nil {
return mcp.NewToolResultError(err.Error()), nil
}
@@ -27,11 +59,20 @@ func CreateToolHandler(executor CommandExecutor, cfg *config.ConfigData) func(ct
// CreateResourceHandler creates an adapter that converts ResourceHandler to the format expected by MCP server
func CreateResourceHandler(handler ResourceHandler, cfg *config.ConfigData) func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
return func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+ if cfg.Verbose {
+ logToolCall(req.Params.Name, req.Params.Arguments)
+ }
+
args, ok := req.Params.Arguments.(map[string]interface{})
if !ok {
return mcp.NewToolResultError("arguments must be a map[string]interface{}, got " + fmt.Sprintf("%T", req.Params.Arguments)), nil
}
result, err := handler.Handle(args, cfg)
+
+ if cfg.Verbose {
+ logToolResult(req.Params.Name, result, err)
+ }
+
if err != nil {
return mcp.NewToolResultError(err.Error()), nil
}