feat: support 'headers' argument for SSE server connection (#23)

I have replaced the `--api-access-token` argument with the more generic
`--headers` argument for SSE. This will allow for other auth mechanisms
such as api keys as well. Intended usage:

```sh
# API key
mcp-proxy http://example.io/sse --headers x-api-key my-super-secret-api-key

# Bearer token
mcp-proxy http://example.io/sse --headers Authorization 'Bearer my-super-secret-bearer-token'

# Multiple headers
mcp-proxy http://example.io/sse --headers Authorization 'Bearer my-super-secret-bearer-token' --headers x-api-key my-super-secret-api-key
```
This commit is contained in:
andersenthomas98
2025-02-07 20:49:27 +01:00
committed by GitHub
parent da9cbf7386
commit 1de8394767
3 changed files with 32 additions and 31 deletions

View File

@@ -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 <API_ACCESS_TOKEN>'` | 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
```

View File

@@ -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

View File

@@ -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):