Files
glances/tests/HOW_TO_TEST_MCP.md
2026-02-21 21:18:27 +01:00

258 lines
6.9 KiB
Markdown

# 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 <your_jwt_token>"}
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.