|
16 | 16 | from werkzeug.wrappers import Request, Response |
17 | 17 | import jsonschema |
18 | 18 |
|
| 19 | +try: |
| 20 | + from starlette.testclient import TestClient |
| 21 | + # Catch RuntimeError to prevent the following exception in aws_lambda tests. |
| 22 | + # RuntimeError: The starlette.testclient module requires the httpx package to be installed. |
| 23 | +except (ImportError, RuntimeError): |
| 24 | + TestClient = None |
19 | 25 |
|
20 | 26 | try: |
21 | 27 | import gevent |
@@ -708,6 +714,79 @@ async def simulate_client(tg, result): |
708 | 714 | return inner |
709 | 715 |
|
710 | 716 |
|
| 717 | +@pytest.fixture() |
| 718 | +def json_rpc(): |
| 719 | + def inner(app, method: str, params, request_id: str): |
| 720 | + with TestClient(app) as client: # type: ignore |
| 721 | + init_response = client.post( |
| 722 | + "/mcp/", |
| 723 | + headers={ |
| 724 | + "Accept": "application/json, text/event-stream", |
| 725 | + "Content-Type": "application/json", |
| 726 | + }, |
| 727 | + json={ |
| 728 | + "jsonrpc": "2.0", |
| 729 | + "method": "initialize", |
| 730 | + "params": { |
| 731 | + "clientInfo": {"name": "test-client", "version": "1.0"}, |
| 732 | + "protocolVersion": "2025-11-25", |
| 733 | + "capabilities": {}, |
| 734 | + }, |
| 735 | + "id": request_id, |
| 736 | + }, |
| 737 | + ) |
| 738 | + |
| 739 | + session_id = init_response.headers["mcp-session-id"] |
| 740 | + |
| 741 | + # Notification response is mandatory. |
| 742 | + # https://modelcontextprotocol.io/specification/2025-11-25/basic/lifecycle |
| 743 | + client.post( |
| 744 | + "/mcp/", |
| 745 | + headers={ |
| 746 | + "Accept": "application/json, text/event-stream", |
| 747 | + "Content-Type": "application/json", |
| 748 | + "mcp-session-id": session_id, |
| 749 | + }, |
| 750 | + json={ |
| 751 | + "jsonrpc": "2.0", |
| 752 | + "method": "notifications/initialized", |
| 753 | + "params": {}, |
| 754 | + }, |
| 755 | + ) |
| 756 | + |
| 757 | + response = client.post( |
| 758 | + "/mcp/", |
| 759 | + headers={ |
| 760 | + "Accept": "application/json, text/event-stream", |
| 761 | + "Content-Type": "application/json", |
| 762 | + "mcp-session-id": session_id, |
| 763 | + }, |
| 764 | + json={ |
| 765 | + "jsonrpc": "2.0", |
| 766 | + "method": method, |
| 767 | + "params": params, |
| 768 | + "id": request_id, |
| 769 | + }, |
| 770 | + ) |
| 771 | + |
| 772 | + return session_id, response |
| 773 | + |
| 774 | + return inner |
| 775 | + |
| 776 | + |
| 777 | +@pytest.fixture() |
| 778 | +def select_mcp_transactions(): |
| 779 | + def inner(events): |
| 780 | + return [ |
| 781 | + event |
| 782 | + for event in events |
| 783 | + if event["type"] == "transaction" |
| 784 | + and event["contexts"]["trace"]["op"] == "mcp.server" |
| 785 | + ] |
| 786 | + |
| 787 | + return inner |
| 788 | + |
| 789 | + |
711 | 790 | class MockServerRequestHandler(BaseHTTPRequestHandler): |
712 | 791 | def do_GET(self): # noqa: N802 |
713 | 792 | # Process an HTTP GET request and return a response. |
|
0 commit comments