paper-cloud-mcp
Allows exposing local MCP servers through a public HTTPS endpoint via Cloudflare Tunnel (cloudflared), enabling remote clients to reach localhost services.
Allows exposing local MCP servers through a public HTTPS endpoint via ngrok, enabling remote clients to reach localhost services.
Allows exposing local MCP servers over a tailnet or publicly via Tailscale Funnel, enabling remote clients to reach localhost services.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@paper-cloud-mcpWhat pages and artboards are in my open Paper file?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
local-mcp-gateway
CLI: lmg
Expose localhost MCP servers (Paper Desktop, etc.) through a small authenticated proxy and a pluggable tunnel (Tailscale Funnel, Cloudflare Tunnel, or ngrok) so Cursor Cloud Agents and other remote clients can reach them.
Why this exists
Paper Desktop only listens on loopback. Cloud Agents cannot reach 127.0.0.1 on your laptop.
Two constraints that bite in practice:
Host rewrite. Paper rejects MCP requests unless the HTTP
Hostheader is127.0.0.1:29979(DNS-rebinding protection). Tailscale / Cloudflare / ngrok send their public hostname → Paper returns403 Invalid host. The gateway rewritesHostand stripsAuthorization,Cookie, andOriginbefore proxying upstream.Public HTTPS for Cloud HTTP MCP. Cursor Cloud Agent HTTP MCP is proxied from Cursor’s backend, which is not on your tailnet. A tailnet-only URL (
tailscale servewithout Funnel) is unreachable. Use Funnel, Cloudflare, or ngrok for Cloud Agents. Tailnet-onlyserveis still valid for other tailnet clients.
Related MCP server: cursor-agent-bridge
Install
# from this repo
uv sync
uv run lmg --help
# or as a tool
uv tool install .
# later: uv tool install git+https://github.com/ppcantidio/local-mcp-gatewayRequires Python 3.12+.
Upgrade (force)
uv tool install does not restart a running lmg serve. Reinstall, then kill and start again:
cd ~/Developer/pessoal/local-mcp-gateway # your clone
git pull
# wipe the installed tool and reinstall from this tree
uv tool uninstall local-mcp-gateway || true
uv tool install --force --reinstall .
lmg --version # expect 0.1.4+
# Ctrl+C the old serve (or: pkill -f 'lmg serve')
lmg serve --publisher tailscale --mode funnel
curl -s http://127.0.0.1:8788/healthz
# expect version 0.1.4, sse_unwrap true, get_sse_disabled falseIf lmg --version is still old, check which lmg — another install (venv / old path) may be first on PATH.
Quick start
lmg init # writes ./lmg.toml, prints LMG_API_KEY once
export LMG_API_KEY=... # paste the key from init / keygen
# Paper Desktop open with a file loaded (confirms http://127.0.0.1:29979/mcp)
lmg serve --publisher tailscale --mode funnelRegister more local MCPs (file or CLI):
lmg add paper http://127.0.0.1:29979 --rewrite-host 127.0.0.1:29979
lmg add other http://127.0.0.1:3100
lmg ls
lmg rm other
# ephemeral for one run
lmg serve --publisher local --mcp demo=http://127.0.0.1:3200Config search order: --config → ./lmg.toml → ~/.config/lmg/config.toml.
LMG_API_KEY is required for serve. It is never written to the TOML file and never logged.
Routing
Each registered MCP is exposed under its name:
Local upstream | Public URL |
|
|
GET / and GET /healthz are unauthenticated (tunnel probes). Everything else requires Authorization: Bearer <LMG_API_KEY>.
Reliability (Cloud Agents + Funnel)
Cursor Cloud MCP discovery opens a GET SSE stream. Returning 405 makes the namespace look "ready" with 0 tools, so GET SSE stays enabled by default. Funnel idle drops are mitigated with SSE heartbeats.
This gateway now:
Keeps GET SSE enabled by default (set
proxy.disable_get_sse = trueonly for POST-only experiments).Injects SSE keepalives on idle GET streams (
proxy.sse_heartbeat_seconds, default15).Retries upstream Paper/Desktop connection blips (
proxy.upstream_retries, default2).Gzip-compresses larger JSON responses.
[proxy]
# disable_get_sse = false
sse_heartbeat_seconds = 15
upstream_retries = 2GET /healthz reports get_sse_disabled and upstream_retries so you can confirm the live process.
Prefer get_basic_info → artboard/nodeId → get_tree_summary with a low depth for huge Paper files.
Publishers
Name | Mode | Public? | Notes |
| — | No |
|
|
| Tailnet only | Not enough for Cursor Cloud HTTP MCP |
|
| Yes | Public HTTPS; needs Funnel enabled on the tailnet |
| — | Yes |
|
| — | Yes |
|
The proxy only binds localhost. A Publisher starts after the server is listening and returns the public origin. Adding a publisher = one module + registry entry (no if publisher == in the proxy).
CLIs must already be on PATH. Missing binary errors include an install hint.
Tailscale stop: lmg turns off the --https=443 serve/funnel mapping it created. It does not wipe unrelated Tailscale serve routes; if you share port 443 with other mappings, stop carefully.
Cursor plugin (Cloud Agents)
This repo is a Team Marketplace. Import it so Cloud Agents can use Paper over your public gateway.
Dashboard → Plugins → Team Marketplaces → Add Marketplace → import
https://github.com/ppcantidio/local-mcp-gatewayInstall the paper-cloud-mcp plugin (not the local Paper Desktop MCP).
Configure variables (same fixed values as your laptop):
Variable | Example |
|
|
| value from |
Enable the MCP for Cloud Agents at cursor.com/agents.
Keep
lmg serve+ Paper Desktop running on the Mac while agents work.
Plugin sources live under plugins/paper-cloud-mcp/ (manifest + mcp.json). Never commit API keys.
Security
Funnel / Cloudflare / ngrok put a public HTTPS endpoint on the internet. The API key is the only gate.
Generate a long
token_urlsafekey (lmg keygen)Rotate when shared or leaked
Never commit
LMG_API_KEYor a TOML file that contains secretsTurn Funnel / tunnels off when idle
CLI
Command | Purpose |
| Write |
| Print a new key (not written to disk) |
| Register a local MCP |
| List / remove |
| Start proxy + publisher |
Develop
uv sync
uv run pytest
uv run ruff check
uv run ty checkPackage layout
src/local_mcp_gateway/
cli/ # Typer entrypoint (`lmg`) — thin UX layer
config/ # TOML models, file IO, env secrets (LMG_API_KEY)
proxy/ # Starlette app: auth, routing, Host rewrite, SSE
publishers/ # Pluggable tunnels (local / tailscale / cloudflare / ngrok)
runtime/ # Process runner + proxy/publisher lifecycle
data/ # Packaged example TOML
errors.py # Shared exceptionsOut of scope (v1)
OAuth / Tailscale identity headers, Windows-specific installers, putting this in the Spryx monorepo, committing API keys.
This server cannot be deployed
Maintenance
Related MCP Connectors
- QuallaaOAuthcom.quallaa
Talk to your public-facing AI from any MCP client — Claude, ChatGPT, Cursor, Cline, Windsurf.
Enable secure connectivity between Sentry issues and debugging data, and LLM clients, using a Model Context Protocol (MCP) server.
Zero-setup MCP gateway securely connecting AI to your tools with authentication and workflows
Secure tunneling, reverse proxy and remote access for local applications.
Related MCP Servers
- AlicenseNot gradedqualityAmaintenanceEnables cloud agents to securely operate local machine resources (files, commands, screenshots) via standard MCP protocol.MIT
- AlicenseNot gradedqualityBmaintenanceEnables MCP clients like Claude Code to delegate coding tasks to the local Cursor Agent CLI, with persistent per-workspace sessions that resume across calls.12 npmMIT
- FlicenseNot gradedqualityBmaintenanceBridges MCP to Cursor Agent via ACP, allowing Codex to delegate file edits and shell commands to Cursor for repository modifications, with security defaults.-
- AlicenseNot gradedqualityAmaintenanceEnables MCP clients to connect to a local workspace over a public tunnel and lets them run shell commands and transfer files bidirectionally.262 npm21GPL 3.0