# How to test the Glances MCP server This guide covers both the automated test suite (`tests/test_mcp.py`) and manual verification of the MCP server that is now built into Glances. --- ## Prerequisites Install Glances with the `mcp` and `web` extras: ```bash pip install 'glances[web,mcp]' ``` Or, inside the development virtual environment: ```bash pip install -e '.[web,mcp]' ``` --- ## Automated test suite ```bash # From the repository root python -m pytest tests/test_mcp.py -v # Or with the project venv .venv/bin/python -m pytest tests/test_mcp.py -v ``` The suite automatically: 1. Starts a Glances web server with `--enable-mcp` on port **61235** 2. Runs smoke tests (HTTP-level) and full MCP-client tests for every resource and prompt 3. Shuts the server down > Tests are skipped automatically when the `mcp` package is not installed. --- ## Manual testing ### 1. Start Glances with MCP enabled ```bash glances -w --enable-mcp ``` | Endpoint | URL | |------------------|------------------------------------------| | SSE transport | `http://localhost:61208/mcp/sse` | | POST messages | `http://localhost:61208/mcp/messages/` | ### 2. Verify the SSE endpoint with curl ```bash curl -N http://localhost:61208/mcp/sse ``` You should see a continuous `text/event-stream` response with an `endpoint` event pointing to the messages URL. ### 3. Explore resources and prompts with the Python client ```python import asyncio, json from mcp.client.sse import sse_client from mcp import ClientSession from pydantic import AnyUrl MCP_SSE = "http://localhost:61208/mcp/sse" async def demo(): async with sse_client(MCP_SSE) as (read, write): async with ClientSession(read, write) as session: await session.initialize() # List static resources res = await session.list_resources() print("Resources:", [str(r.uri) for r in res.resources]) # List resource templates tmpl = await session.list_resource_templates() print("Templates:", [t.uriTemplate for t in tmpl.resourceTemplates]) # Read all plugin names plugins = await session.read_resource(AnyUrl("glances://plugins")) print("Plugins:", json.loads(plugins.contents[0].text)[:5], "…") # Read CPU stats cpu = await session.read_resource(AnyUrl("glances://stats/cpu")) print("CPU:", json.loads(cpu.contents[0].text)) # Read CPU alert thresholds limits = await session.read_resource(AnyUrl("glances://limits/cpu")) print("CPU limits:", json.loads(limits.contents[0].text)) # List prompts prompts = await session.list_prompts() print("Prompts:", [p.name for p in prompts.prompts]) # Run system health summary prompt health = await session.get_prompt("system_health_summary") print("Health prompt (first 200 chars):", health.messages[0].content.text[:200]) # Run alert analysis with a severity filter alerts = await session.get_prompt("alert_analysis", arguments={"level": "critical"}) print("Alerts:", alerts.messages[0].content.text[:200]) # Top-5 processes report procs = await session.get_prompt("top_processes_report", arguments={"nb": "5"}) print("Processes:", procs.messages[0].content.text[:200]) asyncio.run(demo()) ``` ### 4. Connect Claude CLI (Linux) Add the Glances MCP server with: ```bash claude mcp add --transport sse glances http://localhost:61208/mcp/sse ``` Then verify that the server is recognised: ```bash claude mcp list # glances: http://localhost:61208/mcp/sse ``` Once configured, start a Claude CLI session and try: - *"Is my system healthy ?" It will return: ```markdown Here's your system health summary: CPU — Good - 6% total usage, load average 0.88 (on 16 cores) — very light Memory — Warning - RAM: 76.8% used (12.6 GB / 16.4 GB) - Swap: 95.9% used (3.9 GB / 4.0 GB) — critical, nearly exhausted Disk — Good - Root (/): 40.2% used, 530 GB free Temperatures — Good ┌────────────┬──────┐ │ Sensor │ Temp │ ├────────────┼──────┤ │ CPU │ 54°C │ ├────────────┼──────┤ │ HDD │ 40°C │ ├────────────┼──────┤ │ NVMe │ 36°C │ ├────────────┼──────┤ │ RAM │ 41°C │ ├────────────┼──────┤ │ GPU (i915) │ 49°C │ └────────────┴──────┘ - CPU Fan: 2992 RPM, Video Fan: 2822 RPM — both spinning normally Battery — Good - 96%, currently charging Alerts — None active --- Verdict: mostly healthy, but swap is nearly full. Your system is swapping heavily (12.7 GB swapped out), which suggests RAM pressure. You may want to check for memory-hungry processes and consider killing or restarting any you don't need. ``` - *"What is the current CPU usage on my machine ?"* - *"Are there any active alerts ?"* - *"Show me the top 5 processes by CPU."* - *"How is my disk space looking ?"* > If Glances requires authentication, add a Bearer token or Basic Auth header > via the `headers` key in `settings.json` (see the authentication section > below). ### 5. Connect Claude Desktop Add to `claude_desktop_config.json` (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS, `%APPDATA%\Claude\claude_desktop_config.json` on Windows): ```json { "mcpServers": { "glances": { "url": "http://localhost:61208/mcp/sse" } } } ``` Restart Claude Desktop, then try: - *"What is the current CPU usage on my machine?"* - *"Are there any active alerts?"* - *"Show me the top 5 processes by CPU."* - *"How is my disk space looking?"* --- ## Testing authentication Start Glances with a password: ```bash glances -w --enable-mcp --username # Follow the prompts to create a username/password pair ``` A request without credentials should return **HTTP 401**: ```bash curl -I http://localhost:61208/mcp/sse # HTTP/1.1 401 Unauthorized ``` Connect with Basic Auth: ```python import base64 creds = base64.b64encode(b"myuser:mypassword").decode() headers = {"Authorization": f"Basic {creds}"} async with sse_client(MCP_SSE, headers=headers) as (read, write): ... ``` Or with a JWT Bearer token (see the RESTful API docs for how to obtain one): ```python headers = {"Authorization": "Bearer "} async with sse_client(MCP_SSE, headers=headers) as (read, write): ... ``` The unit tests for the auth middleware live in `tests/test_mcp.py` (`TestGlancesMcpAuthMiddleware`) and do not require a running server.