server-admin-mcp

server-admin-mcp

Enables remote administration of a Debian stage server over SSH, including monitoring, Docker Compose, PostgreSQL, logs, Nginx, firewall, and automation with 82 tools.

Category
访问服务器

README

server-admin-mcp

An MCP server that administers a Debian 13 stage host over SSH: monitoring, Docker Compose, deployment, PostgreSQL, logs, Nginx/TLS, firewall and scheduled automation — 82 tools and 4 workflow prompts.

Stage environments only. This server gives an assistant broad, real access to a machine. Everything destructive sits behind an explicit confirmation step, and a denylist rejects unrecoverable commands outright, but neither is a substitute for pointing it at a host you can afford to rebuild. See Security warnings.


Contents


Installation

Requires Node.js 20 or newer on the machine running the MCP client (not on the server).

npm install
npm run build

This produces dist/index.js, which is the entry point your MCP client launches. The server speaks MCP over stdio; it never opens a port of its own.

Available scripts:

Script Purpose
npm run build Compile TypeScript to dist/
npm run typecheck tsc --noEmit
npm test Unit tests for the safety layer (no server needed)
npm run smoke Call every read-only tool against the real server and report
npm run lint ESLint
npm run format Prettier
npm run inspect Launch the MCP Inspector against the built server

Configuration

Every setting is an environment variable prefixed SRVMCP_. Copy .env.example and fill it in, then pass the values through your client's env block.

Optionally, a JSON file with the same keys in camelCase can be pointed at with SRVMCP_CONFIG_FILE (default ./config.json when it exists). Environment variables always win over the file.

SSH connection

Variable Required Default Description
SRVMCP_HOST yes — Hostname or IP of the stage server
SRVMCP_PORT no 22 SSH port
SRVMCP_USER yes — SSH login user (e.g. deploy)
SRVMCP_PRIVATE_KEY_PATH yes — Private key used to authenticate. Must exist; ~ is expanded
SRVMCP_PASSPHRASE no — Passphrase, if the key is encrypted
SRVMCP_KNOWN_HOSTS_PATH no — known_hosts file used to verify the server's key. Strongly recommended
SRVMCP_INSECURE_SKIP_HOST_KEY_CHECK no false Disables host-key verification. Throwaway lab hosts only

Project layout on the server

Variable Required Default Description
SRVMCP_PROJECT_ROOT no /opt/stage Directory holding the compose file. Always writable; writes here need no extra confirmation
SRVMCP_COMPOSE_FILE no docker-compose.yml Relative to PROJECT_ROOT, or absolute
SRVMCP_ALLOWED_PATHS no /opt/stage,/etc/nginx,/var/log Comma-separated roots for file operations. PROJECT_ROOT is always included

PostgreSQL

Reached by running psql on the server, so SRVMCP_PG_HOST is resolved from the server's point of view (localhost or 127.0.0.1 for a container publishing 5432).

Variable Required Default Description
SRVMCP_PG_HOST no localhost Database host, as seen from the server
SRVMCP_PG_PORT no 5432 Database port
SRVMCP_PG_USER for pg_* — Read-only role used by every read tool
SRVMCP_PG_PASSWORD no — Password for that role
SRVMCP_PG_DATABASE for pg_* — Default database
SRVMCP_PG_WRITE_USER no — Write role. Leave unset to make every database write impossible
SRVMCP_PG_WRITE_PASSWORD no — Password for the write role

Limits and behaviour

Variable Required Default Description
SRVMCP_MAX_OUTPUT_LINES no 200 Line cap per command; excess is removed from the middle
SRVMCP_COMMAND_TIMEOUT_MS no 60000 Default command timeout. The process is killed on the server when it expires
SRVMCP_AUDIT_LOG_PATH no ./audit.log JSONL audit trail (local to the client machine)
SRVMCP_READ_ONLY no false When true, tools with readOnlyHint: false are not registered at all
SRVMCP_CONFIG_FILE no ./config.json Optional JSON config file

Configuration is validated at startup with zod. A missing or wrong value produces a message naming the variable and how to fix it, and the process exits before the transport opens — so the client shows the error instead of hanging.


Preparing the server

The server should connect as a dedicated, restricted user — not root.

1. Create the deploy user

sudo adduser --disabled-password --gecos "" deploy
sudo mkdir -p /home/deploy/.ssh
sudo chmod 700 /home/deploy/.ssh

Add the MCP server's public key:

echo 'ssh-ed25519 AAAA... server-admin-mcp' | sudo tee -a /home/deploy/.ssh/authorized_keys
sudo chmod 600 /home/deploy/.ssh/authorized_keys
sudo chown -R deploy:deploy /home/deploy/.ssh

2. Grant Docker access

sudo usermod -aG docker deploy

Group membership only applies to new sessions — restart the MCP server afterwards so it reconnects.

3. Give the project directory to deploy

sudo chown -R deploy:deploy /opt/stage

4. A narrow sudoers file

Only the binaries the tools actually need, with no password prompt (an interactive prompt would hang a non-interactive session):

sudo visudo -f /etc/sudoers.d/deploy-mcp
deploy ALL=(root) NOPASSWD: /usr/bin/systemctl, /usr/sbin/nginx, /usr/bin/journalctl, \
    /usr/bin/apt-get, /usr/sbin/ufw, /usr/bin/certbot, /usr/bin/install, \
    /usr/bin/crontab, /usr/sbin/logrotate, /usr/bin/systemd-analyze, \
    /usr/bin/fail2ban-client, /bin/ln, /bin/rm, /bin/cat, /usr/bin/openssl
sudo chmod 440 /etc/sudoers.d/deploy-mcp
sudo visudo -c        # verify before logging out

Grant only what you need. Dropping /usr/bin/apt-get disables package upgrades; dropping /usr/sbin/ufw disables firewall changes. Each affected tool then returns an actionable "this needs a sudoers entry" error rather than failing obscurely.

To read logs without sudo, add the user to adm instead:

sudo usermod -aG adm deploy    # /var/log/nginx, journal

5. Create a read-only PostgreSQL role

CREATE ROLE mcp_ro LOGIN PASSWORD 'choose-a-strong-password';
GRANT CONNECT ON DATABASE stage_db TO mcp_ro;
GRANT USAGE ON SCHEMA public TO mcp_ro;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO mcp_ro;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO mcp_ro;

The ALTER DEFAULT PRIVILEGES line matters: without it, tables created by future migrations are invisible to mcp_ro. It applies to objects created by the role that runs it, so run it as the role owning your migrations. pg_fix_schema_permissions applies this whole set for you.

Optionally, a separate write role for pg_write, pg_maintenance, pg_restore_db and pg_fix_schema_permissions:

CREATE ROLE mcp_rw LOGIN PASSWORD 'another-strong-password';
GRANT CONNECT ON DATABASE stage_db TO mcp_rw;
GRANT USAGE, CREATE ON SCHEMA public TO mcp_rw;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO mcp_rw;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
    GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO mcp_rw;

Leaving SRVMCP_PG_WRITE_USER unset makes every database write structurally impossible, which is a good default until you need one.

psql must be installed on the server: sudo apt-get install -y postgresql-client.

6. Record the host key

ssh-keyscan -p 22 stage.example.com >> ~/.ssh/known_hosts

Verify the fingerprint out of band before trusting it, then point SRVMCP_KNOWN_HOSTS_PATH at that file.


Connecting a client

Claude Desktop / Claude Code

claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/, Windows: %APPDATA%\Claude\):

{
  "mcpServers": {
    "server-admin": {
      "command": "node",
      "args": ["/absolute/path/to/server-admin-mcp/dist/index.js"],
      "env": {
        "SRVMCP_HOST": "stage.example.com",
        "SRVMCP_USER": "deploy",
        "SRVMCP_PRIVATE_KEY_PATH": "/home/you/.ssh/id_ed25519",
        "SRVMCP_KNOWN_HOSTS_PATH": "/home/you/.ssh/known_hosts",
        "SRVMCP_PROJECT_ROOT": "/opt/stage",
        "SRVMCP_ALLOWED_PATHS": "/opt/stage,/etc/nginx,/var/log",
        "SRVMCP_PG_HOST": "localhost",
        "SRVMCP_PG_USER": "mcp_ro",
        "SRVMCP_PG_PASSWORD": "...",
        "SRVMCP_PG_DATABASE": "stage_db",
        "SRVMCP_AUDIT_LOG_PATH": "/var/log/server-admin-mcp/audit.log"
      }
    }
  }
}

Use absolute paths — the client's working directory is not yours.

A read-only instance, useful for giving an assistant visibility without any ability to change anything:

{
  "mcpServers": {
    "stage-readonly": {
      "command": "node",
      "args": ["/absolute/path/to/server-admin-mcp/dist/index.js"],
      "env": { "SRVMCP_READ_ONLY": "true", "SRVMCP_HOST": "...", "SRVMCP_USER": "deploy" }
    }
  }
}

Safety model

Four independent layers, applied in this order:

1. Denylist — absolute, cannot be overridden

Checked on the fully assembled command string inside the SSH layer, so no tool can route around it, and confirm: true does not help. It targets commands that are unrecoverable on a remote box with no console:

Rule Blocks
rm-rf-root / rm-rf-root-alt-flags rm -rf / and recursive deletes of system directories, in any flag order
rm-home-wildcard Wildcard deletion of a home directory
filesystem-format mkfs, fdisk, parted, sgdisk, mkswap
dd-to-device dd of=/dev/sda and similar
redirect-to-block-device > /dev/sda, > /dev/nvme0n1
power-state-change shutdown, reboot, halt, poweroff, init 0/6, systemctl reboot
chmod-777-system, chmod-777-root-shorthand chmod -R 777 on / or a system path
chown-recursive-system chown -R on / or a system path
fork-bomb :(){ :|:& };:
system-user-delete userdel/groupdel on system accounts
firewall-flush, firewall-default-drop iptables -F, nft flush ruleset, ufw reset, default-DROP on INPUT
history-wipe history -c, truncating shell history
remote-code-execution curl … | bash, wget … | sh, and the python/perl/ruby/node variants
overwrite-shadow Writing over /etc/shadow, /etc/passwd, /etc/sudoers
ssh-lockout Deleting or truncating authorized_keys
docker-kill-all docker rm $(docker ps -q) and friends

The error names the rule that fired, so it is obvious what triggered and why.

Beyond the denylist there is one more hard guard: sec_firewall_rule refuses to deny or delete the SSH port, because applying that rule would disconnect the server permanently.

2. Confirmation — preview, then execute

Destructive tools take confirm: boolean. Without confirm: true the tool executes nothing and instead returns a preview: the exact command, the resulting state change, what is irreversible, and what to check first.

PREVIEW — nothing has been executed.

Command that would run:
  $ docker volume prune -f

Effect: Deletes every Docker volume not currently attached to a container.
Irreversible: YES — volume data cannot be recovered. A stopped database
container makes its data volume look unused.

Before approving:
  - Run compose_ps first: every stack service must exist, otherwise its
    volume counts as unused.
  - Take a database backup with pg_backup and copy it off the server first.

To execute, call docker_prune again with the same arguments plus confirm: true.

Tools requiring confirmation: sys_service_control (stop only), sys_run_command (when the command mutates state), docker_exec (same), compose_action (down/stop/rm), docker_prune, docker_volume_remove, git_reset, deploy_run, migration_run, pg_write, pg_kill_query, pg_maintenance, pg_restore_db, pg_fix_schema_permissions, nginx_write_config, nginx_enable_site, nginx_disable_site, ssl_renew, sec_firewall_rule, sec_package_upgrade, env_set, all five auto_* tools, and file_write/file_patch outside PROJECT_ROOT.

For free-form commands (sys_run_command, docker_exec) confirmation is demanded when the command contains rm, mv, truncate, kill/pkill, chmod/chown, apt, an overwriting redirect (>, but not 2>&1), a Docker removal, or destructive SQL.

3. Path allowlist — checked twice

Every file operation normalises the path (resolving ..), then verifies it is inside one of SRVMCP_ALLOWED_PATHS. A second check runs on the server after readlink -f, so a symlink planted inside an allowed root cannot be used to reach outside it. A sibling with a shared prefix (/opt/stagemore against /opt/stage) is correctly excluded.

4. Audit log — every call, JSONL

Every invocation is appended to SRVMCP_AUDIT_LOG_PATH, successful or not:

{"ts":"2026-07-25T10:00:00.000Z","tool":"compose_action","args":{"action":"restart","services":["api"],"confirm":true},"status":"ok","command":"docker compose -f /opt/stage/docker-compose.yml restart api","exitCode":0,"durationMs":1234,"confirmed":true}

Writes are queued so concurrent calls cannot interleave, and a failure to write warns on stderr without aborting the tool. Credentials are masked before anything is written.

Read-only mode

With SRVMCP_READ_ONLY=true, tools whose readOnlyHint is false are never registered — 49 tools are exposed instead of 82, and a write tool is not merely refused but absent from tools/list entirely. There is no runtime flag an assistant could flip.

Secret masking

Everything leaving the process — tool text, structured content and audit records — passes through a masker covering password/secret/token/api_key/auth/credential-style keys, Bearer/Basic headers, credentials embedded in URLs, JWTs, PEM private-key blocks and PGPASSWORD=. docker_inspect masks environment values by key name, and env_read masks every value unless a single revealKey is named explicitly.

Database credentials are never placed on a command line: a ~/.srvmcp.pgpass file (mode 0600) is written once and referenced through PGPASSFILE, keeping the password out of ps output entirely.


Tool reference

82 tools. RO marks read-only (available in read-only mode); ! marks tools requiring confirm.

System — sys_* (10)

Tool Description
sys_metrics RO CPU, load, RAM, swap, every filesystem, uptime — one structured snapshot
sys_top_processes RO Heaviest processes by CPU or memory
sys_disk_usage RO Directory sizes under a path, largest first
sys_ports RO Listening TCP/UDP sockets with the owning process
sys_services RO systemd units and their state; onlyFailed to scan for breakage
sys_service_control ! start / stop / restart / reload / status a unit (stop needs confirm)
sys_uptime_reboots RO Uptime, recent reboots, OOM kills from the kernel log
sys_run_command ! Arbitrary shell command, subject to the denylist
sys_run_background Start a long command detached; returns PID and log path
sys_check_background RO Status and log tail of a background job

Docker — docker_*, compose_* (13)

Tool Description
docker_ps RO Containers with state, health, ports, uptime
docker_stats RO Per-container CPU / memory / network / block I/O sample
docker_logs RO Container logs with tail, since and grep filters
docker_inspect RO Restart count, exit code, OOM flag, health, mounts, networks, masked env
docker_exec ! Run a command inside a container
compose_ps RO Compose service states
compose_action ! up/down/restart/build/pull/stop/rm
compose_config RO Fully resolved compose configuration; validates the file
docker_images RO Images with size and age
docker_prune ! Reclaim space: images / volumes / builder / containers / system
docker_networks RO Networks and the containers attached to each
docker_volumes RO Volumes with size and whether anything uses them
docker_volume_remove ! Delete one named volume

Deployment and git — git_*, deploy_*, migration_* (9)

Tool Description
git_status RO Branch, HEAD, ahead/behind (fetches first), working-tree changes
git_pull Fast-forward-only pull
git_checkout Switch to a branch, tag or commit
git_log RO Recent commits
git_reset ! Rollback to a ref; returns the previous SHA so it can be undone
deploy_health_check RO Request URLs from the server; status code and response time
deploy_run ! pull → build → up -d → settle → container check → health check, stopping at the first failure
migration_run ! Apply EF Core / Django / Alembic / custom migrations in a container
migration_status RO Applied vs shipped migrations, reporting pending and drift

PostgreSQL — pg_* (13)

Tool Description
pg_query RO Read-only SQL as the read-only role; auto-LIMIT; writes rejected
pg_write ! INSERT / UPDATE / DELETE / DDL as the write role
pg_databases RO Databases with owner, encoding and size
pg_tables RO Tables with estimated rows, table and index size
pg_table_schema RO Columns, indexes and foreign keys of one table
pg_activity RO Active backends, query age, lock waits
pg_kill_query ! Cancel (default) or terminate a backend
pg_slow_queries RO Slowest statements via pg_stat_statements, with setup guidance if absent
pg_maintenance ! VACUUM / VACUUM FULL / ANALYZE / REINDEX
pg_backup pg_dump -Fc to a directory; returns path and size
pg_restore_db ! Restore a custom-format dump
pg_permissions RO Roles, schema ACL and table grants
pg_fix_schema_permissions ! Apply the standard grant set incl. ALTER DEFAULT PRIVILEGES (the PG15+ trap)

Logs — log_* (6)

Tool Description
log_journal RO journalctl with unit/time/priority/grep filters, repeated messages grouped
log_tail RO Tail a log file with optional (invertible) grep
log_nginx_errors RO Nginx errors grouped by message, upstream failures isolated
log_nginx_access_summary RO Requests by status, busiest paths, noisiest IPs, slowest requests, error rate
log_search RO grep across several files with context
log_elasticsearch_query RO POST a search body to Elasticsearch from the server

Nginx and TLS — nginx_*, ssl_* (9)

Tool Description
nginx_list_sites RO vhosts with enabled state, server names, listens, upstreams
nginx_read_config RO Read a config file from /etc/nginx
nginx_write_config ! Backup → write → nginx -t → automatic rollback on failure
nginx_test RO nginx -t
nginx_reload Test, then reload only if the test passes
nginx_enable_site ! Symlink into sites-enabled, validated, auto-reverted on failure
nginx_disable_site ! Remove the symlink; the vhost file is untouched
ssl_cert_info RO Certificate issuer, domains, expiry, days remaining
ssl_renew ! certbot renew, dry run by default

Security — sec_* (9)

Tool Description
sec_firewall_status RO ufw / nftables / iptables rules, plus an SSH-reachability sanity check
sec_firewall_rule ! allow / deny / delete a ufw rule (refuses to touch the SSH port)
sec_fail2ban_status RO Jails, failure counts, banned addresses
sec_ssh_attempts RO Failed logins grouped by source, successful logins listed separately
sec_users RO Accounts, shells, sudo membership, last login, locked state
sec_ssh_keys RO authorized_keys per account — fingerprints and comments only
sec_package_updates RO Upgradable packages, security ones flagged, reboot-required state
sec_package_upgrade ! Non-interactive apt upgrade, keeping existing config files
sec_cron_jobs RO User crontabs, /etc/cron.*, and systemd timers

Files — file_*, env_* (8)

Tool Description
file_read RO Read a file over SFTP (1 MB cap), optionally a line range
file_write ! Atomic write with automatic timestamped backup
file_patch ! Replace one unique string; ambiguous matches are refused
file_list RO Directory listing with size, owner, permissions, mtime
file_search RO Find files by name glob and/or content regex
file_stat RO Metadata for one path, including symlink target
env_read RO dotenv keys with masked values unless one revealKey is named
env_set ! Add or update one key, preserving comments and ordering

Automation — auto_* (5)

Tool Description
auto_create_cron ! Add a cron job (schedule validated first), tagged # SRVMCP
auto_remove_cron ! Remove matching cron lines and their marker comment
auto_create_systemd_unit ! Write a unit, daemon-reload, verify, optionally enable and start
auto_setup_backup ! pg_dump script + retention + cron, test-run once before scheduling
auto_setup_logrotate ! logrotate config, validated with a dry run and removed if invalid

Prompts

Prompt Arguments What it does
diagnose_service service State → history → logs → resources → connectivity → external health, ending in a stated root cause with evidence
daily_health_report — Full read-only sweep, reported by urgency rather than by category
pre_deploy_check services? Disk, git cleanliness, incoming commits, current health, compose validity, database, backups → GO / NO-GO plus the rollback SHA
investigate_5xx since? Correlates Nginx 5xx with backend logs, resources and database over one window

Testing

Unit tests — no server required

npm test

134 assertions covering the denylist (each dangerous command individually, plus a set of safe ones that must not be blocked), the path allowlist including traversal and shared-prefix cases, middle-out truncation, control-character sanitising, secret masking, shell quoting, read-only SQL classification (including SELECT 1; DROP TABLE users), and CSV parsing.

Schema inspection

npm run inspect

Confirms all 82 tools list with valid input and output schemas and complete annotations.

Smoke test — read-only, against the real server

npm run smoke              # every read-only tool
npm run smoke -- sys_ pg_  # only these prefixes

Calls each read-only tool in turn and prints a pass/fail table. It never invokes a tool whose readOnlyHint is false, so it is safe against a live stage box.


Security warnings

This server grants an assistant broad, real access to a machine. Treat it accordingly.

  • Stage only. Do not point it at production. Nothing here distinguishes the two for you.
  • Never connect as root. Use a dedicated user with the narrow sudoers file above. The safety layers are defence in depth, not a replacement for least privilege.
  • Prefer read-only. SRVMCP_READ_ONLY=true removes 33 write tools from the interface entirely. Run a second, writable instance only when you need it.
  • Leave SRVMCP_PG_WRITE_USER unset until a database write is genuinely required — that makes pg_write, pg_restore_db and pg_maintenance structurally unavailable.
  • Set SRVMCP_KNOWN_HOSTS_PATH. Without it the first connection is trusted blindly, which makes a man-in-the-middle attack possible on the first connect.
  • Review the audit log. It records every call with arguments and exit codes. It is the only record of what was actually done:
    tail -f audit.log | jq 'select(.confirmed == true)'          # everything executed with confirmation
    jq 'select(.status == "error")' audit.log                     # everything that failed
    jq -r '[.ts, .tool, .command] | @tsv' audit.log               # a readable timeline
    
  • Keep SRVMCP_ALLOWED_PATHS tight. Every added root widens what can be read and written. Adding / defeats the mechanism.
  • The private key is the whole boundary. Anyone who can read it, and the environment holding the passphrase, has the same access this server does.
  • Backups made by pg_backup and auto_setup_backup stay on the same host as the database. They protect against a bad migration, not against losing the server. Arrange an off-box copy separately.

Troubleshooting

SSH authentication to deploy@host:22 failed (permission denied)

The key is wrong or not authorised. Reproduce outside the MCP server:

ssh -i ~/.ssh/id_ed25519 -p 22 deploy@stage.example.com

Check the public half is in /home/deploy/.ssh/authorized_keys, and that modes are 700 on ~/.ssh and 600 on authorized_keys — sshd silently refuses looser permissions.

Host key verification failed

Either the host is not in known_hosts yet, or its key changed. Verify the fingerprint out of band, then:

ssh-keyscan -p 22 stage.example.com >> ~/.ssh/known_hosts

A changed key on a host you have connected to before deserves investigation before you clear the old entry.

Cannot talk to the Docker daemon: permission denied on /var/run/docker.sock

sudo usermod -aG docker deploy

Then restart the MCP server — group membership only applies to new sessions.

The command needs sudo but the account cannot use it without a password

The binary is missing from /etc/sudoers.d/deploy-mcp. Add it, then sudo visudo -c to verify. A prompting sudo hangs a non-interactive session, which is why sudo -n is used throughout.

The command timed out after 60s

Either raise timeoutMs for that call, or start the work in the background:

sys_run_background(command: "...")   ->  sys_check_background(pid: 1234)

Builds, migrations, large upgrades and pg_dump on a big database are the usual candidates. deploy_run already allows 30 minutes for the build step.

Cannot reach PostgreSQL at localhost:5432

SRVMCP_PG_HOST is resolved from the server's point of view. If the database runs in Docker, check docker_ps for the published port — usually 127.0.0.1:5432. If it is not published at all, run queries inside the container with docker_exec instead.

Refused: path "/etc/shadow" is outside the configured allowlist

Working as intended. Add the root to SRVMCP_ALLOWED_PATHS only if you genuinely want it reachable.

permission denied for schema public after upgrading to PostgreSQL 15

PostgreSQL 15 removed the implicit CREATE grant on public from PUBLIC. Fix it in one call:

pg_fix_schema_permissions(user: "app_user", schema: "public", confirm: true)

A tool is missing from tools/list

Check whether SRVMCP_READ_ONLY=true — write tools are not registered in that mode. The server logs N tools registered, M withheld (read-only mode) to stderr at startup.

Nginx logs are unreadable

They are root-owned by default:

sudo usermod -aG adm deploy

The audit log is not being written

The server warns once on stderr and keeps working. Check that the directory exists and is writable by whoever runs the MCP client.


Design decisions

Choices made where the specification left room, recorded here as required.

Project at the repository root, not in a server-admin-mcp/ sub-directory. The working directory was already the project root; nesting would have added a level for no benefit. The package is still named server-admin-mcp.

Two extra source files beyond the specified layout. src/utils/shell.ts holds shell quoting, and src/safety/paths.ts holds the path allowlist — both are separate concerns that would otherwise have been duplicated across nine tool modules. src/tools/registry.ts, src/tools/types.ts and src/tools/context.ts hold the shared registration, envelope and dependency plumbing that keeps each tool module free of boilerplate. src/ssh/knownHosts.ts implements host-key verification, which ssh2 leaves to the caller.

Manual POSIX quoting instead of shell-quote. Single-quoting is five lines; for a server with this much reach, one fewer dependency to audit was worth more than the package.

PGPASSFILE instead of PGPASSWORD. The specification asked for the password to be passed by environment rather than visibly in the command. Because the environment is exported inside the remote sh -c string, PGPASSWORD would still appear in ps output. A ~/.srvmcp.pgpass file at mode 0600, referenced through PGPASSFILE, keeps it out of process listings entirely.

Uniform output envelope. Every tool's outputSchema carries status and message alongside its own fields, with tool-specific fields optional. That is what lets a confirm preview validate against the same schema as a successful run, so previews are ordinary results rather than errors.

Statement batching rejected in pg_query. Checking only the first keyword would let SELECT 1; DROP TABLE users through. String literals and comments are stripped before keyword matching so a value like 'delete from users' is not mistaken for a write.

Remote timeout(1) plus a local timer. Killing the SSH channel alone leaves the remote process running. The command is wrapped in timeout -k 5s, so it dies on the server; the local timer is only a backstop for a wedged channel.

Atomic writes and double path checking. Files are written to a temporary sibling and renamed, so an interrupted transfer cannot leave a half-written config. Paths are checked textually and again on the server after readlink -f, so a symlink inside an allowed root cannot escape it.

ssl_renew defaults to dryRun: true. Let's Encrypt rate-limits real issuance; a dry run costs nothing and catches a broken challenge setup.

sec_firewall_rule will not touch the SSH port. Not a confirmation — a refusal. Applying such a rule would disconnect the server with no way back in, so it must be done from a console session.

auto_setup_backup test-runs the script before scheduling it. A backup that only fails at 3 a.m. is worse than no backup, so nothing is added to cron until one real dump has succeeded.

Extra tools beyond the specification. file_stat (metadata for a single path, useful when diagnosing permissions) and migration_status support for Alembic alongside EF Core and Django.

docker_exec requires confirmation for mutating commands, matching the rule the specification set for sys_run_command; it is the same class of escape hatch.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选