Install
$ agentstack add mcp-leszczynskikarol-mcp-server ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
mcp-server
> Self-hosted MCP server for Claude.ai > > Give Claude direct access to your AWS account, SSH into your servers, run shell commands on your laptop, query your databases, and manage PM2 processes — all from a Claude chat. No Claude Code subscription needed. Your keys never leave your machine.
All 10 tools live in your Claude.ai sidebar - no Desktop install, no separate client, just a custom connector.
[](https://github.com/LeszczynskiKarol/mcp-server/actions/workflows/ci.yml) [](LICENSE) [](https://nodejs.org/) [](https://modelcontextprotocol.io/)
What is this?
A small Node.js MCP server you run on your own machine (laptop, desktop, VPS) and connect to Claude.ai (or any MCP client) as a Custom Connector. It exposes a configurable set of tools that let Claude do real work on your infrastructure:
- Run AWS CLI commands using your local profile
- SSH into your servers using your
.pemkeys - Execute shell commands on your local machine
- Write files directly on your local disk (full UTF-8, no shell quoting)
- Hit the GitHub REST API with your Personal Access Token
- Query PostgreSQL databases on remote hosts via SSH
- Inspect PM2 processes on remote servers
- Iterate over very long documents (book editing) that exceed the context window
Architecture:
Claude.ai ──HTTPS──► nginx + cert ──HTTP──► frps ──tunnel──► frpc + node
(cloud) on a VPS :8080 (vhost) (your PC)
│
┌───────────────┬───────────────┬─────────┼─────────┐
▼ ▼ ▼ ▼ ▼
AWS CLI ssh -i *.pem cmd.exe psql via pm2 list
(local) user@host git/npm SSH (remote)
SSH keys, GitHub PAT and AWS credentials never leave your local machine. The tunnel only carries MCP requests and their results.
Why self-host this?
The MCP ecosystem mostly does two things today:
- MCP servers as npm packages that run via
stdioand require Claude Desktop. - Hosted MCP services behind someone else's auth and API limits.
This project is the third option: your own MCP server, your keys, your servers, accessible from web Claude.ai (where you already work). It's a single ~1000-line server.js file that you can read end-to-end in 20 minutes and extend in 5.
Killer features:
- ✅ No Claude Code subscription needed — works with regular Claude.ai (web)
- ✅ OAuth 2.1 with PKCE — proper Claude.ai integration, not a hacky workaround
- ✅ Your keys stay yours — SSH keys, GitHub PATs, AWS creds never leave your machine
- ✅ Self-hostable — Windows, Linux, Mac, anything that runs Node 18+
- ✅ Configurable via JSON — add a new server or new key without touching code
- ✅ One file to read — no framework magic, no hidden config
- ✅ Hardened OAuth — PKCE S256,
client_secretenforcement, refresh token rotation, RFC 7009 revocation, dynamic IP allowlist with auto-enroll, anti-clickjacking
Tools
| Tool | What it does | | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | | aws_cli | Run any aws using your local AWS CLI profile | | ssh_exec | SSH into any host using a key defined in hosts.json | | local_exec | Run any shell command on the local machine (cmd.exe on Windows, /bin/sh elsewhere) | | write_file | Write text content to a local file (overwrite or append). Full UTF-8, no shell quoting issues — preferred over local_exec for any non-trivial file write | | github_api | Make REST API requests to GitHub using your Personal Access Token | | postgres_query | Run psql queries via SSH (sudo -u postgres) on a host from hosts.json | | pm2_status | Show pm2 list and optional logs on a remote server | | sftp_download | Stream a file from a remote host to the local filesystem (uses the persistent SSH pool). Essential when working from Claude.ai web — the cloud sandbox has no native scp | | sftp_upload | Stream a local file to a remote host. Optional POSIX mode flag (e.g. 0755). Parent dir must exist on remote | | book_split | Split a large text file into ~3000-word chunks | | book_chunk | Read one chunk from a directory created by book_split | | book_note | Manage JSON notes for iterative work on long documents |
See it in action
Real examples from a real Claude.ai chat using this MCP server.
Server health snapshot over SSH
> "SSH into my matury server and show me: disk usage (df -h), memory usage (free -h), and uptime. Use a single command and present the output nicely."
Claude composes a single SSH command, parses the multi-section output, and renders disk, memory and uptime as a clean snapshot with key numbers highlighted.
List EC2 instances in any region
> "Using AWS CLI, list all my EC2 instances in eu-central-1 with their IDs, types, and state. Format the result as a clean table."
Claude calls aws_cli with a describe-instances --query ... filter, then parses the JSON and renders it as a markdown table with running/stopped status indicators.
Query PostgreSQL databases over SSH
> "Run a SQL query on my 'panel' PostgreSQL database to count the total users, then on database 'smartedu' count rows in the users table, and tell me how my system is doing."_
Two tools work together here: ssh_exec to enumerate databases when the first guess misses, then postgres_query against the right one.
Claude lists every database on the host, identifies the ones with a users table, and runs COUNT(*) queries in parallel.
Requirements
- Node.js 18+ (tested on 22, 24, 25)
- SSH
.pemkeys locally (for hosts you want to control) - AWS CLI configured locally (
aws configure) — only if you use theaws_clitool - A public HTTPS endpoint — only if you want to expose this to Claude.ai
Quick start
git clone https://github.com/LeszczynskiKarol/mcp-server.git
cd mcp-server
npm install
cp .env.example .env # fill in MCP_PASS and MCP_BASE_URL
cp hosts.example.json hosts.json # add your servers
node server.js
You should see:
Loaded N hosts and M keys from ./hosts.json
MCP server: my-mcp-server
Port: 4500
Static IP allowlist: (none)
Auto-enroll: enabled (TTL 30 days)
Trust proxy: false
MCP listening on :4500
That's it for local. To use this from Claude.ai (web), you need to expose it over HTTPS — see [Exposing publicly](#exposing-publicly-claudeai) below.
Configuration
.env (secrets — never commit)
# REQUIRED
MCP_USER=admin
MCP_PASS=
MCP_BASE_URL=https://your-domain.com
# OPTIONAL — GitHub integration
GITHUB_TOKEN=github_pat_xxxxxxxxxxxxxxxx
GITHUB_OWNER=YourGitHubUsername
# OPTIONAL — server tuning
PORT=4500
TOKEN_TTL_SECONDS=2592000 # 30 days
AUTH_CODE_TTL_SECONDS=600 # 10 minutes
CLIENT_TTL_SECONDS=7776000 # 90 days (unused-client cleanup)
EXEC_BUFFER_MB=10
EXEC_TIMEOUT_SECONDS=120 # per-command timeout
MCP_SERVER_NAME=my-mcp-server
HOSTS_CONFIG=./hosts.json
OAUTH_STATE_FILE=./oauth-state.json
# OPTIONAL — IP allowlist (security)
# Comma-separated static IPs/CIDRs that are always allowed.
# Leave empty if you only want auto-enroll via OAuth login.
MCP_ALLOWED_IPS=
# Trust X-Forwarded-For — use "loopback" when behind FRP/nginx on the same box.
# Other valid values: comma-separated list of trusted proxy IPs/CIDRs, "false"
# (default), or "true" (rejected in production — would let any client spoof XFF).
MCP_TRUST_PROXY=loopback
# Auto-enroll the requesting /24 subnet to allowlist after a successful OAuth login
MCP_AUTO_ENROLL=true
# How long an auto-enrolled subnet stays on the allowlist (default 30 days)
MCP_ENROLL_TTL_SECONDS=2592000
hosts.json (server list — never commit)
{
"hosts": {
"production": {
"ip": "1.2.3.4",
"user": "ubuntu",
"key": "main",
"description": "Main production server"
},
"staging": {
"ip": "5.6.7.8",
"user": "ubuntu",
"key": "main",
"description": "Staging environment"
}
},
"keys": {
"main": "/path/to/main.pem"
}
}
Key paths can use:
- Absolute paths:
D:/keys/server.pemorD:\\keys\\server.pem - Tilde expansion:
~/keys/server.pem(resolved to$HOME/%USERPROFILE%) - Forward slashes work on Windows too
Exposing publicly (Claude.ai)
Claude.ai requires HTTPS. The recommended setup uses FRP (Fast Reverse Proxy) + nginx + Let's Encrypt on a small VPS.
1. DNS record
mcp.your-domain.com A TTL 300
2. nginx vhost on VPS
/etc/nginx/sites-available/mcp.your-domain.com:
server {
server_name mcp.your-domain.com;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# SSE / long-lived MCP connections
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_buffering off;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
listen 80;
}
sudo ln -s /etc/nginx/sites-available/mcp.your-domain.com /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d mcp.your-domain.com
3. FRP server (frps) on VPS
/etc/frp/frps.toml:
bindPort = 7000
vhostHTTPPort = 8080
auth.method = "token"
auth.token = ""
4. FRP client (frpc) on your local machine
frpc-mcp.toml:
serverAddr = ""
serverPort = 7000
auth.method = "token"
auth.token = ""
[[proxies]]
name = "mcp"
type = "http"
localPort = 4500
customDomains = ["mcp.your-domain.com"]
5. Add the connector in Claude.ai
- Open Settings → Connectors → Add custom connector
- URL:
https://mcp.your-domain.com/mcp(with/mcpsuffix!) - OAuth Client ID/Secret: leave empty
- Click Connect → a login form appears → enter
MCP_USERandMCP_PASSfrom your.env - In a chat:
+→ Connectors → toggle this MCP on → start a new conversation (tools attach at chat start)
The first request from a new IP will trigger an OAuth re-login, which adds your /24 subnet to the allowlist for 30 days. This is intentional — see [Security](#security).
Recommended Claude.ai preferences
Even with the right tools installed, Claude will sometimes waste tokens on common failure modes: re-reading files after writing them, copying files into the sandbox "to edit them", lying that a file is on disk when only an artifact was created.
[CLAUDE_PREFERENCES.md](CLAUDEPREFERENCES.md) is a curated set of user preferences that closes those holes. Paste them into Settings -> Profile -> Personal preferences in Claude.ai. They're battle-tested against the exact failure modes that prompted writing some of the tools in this server (especially write_file).
Covers, among other things:
- File-tool hierarchy: always
write_filefor new files, PowerShell for surgical edits, never base64-chunks through cmd.exe - "Trust the write" -- don't re-read a file just to confirm it saved
- Sandbox isolation -- the bash sandbox has no access to your local disk; don't bridge it via public file hosts (uguu.se, transfer.sh, etc.)
- Artifacts vs. MCP file writes -- "Created a file" tiles in the chat UI are NOT saved to your disk
- Raw content rule --
write_filecontent must be raw, not JSON-escaped - Anti-loop limits -- stop after 3 failures on the same problem
- Non-ASCII character handling on Windows (
cmd.execode page gotcha)
Running on Windows
The recommended autostart on Windows uses Task Scheduler + a small .bat with a restart loop. PM2 used to be the recommendation but is currently incompatible with Node 25's named-pipe handling (EPERM \\.\pipe\rpc.sock), so the project has switched to plain Task Scheduler.
One-shot install
After .env and hosts.json are in place, run as administrator:
install-task.bat
This is a small batch wrapper around install-task.ps1. The PowerShell script:
- Generates
start-mcp-hidden.vbs(so thecmd.exewindow stays hidden). - Generates
mcp-task.generated.xmlwith%USERDOMAIN%\%USERNAME%filled in — nothing hard-coded. - Registers a Task Scheduler entry named MCP Server that runs at every logon with
HighestAvailableprivilege.
The action of the task is wscript.exe "...\start-mcp-hidden.vbs", which silently launches start-mcp.bat. That batch keeps node alive with a restart loop:
@echo off
cd /d D:\mcp-server
if not exist logs mkdir logs
:loop
node server.js >> logs\mcp.log 2>&1
echo [%date% %time%] node exited, restarting in 5s >> logs\mcp.log
timeout /t 5 /nobreak >nul
goto loop
Useful commands:
schtasks /run /tn "MCP Server" :: start now
schtasks /query /tn "MCP Server" /v /fo LIST :: status
schtasks /delete /tn "MCP Server" /f :: uninstall
tasklist | findstr node.exe :: check that node is alive
type D:\mcp-server\logs\mcp.log :: read the log
To restart after editing server.js:
taskkill /F /IM node.exe /T
The :loop in start-mcp.bat will restart node within 5 seconds.
FRP tunnel autostart
The tunnel is separate from the MCP server. The simplest setup is a start-mcp.bat (different file in a different directory) that only launches frpc:
@echo off
cd /d C:\Users\YourUser\frp\frp_0.61.1_windows_amd64
start "FRP tunnel mcp" /min frpc.exe -c frpc-mcp.toml
Add it to Task Scheduler the same way (At logon, highest privileges), or shove it in shell:startup.
Without autostart
If you just want to run node manually for development:
cd /d D:\mcp-server
node server.js
Ctrl+C to stop.
Extending
Adding a new host
Edit hosts.json:
{
"hosts": {
"production": {},
"new-server": {
"ip": "5.6.7.8",
"user": "ubuntu",
"key": "main",
"description": "New server"
}
}
}
The server reads hosts.json at boot, so kill node (taskkill /F /IM node.exe /T) and the restart loop will pick up the new config in 5 seconds. In Claude.ai, disconnect and reconnect the connector so it sees the new host in the host parameter dropdown.
Adding a new SSH key
{
"keys": {
"main": "/path/to/main.pem",
"client-x": "~/keys/client-x.pem"
}
}
Adding a new tool
In server.js:
…
## Source & license
This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.
- **Author:** [LeszczynskiKarol](https://github.com/LeszczynskiKarol)
- **Source:** [LeszczynskiKarol/mcp-server](https://github.com/LeszczynskiKarol/mcp-server)
- **License:** MIT
- **Homepage:** https://mcpservers.org/servers/leszczynskikarol/mcp-server
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.