Jeremiah Lowin commited on
Commit
7b03b73
·
1 Parent(s): b8612c0

Update docs and test

Browse files
docs/deployment/asgi.mdx CHANGED
@@ -48,7 +48,7 @@ Both approaches return a Starlette application that can be integrated with other
48
  The returned app stores the `FastMCP` instance on `app.state.fastmcp_server`, so you
49
  can access it from custom middleware or routes via `request.app.state.fastmcp_server`.
50
 
51
- The MCP server's endpoint is mounted at the root path `/mcp` for Streamable HTTP transport, and `/sse` for SSE transport, though you can change these paths by passing a `path` argument to the `http_app()` method:
52
 
53
  ```python
54
  # For Streamable HTTP transport
@@ -137,7 +137,7 @@ app = Starlette(
137
  )
138
  ```
139
 
140
- The MCP endpoint will be available at `/mcp-server/mcp` of the resulting Starlette app.
141
 
142
  <Warning>
143
  For Streamable HTTP transport, you **must** pass the lifespan context from the FastMCP app to the resulting Starlette app, as nested lifespans are not recognized. Otherwise, the FastMCP server's session manager will not be properly initialized.
@@ -167,7 +167,7 @@ app = Starlette(
167
  )
168
  ```
169
 
170
- In this setup, the MCP server is accessible at the `/outer/inner/mcp` path of the resulting Starlette app.
171
 
172
  <Warning>
173
  For Streamable HTTP transport, you **must** pass the lifespan context from the FastMCP app to the *outer* Starlette app, as nested lifespans are not recognized. Otherwise, the FastMCP server's session manager will not be properly initialized.
@@ -194,7 +194,7 @@ app = FastAPI(lifespan=mcp_app.lifespan)
194
  app.mount("/mcp-server", mcp_app)
195
  ```
196
 
197
- The MCP endpoint will be available at `/mcp-server/mcp` of the resulting FastAPI app.
198
 
199
  <Warning>
200
  For Streamable HTTP transport, you **must** pass the lifespan context from the FastMCP app to the resulting FastAPI app, as nested lifespans are not recognized. Otherwise, the FastMCP server's session manager will not be properly initialized.
 
48
  The returned app stores the `FastMCP` instance on `app.state.fastmcp_server`, so you
49
  can access it from custom middleware or routes via `request.app.state.fastmcp_server`.
50
 
51
+ The MCP server's endpoint is mounted at the root path `/mcp/` for Streamable HTTP transport, and `/sse/` for SSE transport, though you can change these paths by passing a `path` argument to the `http_app()` method:
52
 
53
  ```python
54
  # For Streamable HTTP transport
 
137
  )
138
  ```
139
 
140
+ The MCP endpoint will be available at `/mcp-server/mcp/` of the resulting Starlette app.
141
 
142
  <Warning>
143
  For Streamable HTTP transport, you **must** pass the lifespan context from the FastMCP app to the resulting Starlette app, as nested lifespans are not recognized. Otherwise, the FastMCP server's session manager will not be properly initialized.
 
167
  )
168
  ```
169
 
170
+ In this setup, the MCP server is accessible at the `/outer/inner/mcp/` path of the resulting Starlette app.
171
 
172
  <Warning>
173
  For Streamable HTTP transport, you **must** pass the lifespan context from the FastMCP app to the *outer* Starlette app, as nested lifespans are not recognized. Otherwise, the FastMCP server's session manager will not be properly initialized.
 
194
  app.mount("/mcp-server", mcp_app)
195
  ```
196
 
197
+ The MCP endpoint will be available at `/mcp-server/mcp/` of the resulting FastAPI app.
198
 
199
  <Warning>
200
  For Streamable HTTP transport, you **must** pass the lifespan context from the FastMCP app to the resulting FastAPI app, as nested lifespans are not recognized. Otherwise, the FastMCP server's session manager will not be properly initialized.
docs/deployment/running-server.mdx CHANGED
@@ -105,7 +105,7 @@ When using Stdio transport, you will typically *not* run the server yourself as
105
 
106
  Streamable HTTP is a modern, efficient transport for exposing your MCP server via HTTP. It is the recommended transport for web-based deployments.
107
 
108
- To run a server using Streamable HTTP, you can use the `run()` method with the `transport` argument set to `"streamable-http"`. This will start a Uvicorn server on the default host (`127.0.0.1`), port (`8000`), and path (`/mcp`).
109
  <CodeGroup>
110
  ```python {6} server.py
111
  from fastmcp import FastMCP
@@ -120,7 +120,7 @@ import asyncio
120
  from fastmcp import Client
121
 
122
  async def example():
123
- async with Client("http://127.0.0.1:8000/mcp") as client:
124
  await client.ping()
125
 
126
  if __name__ == "__main__":
@@ -168,7 +168,7 @@ New applications should use Streamable HTTP transport instead.
168
 
169
  Server-Sent Events (SSE) is an HTTP-based protocol for server-to-client streaming. While FastMCP still supports SSE, it is deprecated and Streamable HTTP is preferred for new projects.
170
 
171
- To run a server using SSE, you can use the `run()` method with the `transport` argument set to `"sse"`. This will start a Uvicorn server on the default host (`127.0.0.1`), port (`8000`), and with default SSE path (`/sse`) and message path (`/messages/`).
172
 
173
  <CodeGroup>
174
  ```python {6} server.py
@@ -186,7 +186,7 @@ from fastmcp.client.transports import SSETransport
186
 
187
  async def example():
188
  async with Client(
189
- transport=SSETransport("http://127.0.0.1:8000/sse")
190
  ) as client:
191
  await client.ping()
192
 
 
105
 
106
  Streamable HTTP is a modern, efficient transport for exposing your MCP server via HTTP. It is the recommended transport for web-based deployments.
107
 
108
+ To run a server using Streamable HTTP, you can use the `run()` method with the `transport` argument set to `"streamable-http"`. This will start a Uvicorn server on the default host (`127.0.0.1`), port (`8000`), and path (`/mcp/`).
109
  <CodeGroup>
110
  ```python {6} server.py
111
  from fastmcp import FastMCP
 
120
  from fastmcp import Client
121
 
122
  async def example():
123
+ async with Client("http://127.0.0.1:8000/mcp/") as client:
124
  await client.ping()
125
 
126
  if __name__ == "__main__":
 
168
 
169
  Server-Sent Events (SSE) is an HTTP-based protocol for server-to-client streaming. While FastMCP still supports SSE, it is deprecated and Streamable HTTP is preferred for new projects.
170
 
171
+ To run a server using SSE, you can use the `run()` method with the `transport` argument set to `"sse"`. This will start a Uvicorn server on the default host (`127.0.0.1`), port (`8000`), and with default SSE path (`/sse/`) and message path (`/messages/`).
172
 
173
  <CodeGroup>
174
  ```python {6} server.py
 
186
 
187
  async def example():
188
  async with Client(
189
+ transport=SSETransport("http://127.0.0.1:8000/sse/")
190
  ) as client:
191
  await client.ping()
192
 
docs/tutorials/rest-api.mdx CHANGED
@@ -103,7 +103,7 @@ from fastmcp import Client
103
 
104
  async def main():
105
  # Connect to the MCP server we just created
106
- async with Client("http://127.0.0.1:8000/mcp") as client:
107
 
108
  # List the tools that were automatically generated
109
  tools = await client.list_tools()
 
103
 
104
  async def main():
105
  # Connect to the MCP server we just created
106
+ async with Client("http://127.0.0.1:8000/mcp/") as client:
107
 
108
  # List the tools that were automatically generated
109
  tools = await client.list_tools()
src/fastmcp/client/auth/oauth.py CHANGED
@@ -307,7 +307,7 @@ def OAuth(
307
 
308
  Args:
309
  mcp_url: Full URL to the MCP endpoint (e.g.,
310
- "http://host/mcp/sse")
311
  scopes: OAuth scopes to request. Can be a
312
  space-separated string or a list of strings.
313
  client_name: Name for this client during registration
 
307
 
308
  Args:
309
  mcp_url: Full URL to the MCP endpoint (e.g.,
310
+ "http://host/mcp/sse/")
311
  scopes: OAuth scopes to request. Can be a
312
  space-separated string or a list of strings.
313
  client_name: Name for this client during registration
src/fastmcp/utilities/mcp_config.py CHANGED
@@ -1,5 +1,6 @@
1
  from __future__ import annotations
2
 
 
3
  from typing import TYPE_CHECKING, Annotated, Any, Literal
4
  from urllib.parse import urlparse
5
 
@@ -28,7 +29,8 @@ def infer_transport_type_from_url(
28
  parsed_url = urlparse(url)
29
  path = parsed_url.path
30
 
31
- if "/sse/" in path or path.rstrip("/").endswith("/sse"):
 
32
  return "sse"
33
  else:
34
  return "streamable-http"
 
1
  from __future__ import annotations
2
 
3
+ import re
4
  from typing import TYPE_CHECKING, Annotated, Any, Literal
5
  from urllib.parse import urlparse
6
 
 
29
  parsed_url = urlparse(url)
30
  path = parsed_url.path
31
 
32
+ # Match /sse followed by /, ?, &, or end of string
33
+ if re.search(r"/sse(/|\?|&|$)", path):
34
  return "sse"
35
  else:
36
  return "streamable-http"
tests/auth/providers/test_bearer.py CHANGED
@@ -67,7 +67,7 @@ def mcp_server_url(rsa_key_pair: RSAKeyPair) -> Generator[str]:
67
  public_key=rsa_key_pair.public_key,
68
  run_kwargs=dict(transport="streamable-http"),
69
  ) as url:
70
- yield f"{url}/mcp"
71
 
72
 
73
  class TestRSAKeyPair:
@@ -698,7 +698,7 @@ class TestFastMCPBearerAuth:
698
  auth_kwargs=dict(required_scopes=["read", "write"]),
699
  run_kwargs=dict(transport="streamable-http"),
700
  ) as url:
701
- mcp_server_url = f"{url}/mcp"
702
  with pytest.raises(httpx.HTTPStatusError) as exc_info:
703
  async with Client(mcp_server_url, auth=BearerAuth(token)) as client:
704
  tools = await client.list_tools() # noqa: F841
@@ -721,7 +721,7 @@ class TestFastMCPBearerAuth:
721
  auth_kwargs=dict(required_scopes=["read", "write"]),
722
  run_kwargs=dict(transport="streamable-http"),
723
  ) as url:
724
- mcp_server_url = f"{url}/mcp"
725
  async with Client(mcp_server_url, auth=BearerAuth(token)) as client:
726
  tools = await client.list_tools()
727
  assert tools
 
67
  public_key=rsa_key_pair.public_key,
68
  run_kwargs=dict(transport="streamable-http"),
69
  ) as url:
70
+ yield f"{url}/mcp/"
71
 
72
 
73
  class TestRSAKeyPair:
 
698
  auth_kwargs=dict(required_scopes=["read", "write"]),
699
  run_kwargs=dict(transport="streamable-http"),
700
  ) as url:
701
+ mcp_server_url = f"{url}/mcp/"
702
  with pytest.raises(httpx.HTTPStatusError) as exc_info:
703
  async with Client(mcp_server_url, auth=BearerAuth(token)) as client:
704
  tools = await client.list_tools() # noqa: F841
 
721
  auth_kwargs=dict(required_scopes=["read", "write"]),
722
  run_kwargs=dict(transport="streamable-http"),
723
  ) as url:
724
+ mcp_server_url = f"{url}/mcp/"
725
  async with Client(mcp_server_url, auth=BearerAuth(token)) as client:
726
  tools = await client.list_tools()
727
  assert tools
tests/auth/test_oauth_client.py CHANGED
@@ -44,7 +44,7 @@ def run_server(host: str, port: int, **kwargs) -> None:
44
  @pytest.fixture(scope="module")
45
  def streamable_http_server() -> Generator[str, None, None]:
46
  with run_server_in_process(run_server, transport="streamable-http") as url:
47
- yield f"{url}/mcp"
48
 
49
 
50
  @pytest.fixture()
 
44
  @pytest.fixture(scope="module")
45
  def streamable_http_server() -> Generator[str, None, None]:
46
  with run_server_in_process(run_server, transport="streamable-http") as url:
47
+ yield f"{url}/mcp/"
48
 
49
 
50
  @pytest.fixture()
tests/client/test_client.py CHANGED
@@ -735,7 +735,8 @@ class TestInferTransport:
735
  "http://example.com/api/sse/stream",
736
  "https://localhost:8080/mcp/sse/endpoint",
737
  "http://example.com/api/sse",
738
- "https://localhost:8080/mcp/sse",
 
739
  "http://example.com/api/sse?param=value",
740
  "https://localhost:8080/mcp/sse/?param=value",
741
  "https://localhost:8000/mcp/sse?x=1&y=2",
@@ -744,6 +745,7 @@ class TestInferTransport:
744
  "path_with_sse_directory",
745
  "path_with_sse_subdirectory",
746
  "path_ending_with_sse",
 
747
  "path_ending_with_sse_https",
748
  "path_with_sse_and_query_params",
749
  "path_with_sse_slash_and_query_params",
@@ -758,7 +760,7 @@ class TestInferTransport:
758
  "url",
759
  [
760
  "http://example.com/api",
761
- "https://localhost:8080/mcp",
762
  "http://example.com/asset/image.jpg",
763
  "https://localhost:8080/sservice/endpoint",
764
  "https://example.com/assets/file",
@@ -779,7 +781,7 @@ class TestInferTransport:
779
  config = {
780
  "mcpServers": {
781
  "test_server": {
782
- "url": "http://localhost:8000/sse",
783
  "headers": {"Authorization": "Bearer 123"},
784
  },
785
  }
@@ -787,7 +789,7 @@ class TestInferTransport:
787
  transport = infer_transport(config)
788
  assert isinstance(transport, MCPConfigTransport)
789
  assert isinstance(transport.transport, SSETransport)
790
- assert transport.transport.url == "http://localhost:8000/sse"
791
  assert transport.transport.headers == {"Authorization": "Bearer 123"}
792
 
793
  def test_infer_local_transport_from_config(self):
@@ -825,7 +827,7 @@ class TestInferTransport:
825
  "args": ["hello"],
826
  },
827
  "remote": {
828
- "url": "http://localhost:8000/sse",
829
  "headers": {"Authorization": "Bearer 123"},
830
  },
831
  }
 
735
  "http://example.com/api/sse/stream",
736
  "https://localhost:8080/mcp/sse/endpoint",
737
  "http://example.com/api/sse",
738
+ "http://example.com/api/sse/",
739
+ "https://localhost:8080/mcp/sse/",
740
  "http://example.com/api/sse?param=value",
741
  "https://localhost:8080/mcp/sse/?param=value",
742
  "https://localhost:8000/mcp/sse?x=1&y=2",
 
745
  "path_with_sse_directory",
746
  "path_with_sse_subdirectory",
747
  "path_ending_with_sse",
748
+ "path_ending_with_sse_slash",
749
  "path_ending_with_sse_https",
750
  "path_with_sse_and_query_params",
751
  "path_with_sse_slash_and_query_params",
 
760
  "url",
761
  [
762
  "http://example.com/api",
763
+ "https://localhost:8080/mcp/",
764
  "http://example.com/asset/image.jpg",
765
  "https://localhost:8080/sservice/endpoint",
766
  "https://example.com/assets/file",
 
781
  config = {
782
  "mcpServers": {
783
  "test_server": {
784
+ "url": "http://localhost:8000/sse/",
785
  "headers": {"Authorization": "Bearer 123"},
786
  },
787
  }
 
789
  transport = infer_transport(config)
790
  assert isinstance(transport, MCPConfigTransport)
791
  assert isinstance(transport.transport, SSETransport)
792
+ assert transport.transport.url == "http://localhost:8000/sse/"
793
  assert transport.transport.headers == {"Authorization": "Bearer 123"}
794
 
795
  def test_infer_local_transport_from_config(self):
 
827
  "args": ["hello"],
828
  },
829
  "remote": {
830
+ "url": "http://localhost:8000/sse/",
831
  "headers": {"Authorization": "Bearer 123"},
832
  },
833
  }
tests/client/test_openapi.py CHANGED
@@ -57,12 +57,12 @@ class TestClientHeaders:
57
  @pytest.fixture(scope="class")
58
  def shttp_server(self) -> Generator[str, None, None]:
59
  with run_server_in_process(run_server, transport="streamable-http") as url:
60
- yield f"{url}/mcp"
61
 
62
  @pytest.fixture(scope="class")
63
  def sse_server(self) -> Generator[str, None, None]:
64
  with run_server_in_process(run_server, transport="sse") as url:
65
- yield f"{url}/sse"
66
 
67
  @pytest.fixture(scope="class")
68
  def proxy_server(self, shttp_server: str) -> Generator[str, None, None]:
@@ -71,7 +71,7 @@ class TestClientHeaders:
71
  shttp_url=shttp_server,
72
  transport="streamable-http",
73
  ) as url:
74
- yield f"{url}/mcp"
75
 
76
  async def test_client_headers_sse_resource(self, sse_server: str):
77
  async with Client(
 
57
  @pytest.fixture(scope="class")
58
  def shttp_server(self) -> Generator[str, None, None]:
59
  with run_server_in_process(run_server, transport="streamable-http") as url:
60
+ yield f"{url}/mcp/"
61
 
62
  @pytest.fixture(scope="class")
63
  def sse_server(self) -> Generator[str, None, None]:
64
  with run_server_in_process(run_server, transport="sse") as url:
65
+ yield f"{url}/sse/"
66
 
67
  @pytest.fixture(scope="class")
68
  def proxy_server(self, shttp_server: str) -> Generator[str, None, None]:
 
71
  shttp_url=shttp_server,
72
  transport="streamable-http",
73
  ) as url:
74
+ yield f"{url}/mcp/"
75
 
76
  async def test_client_headers_sse_resource(self, sse_server: str):
77
  async with Client(
tests/client/test_sse.py CHANGED
@@ -70,7 +70,7 @@ def run_server(host: str, port: int, **kwargs) -> None:
70
  @pytest.fixture(autouse=True, scope="module")
71
  def sse_server() -> Generator[str, None, None]:
72
  with run_server_in_process(run_server, transport="sse") as url:
73
- yield f"{url}/sse"
74
 
75
 
76
  async def test_ping(sse_server: str):
@@ -92,7 +92,7 @@ async def test_http_headers(sse_server: str):
92
 
93
 
94
  def run_nested_server(host: str, port: int) -> None:
95
- app = fastmcp_server().sse_app(path="/mcp/sse", message_path="/mcp/messages")
96
  mount = Starlette(routes=[Mount("/nest-inner", app=app)])
97
  mount2 = Starlette(routes=[Mount("/nest-outer", app=mount)])
98
  server = uvicorn.Server(
@@ -114,7 +114,7 @@ async def test_nested_sse_server_resolves_correctly():
114
 
115
  with run_server_in_process(run_nested_server) as url:
116
  async with Client(
117
- transport=SSETransport(f"{url}/nest-outer/nest-inner/mcp/sse")
118
  ) as client:
119
  result = await client.ping()
120
  assert result is True
 
70
  @pytest.fixture(autouse=True, scope="module")
71
  def sse_server() -> Generator[str, None, None]:
72
  with run_server_in_process(run_server, transport="sse") as url:
73
+ yield f"{url}/sse/"
74
 
75
 
76
  async def test_ping(sse_server: str):
 
92
 
93
 
94
  def run_nested_server(host: str, port: int) -> None:
95
+ app = fastmcp_server().sse_app(path="/mcp/sse/", message_path="/mcp/messages")
96
  mount = Starlette(routes=[Mount("/nest-inner", app=app)])
97
  mount2 = Starlette(routes=[Mount("/nest-outer", app=mount)])
98
  server = uvicorn.Server(
 
114
 
115
  with run_server_in_process(run_nested_server) as url:
116
  async with Client(
117
+ transport=SSETransport(f"{url}/nest-outer/nest-inner/mcp/sse/")
118
  ) as client:
119
  result = await client.ping()
120
  assert result is True
tests/client/test_streamable_http.py CHANGED
@@ -79,7 +79,7 @@ def run_server(host: str, port: int, stateless_http: bool = False, **kwargs) ->
79
 
80
 
81
  def run_nested_server(host: str, port: int) -> None:
82
- mcp_app = fastmcp_server().http_app(path="/final/mcp")
83
 
84
  mount = Starlette(routes=[Mount("/nest-inner", app=mcp_app)])
85
  mount2 = Starlette(
@@ -105,9 +105,9 @@ async def streamable_http_server(
105
  with run_server_in_process(
106
  run_server, stateless_http=stateless_http, transport="streamable-http"
107
  ) as url:
108
- async with Client(transport=StreamableHttpTransport(f"{url}/mcp")) as client:
109
  assert await client.ping()
110
- yield f"{url}/mcp"
111
 
112
 
113
  async def test_ping(streamable_http_server: str):
@@ -156,7 +156,7 @@ async def test_nested_streamable_http_server_resolves_correctly():
156
 
157
  with run_server_in_process(run_nested_server) as url:
158
  async with Client(
159
- transport=StreamableHttpTransport(f"{url}/nest-outer/nest-inner/final/mcp")
160
  ) as client:
161
  result = await client.ping()
162
  assert result is True
 
79
 
80
 
81
  def run_nested_server(host: str, port: int) -> None:
82
+ mcp_app = fastmcp_server().http_app(path="/final/mcp/")
83
 
84
  mount = Starlette(routes=[Mount("/nest-inner", app=mcp_app)])
85
  mount2 = Starlette(
 
105
  with run_server_in_process(
106
  run_server, stateless_http=stateless_http, transport="streamable-http"
107
  ) as url:
108
+ async with Client(transport=StreamableHttpTransport(f"{url}/mcp/")) as client:
109
  assert await client.ping()
110
+ yield f"{url}/mcp/"
111
 
112
 
113
  async def test_ping(streamable_http_server: str):
 
156
 
157
  with run_server_in_process(run_nested_server) as url:
158
  async with Client(
159
+ transport=StreamableHttpTransport(f"{url}/nest-outer/nest-inner/final/mcp/")
160
  ) as client:
161
  result = await client.ping()
162
  assert result is True
tests/deprecated/test_settings.py CHANGED
@@ -123,7 +123,7 @@ class TestDeprecatedServerInitKwargs:
123
  debug=False,
124
  host="127.0.0.1",
125
  port=9999,
126
- sse_path="/sse",
127
  message_path="/msg",
128
  streamable_http_path="/http",
129
  json_response=False,
@@ -162,7 +162,7 @@ class TestDeprecatedServerInitKwargs:
162
  assert server._deprecated_settings.debug is False
163
  assert server._deprecated_settings.host == "127.0.0.1"
164
  assert server._deprecated_settings.port == 9999
165
- assert server._deprecated_settings.sse_path == "/sse"
166
  assert server._deprecated_settings.message_path == "/msg"
167
  assert server._deprecated_settings.streamable_http_path == "/http"
168
  assert server._deprecated_settings.json_response is False
 
123
  debug=False,
124
  host="127.0.0.1",
125
  port=9999,
126
+ sse_path="/sse/",
127
  message_path="/msg",
128
  streamable_http_path="/http",
129
  json_response=False,
 
162
  assert server._deprecated_settings.debug is False
163
  assert server._deprecated_settings.host == "127.0.0.1"
164
  assert server._deprecated_settings.port == 9999
165
+ assert server._deprecated_settings.sse_path == "/sse/"
166
  assert server._deprecated_settings.message_path == "/msg"
167
  assert server._deprecated_settings.streamable_http_path == "/http"
168
  assert server._deprecated_settings.json_response is False
tests/server/http/test_custom_routes.py CHANGED
@@ -55,7 +55,7 @@ class TestCustomRoutes:
55
  """Test that custom routes are included when using create_sse_app directly."""
56
  # Create the app by calling the constructor function directly
57
  app = create_sse_app(
58
- server=server_with_custom_route, message_path="/message", sse_path="/sse"
59
  )
60
 
61
  # Verify that the custom route is included
 
55
  """Test that custom routes are included when using create_sse_app directly."""
56
  # Create the app by calling the constructor function directly
57
  app = create_sse_app(
58
+ server=server_with_custom_route, message_path="/message", sse_path="/sse/"
59
  )
60
 
61
  # Verify that the custom route is included
tests/server/http/test_http_dependencies.py CHANGED
@@ -45,13 +45,13 @@ def run_server(host: str, port: int, **kwargs) -> None:
45
  @pytest.fixture(autouse=True, scope="module")
46
  def shttp_server() -> Generator[str, None, None]:
47
  with run_server_in_process(run_server, transport="streamable-http") as url:
48
- yield f"{url}/mcp"
49
 
50
 
51
  @pytest.fixture(autouse=True, scope="module")
52
  def sse_server() -> Generator[str, None, None]:
53
  with run_server_in_process(run_server, transport="sse") as url:
54
- yield f"{url}/sse"
55
 
56
 
57
  async def test_http_headers_resource_shttp(shttp_server: str):
 
45
  @pytest.fixture(autouse=True, scope="module")
46
  def shttp_server() -> Generator[str, None, None]:
47
  with run_server_in_process(run_server, transport="streamable-http") as url:
48
+ yield f"{url}/mcp/"
49
 
50
 
51
  @pytest.fixture(autouse=True, scope="module")
52
  def sse_server() -> Generator[str, None, None]:
53
  with run_server_in_process(run_server, transport="sse") as url:
54
+ yield f"{url}/sse/"
55
 
56
 
57
  async def test_http_headers_resource_shttp(shttp_server: str):
tests/server/http/test_http_middleware.py CHANGED
@@ -126,7 +126,7 @@ async def test_create_sse_app_with_custom_middleware():
126
  app = create_sse_app(
127
  server=server,
128
  message_path="/message",
129
- sse_path="/sse",
130
  middleware=custom_middleware,
131
  routes=additional_routes,
132
  )
 
126
  app = create_sse_app(
127
  server=server,
128
  message_path="/message",
129
+ sse_path="/sse/",
130
  middleware=custom_middleware,
131
  routes=additional_routes,
132
  )
tests/server/test_app_state.py CHANGED
@@ -16,11 +16,11 @@ def test_http_app_sse_sets_mcp_server_state():
16
 
17
  def test_create_streamable_http_app_sets_state():
18
  server = FastMCP(name="StateTest")
19
- app = create_streamable_http_app(server, "/mcp")
20
  assert app.state.fastmcp_server is server
21
 
22
 
23
  def test_create_sse_app_sets_state():
24
  server = FastMCP(name="StateTest")
25
- app = create_sse_app(server, message_path="/message", sse_path="/sse")
26
  assert app.state.fastmcp_server is server
 
16
 
17
  def test_create_streamable_http_app_sets_state():
18
  server = FastMCP(name="StateTest")
19
+ app = create_streamable_http_app(server, "/mcp/")
20
  assert app.state.fastmcp_server is server
21
 
22
 
23
  def test_create_sse_app_sets_state():
24
  server = FastMCP(name="StateTest")
25
+ app = create_sse_app(server, message_path="/message", sse_path="/sse/")
26
  assert app.state.fastmcp_server is server
tests/server/test_mount.py CHANGED
@@ -273,7 +273,9 @@ class TestMultipleServerMount:
273
  main_app.mount(working_app, "working")
274
 
275
  # Use an unreachable port
276
- unreachable_client = Client(transport=SSETransport("http://127.0.0.1:9999/sse"))
 
 
277
 
278
  # Create a proxy server that will fail to connect
279
  unreachable_proxy = FastMCP.as_proxy(unreachable_client)
 
273
  main_app.mount(working_app, "working")
274
 
275
  # Use an unreachable port
276
+ unreachable_client = Client(
277
+ transport=SSETransport("http://127.0.0.1:9999/sse/")
278
+ )
279
 
280
  # Create a proxy server that will fail to connect
281
  unreachable_proxy = FastMCP.as_proxy(unreachable_client)
tests/server/test_proxy.py CHANGED
@@ -102,10 +102,10 @@ async def test_as_proxy_with_transport(fastmcp_server):
102
 
103
  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 isinstance(proxy.client.transport, StreamableHttpTransport)
108
- assert proxy.client.transport.url == "http://example.com/mcp"
109
 
110
 
111
  class TestTools:
 
102
 
103
  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 isinstance(proxy.client.transport, StreamableHttpTransport)
108
+ assert proxy.client.transport.url == "http://example.com/mcp/"
109
 
110
 
111
  class TestTools:
tests/utilities/test_mcp_config.py CHANGED
@@ -61,21 +61,21 @@ def test_parse_remote_config_with_url_inference():
61
  config = {
62
  "mcpServers": {
63
  "test_server": {
64
- "url": "http://localhost:8000/sse",
65
  }
66
  }
67
  }
68
  mcp_config = MCPConfig.from_dict(config)
69
  transport = mcp_config.mcpServers["test_server"].to_transport()
70
  assert isinstance(transport, SSETransport)
71
- assert transport.url == "http://localhost:8000/sse"
72
 
73
 
74
  def test_parse_multiple_servers():
75
  config = {
76
  "mcpServers": {
77
  "test_server": {
78
- "url": "http://localhost:8000/sse",
79
  },
80
  "test_server_2": {
81
  "command": "echo",
@@ -172,7 +172,7 @@ 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
  }
 
61
  config = {
62
  "mcpServers": {
63
  "test_server": {
64
+ "url": "http://localhost:8000/sse/",
65
  }
66
  }
67
  }
68
  mcp_config = MCPConfig.from_dict(config)
69
  transport = mcp_config.mcpServers["test_server"].to_transport()
70
  assert isinstance(transport, SSETransport)
71
+ assert transport.url == "http://localhost:8000/sse/"
72
 
73
 
74
  def test_parse_multiple_servers():
75
  config = {
76
  "mcpServers": {
77
  "test_server": {
78
+ "url": "http://localhost:8000/sse/",
79
  },
80
  "test_server_2": {
81
  "command": "echo",
 
172
  config = {
173
  "mcpServers": {
174
  "test_server": {
175
+ "url": "http://localhost:8000/sse/",
176
  "auth": "test_token",
177
  }
178
  }