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

Fix passing token string to client auth

Browse files
src/fastmcp/client/transports.py CHANGED
@@ -32,6 +32,7 @@ from mcp.shared.memory import create_connected_server_and_client_session
32
  from pydantic import AnyUrl
33
  from typing_extensions import Unpack
34
 
 
35
  from fastmcp.client.auth.oauth import OAuth
36
  from fastmcp.server.dependencies import get_http_headers
37
  from fastmcp.server.server import FastMCP
@@ -152,7 +153,7 @@ class WSTransport(ClientTransport):
152
  yield session
153
 
154
  def __repr__(self) -> str:
155
- return f"<WebSocket(url='{self.url}')>"
156
 
157
 
158
  class SSETransport(ClientTransport):
@@ -183,8 +184,7 @@ class SSETransport(ClientTransport):
183
  if auth == "oauth":
184
  auth = OAuth(self.url)
185
  elif isinstance(auth, str):
186
- self.headers["Authorization"] = auth
187
- auth = None
188
  self.auth = auth
189
 
190
  @contextlib.asynccontextmanager
@@ -221,7 +221,7 @@ class SSETransport(ClientTransport):
221
  yield session
222
 
223
  def __repr__(self) -> str:
224
- return f"<SSE(url='{self.url}')>"
225
 
226
 
227
  class StreamableHttpTransport(ClientTransport):
@@ -252,8 +252,7 @@ class StreamableHttpTransport(ClientTransport):
252
  if auth == "oauth":
253
  auth = OAuth(self.url)
254
  elif isinstance(auth, str):
255
- self.headers["Authorization"] = auth
256
- auth = None
257
  self.auth = auth
258
 
259
  @contextlib.asynccontextmanager
@@ -291,7 +290,7 @@ class StreamableHttpTransport(ClientTransport):
291
  yield session
292
 
293
  def __repr__(self) -> str:
294
- return f"<StreamableHttp(url='{self.url}')>"
295
 
296
 
297
  class StdioTransport(ClientTransport):
@@ -683,7 +682,7 @@ class FastMCPTransport(ClientTransport):
683
  yield session
684
 
685
  def __repr__(self) -> str:
686
- return f"<FastMCP(server='{self.server.name}')>"
687
 
688
 
689
  class MCPConfigTransport(ClientTransport):
@@ -769,7 +768,7 @@ class MCPConfigTransport(ClientTransport):
769
  yield session
770
 
771
  def __repr__(self) -> str:
772
- return f"<MCPConfig(config='{self.config}')>"
773
 
774
 
775
  @overload
 
32
  from pydantic import AnyUrl
33
  from typing_extensions import Unpack
34
 
35
+ from fastmcp.client.auth.bearer import BearerAuth
36
  from fastmcp.client.auth.oauth import OAuth
37
  from fastmcp.server.dependencies import get_http_headers
38
  from fastmcp.server.server import FastMCP
 
153
  yield session
154
 
155
  def __repr__(self) -> str:
156
+ return f"<WebSocketTransport(url='{self.url}')>"
157
 
158
 
159
  class SSETransport(ClientTransport):
 
184
  if auth == "oauth":
185
  auth = OAuth(self.url)
186
  elif isinstance(auth, str):
187
+ auth = BearerAuth(auth)
 
188
  self.auth = auth
189
 
190
  @contextlib.asynccontextmanager
 
221
  yield session
222
 
223
  def __repr__(self) -> str:
224
+ return f"<SSETransport(url='{self.url}')>"
225
 
226
 
227
  class StreamableHttpTransport(ClientTransport):
 
252
  if auth == "oauth":
253
  auth = OAuth(self.url)
254
  elif isinstance(auth, str):
255
+ auth = BearerAuth(auth)
 
256
  self.auth = auth
257
 
258
  @contextlib.asynccontextmanager
 
290
  yield session
291
 
292
  def __repr__(self) -> str:
293
+ return f"<StreamableHttpTransport(url='{self.url}')>"
294
 
295
 
296
  class StdioTransport(ClientTransport):
 
682
  yield session
683
 
684
  def __repr__(self) -> str:
685
+ return f"<FastMCPTransport(server='{self.server.name}')>"
686
 
687
 
688
  class MCPConfigTransport(ClientTransport):
 
768
  yield session
769
 
770
  def __repr__(self) -> str:
771
+ return f"<MCPConfigTransport(config='{self.config}')>"
772
 
773
 
774
  @overload
tests/client/test_client.py CHANGED
@@ -4,9 +4,11 @@ from typing import cast
4
 
5
  import pytest
6
  from mcp import McpError
 
7
  from pydantic import AnyUrl
8
 
9
  from fastmcp.client import Client
 
10
  from fastmcp.client.transports import (
11
  FastMCPTransport,
12
  MCPConfigTransport,
@@ -810,3 +812,73 @@ class TestInferTransport:
810
  server = FastMCP1()
811
  transport = infer_transport(server)
812
  assert isinstance(transport, FastMCPTransport)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
 
5
  import pytest
6
  from mcp import McpError
7
+ from mcp.client.auth import OAuthClientProvider
8
  from pydantic import AnyUrl
9
 
10
  from fastmcp.client import Client
11
+ from fastmcp.client.auth.bearer import BearerAuth
12
  from fastmcp.client.transports import (
13
  FastMCPTransport,
14
  MCPConfigTransport,
 
812
  server = FastMCP1()
813
  transport = infer_transport(server)
814
  assert isinstance(transport, FastMCPTransport)
815
+
816
+
817
+ class TestAuth:
818
+ def test_default_auth_is_none(self):
819
+ client = Client(transport=StreamableHttpTransport("http://localhost:8000"))
820
+ assert client.transport.auth is None
821
+
822
+ def test_stdio_doesnt_support_auth(self):
823
+ with pytest.raises(ValueError, match="This transport does not support auth"):
824
+ Client(transport=StdioTransport("echo", ["hello"]), auth="oauth")
825
+
826
+ def test_oauth_literal_sets_up_oauth_shttp(self):
827
+ client = Client(
828
+ transport=StreamableHttpTransport("http://localhost:8000"), auth="oauth"
829
+ )
830
+ assert isinstance(client.transport, StreamableHttpTransport)
831
+ assert isinstance(client.transport.auth, OAuthClientProvider)
832
+
833
+ def test_oauth_literal_pass_direct_to_transport(self):
834
+ client = Client(
835
+ transport=StreamableHttpTransport("http://localhost:8000", auth="oauth"),
836
+ )
837
+ assert isinstance(client.transport, StreamableHttpTransport)
838
+ assert isinstance(client.transport.auth, OAuthClientProvider)
839
+
840
+ def test_oauth_literal_sets_up_oauth_sse(self):
841
+ client = Client(transport=SSETransport("http://localhost:8000"), auth="oauth")
842
+ assert isinstance(client.transport, SSETransport)
843
+ assert isinstance(client.transport.auth, OAuthClientProvider)
844
+
845
+ def test_oauth_literal_pass_direct_to_transport_sse(self):
846
+ client = Client(transport=SSETransport("http://localhost:8000", auth="oauth"))
847
+ assert isinstance(client.transport, SSETransport)
848
+ assert isinstance(client.transport.auth, OAuthClientProvider)
849
+
850
+ def test_auth_string_sets_up_bearer_auth_shttp(self):
851
+ client = Client(
852
+ transport=StreamableHttpTransport("http://localhost:8000"),
853
+ auth="test_token",
854
+ )
855
+ assert isinstance(client.transport, StreamableHttpTransport)
856
+ assert isinstance(client.transport.auth, BearerAuth)
857
+ assert client.transport.auth.token.get_secret_value() == "test_token"
858
+
859
+ def test_auth_string_pass_direct_to_transport_shttp(self):
860
+ client = Client(
861
+ transport=StreamableHttpTransport(
862
+ "http://localhost:8000", auth="test_token"
863
+ ),
864
+ )
865
+ assert isinstance(client.transport, StreamableHttpTransport)
866
+ assert isinstance(client.transport.auth, BearerAuth)
867
+ assert client.transport.auth.token.get_secret_value() == "test_token"
868
+
869
+ def test_auth_string_sets_up_bearer_auth_sse(self):
870
+ client = Client(
871
+ transport=SSETransport("http://localhost:8000"),
872
+ auth="test_token",
873
+ )
874
+ assert isinstance(client.transport, SSETransport)
875
+ assert isinstance(client.transport.auth, BearerAuth)
876
+ assert client.transport.auth.token.get_secret_value() == "test_token"
877
+
878
+ def test_auth_string_pass_direct_to_transport_sse(self):
879
+ client = Client(
880
+ transport=SSETransport("http://localhost:8000", auth="test_token"),
881
+ )
882
+ assert isinstance(client.transport, SSETransport)
883
+ assert isinstance(client.transport.auth, BearerAuth)
884
+ assert client.transport.auth.token.get_secret_value() == "test_token"
tests/server/test_proxy.py CHANGED
@@ -9,7 +9,7 @@ from pydantic import AnyUrl
9
 
10
  from fastmcp import FastMCP
11
  from fastmcp.client import Client
12
- from fastmcp.client.transports import FastMCPTransport
13
  from fastmcp.exceptions import ToolError
14
  from fastmcp.server.proxy import FastMCPProxy
15
 
@@ -104,7 +104,8 @@ def test_as_proxy_with_url():
104
  """FastMCP.as_proxy should accept a URL without connecting."""
105
  proxy = FastMCP.as_proxy("http://example.com/mcp")
106
  assert isinstance(proxy, FastMCPProxy)
107
- assert repr(proxy.client.transport).startswith("<StreamableHttp(")
 
108
 
109
 
110
  class TestTools:
 
9
 
10
  from fastmcp import FastMCP
11
  from fastmcp.client import Client
12
+ from fastmcp.client.transports import FastMCPTransport, StreamableHttpTransport
13
  from fastmcp.exceptions import ToolError
14
  from fastmcp.server.proxy import FastMCPProxy
15
 
 
104
  """FastMCP.as_proxy should accept a URL without connecting."""
105
  proxy = FastMCP.as_proxy("http://example.com/mcp")
106
  assert isinstance(proxy, FastMCPProxy)
107
+ assert isinstance(proxy.client.transport, StreamableHttpTransport)
108
+ assert proxy.client.transport.url == "http://example.com/mcp"
109
 
110
 
111
  class TestTools: