diff --git a/README.md b/README.md index 863d8eb..cf32e68 100644 --- a/README.md +++ b/README.md @@ -51,16 +51,16 @@ This mode requires passing the URL to the MCP Server SSE endpoint as the first a 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 | +| Name | Required | Description | Example | +| -------------------- | -------- | --------------------------------------------------------------------- | ----------------------------------------------| +| `command_or_url` | Yes | The MCP server SSE endpoint to connect to | http://example.io/sse | +| `--headers` | No | Headers to use for the MCP server SSE connection | Authorization 'Bearer my-secret-access-token' | Environment Variables -| Name | Required | Description | Example | -| ------------------ | -------- | ------------------------------------------- | ---------- | -| `API_ACCESS_TOKEN` | No | Can be used instead of `--api-access-token` | YOUR_TOKEN | +| Name | Required | Description | Example | +| ------------------ | -------- | ---------------------------------------------------------------------------- | ---------- | +| `API_ACCESS_TOKEN` | No | Can be used instead of `--headers Authorization 'Bearer '` | YOUR_TOKEN | ### 1.2 Example usage @@ -181,19 +181,19 @@ docker run -t ghcr.io/sparfenyuk/mcp-proxy:v0.3.2-alpine --help ## Command line arguments ```bash -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 ...] +usage: mcp-proxy [-h] [-H KEY VALUE] [-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. +Start the MCP proxy in one of two possible modes: as an 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. + command_or_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. 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. + -H KEY VALUE, --headers KEY VALUE + Headers to pass to the SSE server. Can be used multiple times. stdio client options: args Any extra arguments to the command to spawn the server @@ -201,12 +201,12 @@ stdio client options: 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 + --sse-port SSE_PORT Port to expose an SSE server on. Default is a random port + --sse-host SSE_HOST Host to expose an 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 --headers Authorization 'Bearer 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 ``` diff --git a/src/mcp_proxy/__main__.py b/src/mcp_proxy/__main__.py index 20462f8..9ba70eb 100644 --- a/src/mcp_proxy/__main__.py +++ b/src/mcp_proxy/__main__.py @@ -22,8 +22,7 @@ logging.basicConfig(level=logging.DEBUG) 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: @@ -35,7 +34,7 @@ def main() -> None: epilog=( "Examples:\n" " mcp-proxy http://localhost:8080/sse\n" - " mcp-proxy --api-access-token YOUR_TOKEN http://localhost:8080/sse\n" + " mcp-proxy --headers Authorization 'Bearer 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" ), @@ -54,12 +53,13 @@ def main() -> None: sse_client_group = parser.add_argument_group("SSE client options") sse_client_group.add_argument( - "--api-access-token", - default=API_ACCESS_TOKEN, - help=( - "Access token Authorization header passed by the client to the SSE " - "server. Can also be set as environment variable API_ACCESS_TOKEN." - ), + "-H", + "--headers", + nargs=2, + action="append", + metavar=("KEY", "VALUE"), + help="Headers to pass to the SSE server. Can be used multiple times.", + default=[], ) stdio_client_options = parser.add_argument_group("stdio client options") @@ -104,7 +104,10 @@ def main() -> None: ): # Start a client connected to the SSE server, and expose as a stdio server logging.debug("Starting SSE client and stdio server") - asyncio.run(run_sse_client(args.command_or_url, api_access_token=API_ACCESS_TOKEN)) + headers = dict(args.headers) + if api_access_token := os.getenv("API_ACCESS_TOKEN", None): + headers["Authorization"] = f"Bearer {api_access_token}" + asyncio.run(run_sse_client(args.command_or_url, headers=headers)) return # Start a client connected to the given command, and expose as an SSE server diff --git a/src/mcp_proxy/sse_client.py b/src/mcp_proxy/sse_client.py index 3613dc9..9a3a2ff 100644 --- a/src/mcp_proxy/sse_client.py +++ b/src/mcp_proxy/sse_client.py @@ -1,5 +1,7 @@ """Create a local server that proxies requests to a remote server over SSE.""" +from typing import Any + from mcp.client.session import ClientSession from mcp.client.sse import sse_client from mcp.server.stdio import stdio_server @@ -7,18 +9,14 @@ from mcp.server.stdio import stdio_server from .proxy_server import create_proxy_server -async def run_sse_client(url: str, api_access_token: str | None = None) -> None: +async def run_sse_client(url: str, headers: dict[str, Any] | None = None) -> None: """Run the SSE client. Args: url: The URL to connect to. - api_access_token: The API access token to use for authentication. + headers: Headers for connecting to MCP server. """ - headers = {} - if api_access_token is not None: - headers["Authorization"] = f"Bearer {api_access_token}" - async with sse_client(url=url, headers=headers) as streams, ClientSession(*streams) as session: app = await create_proxy_server(session) async with stdio_server() as (read_stream, write_stream):