Skip to content

Commit 6fbb555

Browse files
committed
Support remote auth in mcpconfig
1 parent 6d36171 commit 6fbb555

File tree

2 files changed

+70
-3
lines changed

2 files changed

+70
-3
lines changed

src/fastmcp/utilities/mcp_config.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from __future__ import annotations
22

3-
from typing import TYPE_CHECKING, Any, Literal
3+
from typing import TYPE_CHECKING, Annotated, Any, Literal
44
from urllib.parse import urlparse
55

66
from pydantic import AnyUrl, Field
@@ -56,6 +56,12 @@ class RemoteMCPServer(FastMCPBaseModel):
5656
url: str
5757
headers: dict[str, str] = Field(default_factory=dict)
5858
transport: Literal["streamable-http", "sse", "http"] | None = None
59+
auth: Annotated[
60+
str | Literal["oauth"] | None,
61+
Field(
62+
description='Either a string representing a Bearer token or the literal "oauth" to use OAuth authentication.'
63+
),
64+
] = None
5965

6066
def to_transport(self) -> StreamableHttpTransport | SSETransport:
6167
from fastmcp.client.transports import SSETransport, StreamableHttpTransport
@@ -66,9 +72,11 @@ def to_transport(self) -> StreamableHttpTransport | SSETransport:
6672
transport = self.transport
6773

6874
if transport == "sse":
69-
return SSETransport(self.url, headers=self.headers)
75+
return SSETransport(self.url, headers=self.headers, auth=self.auth)
7076
else:
71-
return StreamableHttpTransport(self.url, headers=self.headers)
77+
return StreamableHttpTransport(
78+
self.url, headers=self.headers, auth=self.auth
79+
)
7280

7381

7482
class MCPConfig(FastMCPBaseModel):

tests/utilities/test_mcp_config.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import inspect
22
from pathlib import Path
33

4+
from fastmcp.client.auth.bearer import BearerAuth
5+
from fastmcp.client.auth.oauth import OAuthClientProvider
46
from fastmcp.client.client import Client
57
from fastmcp.client.transports import (
68
SSETransport,
@@ -136,3 +138,60 @@ def add(a: int, b: int) -> int:
136138
result_2 = await client.call_tool("test_2_add", {"a": 1, "b": 2})
137139
assert result_1[0].text == "3" # type: ignore[attr-dict]
138140
assert result_2[0].text == "3" # type: ignore[attr-dict]
141+
142+
143+
async def test_remote_config_default_no_auth():
144+
config = {
145+
"mcpServers": {
146+
"test_server": {
147+
"url": "http://localhost:8000",
148+
}
149+
}
150+
}
151+
client = Client(config)
152+
assert isinstance(client.transport.transport, StreamableHttpTransport)
153+
assert client.transport.transport.auth is None
154+
155+
156+
async def test_remote_config_with_auth_token():
157+
config = {
158+
"mcpServers": {
159+
"test_server": {
160+
"url": "http://localhost:8000",
161+
"auth": "test_token",
162+
}
163+
}
164+
}
165+
client = Client(config)
166+
assert isinstance(client.transport.transport, StreamableHttpTransport)
167+
assert isinstance(client.transport.transport.auth, BearerAuth)
168+
assert client.transport.transport.auth.token.get_secret_value() == "test_token"
169+
170+
171+
async def test_remote_config_sse_with_auth_token():
172+
config = {
173+
"mcpServers": {
174+
"test_server": {
175+
"url": "http://localhost:8000/sse",
176+
"auth": "test_token",
177+
}
178+
}
179+
}
180+
client = Client(config)
181+
assert isinstance(client.transport.transport, SSETransport)
182+
assert isinstance(client.transport.transport.auth, BearerAuth)
183+
assert client.transport.transport.auth.token.get_secret_value() == "test_token"
184+
185+
186+
async def test_remote_config_with_oauth_literal():
187+
config = {
188+
"mcpServers": {
189+
"test_server": {
190+
"url": "http://localhost:8000",
191+
"auth": "oauth",
192+
}
193+
}
194+
}
195+
client = Client(config)
196+
assert isinstance(client.transport.transport, StreamableHttpTransport)
197+
assert isinstance(client.transport.transport.auth, OAuthClientProvider)

0 commit comments

Comments
 (0)