Linux system control MCP server over Streamable HTTP with Bearer token auth
Project description
Linux MCP Server
A Python MCP server that exposes full Linux system control to AI clients (Claude Desktop, Claude Code, Cursor, Gemini CLI, etc.) over Streamable HTTP.
Features
- 6 MCP tools:
bash_execute,read_file,write_file,edit_file,glob,grep - Per-token permissions: read-only (
ro) or read-write (rw) roles - Audit logging: JSON Lines audit trail with error details for all tool invocations
- Application logging: errors and warnings output to stderr (captured by journald)
- Protected paths: MCP's own files are protected from tool access
- Multi-user auth: Bearer token authentication with per-token management
- Admin API: create/revoke tokens without restarting the server
- Streamable HTTP transport: stateless mode, each request is independent (no session issues on reconnect)
Requirements
- Python 3.11+
ripgrep(rg) — optional but recommended for fastergreptool (falls back to Python regex)
Install
Requires Python 3.11+ on Linux.
pipx install algony-mymcp
The PyPI distribution name is algony-mymcp (the bare name mymcp is reserved
on PyPI). After install the command and the Python import path are still
plain mymcp.
Plain pip works too (a venv is recommended):
python3 -m venv ~/.local/share/mymcp-env
~/.local/share/mymcp-env/bin/pip install algony-mymcp
ln -s ~/.local/share/mymcp-env/bin/mymcp ~/.local/bin/mymcp
Quick try (foreground, no system service)
mymcp serve
mymcp prints a temporary admin and rw token to stderr, listens on
127.0.0.1:8765, and discards both tokens on exit.
Production install (systemd)
sudo mymcp install-service --yes
sudo systemctl start mymcp
This writes /etc/mymcp/.env, generates an admin token (printed once),
optionally generates a metrics token, installs /etc/systemd/system/mymcp.service,
sets up logrotate for /var/log/mymcp/audit.log, and (by default) installs
ripgrep for fast file search.
Useful flags: --port 9000, --bind 127.0.0.1, --config-dir, --log-dir,
--service-user mymcp (run as a restricted user), --no-metrics,
--no-audit, --skip-ripgrep.
Upgrade
pipx upgrade algony-mymcp
sudo systemctl restart mymcp
Air-gapped install
Each GitHub Release ships a mymcp-X.Y.Z-offline-bundle.tar.gz containing
all wheels and ripgrep binaries:
tar xzf mymcp-2.0.0-offline-bundle.tar.gz
cd mymcp-2.0.0-offline-bundle
sudo ./install-offline.sh
sudo mymcp install-service --yes
Upgrading from 1.x to 2.0
Breaking changes:
- Environment variable prefix renamed:
MCP_*→MYMCP_*(no compat shim). - Install layout:
/opt/mymcp/(1.x) →/etc/mymcp/(2.0). Code is now managed bypipx, not unpacked into/opt/mymcp/. - Install method:
git clone + deploy/install.sh→pipx install algony-mymcp.
One-line migration:
pipx install algony-mymcp
sudo mymcp migrate-from-legacy
sudo rm -rf /opt/mymcp # after verifying the new service is healthy
mymcp migrate-from-legacy reads /opt/mymcp/.env, rewrites MCP_* keys to
MYMCP_*, copies tokens.json, installs the new systemd unit, and restarts
the service. Pass --dry-run to see what it would do without making changes.
The legacy deploy/install.sh and deploy/upgrade.sh scripts remain in the
repository through the 2.0.x lifecycle for users who can't migrate yet.
Configuration
mymcp install-service writes /etc/mymcp/.env. The serve command also
honors --env-file PATH, MYMCP_ENV_FILE, and (in dev) ./.env.
Core
| Variable | Default | Description |
|---|---|---|
MYMCP_ADMIN_TOKEN |
(required for /admin) | Admin token for managing user tokens |
MYMCP_METRICS_TOKEN |
(empty = disabled) | Bearer for /metrics endpoint |
MYMCP_HOST |
0.0.0.0 |
Bind address |
MYMCP_PORT |
8765 |
Listen port |
MYMCP_TOKEN_FILE |
/etc/mymcp/tokens.json |
Token store path |
MYMCP_PROTECTED_PATHS |
(empty) | Additional protected paths, comma-separated |
MYMCP_SHUTDOWN_GRACE_SEC |
5 |
Seconds to wait for in-flight bash children on SIGTERM |
Audit Logging
| Variable | Default | Description |
|---|---|---|
MYMCP_AUDIT_ENABLED |
false |
Enable audit logging |
MYMCP_AUDIT_LOG_DIR |
/var/log/mymcp |
Audit log directory (auto-protected) |
MYMCP_AUDIT_MAX_BYTES |
10485760 |
Max audit log file size before rotation (10MB) |
MYMCP_AUDIT_BACKUP_COUNT |
5 |
Number of rotated log files to keep |
Tool Limits
All limits are configurable via environment variables. Default values work well for most use cases.
| Variable | Default | Description |
|---|---|---|
MYMCP_BASH_MAX_OUTPUT_BYTES |
102400 |
bash stdout/stderr default cap (100KB) |
MYMCP_BASH_MAX_OUTPUT_BYTES_HARD |
1048576 |
bash output hard cap (1MB) |
MYMCP_READ_FILE_DEFAULT_LIMIT |
2000 |
read_file default lines per request |
MYMCP_READ_FILE_MAX_LIMIT |
50000 |
read_file max lines per request |
MYMCP_READ_FILE_MAX_LINE_BYTES |
32768 |
Max bytes per line before truncation (32KB) |
MYMCP_WRITE_FILE_MAX_BYTES |
10485760 |
write_file max size (10MB) |
MYMCP_EDIT_STRING_MAX_BYTES |
1048576 |
edit_file max old/new string size (1MB) |
MYMCP_GLOB_MAX_RESULTS |
1000 |
Max file paths returned by glob |
MYMCP_GREP_DEFAULT_MAX_RESULTS |
500 |
grep default max matches |
MYMCP_GREP_MAX_RESULTS |
5000 |
grep hard max matches |
Managing Tokens
The mymcp token subcommands operate on the local token store directly (no
admin API call required). They read /etc/mymcp/.env by default; use
MYMCP_ENV_FILE=... to point elsewhere.
# List all tokens (admin/metrics state + ro/rw entries)
sudo mymcp token list
# Create a read-only token
sudo mymcp token add --name my-claude-desktop --role ro
# Create a read-write token
sudo mymcp token add --name my-admin-client --role rw
# Revoke
sudo mymcp token revoke tok_abc123
# Rotate the admin or metrics token (rewrites .env)
sudo mymcp token rotate-admin
sudo mymcp token rotate-metrics
# Disable the /metrics endpoint by emptying the metrics token
sudo mymcp token disable-metrics
The HTTP /admin/* API still works for clients that need to manage tokens
remotely; it requires Authorization: Bearer <MYMCP_ADMIN_TOKEN>.
Connecting Clients
Claude Desktop / Cursor
Add to MCP settings:
{
"mcpServers": {
"linux-server": {
"type": "streamableHttp",
"url": "http://your-server:8765/mcp",
"headers": {
"Authorization": "Bearer tok_abc123"
}
}
}
}
Claude Code
claude mcp add linux-server \
--transport streamable-http \
--url http://your-server:8765/mcp \
--header "Authorization: Bearer tok_abc123"
MCP Tools
| Tool | Permission | Description |
|---|---|---|
bash_execute |
rw | Run any shell command |
read_file |
ro | Read file with line numbers and pagination |
write_file |
rw | Create or overwrite a file (max 10MB) |
edit_file |
rw | Replace a string in a file |
glob |
ro | Find files by pattern |
grep |
ro | Search file contents with regex |
Logging
Audit Log
When enabled (MYMCP_AUDIT_ENABLED=true), all tool invocations are logged to <MYMCP_AUDIT_LOG_DIR>/audit.log in JSON Lines format:
{"ts":"2026-04-10T15:30:22Z","token_name":"my-client","role":"rw","ip":"203.0.113.5","tool":"bash_execute","params":{"command":"apt update"},"result":"ok","duration_ms":1523}
Error entries include error_code and error_message:
{"ts":"2026-04-10T15:31:00Z","token_name":"ro-client","role":"ro","ip":"203.0.113.5","tool":"read_file","params":{"file_path":"/var/log/mymcp/audit.log"},"result":"error","error_code":"ProtectedPath","error_message":"Access denied: path is within protected directory","duration_ms":0}
Logs rotate automatically (default 10MB with 5 backups).
Application Log
Tool errors and warnings are also output to stderr, which is captured by journald when running as a systemd service:
journalctl -u mymcp -f
Protected Paths
MCP automatically protects its own installation directory and audit log directory from access via file tools (read_file, write_file, edit_file, glob, grep). This prevents AI clients from reading tokens, modifying server code, or tampering with audit logs.
Add extra protected paths via MYMCP_PROTECTED_PATHS=/path/one,/path/two.
Note: bash_execute is not subject to path protection — use ro tokens for untrusted clients.
Monitoring
mymcp provides a Prometheus-compatible /metrics endpoint.
Configuration
- Metrics Token: Set the
MYMCP_METRICS_TOKENenvironment variable to secure the endpoint. - Prometheus Scrape: Configure Prometheus to scrape
/metricsusing the bearer token.scrape_configs: - job_name: mymcp metrics_path: /metrics authorization: type: Bearer credentials: <MYMCP_METRICS_TOKEN> static_configs: - targets: ["your-host:8765"]
Dashboard
A pre-built Grafana dashboard is available in deploy/grafana/mymcp-dashboard.json. It provides visualization for:
- Tool call rates and error status.
- p50/p95/p99 latency per tool.
- HTTP request statistics.
- System health (CPU, Memory, FDs).
See deploy/grafana/README.md for detailed import and setup instructions.
Testing
# Run all tests (excludes benchmarks)
python -m pytest tests/ -v --benchmark-disable
# Run with coverage report
python -m pytest tests/ -v --cov=. --cov-branch --cov-report=term-missing --benchmark-disable
# Run benchmark tests only
python -m pytest tests/test_benchmark.py --benchmark-only -v
# Save benchmark baseline for comparison
python -m pytest tests/test_benchmark.py --benchmark-save=baseline
# Run mutation testing
python -m mutmut run --use-coverage
python -m mutmut results
# Run load tests (start server first: mymcp serve)
export MYMCP_TEST_TOKEN=<your-rw-token>
locust -f tests/loadtest/locustfile.py --host http://localhost:8765
Test Dimensions
| Dimension | Tool | Target |
|---|---|---|
| Line coverage | pytest-cov | 97%+ |
| Branch coverage | pytest-cov --cov-branch | tracked |
| Integration tests | httpx ASGITransport | full auth->tool->audit chain |
| Boundary analysis | pytest | all parameter edge cases |
| Performance benchmarks | pytest-benchmark | per-function timing |
| Load testing | locust | multi-user concurrency |
| Mutation testing | mutmut | 80%+ score |
Security Note
This server grants system access to AI clients. Security measures:
- Permissions: New tokens default to
ro(read-only). Only grantrwto trusted clients. - Audit: Enable audit logging to track all tool invocations.
- Protected paths: Server files are automatically protected from tool access.
- Network: Run behind a firewall and consider TLS (e.g. via nginx reverse proxy).
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file algony_mymcp-2.0.1.tar.gz.
File metadata
- Download URL: algony_mymcp-2.0.1.tar.gz
- Upload date:
- Size: 82.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
905193baf9e58afe1aacbb3d7ec8bca560adce5b02f0dbbca34e77436c386504
|
|
| MD5 |
acad132ef30fed6016137e1bca0d5454
|
|
| BLAKE2b-256 |
dfd72b41604773ce4ec2f6d7881684578bb4bb27b0e3451edc605da07a883c36
|
Provenance
The following attestation bundles were made for algony_mymcp-2.0.1.tar.gz:
Publisher:
release.yml on algony-tony/mymcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
algony_mymcp-2.0.1.tar.gz -
Subject digest:
905193baf9e58afe1aacbb3d7ec8bca560adce5b02f0dbbca34e77436c386504 - Sigstore transparency entry: 1397486792
- Sigstore integration time:
-
Permalink:
algony-tony/mymcp@bb64c092428f183dbf983a7fdbda86c5aa88f7b4 -
Branch / Tag:
refs/tags/v2.0.1 - Owner: https://github.com/algony-tony
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@bb64c092428f183dbf983a7fdbda86c5aa88f7b4 -
Trigger Event:
push
-
Statement type:
File details
Details for the file algony_mymcp-2.0.1-py3-none-any.whl.
File metadata
- Download URL: algony_mymcp-2.0.1-py3-none-any.whl
- Upload date:
- Size: 34.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fd2f2acc91a9402a148a43093009d0b772abc5595baaf3dfc0ea1e5dfd4b7fc8
|
|
| MD5 |
ccb0ef1226c9b428ea698347d4195496
|
|
| BLAKE2b-256 |
692625207f7a29c00fa1e19e2babf809dc52aadc9c0f92db0f8086003d02d24e
|
Provenance
The following attestation bundles were made for algony_mymcp-2.0.1-py3-none-any.whl:
Publisher:
release.yml on algony-tony/mymcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
algony_mymcp-2.0.1-py3-none-any.whl -
Subject digest:
fd2f2acc91a9402a148a43093009d0b772abc5595baaf3dfc0ea1e5dfd4b7fc8 - Sigstore transparency entry: 1397486824
- Sigstore integration time:
-
Permalink:
algony-tony/mymcp@bb64c092428f183dbf983a7fdbda86c5aa88f7b4 -
Branch / Tag:
refs/tags/v2.0.1 - Owner: https://github.com/algony-tony
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@bb64c092428f183dbf983a7fdbda86c5aa88f7b4 -
Trigger Event:
push
-
Statement type: