@@ -167,3 +167,226 @@ async def test_http_initialize_request(http_client: httpx.AsyncClient, server: s
167167 assert result ["id" ] == 1
168168 assert "result" in result
169169 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