Jeremiah Lowin commited on
Commit
a74ac5b
·
1 Parent(s): 4c1f058

Update lifespan tests

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