Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ Arguments
| `--client-id` | No | OAuth2 client ID for authentication | your_client_id |
| `--client-secret`| No | OAuth2 client secret for authentication | your_client_secret |
| `--token-url` | No | OAuth2 token endpoint URL for authentication | https://auth.example.com/oauth/token |
| `--username` | No | Username for authentication | user |
| `--password` | No | Password for authentication | password |
| `--digest` | No | Use digest authentication | |

Environment Variables

Expand Down Expand Up @@ -351,6 +354,9 @@ SSE/StreamableHTTP client options:
OAuth2 client secret for authentication
--token-url TOKEN_URL
OAuth2 token URL for authentication
--username USERNAME Username for authentication
--password PASSWORD Password for authentication
--digest Use digest authentication

stdio client options:
args Any extra arguments to the command to spawn the default server. Ignored if only named servers are defined.
Expand Down Expand Up @@ -381,6 +387,7 @@ Examples:
mcp-proxy http://localhost:8080/sse
mcp-proxy --no-verify-ssl https://server.local/sse
mcp-proxy --transport streamablehttp http://localhost:8080/mcp
mcp-proxy --transport streamablehttp --username user --password pass https://server.local/mcp
mcp-proxy --headers Authorization 'Bearer YOUR_TOKEN' http://localhost:8080/sse
mcp-proxy --client-id CLIENT_ID --client-secret CLIENT_SECRET --token-url https://auth.example.com/token http://localhost:8080/sse
mcp-proxy --port 8080 -- your-command --arg1 value1 --arg2 value2
Expand Down
38 changes: 32 additions & 6 deletions src/mcp_proxy/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import typing as t
from importlib.metadata import version

from httpx import Auth, BasicAuth, DigestAuth
from httpx_auth import OAuth2ClientCredentials
from mcp.client.stdio import StdioServerParameters

Expand Down Expand Up @@ -54,6 +55,8 @@ def _setup_argument_parser() -> argparse.ArgumentParser:
" mcp-proxy http://localhost:8080/sse\n"
" mcp-proxy --no-verify-ssl https://server.local/sse\n"
" mcp-proxy --transport streamablehttp http://localhost:8080/mcp\n"
" mcp-proxy --transport streamablehttp --username user --password pass "
"https://server.local/mcp\n"
" mcp-proxy --headers Authorization 'Bearer YOUR_TOKEN' http://localhost:8080/sse\n"
" mcp-proxy --port 8080 -- your-command --arg1 value1 --arg2 value2\n"
" mcp-proxy --named-server fetch 'uvx mcp-server-fetch' --port 8080\n"
Expand Down Expand Up @@ -125,6 +128,21 @@ def _add_arguments_to_parser(parser: argparse.ArgumentParser) -> None:
type=str,
help="OAuth2 token URL for authentication",
)
client_group.add_argument(
"--username",
type=str,
help="Username for authentication",
)
client_group.add_argument(
"--password",
type=str,
help="Password for authentication",
)
client_group.add_argument(
"--digest",
action="store_true",
help="Use digest authentication",
)
client_group.add_argument(
"--verify-ssl",
nargs="?",
Expand Down Expand Up @@ -301,20 +319,28 @@ def _handle_sse_client_mode(
if api_access_token := os.getenv("API_ACCESS_TOKEN", None):
headers["Authorization"] = f"Bearer {api_access_token}"

# Collect client credentials and token url if provided
# Collect client credentials if provided
client_id = args_parsed.client_id
client_secret = args_parsed.client_secret
token_url = args_parsed.token_url
username = args_parsed.username
password = args_parsed.password
digest = args_parsed.digest

auth = (
OAuth2ClientCredentials(
auth: Auth | None = None
if client_id and client_secret and token_url:
auth = OAuth2ClientCredentials(
client_id=client_id,
client_secret=client_secret,
token_url=token_url,
)
if client_id and client_secret and token_url
else None
)
elif username and password:
auth = DigestAuth(username, password) if digest else BasicAuth(username, password)
elif username or password or digest:
logger.error("Both --username and --password are required")
sys.exit(1)
else:
auth = None

if args_parsed.transport == "streamablehttp":
asyncio.run(
Expand Down
Loading