Skip to main content

mcp-reverse-proxy-client

Outbound-only reverse proxy for MCP servers. It reads a project's mcp.json, launches each configured server as a local subprocess, and opens one authenticated, persistent outbound WebSocket tunnel per server to an MCP gateway (Context Forge). No inbound port is ever opened — this is the mirror image of the usual mcp-proxy tools, which run a local HTTP server that something else has to reach.

Two pieces, published separately:

  1. install.sh — a POSIX-shell bootstrap you curl | sh. It makes sure npx and uvx exist, then hands off to the real tool via uvx.
  2. This PyPI package — the actual reverse-proxy client, run via uvx mcp-reverse-proxy-client ....

Quick start

curl -fsSL https://akospapp.github.io/agent-reverse-proxy/install.sh | sh -s -- \
  --gateway-url https://gateway.example.com \
  --token "$MY_GATEWAY_TOKEN"

or, if you already have uv installed:

uvx mcp-reverse-proxy-client --gateway-url https://gateway.example.com --token "$TOKEN"

CLI

Flag Env var Required Notes
--gateway-url REVERSE_PROXY_GATEWAY yes Same env var name Context Forge's own mcpgateway.reverse_proxy uses, so a value exported for that tool works unchanged here.
--token REVERSE_PROXY_TOKEN yes Same convention as above.
--config — no Path to the MCP server config. Defaults to ./mcp.json, resolved strictly relative to the current working directory — no upward directory search.
--reconnect-delay — no Initial reconnect backoff in seconds (default 1.0). Doubles on each attempt, capped at 60s — same shape Context Forge's client uses.
--max-retries — no Per-server reconnect attempts before giving up, 0 = infinite (default).
--keepalive — no Heartbeat interval in seconds (default 30).
--log-level — no DEBUG/INFO/WARNING/ERROR/CRITICAL (default INFO).

Any other argument is rejected with an error rather than silently ignored, since install.sh forwards its own arguments to this tool blindly.

Config file

Two shapes are recognized in the same mcp.json:

1. Plain command entries (default)

The widely-used shape from Claude Desktop / Cursor / VS Code:

{
  "mcpServers": {
    "git": {
      "command": "uvx",
      "args": ["mcp-server-git"],
      "env": { "SOME_VAR": "value" }
    }
  }
}

Each entry is spawned directly as command args... with env merged on top of the current environment.

2. FastMCP-style entries

Discriminator rule (exact): if an entry contains a top-level source key, it is treated as a FastMCP config instead of a plain command. A config file can also be a bare FastMCP config itself (no mcpServers wrapper) if its top level has a source key — in that case it's treated as a single server named after its name field or the file's stem.

{
  "mcpServers": {
    "my-fastmcp-server": {
      "source": { "path": "server.py", "entrypoint": "mcp" },
      "environment": { "dependencies": ["httpx"] },
      "deployment": { "transport": "stdio" }
    }
  }
}

FastMCP-style entries are launched with fastmcp run <generated-config> (via uvx), letting FastMCP itself resolve the environment/source blocks with uv. Only stdio transport is tunneled — the wire protocol only bridges stdin/stdout, so deployment.transport is forced to stdio regardless of what the entry declares (a warning is logged if it declared something else).

Protocol

Each server's tunnel is wire-compatible with Context Forge's own mcpgateway.reverse_proxy: it connects to <gateway-url>/reverse-proxy/ws, authenticates with Authorization: Bearer <token> and X-Session-ID, and exchanges the same JSON envelope shape ({"type": "register" | "request" | "response" | "notification" | "heartbeat" | "unregister" | "error", "sessionId": ..., ...}). All servers in a config run concurrently from a single invocation; one tunnel reconnecting or dying does not affect the others. SIGINT/SIGTERM trigger a clean shutdown: each tunnel unregisters, closes its WebSocket, and terminates (then kills, if needed) its subprocess — no orphaned processes.

Shell installer (install.sh)

Responsibilities, in order:

  1. If npx and uvx are both already on PATH, skip straight to step 4.
  2. Else, if nix is on PATH, use nix shell nixpkgs#nodejs nixpkgs#uv to provide whatever's missing for this invocation only — nothing is permanently installed.
  3. Else, install natively without sudo or touching system package state:
    • uv/uvx via the official installer (astral.sh/uv/install.sh).
    • Node/npx by downloading the official Node.js LTS tarball for the detected OS/arch straight from nodejs.org into a cache directory (~/.cache/mcp-reverse-proxy-installer) and using its bundled npx for this run. The pinned version can be overridden with the NODE_VERSION env var.
  4. Every download works with either curl or wget (whichever is present); the script fails loudly if neither exists.
  5. Once both tools are available, it execs uvx mcp-reverse-proxy-client "$@", forwarding all of the installer's own arguments unmodified.

The script is POSIX sh, idempotent, and safe to re-run.

Package / repo naming

  • PyPI package: mcp-reverse-proxy-client
  • Console script: mcp-reverse-proxy-client
  • GitHub repo: AkosPapp/agent-reverse-proxy
  • Installer hosting: GitHub Pages, deployed by CI — https://akospapp.github.io/agent-reverse-proxy/install.sh

CI / releasing

Three workflows under .github/workflows/:

  • ci.yml — runs the test suite (Python 3.9 and 3.12) and shellchecks install.sh on every push and pull request.
  • pages.yml — on every push to main that touches install.sh, publishes it to GitHub Pages so the install URL above always serves the latest committed script. Also runnable manually (workflow_dispatch).
  • publish-pypi.yml — on every GitHub Release publish, builds the sdist
    • wheel and uploads them to PyPI with twine, authenticating via the PYPI_API_TOKEN repo secret. It first checks that the release tag (vX.Y.Z) matches the version in pyproject.toml and fails loudly on a mismatch, so you can't accidentally publish the wrong version.

One-time setup before these run:

  1. GitHub Pages: repo Settings → Pages → Source → "GitHub Actions".
  2. PyPI token: create an API token scoped to this project on PyPI (or an account-wide token for the very first publish, since the project won't exist yet), then add it as a repo secret named PYPI_API_TOKEN (Settings → Secrets and variables → Actions).

To cut a release: bump version in pyproject.toml, commit, tag as vX.Y.Z (matching that version exactly), and publish a GitHub Release from that tag — publish-pypi.yml does the rest.

Metadata

Release files for mcp-reverse-proxy-client 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for mcp-reverse-proxy-client 0.1.0
File Size Uploaded Provenance Actions
mcp_reverse_proxy_client-0.1.0.tar.gz 17.0 kB Without provenance Details for mcp_reverse_proxy_client-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for mcp-reverse-proxy-client 0.1.0
File Interpreter ABI Platform Actions
mcp_reverse_proxy_client-0.1.0-py3-none-any.whl Python 3 none any Details for mcp_reverse_proxy_client-0.1.0-py3-none-any.whl

Total release size: 31.6 kB

Release files / mcp_reverse_proxy_client-0.1.0.tar.gz

Download URL mcp_reverse_proxy_client-0.1.0.tar.gz
Size 17.0 kB
Tags Source
SHA-256 checksum
How to use checksums
4b45cd47c2d2803c9efc61e8a4acf6ec51fd011d2348a99c8d2f873c9f83237c
BLAKE2b-256 checksum
How to use checksums
1948aa40aea0eb5afe20a57380d819e863bf93d3d51d26ecf32dc53e555545d1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.14

Release files / mcp_reverse_proxy_client-0.1.0-py3-none-any.whl

Download URL mcp_reverse_proxy_client-0.1.0-py3-none-any.whl
Size 14.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e5657a4f16fa5be6b07b493ae7db9ab35993b831db1c771fee506e9d39f7af6d
BLAKE2b-256 checksum
How to use checksums
374bf3cf7876ee5185658dfc3f397a7f6f8682f30bc7e7aad1e74ad79cd199f6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.14

Security for mcp-reverse-proxy-client 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page