beszel-mcp
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@beszel-mcpWhat alerts are firing right now on any of our servers?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
beszel-mcp
A read-only MCP server for a Beszel monitoring hub. Twelve tools let an agent answer questions about your servers — load, containers, services, disk health, alerts — without knowing anything about Beszel's API.
An independent project, not affiliated with or endorsed by the Beszel maintainers.
Quick start
docker run -p 3000:3000 \
-e BESZEL_URL=https://beszel.example.com \
-e BESZEL_EMAIL=you@example.com \
-e BESZEL_PASSWORD=your-password \
-e MCP_AUTH_TOKEN=some-long-random-string \
ghcr.io/maxbth/beszel-mcpThe MCP endpoint is POST http://localhost:3000/mcp (Streamable HTTP).
stdio
For local clients that speak stdio:
docker run -i --rm \
-e BESZEL_URL=https://beszel.example.com \
-e BESZEL_EMAIL=you@example.com \
-e BESZEL_PASSWORD=your-password \
ghcr.io/maxbth/beszel-mcp --stdioWired into an MCP client — Claude Desktop, Claude Code, or anything else that launches a
subprocess. -i is required (the client talks to the container's stdin) and --rm keeps a
container from being left behind on every restart:
{
"mcpServers": {
"beszel": {
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "BESZEL_URL",
"-e", "BESZEL_EMAIL",
"-e", "BESZEL_PASSWORD",
"ghcr.io/maxbth/beszel-mcp", "--stdio"
],
"env": {
"BESZEL_URL": "https://beszel.example.com",
"BESZEL_EMAIL": "you@example.com",
"BESZEL_PASSWORD": "your-password"
}
}
}
}MCP_AUTH_TOKEN has no role here: there is no port to reach, so the only thing that can talk
to the server is the process that spawned it.
Image tags
Tag | Points at |
| the most recent tagged release |
| the current tip of |
dev moves. It is the right tag for trying unreleased changes and the wrong one
for anything you depend on, since the digest behind it changes without warning.
Pull a version tag for that.
Related MCP server: Linux MCP Server
Tools
Tool | What it answers |
| Can I reach the hub, and what is it running? |
| What machines exist and how are they doing right now? |
| Full hardware and current state for one machine |
| How has this machine behaved over the last hour/day/week? |
| What containers are running, and are any unhealthy? |
| Which container is eating the CPU? |
| What is this container saying? |
| Which systemd units are failed? |
| Why is this unit unhappy? |
| Is any disk dying? |
| What alerts are configured, and which are firing? |
| Is this problem recurring? |
Every tool is read-only. This server never writes to your hub.
Configuration
Variable | Default | Notes |
| — | required |
| — | required unless |
| — | skips login |
|
| authenticate against |
|
| per-request timeout |
|
|
|
|
| the image sets |
|
| |
| unset | when set, a matching bearer token is required |
| empty | comma-separated hostnames (not full origins), added to the localhost defaults on a localhost bind |
|
|
Securing the endpoint with MCP_AUTH_TOKEN
Set MCP_AUTH_TOKEN and every request to /mcp must carry a matching
Authorization: Bearer header. Leave it unset and the endpoint is open to anything that can
reach the port. There is no default value and no built-in fallback — an unset token means no
authentication, not a weak one.
1. Generate one. Any long random string works; these produce 32 bytes of base64:
openssl rand -base64 322. Give it to the server. Prefer a file over an inline -e, which lands in your shell
history and in docker inspect output:
echo "MCP_AUTH_TOKEN=$(openssl rand -base64 32)" >> .env
docker run -p 3000:3000 --env-file .env ghcr.io/maxbth/beszel-mcp3. Give it to the client. In an MCP client's server config:
{
"mcpServers": {
"beszel": {
"type": "http",
"url": "https://mcp.example.com/mcp",
"headers": {"Authorization": "Bearer PASTE_THE_TOKEN_HERE"}
}
}
}To check it by hand — the first should return 401, the second should not:
curl -s -o /dev/null -w '%{http_code}\n' -X POST https://mcp.example.com/mcp
curl -s -o /dev/null -w '%{http_code}\n' -X POST https://mcp.example.com/mcp -H "Authorization: Bearer $MCP_AUTH_TOKEN"Comparison is constant-time, and the token is never written to a log line or an error message. To rotate it, change the value and restart: there is no session state, so nothing survives the restart and every client simply presents the new token on its next request.
MCP_AUTH_TOKEN protects /mcp only — see below for why /health is deliberately open.
Health checks
GET /health returns 200 {"status":"ok"} and is reachable without a bearer token, from
outside the container, through a reverse proxy. It is exempt from the bearer check and from
the Host/Origin validation that guards /mcp, so an uptime monitor or tunnel — Pangolin,
Traefik, a Kubernetes liveness probe, the image's own HEALTHCHECK — can reach it without
being configured as a browser origin or preserving a particular Host header.
That exemption is safe because the route has nothing to protect: it returns no monitoring
data, no configuration and no credential, and it changes nothing. Host/Origin validation
exists to stop a malicious web page using a victim's browser to read data from a server on
their network — and there is no data here to read. Anyone who reaches /health learns only
that something is listening, which the TCP handshake already told them.
/mcp keeps every protection. A valid bearer token still does not buy past Origin
validation, and vice versa.
For Pangolin specifically, point the health check at /health on the same port you expose:
Health check path: /health
Expected status: 200A note on exposure
The image binds 0.0.0.0 so the container is reachable from outside itself. A wildcard
bind gets no automatic Host-header validation, so set MCP_AUTH_TOKEN — otherwise
anything that can reach the port can read every monitored machine, container and disk, and
fetch container logs. The server warns on stderr when you do not. Origin validation is
always on: on a wildcard bind with MCP_ALLOWED_ORIGINS empty, browser requests are rejected
outright and only non-browser clients (which send no Origin) get through.
On a localhost bind (127.0.0.1, localhost, ::1 — the default outside the image) the
localhost hostnames localhost, 127.0.0.1 and [::1] are allowed on top of whatever
MCP_ALLOWED_ORIGINS lists, so browser-hosted clients such as the MCP Inspector work with no
configuration. Non-localhost origins are still rejected.
A note on visibility
Beszel scopes systems per user. If tools report no systems, the configured
account is probably not a member of them — either add it to each system, or run
the hub with SHARE_ALL_SYSTEMS=true.
get_container_logs additionally requires the hub to have container details
enabled (it is, unless the hub sets CONTAINER_DETAILS=false).
Hub compatibility
Built and tested against Beszel 0.19. An older hub still works: 0.19 moved hostname, kernel,
CPU model, core/thread counts, OS and podman out of systems.info into a system_details
collection, so list_systems joins it and falls back to the legacy blob when the collection
answers 404. Decoding likewise accepts both the current byte fields and the deprecated MB/s pairs.
Nothing needs configuring either way.
Development
Requires Bun 1.3+.
bun install
bun test
bun run typecheck # Bun does not typecheck; this is what does
bun run lint:check
bun run devIssues and pull requests are welcome. Run those three commands before opening a PR — CI runs
exactly the same gate, plus a multi-arch Docker build, and fails on any lint warning. Tests are
colocated with what they test (src/config.ts / src/config.test.ts) and every tool is
read-only by contract, so a change that writes to the hub will not be accepted.
License
MIT © Maxime Bertheau
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseAqualityCmaintenanceMCP server for Beszel system monitoring that provides tools to list systems, containers, alerts, and query CPU, memory, disk, and network stats.614MIT
- Alicense-qualityBmaintenanceA read-only MCP server for Linux and macOS system administration, diagnostics, and troubleshooting, supporting remote SSH execution and multi-host management.Apache 2.0
- Flicense-qualityCmaintenanceA read-only MCP server for AWS observability that provides tools to monitor EC2, EKS, RDS, ElastiCache Redis, and CloudWatch.
- Flicense-qualityBmaintenanceRead-only MCP server that provides Zabbix monitoring data to AI agents, enabling investigation of hosts, metrics, incidents, and triggers with deterministic aggregation.
Related MCP Connectors
An MCP server giving access to Grafana dashboards, data and more.
Read-only MCP server for ClassQuill, a tutoring-business-management platform.
Read-only MCP server for wafergraph.com's semiconductor & AI supply-chain data: 30 tools, no auth.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/maxbth/beszel-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server