Spaces:
Running
Running
Merge pull request #649 from jlowin/simplify-process
Browse filesSimplify code for running servers in processes during tests
- src/fastmcp/utilities/tests.py +15 -5
- tests/auth/test_oauth_client.py +3 -22
- tests/client/test_openapi.py +17 -60
- tests/client/test_sse.py +11 -25
- tests/client/test_streamable_http.py +22 -42
- tests/server/http/test_http_dependencies.py +4 -40
src/fastmcp/utilities/tests.py
CHANGED
|
@@ -72,14 +72,21 @@ def _run_server(mcp_server: FastMCP, transport: Literal["sse"], port: int) -> No
|
|
| 72 |
|
| 73 |
@contextmanager
|
| 74 |
def run_server_in_process(
|
| 75 |
-
server_fn: Callable[..., None],
|
|
|
|
|
|
|
|
|
|
| 76 |
) -> Generator[str, None, None]:
|
| 77 |
"""
|
| 78 |
-
Context manager that runs a
|
| 79 |
-
server URL. When the context manager is exited, the server process is killed.
|
| 80 |
|
| 81 |
Args:
|
| 82 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 83 |
|
| 84 |
Returns:
|
| 85 |
The server URL.
|
|
@@ -87,8 +94,11 @@ def run_server_in_process(
|
|
| 87 |
host = "127.0.0.1"
|
| 88 |
port = find_available_port()
|
| 89 |
|
|
|
|
|
|
|
|
|
|
| 90 |
proc = multiprocessing.Process(
|
| 91 |
-
target=server_fn, args=
|
| 92 |
)
|
| 93 |
proc.start()
|
| 94 |
|
|
|
|
| 72 |
|
| 73 |
@contextmanager
|
| 74 |
def run_server_in_process(
|
| 75 |
+
server_fn: Callable[..., None],
|
| 76 |
+
*args,
|
| 77 |
+
provide_host_and_port: bool = True,
|
| 78 |
+
**kwargs,
|
| 79 |
) -> Generator[str, None, None]:
|
| 80 |
"""
|
| 81 |
+
Context manager that runs a FastMCP server in a separate process and
|
| 82 |
+
returns the server URL. When the context manager is exited, the server process is killed.
|
| 83 |
|
| 84 |
Args:
|
| 85 |
+
server_fn: The function that runs a FastMCP server. FastMCP servers are
|
| 86 |
+
not pickleable, so we need a function that creates and runs one.
|
| 87 |
+
*args: Arguments to pass to the server function.
|
| 88 |
+
provide_host_and_port: Whether to provide the host and port to the server function as kwargs.
|
| 89 |
+
**kwargs: Keyword arguments to pass to the server function.
|
| 90 |
|
| 91 |
Returns:
|
| 92 |
The server URL.
|
|
|
|
| 94 |
host = "127.0.0.1"
|
| 95 |
port = find_available_port()
|
| 96 |
|
| 97 |
+
if provide_host_and_port:
|
| 98 |
+
kwargs |= {"host": host, "port": port}
|
| 99 |
+
|
| 100 |
proc = multiprocessing.Process(
|
| 101 |
+
target=server_fn, args=args, kwargs=kwargs, daemon=True
|
| 102 |
)
|
| 103 |
proc.start()
|
| 104 |
|
tests/auth/test_oauth_client.py
CHANGED
|
@@ -1,11 +1,9 @@
|
|
| 1 |
-
import sys
|
| 2 |
from collections.abc import Generator
|
| 3 |
from unittest.mock import patch
|
| 4 |
from urllib.parse import parse_qs, urlparse
|
| 5 |
|
| 6 |
import httpx
|
| 7 |
import pytest
|
| 8 |
-
import uvicorn
|
| 9 |
|
| 10 |
import fastmcp.client.auth # Import module, not the function directly
|
| 11 |
from fastmcp.client import Client
|
|
@@ -39,30 +37,13 @@ def fastmcp_server(issuer_url: str):
|
|
| 39 |
return server
|
| 40 |
|
| 41 |
|
| 42 |
-
def run_server(host: str, port: int,
|
| 43 |
-
|
| 44 |
-
# Configure OAuth provider with the actual server URL
|
| 45 |
-
issuer_url = f"http://{host}:{port}"
|
| 46 |
-
app = fastmcp_server(issuer_url).http_app()
|
| 47 |
-
server = uvicorn.Server(
|
| 48 |
-
config=uvicorn.Config(
|
| 49 |
-
app=app,
|
| 50 |
-
host=host,
|
| 51 |
-
port=port,
|
| 52 |
-
log_level="error",
|
| 53 |
-
lifespan="on",
|
| 54 |
-
)
|
| 55 |
-
)
|
| 56 |
-
server.run()
|
| 57 |
-
except Exception as e:
|
| 58 |
-
print(f"Server error: {e}")
|
| 59 |
-
sys.exit(1)
|
| 60 |
-
sys.exit(0)
|
| 61 |
|
| 62 |
|
| 63 |
@pytest.fixture(scope="module")
|
| 64 |
def streamable_http_server() -> Generator[str, None, None]:
|
| 65 |
-
with run_server_in_process(run_server) as url:
|
| 66 |
yield f"{url}/mcp"
|
| 67 |
|
| 68 |
|
|
|
|
|
|
|
| 1 |
from collections.abc import Generator
|
| 2 |
from unittest.mock import patch
|
| 3 |
from urllib.parse import parse_qs, urlparse
|
| 4 |
|
| 5 |
import httpx
|
| 6 |
import pytest
|
|
|
|
| 7 |
|
| 8 |
import fastmcp.client.auth # Import module, not the function directly
|
| 9 |
from fastmcp.client import Client
|
|
|
|
| 37 |
return server
|
| 38 |
|
| 39 |
|
| 40 |
+
def run_server(host: str, port: int, **kwargs) -> None:
|
| 41 |
+
fastmcp_server(f"http://{host}:{port}").run(host=host, port=port, **kwargs)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 42 |
|
| 43 |
|
| 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 |
|
tests/client/test_openapi.py
CHANGED
|
@@ -1,9 +1,7 @@
|
|
| 1 |
import json
|
| 2 |
-
import sys
|
| 3 |
from collections.abc import Generator
|
| 4 |
|
| 5 |
import pytest
|
| 6 |
-
import uvicorn
|
| 7 |
from fastapi import FastAPI, Request
|
| 8 |
|
| 9 |
from fastmcp import Client, FastMCP
|
|
@@ -34,75 +32,34 @@ def fastmcp_server_for_headers() -> FastMCP:
|
|
| 34 |
return mcp
|
| 35 |
|
| 36 |
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
try:
|
| 40 |
-
app = fastmcp_server_for_headers().http_app(transport="streamable-http")
|
| 41 |
-
server = uvicorn.Server(
|
| 42 |
-
config=uvicorn.Config(
|
| 43 |
-
app=app,
|
| 44 |
-
host=host,
|
| 45 |
-
port=port,
|
| 46 |
-
log_level="error",
|
| 47 |
-
lifespan="on",
|
| 48 |
-
)
|
| 49 |
-
)
|
| 50 |
-
server.run()
|
| 51 |
-
except Exception as e:
|
| 52 |
-
print(f"Server error: {e}")
|
| 53 |
-
sys.exit(1)
|
| 54 |
-
sys.exit(0)
|
| 55 |
-
|
| 56 |
-
def run_sse_server(self, host: str, port: int) -> None:
|
| 57 |
-
try:
|
| 58 |
-
app = fastmcp_server_for_headers().http_app(transport="sse")
|
| 59 |
-
server = uvicorn.Server(
|
| 60 |
-
config=uvicorn.Config(
|
| 61 |
-
app=app,
|
| 62 |
-
host=host,
|
| 63 |
-
port=port,
|
| 64 |
-
log_level="error",
|
| 65 |
-
lifespan="on",
|
| 66 |
-
)
|
| 67 |
-
)
|
| 68 |
-
server.run()
|
| 69 |
-
except Exception as e:
|
| 70 |
-
print(f"Server error: {e}")
|
| 71 |
-
sys.exit(1)
|
| 72 |
-
sys.exit(0)
|
| 73 |
-
|
| 74 |
-
def run_proxy_server(self, host: str, port: int, remote_url: str) -> None:
|
| 75 |
-
try:
|
| 76 |
-
client = Client(transport=StreamableHttpTransport(remote_url))
|
| 77 |
-
app = FastMCP.as_proxy(client).http_app(transport="streamable-http")
|
| 78 |
-
server = uvicorn.Server(
|
| 79 |
-
config=uvicorn.Config(
|
| 80 |
-
app=app,
|
| 81 |
-
host=host,
|
| 82 |
-
port=port,
|
| 83 |
-
log_level="error",
|
| 84 |
-
lifespan="on",
|
| 85 |
-
)
|
| 86 |
-
)
|
| 87 |
-
server.run()
|
| 88 |
-
except Exception as e:
|
| 89 |
-
print(f"Server error: {e}")
|
| 90 |
-
sys.exit(1)
|
| 91 |
-
sys.exit(0)
|
| 92 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 93 |
@pytest.fixture(scope="class")
|
| 94 |
def shttp_server(self) -> Generator[str, None, None]:
|
| 95 |
-
with run_server_in_process(
|
| 96 |
yield f"{url}/mcp"
|
| 97 |
|
| 98 |
@pytest.fixture(scope="class")
|
| 99 |
def sse_server(self) -> Generator[str, None, None]:
|
| 100 |
-
with run_server_in_process(
|
| 101 |
yield f"{url}/sse"
|
| 102 |
|
| 103 |
@pytest.fixture(scope="class")
|
| 104 |
def proxy_server(self, shttp_server: str) -> Generator[str, None, None]:
|
| 105 |
-
with run_server_in_process(
|
|
|
|
|
|
|
|
|
|
|
|
|
| 106 |
yield f"{url}/mcp"
|
| 107 |
|
| 108 |
async def test_client_headers_sse_resource(self, sse_server: str):
|
|
|
|
| 1 |
import json
|
|
|
|
| 2 |
from collections.abc import Generator
|
| 3 |
|
| 4 |
import pytest
|
|
|
|
| 5 |
from fastapi import FastAPI, Request
|
| 6 |
|
| 7 |
from fastmcp import Client, FastMCP
|
|
|
|
| 32 |
return mcp
|
| 33 |
|
| 34 |
|
| 35 |
+
def run_server(host: str, port: int, **kwargs) -> None:
|
| 36 |
+
fastmcp_server_for_headers().run(host=host, port=port, **kwargs)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
|
| 38 |
+
|
| 39 |
+
def run_proxy_server(host: str, port: int, shttp_url: str, **kwargs) -> None:
|
| 40 |
+
client = Client(transport=StreamableHttpTransport(shttp_url))
|
| 41 |
+
app = FastMCP.as_proxy(client)
|
| 42 |
+
app.run(host=host, port=port, **kwargs)
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
class TestClientHeaders:
|
| 46 |
@pytest.fixture(scope="class")
|
| 47 |
def shttp_server(self) -> Generator[str, None, None]:
|
| 48 |
+
with run_server_in_process(run_server, transport="streamable-http") as url:
|
| 49 |
yield f"{url}/mcp"
|
| 50 |
|
| 51 |
@pytest.fixture(scope="class")
|
| 52 |
def sse_server(self) -> Generator[str, None, None]:
|
| 53 |
+
with run_server_in_process(run_server, transport="sse") as url:
|
| 54 |
yield f"{url}/sse"
|
| 55 |
|
| 56 |
@pytest.fixture(scope="class")
|
| 57 |
def proxy_server(self, shttp_server: str) -> Generator[str, None, None]:
|
| 58 |
+
with run_server_in_process(
|
| 59 |
+
run_proxy_server,
|
| 60 |
+
shttp_url=shttp_server,
|
| 61 |
+
transport="streamable-http",
|
| 62 |
+
) as url:
|
| 63 |
yield f"{url}/mcp"
|
| 64 |
|
| 65 |
async def test_client_headers_sse_resource(self, sse_server: str):
|
tests/client/test_sse.py
CHANGED
|
@@ -63,22 +63,13 @@ def fastmcp_server():
|
|
| 63 |
return server
|
| 64 |
|
| 65 |
|
| 66 |
-
def run_server(host: str, port: int,
|
| 67 |
-
|
| 68 |
-
app = fastmcp_server().http_app(transport="sse", path=path)
|
| 69 |
-
server = uvicorn.Server(
|
| 70 |
-
config=uvicorn.Config(app=app, host=host, port=port, log_level="error")
|
| 71 |
-
)
|
| 72 |
-
server.run()
|
| 73 |
-
except Exception as e:
|
| 74 |
-
print(f"Server error: {e}")
|
| 75 |
-
sys.exit(1)
|
| 76 |
-
sys.exit(0)
|
| 77 |
|
| 78 |
|
| 79 |
@pytest.fixture(autouse=True, scope="module")
|
| 80 |
def sse_server() -> Generator[str, None, None]:
|
| 81 |
-
with run_server_in_process(run_server) as url:
|
| 82 |
yield f"{url}/sse"
|
| 83 |
|
| 84 |
|
|
@@ -101,22 +92,17 @@ async def test_http_headers(sse_server: str):
|
|
| 101 |
|
| 102 |
|
| 103 |
def run_nested_server(host: str, port: int) -> None:
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
server.run()
|
| 112 |
-
except Exception as e:
|
| 113 |
-
print(f"Server error: {e}")
|
| 114 |
-
sys.exit(1)
|
| 115 |
-
sys.exit(0)
|
| 116 |
|
| 117 |
|
| 118 |
async def test_run_server_on_path():
|
| 119 |
-
with run_server_in_process(run_server, "/help") as url:
|
| 120 |
async with Client(transport=SSETransport(f"{url}/help")) as client:
|
| 121 |
result = await client.ping()
|
| 122 |
assert result is True
|
|
|
|
| 63 |
return server
|
| 64 |
|
| 65 |
|
| 66 |
+
def run_server(host: str, port: int, **kwargs) -> None:
|
| 67 |
+
fastmcp_server().run(host=host, port=port, **kwargs)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 68 |
|
| 69 |
|
| 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 |
|
|
|
|
| 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(
|
| 99 |
+
config=uvicorn.Config(app=mount2, host=host, port=port, log_level="error")
|
| 100 |
+
)
|
| 101 |
+
server.run()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 102 |
|
| 103 |
|
| 104 |
async def test_run_server_on_path():
|
| 105 |
+
with run_server_in_process(run_server, transport="sse", path="/help") as url:
|
| 106 |
async with Client(transport=SSETransport(f"{url}/help")) as client:
|
| 107 |
result = await client.ping()
|
| 108 |
assert result is True
|
tests/client/test_streamable_http.py
CHANGED
|
@@ -63,28 +63,33 @@ def fastmcp_server():
|
|
| 63 |
return server
|
| 64 |
|
| 65 |
|
| 66 |
-
def run_server(host: str, port: int) -> None:
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 77 |
)
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
print(f"Server error: {e}")
|
| 81 |
-
sys.exit(1)
|
| 82 |
-
sys.exit(0)
|
| 83 |
|
| 84 |
|
| 85 |
@pytest.fixture(scope="module")
|
| 86 |
def streamable_http_server() -> Generator[str, None, None]:
|
| 87 |
-
with run_server_in_process(run_server) as url:
|
| 88 |
yield f"{url}/mcp"
|
| 89 |
|
| 90 |
|
|
@@ -110,31 +115,6 @@ async def test_http_headers(streamable_http_server: str):
|
|
| 110 |
assert json_result["x-demo-header"] == "ABC"
|
| 111 |
|
| 112 |
|
| 113 |
-
def run_nested_server(host: str, port: int) -> None:
|
| 114 |
-
try:
|
| 115 |
-
mcp_app = fastmcp_server().http_app(path="/final/mcp")
|
| 116 |
-
|
| 117 |
-
mount = Starlette(routes=[Mount("/nest-inner", app=mcp_app)])
|
| 118 |
-
mount2 = Starlette(
|
| 119 |
-
routes=[Mount("/nest-outer", app=mount)],
|
| 120 |
-
lifespan=mcp_app.lifespan,
|
| 121 |
-
)
|
| 122 |
-
server = uvicorn.Server(
|
| 123 |
-
config=uvicorn.Config(
|
| 124 |
-
app=mount2,
|
| 125 |
-
host=host,
|
| 126 |
-
port=port,
|
| 127 |
-
log_level="error",
|
| 128 |
-
lifespan="on",
|
| 129 |
-
)
|
| 130 |
-
)
|
| 131 |
-
server.run()
|
| 132 |
-
except Exception as e:
|
| 133 |
-
print(f"Server error: {e}")
|
| 134 |
-
sys.exit(1)
|
| 135 |
-
sys.exit(0)
|
| 136 |
-
|
| 137 |
-
|
| 138 |
async def test_nested_streamable_http_server_resolves_correctly():
|
| 139 |
# tests patch for
|
| 140 |
# https://github.com/modelcontextprotocol/python-sdk/pull/659
|
|
|
|
| 63 |
return server
|
| 64 |
|
| 65 |
|
| 66 |
+
def run_server(host: str, port: int, **kwargs) -> None:
|
| 67 |
+
fastmcp_server().run(host=host, port=port, **kwargs)
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
def run_nested_server(host: str, port: int) -> None:
|
| 71 |
+
mcp_app = fastmcp_server().http_app(path="/final/mcp")
|
| 72 |
+
|
| 73 |
+
mount = Starlette(routes=[Mount("/nest-inner", app=mcp_app)])
|
| 74 |
+
mount2 = Starlette(
|
| 75 |
+
routes=[Mount("/nest-outer", app=mount)],
|
| 76 |
+
lifespan=mcp_app.lifespan,
|
| 77 |
+
)
|
| 78 |
+
server = uvicorn.Server(
|
| 79 |
+
config=uvicorn.Config(
|
| 80 |
+
app=mount2,
|
| 81 |
+
host=host,
|
| 82 |
+
port=port,
|
| 83 |
+
log_level="error",
|
| 84 |
+
lifespan="on",
|
| 85 |
)
|
| 86 |
+
)
|
| 87 |
+
server.run()
|
|
|
|
|
|
|
|
|
|
| 88 |
|
| 89 |
|
| 90 |
@pytest.fixture(scope="module")
|
| 91 |
def streamable_http_server() -> Generator[str, None, None]:
|
| 92 |
+
with run_server_in_process(run_server, transport="streamable-http") as url:
|
| 93 |
yield f"{url}/mcp"
|
| 94 |
|
| 95 |
|
|
|
|
| 115 |
assert json_result["x-demo-header"] == "ABC"
|
| 116 |
|
| 117 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 118 |
async def test_nested_streamable_http_server_resolves_correctly():
|
| 119 |
# tests patch for
|
| 120 |
# https://github.com/modelcontextprotocol/python-sdk/pull/659
|
tests/server/http/test_http_dependencies.py
CHANGED
|
@@ -1,9 +1,7 @@
|
|
| 1 |
import json
|
| 2 |
-
import sys
|
| 3 |
from collections.abc import Generator
|
| 4 |
|
| 5 |
import pytest
|
| 6 |
-
import uvicorn
|
| 7 |
|
| 8 |
from fastmcp.client import Client
|
| 9 |
from fastmcp.client.transports import SSETransport, StreamableHttpTransport
|
|
@@ -40,53 +38,19 @@ def fastmcp_server():
|
|
| 40 |
return server
|
| 41 |
|
| 42 |
|
| 43 |
-
def
|
| 44 |
-
|
| 45 |
-
app = fastmcp_server().http_app(transport="streamable-http")
|
| 46 |
-
server = uvicorn.Server(
|
| 47 |
-
config=uvicorn.Config(
|
| 48 |
-
app=app,
|
| 49 |
-
host=host,
|
| 50 |
-
port=port,
|
| 51 |
-
log_level="error",
|
| 52 |
-
lifespan="on",
|
| 53 |
-
)
|
| 54 |
-
)
|
| 55 |
-
server.run()
|
| 56 |
-
except Exception as e:
|
| 57 |
-
print(f"Server error: {e}")
|
| 58 |
-
sys.exit(1)
|
| 59 |
-
sys.exit(0)
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
def run_sse_server(host: str, port: int) -> None:
|
| 63 |
-
try:
|
| 64 |
-
app = fastmcp_server().http_app(transport="sse")
|
| 65 |
-
server = uvicorn.Server(
|
| 66 |
-
config=uvicorn.Config(
|
| 67 |
-
app=app,
|
| 68 |
-
host=host,
|
| 69 |
-
port=port,
|
| 70 |
-
log_level="error",
|
| 71 |
-
lifespan="on",
|
| 72 |
-
)
|
| 73 |
-
)
|
| 74 |
-
server.run()
|
| 75 |
-
except Exception as e:
|
| 76 |
-
print(f"Server error: {e}")
|
| 77 |
-
sys.exit(1)
|
| 78 |
-
sys.exit(0)
|
| 79 |
|
| 80 |
|
| 81 |
@pytest.fixture(autouse=True, scope="module")
|
| 82 |
def shttp_server() -> Generator[str, None, None]:
|
| 83 |
-
with run_server_in_process(
|
| 84 |
yield f"{url}/mcp"
|
| 85 |
|
| 86 |
|
| 87 |
@pytest.fixture(autouse=True, scope="module")
|
| 88 |
def sse_server() -> Generator[str, None, None]:
|
| 89 |
-
with run_server_in_process(
|
| 90 |
yield f"{url}/sse"
|
| 91 |
|
| 92 |
|
|
|
|
| 1 |
import json
|
|
|
|
| 2 |
from collections.abc import Generator
|
| 3 |
|
| 4 |
import pytest
|
|
|
|
| 5 |
|
| 6 |
from fastmcp.client import Client
|
| 7 |
from fastmcp.client.transports import SSETransport, StreamableHttpTransport
|
|
|
|
| 38 |
return server
|
| 39 |
|
| 40 |
|
| 41 |
+
def run_server(host: str, port: int, **kwargs) -> None:
|
| 42 |
+
fastmcp_server().run(host=host, port=port, **kwargs)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 43 |
|
| 44 |
|
| 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 |
|