From 874ae38087d8d7bf02e6af7db52d55a087fe572d Mon Sep 17 00:00:00 2001 From: Sergey Parfenyuk Date: Thu, 2 Jan 2025 21:22:59 +0100 Subject: [PATCH] docs: update readme and --help output (#13) * Refine CLI and reflect the change in the documentation * Update README.md with concise examples * Bump version --------- Co-authored-by: Allen Porter --- README.md | 230 ++++++++++++++++++++++++-------------- pyproject.toml | 2 +- src/mcp_proxy/__main__.py | 33 ++++-- uv.lock | 2 +- 4 files changed, 170 insertions(+), 97 deletions(-) diff --git a/README.md b/README.md index 7092386..dbe5fe4 100644 --- a/README.md +++ b/README.md @@ -2,52 +2,140 @@ - [mcp-proxy](#mcp-proxy) - [About](#about) + - [1. stdio to SSE](#1-stdio-to-sse) + - [1.1 Configuration](#11-configuration) + - [1.2 Example usage](#12-example-usage) + - [2. SSE to stdio](#2-sse-to-stdio) + - [2.1 Configuration](#21-configuration) + - [2.2 Example usage](#22-example-usage) - [Installation](#installation) - - [Configuration](#configuration) - - [Claude Desktop Configuration](#claude-desktop-configuration) - - [Advanced Configuration](#advanced-configuration) - - [Environment Variables](#environment-variables) + - [Command line arguments](#command-line-arguments) + - [Testing](#testing) ## About -Connect to MCP servers that run on SSE transport, or expose stdio servers as an SSE server using the MCP Proxy server. +The `mcp-proxy` is a tool that lets you switch between server transports. There are two supported modes: -## stdio to SSE +1. stdio to SSE +2. SSE to stdio + +## 1. stdio to SSE + +Run a proxy server from stdio that connects to a remote SSE server. + +This mode allows clients like Claude Desktop to communicate to a remote server over SSE even though it is not supported natively. ```mermaid graph LR - A["Claude Desktop"] <--> B["mcp-proxy"] - B <--> C["External MCP Server"] + A["Claude Desktop"] <--> |stdio| B["mcp-proxy"] + B <--> |SSE| C["External MCP Server"] style A fill:#ffe6f9,stroke:#333,color:black,stroke-width:2px style B fill:#e6e6ff,stroke:#333,color:black,stroke-width:2px style C fill:#e6ffe6,stroke:#333,color:black,stroke-width:2px ``` -> [!TIP] -> As of now, Claude Desktop does not support MCP servers that run on SSE transport. This server is a workaround to enable the support. +### 1.1 Configuration -## SSE to stdio +This mode requires passing the URL to the MCP Server SSE endpoint as the first argument to the program. + +Arguments + +| Name | Required | Description | Example | +| -------------------- | -------- | --------------------------------------------------------------------- | --------------------- | +| `command_or_url` | Yes | The MCP server SSE endpoint to connect to | http://example.io/sse | +| `--api-access-token` | No | Will be sent as a `Bearer` access token in the `Authorization` header | YOUR_TOKEN | + +Environment Variables + +| Name | Required | Description | Example | +| ------------------ | -------- | ------------------------------------------- | ---------- | +| `API_ACCESS_TOKEN` | No | Can be used instead of `--api-access-token` | YOUR_TOKEN | + +### 1.2 Example usage + +`mcp-proxy` is supposed to be started by the MCP Client, so the configuration must be done accordingly. + +For Claude Desktop, the configuration entry can look like this: + +```json +{ + "mcpServers": { + "mcp-proxy": { + "command": "mcp-proxy", + "args": ["http://example.io/sse"], + "env": { + "API_ACCESS_TOKEN": "access-token" + } + } + } +} +``` + +## 2. SSE to stdio + +Run a proxy server exposing a SSE server that connects to a local stdio server. + +This allows remote connections to the local stdio server. The `mcp-proxy` opens a port to listen for SSE requests, spawns a local stdio server that handles MCP requests. ```mermaid graph LR - A["LLM Client"] <--> B["mcp-proxy"] - B <--> C["Local MCP Server"] + A["LLM Client"] <-->|SSE| B["mcp-proxy"] + B <-->|stdio| C["Local MCP Server"] style A fill:#ffe6f9,stroke:#333,color:black,stroke-width:2px style B fill:#e6e6ff,stroke:#333,color:black,stroke-width:2px style C fill:#e6ffe6,stroke:#333,color:black,stroke-width:2px ``` +### 2.1 Configuration + +This mode requires the `--sse-port` argument to be set. The `--sse-host` argument can be set to specify the host IP address that the SSE server will listen on. Additional environment variables can be passed to the local stdio server using the `--env` argument. The command line arguments for the local stdio server must be passed after the `--` separator. + +Arguments + +| Name | Required | Description | Example | +| ---------------- | -------------------------- | ---------------------------------------------------------------- | -------------------- | +| `command_or_url` | Yes | The command to spawn the MCP stdio server | uvx mcp-server-fetch | +| `--sse-port` | No, random available | The SSE server port to listen on | 8080 | +| `--sse-host` | No, `127.0.0.1` by default | The host IP address that the SSE server will listen on | 0.0.0.0 | +| `--env` | No | Additional environment variables to pass to the MCP stdio server | FOO=BAR | + +### 2.2 Example usage + +To start the `mcp-proxy` server that listens on port 8080 and connects to the local MCP server: + +```bash +# Start the MCP server behind the proxy +mcp-proxy uvx mcp-server-fetch + +# Start the MCP server behind the proxy with a custom port +mcp-proxy --sse-port=8080 uvx mcp-server-fetch + +# Start the MCP server behind the proxy with a custom host and port +mcp-proxy --sse-host=0.0.0.0 --sse-port=8080 uvx mcp-server-fetch + +# Start the MCP server behind the proxy with a custom user agent +# Note that the `--` separator is used to separate the `mcp-proxy` arguments from the `mcp-server-fetch` arguments +mcp-proxy --sse-port=8080 -- uvx mcp-server-fetch --user-agent=YourUserAgent +``` + +This will start an MCP server that can be connected to at `http://127.0.0.1:8080/sse` ## Installation The stable version of the package is available on the PyPI repository. You can install it using the following command: ```bash +# Option 1: With uv (recommended) uv tool install mcp-proxy + +# Option 2: With pipx (alternative) +pipx install mcp-proxy ``` +Once installed, you can run the server using the `mcp-proxy` command. See configuration options for each mode above. + The latest version of the package can be installed from the git repository using the following command: ```bash @@ -60,84 +148,54 @@ uv tool install git+https://github.com/sparfenyuk/mcp-proxy > [!NOTE] > If you want to delete the server, use the `uv tool uninstall mcp-proxy` command. -## Configuration - -### Claude Desktop Configuration - -Configure Claude Desktop to recognize the MCP server. - -1. Open the Claude Desktop configuration file: - - in MacOS, the configuration file is located at `~/Library/Application Support/Claude/claude_desktop_config.json` - - in Windows, the configuration file is located at `%APPDATA%\Claude\claude_desktop_config.json` - - > __Note:__ - > You can also find claude_desktop_config.json inside the settings of Claude Desktop app - -2. Add the server configuration - -```json -{ - "mcpServers": { - "mcp-proxy": { - "command": "mcp-proxy", - "env": { - "SSE_URL": "http://example.io/sse" - } - } - } -} - -``` - -## Detailed Configuration - -The MCP Proxy server can support two different approaches for proxying: -- stdio to SSE: To allow clients like Claude Desktop to run this proxy directly. The proxy is started by the LLM Client as a server that proxies to a remote server over SSE. -- SSE to stdio: To allow a client that supports remote SSE servers to access a local stdio server. This proxy opens -a port to listen for SSE requests, then spawns a local stdio server that handles MCP requests. - -### stdio to SSE - -Run a proxy server from stdio that connects to a remote SSE server. - -Arguments - -| Name | Description | -| ------------------ | ---------------------------------------------------------------------------------- | -| `--sse-url` | Required. The MCP server SSE endpoint to connect to e.g. http://example.io/sse same as environment variable `SSE_URL` | - -Environment Variables - -| Name | Description | -| ---------------- | ---------------------------------------------------------------------------------- | -| `SSE_URL` | The MCP server SSE endpoint to connect to e.g. http://example.io/sse same as `--sse-url` | -| `API_ACCESS_TOKEN` | Added in the `Authorization` header of the HTTP request as a `Bearer` access token | - - -Example usage: +## Command line arguments ```bash -uv run mcp-proxy --sse-url=http://example.io/sse +usage: mcp-proxy [-h] [--api-access-token API_ACCESS_TOKEN] [-e KEY VALUE] [--sse-port SSE_PORT] [--sse-host SSE_HOST] [command_or_url] [args ...] + +Start the MCP proxy in one of two possible modes: as a SSE or stdio client. + +positional arguments: + command_or_url Command or URL to connect to. When a URL, will run a SSE client, otherwise will run the given command and connect as a stdio client. See corresponding options for more details. + +options: + -h, --help show this help message and exit + +SSE client options: + --api-access-token API_ACCESS_TOKEN + Access token Authorization header passed by the client to the SSE server. Can also be set as environment variable API_ACCESS_TOKEN. + +stdio client options: + args Any extra arguments to the command to spawn the server + -e KEY VALUE, --env KEY VALUE + Environment variables used when spawning the server. Can be used multiple times. + +SSE server options: + --sse-port SSE_PORT Port to expose a SSE server on. Default is a random port + --sse-host SSE_HOST Host to expose a SSE server on. Default is 127.0.0.1 + +Examples: + mcp-proxy http://localhost:8080/sse + mcp-proxy --api-access-token YOUR_TOKEN http://localhost:8080/sse + mcp-proxy --sse-port 8080 -- your-command --arg1 value1 --arg2 value2 + mcp-proxy your-command --sse-port 8080 -e KEY VALUE -e ANOTHER_KEY ANOTHER_VALUE ``` +## Testing -### SSE to stdio - -Run a proxy server exposing an SSE server that connects to a local stdio server. This allows remote connections to the stdio server. - -Arguments - -| Name | Description | -| ------------------ | ---------------------------------------------------------------------------------- | -| `--sse-port` | Required. The SSE server port to listen to e.g. `8080` | -| `--sse-host` | Optional. The host IP address that the SSE server will listen on e.g. `0.0.0.0`. By default only listens on localhost. | -| command | Required. The path for the MCP stdio server command line. | -| arg1 arg2 ... | Optional. Additional arguments to the MCP stdio server command line program. | - -Example usage: +Check the `mcp-proxy` server by running it with the `mcp-server-fetch` server. You can use the [inspector tool](https://modelcontextprotocol.io/docs/tools/inspector) to test the target server. ```bash -uv run mcp-proxy --sse-port=8080 -e FOO=BAR -- /path/to/command arg1 arg2 -``` +# Run the stdio server called mcp-server-fetch behind the proxy over SSE +mcp-proxy --sse-port=8080 uvx mcp-server-fetch & -This will start an MCP server that can be connected to at `http://127.0.0.1:8080/sse` +# Connect to the SSE proxy server spawned above using another instance of mcp-proxy given the URL of the SSE server +mcp-proxy http://localhost:8080/sse + +# Send CTRL+C to stop the second server + +# Bring the first server to the foreground +fg + +# Send CTRL+C to stop the first server +``` diff --git a/pyproject.toml b/pyproject.toml index 9bc2dbb..e1e4a65 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ classifiers = [ "Topic :: Utilities", "Typing :: Typed", ] -version = "0.2.2" +version = "0.3.0" requires-python = ">=3.11" dependencies = ["mcp", "uvicorn>=0.34.0"] diff --git a/src/mcp_proxy/__main__.py b/src/mcp_proxy/__main__.py index 83f3fa3..20462f8 100644 --- a/src/mcp_proxy/__main__.py +++ b/src/mcp_proxy/__main__.py @@ -19,19 +19,34 @@ from .sse_client import run_sse_client from .sse_server import SseServerSettings, run_sse_server logging.basicConfig(level=logging.DEBUG) -SSE_URL: t.Final[str | None] = os.getenv("SSE_URL", None) +SSE_URL: t.Final[str | None] = os.getenv( + "SSE_URL", + None, +) # Left for backwards compatibility. Will be removed in future. API_ACCESS_TOKEN: t.Final[str | None] = os.getenv("API_ACCESS_TOKEN", None) def main() -> None: """Start the client using asyncio.""" - parser = argparse.ArgumentParser() + parser = argparse.ArgumentParser( + description=( + "Start the MCP proxy in one of two possible modes: as an SSE or stdio client." + ), + epilog=( + "Examples:\n" + " mcp-proxy http://localhost:8080/sse\n" + " mcp-proxy --api-access-token YOUR_TOKEN http://localhost:8080/sse\n" + " mcp-proxy --sse-port 8080 -- your-command --arg1 value1 --arg2 value2\n" + " mcp-proxy your-command --sse-port 8080 -e KEY VALUE -e ANOTHER_KEY ANOTHER_VALUE\n" + ), + formatter_class=argparse.RawTextHelpFormatter, + ) parser.add_argument( "command_or_url", help=( - "Command or URL to connect to. When a URL, will run an SSE client " - "to connect to the server, otherwise will run the command and " - "connect as a stdio client. Can also be set as environment variable SSE_URL." + "Command or URL to connect to. When a URL, will run an SSE client, " + "otherwise will run the given command and connect as a stdio client. " + "See corresponding options for more details." ), nargs="?", # Required below to allow for coming form env var default=SSE_URL, @@ -51,7 +66,7 @@ def main() -> None: stdio_client_options.add_argument( "args", nargs="*", - help="Arguments to the command to run to spawn the server", + help="Any extra arguments to the command to spawn the server", ) stdio_client_options.add_argument( "-e", @@ -67,13 +82,13 @@ def main() -> None: sse_server_group.add_argument( "--sse-port", type=int, - default=None, - help="Port to expose an SSE server on", + default=0, + help="Port to expose an SSE server on. Default is a random port", ) sse_server_group.add_argument( "--sse-host", default="127.0.0.1", - help="Host to expose an SSE server on", + help="Host to expose an SSE server on. Default is 127.0.0.1", ) args = parser.parse_args() diff --git a/uv.lock b/uv.lock index 974b890..12e222d 100644 --- a/uv.lock +++ b/uv.lock @@ -185,7 +185,7 @@ wheels = [ [[package]] name = "mcp-proxy" -version = "0.2.2" +version = "0.3.0" source = { editable = "." } dependencies = [ { name = "mcp" },