The setup in one sentence
This pattern runs a stdio MCP server on a small VPS and makes it reachable from Claude via an MCP gateway, without exposing the raw stdio server to the internet or wiring up OAuth.
The structure looks like:
- stdio MCP server behind a supergateway, speaking HTTP on
localhost nginxin front as a reverse proxy- firewall port closed in
ufw, onlynginxexposed - a
systemdservice running under its own user
The unusual part is that the only practical authentication ends up being a long, unguessable URL path.
Why the URL is the only auth
The Claude connectors for MCP give you two options for talking to a remote HTTP endpoint:
- Plain URL
- OAuth
There is no option to attach arbitrary headers. No API key header, no HMAC, nothing you can control at the HTTP level apart from the URL itself.
That constraint rules out the usual pattern of putting a shared secret in a header and validating it at the gateway or proxy. If you want header-based auth, you have to put something in front of the MCP endpoint that can enforce it before traffic ever reaches the connector-facing URL.
For a small, private tool, OAuth is often heavier than the rest of the stack combined. If you do not want to run an OAuth provider or wire into an existing one, the remaining option inside the connector model is to treat the URL path as a secret:
- No public, guessable path like
/mcp - A long, random-looking path that you never paste in public logs or screenshots
In that setup the URL path behaves like a shared secret. If someone has it, they can reach the server. If they do not, they have no obvious handle to brute-force, as long as the path has enough entropy.
I wrote about this earlier in I checked who reads my llms.txt. Almost nobody, and most of "GPTBot" wasn't GPTBot.
This is not ideal security. It is security-by-entropy, with the usual caveats. Given the connector constraints, the trade-off is between:
- Using a secret URL path and tightening everything around it, or
- Implementing and maintaining OAuth for a tiny personal service
What tends to break first: process spawning and memory
The stdio MCP server runs as a process. The gateway talks to it over stdio. HTTP, nginx, and the MCP gateway all think in slightly different units: requests, connections, and sessions.
Many gateway setups lean toward a model of “one process per session.” Each new MCP session from Claude can spin up a fresh process for the tool server. On a small droplet with limited RAM, that is not just a theoretical concern. Multiple active sessions mean multiple processes and real memory pressure.
The mental model that helps here:
- Each connected MCP tool session is a process with non-trivial memory use.
- Parallel chats, models, or experiments can stack up processes.
- On a small machine, enough concurrent sessions can push the kernel into killing processes under memory pressure.
Running the MCP server under its own user as a systemd service limits the blast radius, but does not remove the underlying constraint. If you adopt this pattern, plan for:
- Session limits or caps on concurrent processes
- Monitoring for memory usage
- Occasional restarts or cleanup when sessions accumulate
What tends to break second: logs as a side-channel
When the URL path is the only secret, every place that path appears becomes a potential leak. nginx access logs are a common example.
By default, every request to the secret path lands in access_log. That means:
More on this in my article This week on richardlemon.com — October 16, 2026.
- Anyone with log access sees the exact secret path
- Backups or log shipping can carry the secret into other systems
- Dashboards and log viewers become additional exposure surfaces
A simple mitigation is to turn off access logging for the specific location that fronts the MCP gateway:
location /very-long-secret-path/ {
access_log off;
proxy_pass http://127.0.0.1:PORT;
# other proxy settings
}
Error logs can stay on, because they are useful when something is broken, but the normal request flow should leave as little trace as possible if the URL is acting as your auth.
Other small guardrails
Because the URL path is doing more work than it should, everything around it benefits from being tighter:
- Firewall: the MCP server itself is not reachable directly. The port is closed in
ufw. Onlynginxlistens on the public port. - Separate user: the
systemdservice runs under its own user, with only the permissions it actually needs. - Local-only gateway: the supergateway speaks HTTP on
localhostonly, so there is no second public port to manage.
None of this turns the setup into a high-assurance security boundary, but it does reduce the impact of mistakes in any single layer.
When this setup makes sense
This structure is only a good fit in a narrow set of cases:
- A single user, or a very small, trusted group
- Low-risk data and actions behind the MCP server
- Comfort with treating the URL path as a secret and rotating it if it leaks
- Willingness to watch memory and occasionally restart services or prune sessions
In that scenario, the trade-off is straightforward: a private remote MCP tool without building a full auth system or running extra infrastructure, in exchange for accepting that the URL is doing more security work than it should.
Read also From hobby banknote app to an enterprise AI pitch.
When you should not do this
There are also clear cases where this pattern is the wrong choice:
- Anything where “a long URL” is obviously not an acceptable protection level
- Multiple users or teams depending on the tool for real work
- Environments where you cannot control where the URL ends up: tickets, screenshots, logs, chat, documentation
- Requirements for proper audit trails, revocation, and policy, not just “change the path and tell everyone”
In those situations, invest in real authentication. Use OAuth, or put the MCP gateway behind something that can enforce headers, tokens, or mutual TLS before traffic ever reaches it.
A small droplet with a secret path can be acceptable for a personal tool you are willing to babysit. Once it grows beyond that, the shortcuts stop being shortcuts.
Subscribe to my newsletter to get the latest updates and news