Jeremiah Lowin commited on
Commit
ad2e1e9
·
1 Parent(s): c33c81f

Fix tests

Browse files
Files changed (2) hide show
  1. tests/server/test_lifespan.py +140 -17
  2. uv.lock +0 -0
tests/server/test_lifespan.py CHANGED
@@ -4,6 +4,11 @@ from collections.abc import AsyncIterator
4
  from contextlib import asynccontextmanager
5
 
6
  import anyio
 
 
 
 
 
7
  from mcp.types import (
8
  ClientCapabilities,
9
  Implementation,
@@ -14,9 +19,119 @@ from mcp.types import (
14
  )
15
  from pydantic import TypeAdapter
16
 
17
- from fastmcp import Context, FastMCP
18
 
 
 
 
19
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  async def test_fastmcp_server_lifespan():
21
  """Test that lifespan works in FastMCP server."""
22
 
@@ -71,41 +186,49 @@ async def test_fastmcp_server_lifespan():
71
  clientInfo=Implementation(name="test-client", version="0.1.0"),
72
  )
73
  await send_stream1.send(
74
- JSONRPCMessage(
75
- root=JSONRPCRequest(
76
- jsonrpc="2.0",
77
- id=1,
78
- method="initialize",
79
- params=TypeAdapter(InitializeRequestParams).dump_python(params),
 
 
80
  )
81
  )
82
  )
83
  response = await receive_stream2.receive()
 
84
 
85
  # Send initialized notification
86
  await send_stream1.send(
87
- JSONRPCMessage(
88
- root=JSONRPCNotification(
89
- jsonrpc="2.0",
90
- method="notifications/initialized",
 
 
91
  )
92
  )
93
  )
94
 
95
  # Call the tool to verify lifespan context
96
  await send_stream1.send(
97
- JSONRPCMessage(
98
- root=JSONRPCRequest(
99
- jsonrpc="2.0",
100
- id=2,
101
- method="tools/call",
102
- params={"name": "check_lifespan", "arguments": {}},
 
 
103
  )
104
  )
105
  )
106
 
107
  # Get response and verify
108
  response = await receive_stream2.receive()
 
109
  assert response.root.result["content"][0]["text"] == "true"
110
 
111
  # Cancel server task
 
4
  from contextlib import asynccontextmanager
5
 
6
  import anyio
7
+ import pytest
8
+ from mcp.server.fastmcp import Context, FastMCP
9
+ from mcp.server.lowlevel.server import NotificationOptions, Server
10
+ from mcp.server.models import InitializationOptions
11
+ from mcp.shared.message import SessionMessage
12
  from mcp.types import (
13
  ClientCapabilities,
14
  Implementation,
 
19
  )
20
  from pydantic import TypeAdapter
21
 
 
22
 
23
+ @pytest.mark.anyio
24
+ async def test_lowlevel_server_lifespan():
25
+ """Test that lifespan works in low-level server."""
26
 
27
+ @asynccontextmanager
28
+ async def test_lifespan(server: Server) -> AsyncIterator[dict[str, bool]]:
29
+ """Test lifespan context that tracks startup/shutdown."""
30
+ context = {"started": False, "shutdown": False}
31
+ try:
32
+ context["started"] = True
33
+ yield context
34
+ finally:
35
+ context["shutdown"] = True
36
+
37
+ server = Server("test", lifespan=test_lifespan)
38
+
39
+ # Create memory streams for testing
40
+ send_stream1, receive_stream1 = anyio.create_memory_object_stream(100)
41
+ send_stream2, receive_stream2 = anyio.create_memory_object_stream(100)
42
+
43
+ # Create a tool that accesses lifespan context
44
+ @server.call_tool()
45
+ async def check_lifespan(name: str, arguments: dict) -> list:
46
+ ctx = server.request_context
47
+ assert isinstance(ctx.lifespan_context, dict)
48
+ assert ctx.lifespan_context["started"]
49
+ assert not ctx.lifespan_context["shutdown"]
50
+ return [{"type": "text", "text": "true"}]
51
+
52
+ # Run server in background task
53
+ async with (
54
+ anyio.create_task_group() as tg,
55
+ send_stream1,
56
+ receive_stream1,
57
+ send_stream2,
58
+ receive_stream2,
59
+ ):
60
+
61
+ async def run_server():
62
+ await server.run(
63
+ receive_stream1,
64
+ send_stream2,
65
+ InitializationOptions(
66
+ server_name="test",
67
+ server_version="0.1.0",
68
+ capabilities=server.get_capabilities(
69
+ notification_options=NotificationOptions(),
70
+ experimental_capabilities={},
71
+ ),
72
+ ),
73
+ raise_exceptions=True,
74
+ )
75
+
76
+ tg.start_soon(run_server)
77
+
78
+ # Initialize the server
79
+ params = InitializeRequestParams(
80
+ protocolVersion="2024-11-05",
81
+ capabilities=ClientCapabilities(),
82
+ clientInfo=Implementation(name="test-client", version="0.1.0"),
83
+ )
84
+ await send_stream1.send(
85
+ SessionMessage(
86
+ JSONRPCMessage(
87
+ root=JSONRPCRequest(
88
+ jsonrpc="2.0",
89
+ id=1,
90
+ method="initialize",
91
+ params=TypeAdapter(InitializeRequestParams).dump_python(params),
92
+ )
93
+ )
94
+ )
95
+ )
96
+ response = await receive_stream2.receive()
97
+ response = response.message
98
+
99
+ # Send initialized notification
100
+ await send_stream1.send(
101
+ SessionMessage(
102
+ JSONRPCMessage(
103
+ root=JSONRPCNotification(
104
+ jsonrpc="2.0",
105
+ method="notifications/initialized",
106
+ )
107
+ )
108
+ )
109
+ )
110
+
111
+ # Call the tool to verify lifespan context
112
+ await send_stream1.send(
113
+ SessionMessage(
114
+ JSONRPCMessage(
115
+ root=JSONRPCRequest(
116
+ jsonrpc="2.0",
117
+ id=2,
118
+ method="tools/call",
119
+ params={"name": "check_lifespan", "arguments": {}},
120
+ )
121
+ )
122
+ )
123
+ )
124
+
125
+ # Get response and verify
126
+ response = await receive_stream2.receive()
127
+ response = response.message
128
+ assert response.root.result["content"][0]["text"] == "true"
129
+
130
+ # Cancel server task
131
+ tg.cancel_scope.cancel()
132
+
133
+
134
+ @pytest.mark.anyio
135
  async def test_fastmcp_server_lifespan():
136
  """Test that lifespan works in FastMCP server."""
137
 
 
186
  clientInfo=Implementation(name="test-client", version="0.1.0"),
187
  )
188
  await send_stream1.send(
189
+ SessionMessage(
190
+ JSONRPCMessage(
191
+ root=JSONRPCRequest(
192
+ jsonrpc="2.0",
193
+ id=1,
194
+ method="initialize",
195
+ params=TypeAdapter(InitializeRequestParams).dump_python(params),
196
+ )
197
  )
198
  )
199
  )
200
  response = await receive_stream2.receive()
201
+ response = response.message
202
 
203
  # Send initialized notification
204
  await send_stream1.send(
205
+ SessionMessage(
206
+ JSONRPCMessage(
207
+ root=JSONRPCNotification(
208
+ jsonrpc="2.0",
209
+ method="notifications/initialized",
210
+ )
211
  )
212
  )
213
  )
214
 
215
  # Call the tool to verify lifespan context
216
  await send_stream1.send(
217
+ SessionMessage(
218
+ JSONRPCMessage(
219
+ root=JSONRPCRequest(
220
+ jsonrpc="2.0",
221
+ id=2,
222
+ method="tools/call",
223
+ params={"name": "check_lifespan", "arguments": {}},
224
+ )
225
  )
226
  )
227
  )
228
 
229
  # Get response and verify
230
  response = await receive_stream2.receive()
231
+ response = response.message
232
  assert response.root.result["content"][0]["text"] == "true"
233
 
234
  # Cancel server task
uv.lock CHANGED
The diff for this file is too large to render. See raw diff