@@ -167,3 +167,226 @@ async def test_http_initialize_request(http_client: httpx.AsyncClient, server: s
167
167
assert result ["id" ] == 1
168
168
assert "result" in result
169
169
assert result ["result" ]["serverInfo" ]["name" ] == SERVER_NAME
170
+
171
+
172
+ @pytest .mark .anyio
173
+ async def test_http_list_tools (http_client : httpx .AsyncClient , server : str ) -> None :
174
+ """Test tool listing via HTTP POST with JSON response."""
175
+ mcp_path = "/mcp"
176
+
177
+ init_response = await http_client .post (
178
+ mcp_path ,
179
+ json = {
180
+ "jsonrpc" : "2.0" ,
181
+ "method" : "initialize" ,
182
+ "id" : 1 ,
183
+ "params" : {
184
+ "protocolVersion" : types .LATEST_PROTOCOL_VERSION ,
185
+ "capabilities" : {},
186
+ "clientInfo" : {"name" : "test-client" , "version" : "1.0.0" },
187
+ },
188
+ },
189
+ headers = {"Accept" : "application/json, text/event-stream" , "Content-Type" : "application/json" },
190
+ )
191
+ assert init_response .status_code == 200
192
+
193
+ initialized_response = await http_client .post (
194
+ mcp_path ,
195
+ json = {
196
+ "jsonrpc" : "2.0" ,
197
+ "method" : "notifications/initialized" ,
198
+ },
199
+ headers = {"Accept" : "application/json, text/event-stream" , "Content-Type" : "application/json" },
200
+ )
201
+ assert initialized_response .status_code == 202
202
+
203
+ response = await http_client .post (
204
+ mcp_path ,
205
+ json = {
206
+ "jsonrpc" : "2.0" ,
207
+ "method" : "tools/list" ,
208
+ "id" : 2 ,
209
+ },
210
+ headers = {"Accept" : "application/json, text/event-stream" , "Content-Type" : "application/json" },
211
+ )
212
+
213
+ assert response .status_code == 200
214
+ result = response .json ()
215
+ assert result ["jsonrpc" ] == "2.0"
216
+ assert result ["id" ] == 2
217
+ assert "result" in result
218
+ assert "tools" in result ["result" ]
219
+ assert len (result ["result" ]["tools" ]) > 0
220
+
221
+ # Verify we have the expected tools from the simple FastAPI app
222
+ tool_names = [tool ["name" ] for tool in result ["result" ]["tools" ]]
223
+ assert "get_item" in tool_names
224
+ assert "list_items" in tool_names
225
+
226
+
227
+ @pytest .mark .anyio
228
+ async def test_http_call_tool (http_client : httpx .AsyncClient , server : str ) -> None :
229
+ """Test tool calling via HTTP POST with JSON response."""
230
+ mcp_path = "/mcp"
231
+
232
+ init_response = await http_client .post (
233
+ mcp_path ,
234
+ json = {
235
+ "jsonrpc" : "2.0" ,
236
+ "method" : "initialize" ,
237
+ "id" : 1 ,
238
+ "params" : {
239
+ "protocolVersion" : types .LATEST_PROTOCOL_VERSION ,
240
+ "capabilities" : {},
241
+ "clientInfo" : {"name" : "test-client" , "version" : "1.0.0" },
242
+ },
243
+ },
244
+ headers = {"Accept" : "application/json, text/event-stream" , "Content-Type" : "application/json" },
245
+ )
246
+ assert init_response .status_code == 200
247
+
248
+ initialized_response = await http_client .post (
249
+ mcp_path ,
250
+ json = {
251
+ "jsonrpc" : "2.0" ,
252
+ "method" : "notifications/initialized" ,
253
+ },
254
+ headers = {"Accept" : "application/json, text/event-stream" , "Content-Type" : "application/json" },
255
+ )
256
+ assert initialized_response .status_code == 202
257
+
258
+ response = await http_client .post (
259
+ mcp_path ,
260
+ json = {
261
+ "jsonrpc" : "2.0" ,
262
+ "method" : "tools/call" ,
263
+ "id" : 3 ,
264
+ "params" : {
265
+ "name" : "get_item" ,
266
+ "arguments" : {"item_id" : 1 },
267
+ },
268
+ },
269
+ headers = {"Accept" : "application/json, text/event-stream" , "Content-Type" : "application/json" },
270
+ )
271
+
272
+ assert response .status_code == 200
273
+ result = response .json ()
274
+ assert result ["jsonrpc" ] == "2.0"
275
+ assert result ["id" ] == 3
276
+ assert "result" in result
277
+ assert result ["result" ]["isError" ] is False
278
+ assert "content" in result ["result" ]
279
+ assert len (result ["result" ]["content" ]) > 0
280
+
281
+ # Verify the response contains expected item data
282
+ content = result ["result" ]["content" ][0 ]
283
+ assert content ["type" ] == "text"
284
+ assert "Item 1" in content ["text" ] # Should contain the item name
285
+
286
+
287
+ @pytest .mark .anyio
288
+ async def test_http_ping (http_client : httpx .AsyncClient , server : str ) -> None :
289
+ """Test ping functionality via HTTP POST."""
290
+ mcp_path = "/mcp"
291
+
292
+ init_response = await http_client .post (
293
+ mcp_path ,
294
+ json = {
295
+ "jsonrpc" : "2.0" ,
296
+ "method" : "initialize" ,
297
+ "id" : 1 ,
298
+ "params" : {
299
+ "protocolVersion" : types .LATEST_PROTOCOL_VERSION ,
300
+ "capabilities" : {},
301
+ "clientInfo" : {"name" : "test-client" , "version" : "1.0.0" },
302
+ },
303
+ },
304
+ headers = {"Accept" : "application/json, text/event-stream" , "Content-Type" : "application/json" },
305
+ )
306
+ assert init_response .status_code == 200
307
+
308
+ initialized_response = await http_client .post (
309
+ mcp_path ,
310
+ json = {
311
+ "jsonrpc" : "2.0" ,
312
+ "method" : "notifications/initialized" ,
313
+ },
314
+ headers = {"Accept" : "application/json, text/event-stream" , "Content-Type" : "application/json" },
315
+ )
316
+ assert initialized_response .status_code == 202
317
+
318
+ response = await http_client .post (
319
+ mcp_path ,
320
+ json = {
321
+ "jsonrpc" : "2.0" ,
322
+ "method" : "ping" ,
323
+ "id" : 4 ,
324
+ },
325
+ headers = {"Accept" : "application/json, text/event-stream" , "Content-Type" : "application/json" },
326
+ )
327
+
328
+ assert response .status_code == 200
329
+ result = response .json ()
330
+ assert result ["jsonrpc" ] == "2.0"
331
+ assert result ["id" ] == 4
332
+ assert "result" in result
333
+
334
+
335
+ @pytest .mark .anyio
336
+ async def test_http_error_handling (http_client : httpx .AsyncClient , server : str ) -> None :
337
+ """Test error handling for invalid requests."""
338
+ mcp_path = "/mcp"
339
+
340
+ response = await http_client .post (
341
+ mcp_path ,
342
+ content = "invalid json" ,
343
+ headers = {"Accept" : "application/json, text/event-stream" , "Content-Type" : "application/json" },
344
+ )
345
+
346
+ assert response .status_code == 400
347
+ result = response .json ()
348
+ assert "error" in result
349
+ assert result ["error" ]["code" ] == - 32700 # Parse error
350
+
351
+
352
+ @pytest .mark .anyio
353
+ async def test_http_invalid_method (http_client : httpx .AsyncClient , server : str ) -> None :
354
+ """Test error handling for invalid methods."""
355
+ mcp_path = "/mcp"
356
+
357
+ response = await http_client .post (
358
+ mcp_path ,
359
+ json = {
360
+ "jsonrpc" : "2.0" ,
361
+ "method" : "invalid/method" ,
362
+ "id" : 5 ,
363
+ },
364
+ headers = {"Accept" : "application/json, text/event-stream" , "Content-Type" : "application/json" },
365
+ )
366
+
367
+ assert response .status_code == 200
368
+ result = response .json ()
369
+ assert result ["jsonrpc" ] == "2.0"
370
+ assert result ["id" ] == 5
371
+ assert "error" in result
372
+ assert result ["error" ]["code" ] == - 32602 # Invalid request parameters
373
+
374
+
375
+ @pytest .mark .anyio
376
+ async def test_http_notification_handling (http_client : httpx .AsyncClient , server : str ) -> None :
377
+ """Test that notifications return 202 Accepted without response body."""
378
+ mcp_path = "/mcp"
379
+
380
+ response = await http_client .post (
381
+ mcp_path ,
382
+ json = {
383
+ "jsonrpc" : "2.0" ,
384
+ "method" : "notifications/cancelled" ,
385
+ "params" : {"requestId" : "test-123" },
386
+ },
387
+ headers = {"Accept" : "application/json, text/event-stream" , "Content-Type" : "application/json" },
388
+ )
389
+
390
+ assert response .status_code == 202
391
+ # Notifications should return empty body
392
+ assert response .content == b"" or response .text == "null"
0 commit comments