Skip to main content

Scaffold a production-ready remote MCP (Model Context Protocol) server with OAuth, RFC 8414/9728 discovery, retry, telemetry, and tests — in one command. Zero runtime dependency on this package.

Project description

remote-mcp

One command to a production-ready remote MCP server.

PyPI Python CI License: MIT


Building a remote MCP server means solving the same hard problems every time: OAuth pass-through, RFC 8414/9728 discovery endpoints, middleware stack, telemetry, retry logic, and a test suite that passes on day one.

remote-mcp scaffolds all of it from two prompts. Generated projects have zero runtime dependency on this package — every file is yours to read, audit, and modify.

Install

pipx install remote-mcp   # recommended
# or
pip install remote-mcp

Usage

remote-mcp new my-project
  FastMCP Remote Server Generator

  Project name    [my-project]:
  Service name    [My Project]:

  Scaffolding my-project...  ✓

  Done! Next steps:
    cd my-project
    python -m venv venv && source venv/bin/activate
    pip install -e ".[dev]"
    cp env.example .env
    uvicorn asgi:application --reload --port 8001 --lifespan on

Server is live at http://localhost:8001. MCP endpoint: http://localhost:8001/sse.

What you get

my-project/
├── src/
│   ├── server.py              # FastMCP("My Project") instance + tool mounts
│   ├── app.py                 # Starlette factory — routes, middleware, ASGI wiring
│   ├── config/settings.py     # Pydantic BaseSettings — all config via env vars
│   ├── core/
│   │   ├── auth.py            # extract_bearer_token() — OAuth pass-through
│   │   ├── errors.py          # MyProjectError hierarchy
│   │   ├── http_client.py     # api_get/post/patch/put/delete/upload + tenacity retry
│   │   ├── telemetry.py       # anonymized SHA-256 JSONL event log
│   │   └── handlers.py        # @tool_handler decorator, get_client_and_token()
│   ├── middleware/
│   │   ├── auth.py            # RequireAuthMiddleware — Bearer enforcement + probe
│   │   └── telemetry.py       # TelemetryMiddleware — auth failures, connections
│   ├── views/
│   │   ├── health.py          # GET /health
│   │   ├── oauth.py           # RFC 8414 + RFC 9728 discovery endpoints
│   │   └── root.py            # landing page at /
│   └── tools/example.py       # echo tool — your first tool, ready to replace
├── tests/
│   ├── test_auth.py           # extract_bearer_token edge cases
│   ├── test_middleware.py      # auth bypass, 401 on missing token
│   └── test_telemetry.py      # hash_token stability, record_event never raises
├── templates/index.html       # landing page served at /
├── asgi.py                    # production ASGI entrypoint
├── env.example                # all env vars with safe defaults
├── pyproject.toml
└── DEPLOYMENT.md              # Render / Railway / Fly.io deploy guide

Included infrastructure

Module What it provides
core/auth.py extract_bearer_token(ctx) — forward Bearer token verbatim to your backend
core/http_client.py api_get, api_post, api_patch, api_put, api_delete, api_upload — pooled httpx client with tenacity retry
core/errors.py MyProjectError + Auth, Forbidden, Validation, Backend, RateLimit subclasses
core/telemetry.py Rotating JSONL log, user IDs hashed (SHA-256, non-reversible)
core/handlers.py @tool_handler — catches errors, formats responses, records telemetry
middleware/auth.py CORS → Auth → optional backend token probe on SSE connect
middleware/telemetry.py Connection-level event recording (auth failures, rate limits, SSE connects)
views/ Health, OAuth discovery (RFC 8414 + 9728), landing page — each in its own file

Nothing is forced on you. Delete what you don't need.

Adding a tool

Open src/tools/example.py — it's already wired up as a working echo tool. Replace it or add alongside it:

# src/tools/my_tool.py
from fastmcp import FastMCP, Context
from src.core.handlers import tool_handler, get_client_and_token

my_router = FastMCP("my-tool")

@my_router.tool()
@tool_handler
async def my_tool(param: str, ctx: Context) -> str:
    client, auth_header = await get_client_and_token(ctx)
    response = await client.get("/some/endpoint", headers={"Authorization": auth_header})
    return response.json()

Mount it in src/server.py:

from src.tools.my_tool import my_router
mcp.mount(my_router)

Connecting to Claude

Claude.ai (web): Settings → Connectors → Add → Custom → Web

  • URL: https://your-server.example.com/mcp/sse

Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json):

{
  "mcpServers": {
    "my-project": {
      "command": "npx",
      "args": ["-y", "mcp-remote@latest", "https://your-server.example.com/mcp/sse"]
    }
  }
}

Claude Code:

claude mcp add my-project --transport http https://your-server.example.com/mcp/sse

Configuration

Copy env.example to .env and edit. Key variables:

Variable Default Description
API_BASE_URL https://api.example.com Your upstream API
MCP_PUBLIC_URL http://localhost:8001/mcp Public MCP URL (in landing page)
OAUTH_ISSUER_URL http://localhost:8001 OAuth issuer for RFC 8414 discovery
AUTH_PROBE_ENABLED false Validate token against backend on SSE connect
AUTH_PROBE_PATH /health/ Endpoint used for token probe
LOGO_URI `` Logo shown in OAuth discovery (optional)
ALLOWED_ORIGINS https://claude.ai,... CORS origins

Full variable list and deploy instructions in DEPLOYMENT.md.

How it works

remote-mcp renders Jinja2 templates into your project directory at scaffold time. After that, it's gone — no version pinning, no update command, no hidden runtime. You own every line.

Requirements

  • Python ≥ 3.10 (this CLI). Generated project supports the same range.
  • FastMCP ≥ 3.0 (installed in the generated project, not this package)

License

MIT — see LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

remote_mcp-0.2.0.tar.gz (30.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

remote_mcp-0.2.0-py3-none-any.whl (39.8 kB view details)

Uploaded Python 3

File details

Details for the file remote_mcp-0.2.0.tar.gz.

File metadata

  • Download URL: remote_mcp-0.2.0.tar.gz
  • Upload date:
  • Size: 30.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for remote_mcp-0.2.0.tar.gz
Algorithm Hash digest
SHA256 1d709087be0f34652efe8eb094107d0af1bedc472bae00d88f0543dc3a3789ba
MD5 e02dbc76511b4df1ea5163f825d9f8a1
BLAKE2b-256 58bb740fcca663f1953c1e8d96fbc5d7ac07abd30794a7d746ee8738e7ac2904

See more details on using hashes here.

Provenance

The following attestation bundles were made for remote_mcp-0.2.0.tar.gz:

Publisher: publish.yml on pushpendra-tripathi/remote-mcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file remote_mcp-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: remote_mcp-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 39.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for remote_mcp-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 acf84c9e9442a6078135bb3029d022795576635951f63ab6ab7be06c542f83bd
MD5 8709a5e2c35aec2bd24aa32d462199f9
BLAKE2b-256 ec20f3358699c04cc951c599523b7c4a1f3c7bc7870ad9265ea5d8d3a36a5b4f

See more details on using hashes here.

Provenance

The following attestation bundles were made for remote_mcp-0.2.0-py3-none-any.whl:

Publisher: publish.yml on pushpendra-tripathi/remote-mcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page