Jeremiah Lowin commited on
Commit
b86019e
·
1 Parent(s): 282875e

Support remote auth in mcpconfig

Browse files
src/fastmcp/utilities/mcp_config.py CHANGED
@@ -1,6 +1,6 @@
1
  from __future__ import annotations
2
 
3
- from typing import TYPE_CHECKING, Any, Literal
4
  from urllib.parse import urlparse
5
 
6
  from pydantic import AnyUrl, Field
@@ -56,6 +56,12 @@ class RemoteMCPServer(FastMCPBaseModel):
56
  url: str
57
  headers: dict[str, str] = Field(default_factory=dict)
58
  transport: Literal["streamable-http", "sse", "http"] | None = None
 
 
 
 
 
 
59
 
60
  def to_transport(self) -> StreamableHttpTransport | SSETransport:
61
  from fastmcp.client.transports import SSETransport, StreamableHttpTransport
@@ -66,9 +72,11 @@ class RemoteMCPServer(FastMCPBaseModel):
66
  transport = self.transport
67
 
68
  if transport == "sse":
69
- return SSETransport(self.url, headers=self.headers)
70
  else:
71
- return StreamableHttpTransport(self.url, headers=self.headers)
 
 
72
 
73
 
74
  class MCPConfig(FastMCPBaseModel):
 
1
  from __future__ import annotations
2
 
3
+ from typing import TYPE_CHECKING, Annotated, Any, Literal
4
  from urllib.parse import urlparse
5
 
6
  from pydantic import AnyUrl, Field
 
56
  url: str
57
  headers: dict[str, str] = Field(default_factory=dict)
58
  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
65
 
66
  def to_transport(self) -> StreamableHttpTransport | SSETransport:
67
  from fastmcp.client.transports import SSETransport, StreamableHttpTransport
 
72
  transport = self.transport
73
 
74
  if transport == "sse":
75
+ return SSETransport(self.url, headers=self.headers, auth=self.auth)
76
  else:
77
+ return StreamableHttpTransport(
78
+ self.url, headers=self.headers, auth=self.auth
79
+ )
80
 
81
 
82
  class MCPConfig(FastMCPBaseModel):
tests/utilities/test_mcp_config.py CHANGED
@@ -1,6 +1,8 @@
1
  import inspect
2
  from pathlib import Path
3
 
 
 
4
  from fastmcp.client.client import Client
5
  from fastmcp.client.transports import (
6
  SSETransport,
@@ -136,3 +138,60 @@ async def test_multi_client(tmp_path: Path):
136
  result_2 = await client.call_tool("test_2_add", {"a": 1, "b": 2})
137
  assert result_1[0].text == "3" # type: ignore[attr-dict]
138
  assert result_2[0].text == "3" # type: ignore[attr-dict]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import inspect
2
  from pathlib import Path
3
 
4
+ from fastmcp.client.auth.bearer import BearerAuth
5
+ from fastmcp.client.auth.oauth import OAuthClientProvider
6
  from fastmcp.client.client import Client
7
  from fastmcp.client.transports import (
8
  SSETransport,
 
138
  result_2 = await client.call_tool("test_2_add", {"a": 1, "b": 2})
139
  assert result_1[0].text == "3" # type: ignore[attr-dict]
140
  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)