mcp-secure-remote
A stdio-to-remote MCP bridge with mTLS client certificate authentication for secure remote MCP server access.
README
mcp-secure-remote
A stdio ↔ remote bridge for the Model Context Protocol with first-class mTLS (mutual TLS) client-certificate authentication.
Works with any MCP-capable AI agent or IDE — Claude Desktop, Claude Code, Cursor, Windsurf, Cline, Continue, Zed, VS Code MCP extensions, and any custom client that speaks the MCP stdio transport.
Contents
- What it does
- How it works
- Prerequisites
- Install
- Generate or obtain client certificates
- Quick start
- CLI parameters
- Environment variables
- AI agent / IDE integration
- Testing your setup
- Security notes
- Troubleshooting
- Development
- License
What it does
mcp-secure-remote spawns as a local stdio MCP server and forwards every
JSON-RPC message to a remote MCP server over HTTPS. Every outbound request
carries a client certificate you supply, so the remote server sees a
cryptographically authenticated connection — no OAuth dance, no bearer
tokens on the wire, no shared API keys.
┌──────────────┐ stdio ┌────────────────────┐ HTTPS + mTLS ┌───────────────┐
│ MCP client │───────────▶│ mcp-secure-remote │─────────────────▶│ Remote MCP │
│ (Claude, │ │ (this proxy) │ │ server │
│ Cursor, …) │◀───────────│ │◀─────────────────│ │
└──────────────┘ └────────────────────┘ └───────────────┘
How it works
- AI agent launches
mcp-secure-remoteas a local subprocess and talks to it over stdio (the transport every MCP client already supports). - Proxy builds an undici HTTPS dispatcher seeded with your client cert, private key, and trusted CA bundle.
- Proxy opens either a Streamable HTTP or SSE transport to the remote server (configurable). TLS handshake presents the client cert; server validates it before forwarding the MCP session.
- JSON-RPC frames flow bidirectionally. All proxy logging goes to stderr so the stdio channel stays clean.
Prerequisites
- Node.js ≥ 18 (for built-in
fetch,undici, and native TLS features). - A client certificate + private key issued by a CA the remote MCP server trusts (or a PKCS#12 bundle containing both).
- The CA bundle used by the remote server, if it is not in your OS trust store (private/corporate CAs almost always need this).
- The remote MCP server URL (typically
https://host/mcporhttps://host/sse).
Install
# global
npm install -g mcp-secure-remote
# or ephemeral (recommended for agent configs)
npx mcp-secure-remote <server-url> [options]
Generate or obtain client certificates
If your team already issues client certs, skip this section. For local testing, generate a throw-away CA + client cert pair with OpenSSL:
# CA
openssl req -x509 -newkey rsa:4096 -sha256 -days 365 -nodes \
-keyout ca.key -out ca.crt -subj "/CN=dev-ca"
# client key + CSR
openssl req -newkey rsa:4096 -nodes \
-keyout client.key -out client.csr -subj "/CN=dev-client"
# sign client cert with CA
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
-out client.crt -days 365 -sha256
Configure the remote MCP server to require client certs signed by ca.crt.
Point the proxy at client.crt + client.key + the server's CA bundle.
Quick start
Cert + key pair:
npx mcp-secure-remote https://mcp.example.com/mcp \
--tls-cert ./certs/client.crt \
--tls-key ./certs/client.key \
--tls-ca ./certs/ca-bundle.pem
PKCS#12 bundle:
npx mcp-secure-remote https://mcp.example.com/mcp \
--tls-pfx ./certs/client.p12 \
--tls-passphrase "$P12_PASSPHRASE" \
--tls-ca ./certs/ca-bundle.pem
Force SSE transport + pin minimum TLS:
npx mcp-secure-remote https://mcp.example.com/sse \
--transport sse-only \
--tls-min-version TLSv1.3 \
--tls-cert ./certs/client.crt \
--tls-key ./certs/client.key \
--tls-ca ./certs/ca-bundle.pem
CLI parameters
Usage: mcp-secure-remote <server-url> [options]
<server-url> is a positional argument (required). Everything else is a
named flag.
General
| Flag | Type | Default | Description |
|---|---|---|---|
<server-url> |
string (URL) | — | Required. Remote MCP endpoint. Must be https://… unless --allow-http is set. |
--header "Name: value" |
string (repeatable) | — | Extra HTTP header on every outbound request. Repeat the flag for multiple headers. |
--transport <strategy> |
enum | http-first |
Transport negotiation. One of http-first, sse-first, http-only, sse-only. -first variants try the preferred transport then fall back; -only variants never fall back. |
--allow-http |
boolean | false |
Permit plain http:// URLs. Off by default; mTLS is meaningless over HTTP. |
--debug |
boolean | false |
Verbose logging to stderr (parsed args, per-message trace, transport selection). |
-h, --help |
boolean | — | Print usage and exit. |
mTLS / TLS
| Flag | Type | Default | Description |
|---|---|---|---|
--tls-cert <path> |
path | — | PEM client certificate (leaf, optionally followed by chain intermediates). |
--tls-key <path> |
path | — | PEM private key matching --tls-cert. Must be supplied together with --tls-cert. |
--tls-ca <path> |
path | — | PEM CA bundle used to verify the remote server. Required for private CAs not in the OS trust store. |
--tls-pfx <path> |
path | — | PKCS#12 (.pfx / .p12) bundle. Mutually exclusive with --tls-cert/--tls-key. |
--tls-passphrase <value> |
string | — | Passphrase protecting the private key or PFX bundle. Prefer the env var to keep secrets off the command line. |
--tls-servername <name> |
string | URL hostname | SNI override. Use when the server cert's SAN differs from the URL host (e.g. IP literal, internal DNS). |
--tls-min-version <ver> |
enum | Node default | Minimum TLS version: TLSv1.2 or TLSv1.3. |
--tls-insecure-skip-verify, --tls-no-verify |
boolean | false |
Disable server certificate validation. Dev only. Proxy prints a warning when enabled. |
Parameter rules
--tls-certand--tls-keymust appear together.--tls-pfxcannot combine with--tls-cert/--tls-key.--allow-httpis required for anyhttp://URL. Supplying mTLS flags withhttp://triggers a warning (cert is not sent over plain HTTP).- Unknown
--flagscause parse failure with exit code 2. - Argument errors exit with code 2; runtime errors exit with code 1.
Environment variables
Every TLS flag has an env-var fallback so secrets can stay out of shell history and MCP client configs.
| Variable | Equivalent flag | Values |
|---|---|---|
MCP_REMOTE_TLS_CERT |
--tls-cert |
path |
MCP_REMOTE_TLS_KEY |
--tls-key |
path |
MCP_REMOTE_TLS_CA |
--tls-ca |
path |
MCP_REMOTE_TLS_PFX |
--tls-pfx |
path |
MCP_REMOTE_TLS_PASSPHRASE |
--tls-passphrase |
string |
MCP_REMOTE_TLS_SERVERNAME |
--tls-servername |
string |
MCP_REMOTE_TLS_MIN_VERSION |
--tls-min-version |
TLSv1.2 | TLSv1.3 |
MCP_REMOTE_TLS_INSECURE |
--tls-insecure-skip-verify |
1 / true / yes to disable verify |
Precedence: explicit CLI flag overrides env var.
AI agent / IDE integration
Every agent below launches the proxy as a local stdio MCP server. Pattern is identical — only the config file format differs. Use absolute paths; agents do not inherit your shell's working directory.
Claude Desktop
File: ~/Library/Application Support/Claude/claude_desktop_config.json
(macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows).
{
"mcpServers": {
"example": {
"command": "npx",
"args": [
"mcp-secure-remote",
"https://mcp.example.com/mcp",
"--tls-cert", "/absolute/path/client.crt",
"--tls-key", "/absolute/path/client.key",
"--tls-ca", "/absolute/path/ca-bundle.pem"
]
}
}
}
Restart Claude Desktop after editing.
Claude Code (CLI)
Add a server via the claude mcp add command or edit
~/.claude.json / project .mcp.json:
claude mcp add example npx -- mcp-secure-remote \
https://mcp.example.com/mcp \
--tls-cert /absolute/path/client.crt \
--tls-key /absolute/path/client.key \
--tls-ca /absolute/path/ca-bundle.pem
Or in .mcp.json:
{
"mcpServers": {
"example": {
"command": "npx",
"args": [
"mcp-secure-remote",
"https://mcp.example.com/mcp",
"--tls-cert", "/absolute/path/client.crt",
"--tls-key", "/absolute/path/client.key",
"--tls-ca", "/absolute/path/ca-bundle.pem"
]
}
}
}
Cursor
File: ~/.cursor/mcp.json (global) or .cursor/mcp.json (per project).
{
"mcpServers": {
"example": {
"command": "npx",
"args": [
"mcp-secure-remote",
"https://mcp.example.com/mcp",
"--tls-cert", "/absolute/path/client.crt",
"--tls-key", "/absolute/path/client.key",
"--tls-ca", "/absolute/path/ca-bundle.pem"
],
"env": {
"MCP_REMOTE_TLS_PASSPHRASE": "…optional…"
}
}
}
}
Windsurf
File: ~/.codeium/windsurf/mcp_config.json.
{
"mcpServers": {
"example": {
"command": "npx",
"args": [
"mcp-secure-remote",
"https://mcp.example.com/mcp",
"--tls-cert", "/absolute/path/client.crt",
"--tls-key", "/absolute/path/client.key",
"--tls-ca", "/absolute/path/ca-bundle.pem"
]
}
}
}
Cline (VS Code)
Cline reads cline_mcp_settings.json from its extension storage. Open
the Cline MCP panel → "Configure MCP Servers" or edit the file directly:
{
"mcpServers": {
"example": {
"command": "npx",
"args": [
"mcp-secure-remote",
"https://mcp.example.com/mcp",
"--tls-cert", "/absolute/path/client.crt",
"--tls-key", "/absolute/path/client.key",
"--tls-ca", "/absolute/path/ca-bundle.pem"
],
"disabled": false,
"autoApprove": []
}
}
}
Continue (VS Code / JetBrains)
File: ~/.continue/config.json (or config.yaml).
{
"experimental": {
"modelContextProtocolServers": [
{
"transport": {
"type": "stdio",
"command": "npx",
"args": [
"mcp-secure-remote",
"https://mcp.example.com/mcp",
"--tls-cert", "/absolute/path/client.crt",
"--tls-key", "/absolute/path/client.key",
"--tls-ca", "/absolute/path/ca-bundle.pem"
]
}
}
]
}
}
Zed
File: ~/.config/zed/settings.json.
{
"context_servers": {
"example": {
"command": {
"path": "npx",
"args": [
"mcp-secure-remote",
"https://mcp.example.com/mcp",
"--tls-cert", "/absolute/path/client.crt",
"--tls-key", "/absolute/path/client.key",
"--tls-ca", "/absolute/path/ca-bundle.pem"
]
}
}
}
}
Generic MCP client
Any client that spawns stdio MCP servers works. Required pieces:
command:npx(or absolute path tonode+dist/proxy.js).args:["mcp-secure-remote", "<server-url>", …flags].- Optional
envblock forMCP_REMOTE_TLS_*variables to keep secrets out of the args array.
Testing your setup
Bundled mcp-secure-remote-client verifies the TLS handshake and enumerates the
server's capabilities — no real agent needed:
npx mcp-secure-remote-client https://mcp.example.com/mcp \
--tls-cert ./certs/client.crt \
--tls-key ./certs/client.key \
--tls-ca ./certs/ca-bundle.pem
Output: negotiated capabilities + lists of tools, resources, prompts.
Add --debug for per-message tracing.
Security notes
- HTTPS only by default.
http://URLs are refused unless--allow-httpis explicitly set. Proxy additionally warns when mTLS flags are combined withhttp://because the client cert will not be sent. - Skip-verify prints a warning.
--tls-insecure-skip-verifydisables server certificate validation; intended for local dev loops only. - Prefer env vars for passphrases. Anything on the CLI may leak into process listings, shell history, or agent logs.
- Proxy logs to stderr. stdout is reserved for the MCP JSON-RPC stream.
- No credential persistence. Proxy does not write certs, keys, or tokens to disk.
- Pin TLS 1.3 (
--tls-min-version TLSv1.3) when the server supports it, to avoid downgrade-prone 1.2 cipher suites.
Troubleshooting
self signed certificate in certificate chain / unable to verify the first certificate
Point --tls-ca at the PEM bundle that signed the remote server's cert.
OS trust store alone is not enough for private CAs.
Hostname/IP does not match certificate's altnames
Set --tls-servername to the SAN the server cert presents.
error:0909006C:PEM routines:get_name:no start line
Private key file malformed or encrypted. If encrypted, supply
--tls-passphrase (or MCP_REMOTE_TLS_PASSPHRASE).
ERR_SSL_SSLV3_ALERT_HANDSHAKE_FAILURE / alert bad certificate
Server rejected your client cert. Check:
- Cert signed by a CA the server trusts.
- Key matches cert:
openssl x509 -noout -modulus -in client.crt | openssl md5vs.openssl rsa -noout -modulus -in client.key | openssl md5. - Intermediate chain present in
--tls-cert.
Agent shows "failed to start server" with no detail. Run the exact same command in a terminal to see stderr. Agents hide subprocess stderr by default.
Remote transport hangs.
Try --transport sse-only or --transport http-only to isolate which
transport the server actually implements. Add --debug.
already started error in mcp-secure-remote-client.
Upgrade — prior versions double-started the transport. Fixed in current
release.
Development
npm install
npm run typecheck
npm run build
Build artifacts land in dist/. dist/proxy.js and dist/client.js are
the two bin entrypoints.
License
MIT — see LICENSE.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。