From 2cbd155e991b6177a47eb75eedc0a36956775e1b Mon Sep 17 00:00:00 2001 From: Vinisha Projects Date: Tue, 29 Apr 2025 11:21:18 -0500 Subject: [PATCH 1/5] Add SSE demo notebook to PR --- examples/python mcp_server_client_demo.ipynb | 184 +++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 examples/python mcp_server_client_demo.ipynb diff --git a/examples/python mcp_server_client_demo.ipynb b/examples/python mcp_server_client_demo.ipynb new file mode 100644 index 000000000..acbef82cf --- /dev/null +++ b/examples/python mcp_server_client_demo.ipynb @@ -0,0 +1,184 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 2, + "id": "8f0351de-69f9-40c3-9298-5d6321f33a1d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: flask in c:\\users\\vinis\\anaconda3\\lib\\site-packages (2.2.5)\n", + "Requirement already satisfied: requests in c:\\users\\vinis\\anaconda3\\lib\\site-packages (2.31.0)\n", + "Requirement already satisfied: Werkzeug>=2.2.2 in c:\\users\\vinis\\anaconda3\\lib\\site-packages (from flask) (2.2.3)\n", + "Requirement already satisfied: Jinja2>=3.0 in c:\\users\\vinis\\anaconda3\\lib\\site-packages (from flask) (3.1.3)\n", + "Requirement already satisfied: itsdangerous>=2.0 in c:\\users\\vinis\\anaconda3\\lib\\site-packages (from flask) (2.0.1)\n", + "Requirement already satisfied: click>=8.0 in c:\\users\\vinis\\anaconda3\\lib\\site-packages (from flask) (8.1.7)\n", + "Requirement already satisfied: charset-normalizer<4,>=2 in c:\\users\\vinis\\anaconda3\\lib\\site-packages (from requests) (2.0.4)\n", + "Requirement already satisfied: idna<4,>=2.5 in c:\\users\\vinis\\anaconda3\\lib\\site-packages (from requests) (3.4)\n", + "Requirement already satisfied: urllib3<3,>=1.21.1 in c:\\users\\vinis\\anaconda3\\lib\\site-packages (from requests) (2.0.7)\n", + "Requirement already satisfied: certifi>=2017.4.17 in c:\\users\\vinis\\anaconda3\\lib\\site-packages (from requests) (2024.8.30)\n", + "Requirement already satisfied: colorama in c:\\users\\vinis\\anaconda3\\lib\\site-packages (from click>=8.0->flask) (0.4.6)\n", + "Requirement already satisfied: MarkupSafe>=2.0 in c:\\users\\vinis\\anaconda3\\lib\\site-packages (from Jinja2>=3.0->flask) (2.1.3)\n", + "Note: you may need to restart the kernel to use updated packages.\n" + ] + } + ], + "source": [ + "pip install flask requests\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "564f36a1-de43-4eb8-b0f4-b3e259f980c2", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " * Serving Flask app '__main__'\n", + " * Debug mode: off\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.\n", + " * Running on http://127.0.0.1:5000\n", + "Press CTRL+C to quit\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "āœ… Server started at http://127.0.0.1:5000\n", + "\n", + "šŸ“„ Received request: {'tool': 'example_tool', 'input': {'message': 'Hello via SSE!'}}\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "127.0.0.1 - - [28/Apr/2025 17:31:39] \"POST /mcp HTTP/1.1\" 200 -\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "šŸš€ Connecting to SSE Server...\n", + "\n", + "data: {\"message\": \"Update 1\"}\n", + ": keep-alive\n", + "data: {\"message\": \"Update 2\"}\n", + "\n", + "šŸŽÆ Successfully received all SSE events!\n" + ] + } + ], + "source": [ + "import threading\n", + "import time\n", + "from flask import Flask, Response, request\n", + "import requests\n", + "\n", + "# --- 1. SETUP FLASK MOCK SERVER ---\n", + "\n", + "app = Flask(__name__)\n", + "\n", + "@app.route('/mcp', methods=['POST'])\n", + "def mcp_handler():\n", + " data = request.json\n", + " print(\"\\nšŸ“„ Received request:\", data)\n", + "\n", + " def stream():\n", + " for i in range(3):\n", + " time.sleep(1)\n", + " yield f\"data: {{\\\"message\\\": \\\"Update {i+1}\\\"}}\\n\\n\"\n", + " yield \": keep-alive\\n\\n\" # Force immediate flush\n", + "\n", + " return Response(stream(), mimetype='text/event-stream')\n", + "\n", + "@app.route('/')\n", + "def home():\n", + " return \"šŸ  MCP Mock Server Running!\"\n", + "\n", + "def start_server():\n", + " app.run(port=5000)\n", + "\n", + "# --- 2. START SERVER IN BACKGROUND THREAD ---\n", + "server_thread = threading.Thread(target=start_server)\n", + "server_thread.daemon = True\n", + "server_thread.start()\n", + "\n", + "# --- 3. WAIT FOR SERVER TO BE READY ---\n", + "time.sleep(3) # Give Flask server a few seconds to boot\n", + "\n", + "print(\"\\nāœ… Server started at http://127.0.0.1:5000\")\n", + "\n", + "# --- 4. CLIENT CODE: CONNECT TO SERVER ---\n", + "\n", + "try:\n", + " SERVER_URL = \"http://127.0.0.1:5000\"\n", + "\n", + " response = requests.post(f\"{SERVER_URL}/mcp\", stream=True, json={\n", + " \"tool\": \"example_tool\",\n", + " \"input\": {\"message\": \"Hello via SSE!\"}\n", + " })\n", + "\n", + " print(\"\\nšŸš€ Connecting to SSE Server...\\n\")\n", + " event_count = 0\n", + " for line in response.iter_lines():\n", + " if line:\n", + " decoded_line = line.decode('utf-8')\n", + " print(decoded_line)\n", + " event_count += 1\n", + " if event_count >= 3:\n", + " break # Stop after 3 messages\n", + "\n", + " print(\"\\nšŸŽÆ Successfully received all SSE events!\")\n", + "\n", + "except Exception as e:\n", + " print(\"āŒ Connection failed:\", e)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9f9497e8-4354-44e7-b79a-105091ee479b", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.10" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From e1b8b1d907752b310aa312ce498a47a7a20e37d1 Mon Sep 17 00:00:00 2001 From: Vinisha Projects Date: Sun, 4 May 2025 11:24:59 -0500 Subject: [PATCH 2/5] Add test for aconnect_sse to resolve #109 --- tests/test_sse_client_server.py | 118 ++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 tests/test_sse_client_server.py diff --git a/tests/test_sse_client_server.py b/tests/test_sse_client_server.py new file mode 100644 index 000000000..12453b011 --- /dev/null +++ b/tests/test_sse_client_server.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python +# coding: utf-8 + +# In[1]: + + +get_ipython().run_line_magic('cd', 'C:/Users/vinis/Documents/python-sdk') + + +# In[2]: + + +import os +print(os.getcwd()) + + +# In[3]: + + +import sys, os +sys.path.append(os.path.abspath("src")) + + +# In[4]: + + +pip install fastapi uvicorn httpx httpx-sse sse-starlette anyio + + +# In[5]: + + +pip install -e . + + +# In[6]: + + +import os + +for root, dirs, files in os.walk("C:/Users/vinis/Documents/python-sdk"): + for file in files: + if file == "sse.py": + print(os.path.join(root, file)) + + +# In[7]: + + +import sys, os +sys.path.append(os.path.abspath("src")) + +import mcp.client.sse as sse_module + +print("Top-level definitions in mcp.client.sse:") +print(dir(sse_module)) + + +# In[8]: + + +import inspect +from mcp.client.sse import aconnect_sse + +print(inspect.signature(aconnect_sse)) + + +# In[9]: + + +import asyncio +from fastapi import FastAPI +from starlette.responses import StreamingResponse +import uvicorn +from threading import Thread +import httpx +from mcp.client.sse import aconnect_sse + +app = FastAPI() + +@app.get("/sse") +async def sse_endpoint(): + async def event_stream(): + for i in range(3): + yield f"data: Hello {i+1}\n\n" + await asyncio.sleep(0.1) + return StreamingResponse(event_stream(), media_type="text/event-stream") + +def run_mock_server(): + uvicorn.run(app, host="127.0.0.1", port=8012, log_level="warning") + +async def main(): + server_thread = Thread(target=run_mock_server, daemon=True) + server_thread.start() + await asyncio.sleep(1) + + messages = [] + + async with httpx.AsyncClient() as client: + async with aconnect_sse(client, "GET", "http://127.0.0.1:8012/sse") as event_source: + async for event in event_source.aiter_sse(): + if event.data: + print(" Event received:", event.data) + messages.append(event.data) + if len(messages) == 3: + break + + assert messages == ["Hello 1", "Hello 2", "Hello 3"] + print("\n Test passed! SSE connection via aconnect_sse worked correctly.") + +await main() + + +# In[ ]: + + + + From efd47be73d484f8dfcbcf1b4db47eb58a7b2ece9 Mon Sep 17 00:00:00 2001 From: Vinisha Projects Date: Sun, 4 May 2025 11:26:28 -0500 Subject: [PATCH 3/5] Add screenshot --- tests/test_sse_client_server.png | Bin 0 -> 14769 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/test_sse_client_server.png diff --git a/tests/test_sse_client_server.png b/tests/test_sse_client_server.png new file mode 100644 index 0000000000000000000000000000000000000000..c006bb4d93a410f8544c675b7e406c1794f32c89 GIT binary patch literal 14769 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 Date: Sun, 4 May 2025 11:42:23 -0500 Subject: [PATCH 4/5] Remove demo notebook from PR --- examples/python mcp_server_client_demo.ipynb | 184 ------------------- 1 file changed, 184 deletions(-) delete mode 100644 examples/python mcp_server_client_demo.ipynb diff --git a/examples/python mcp_server_client_demo.ipynb b/examples/python mcp_server_client_demo.ipynb deleted file mode 100644 index acbef82cf..000000000 --- a/examples/python mcp_server_client_demo.ipynb +++ /dev/null @@ -1,184 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 2, - "id": "8f0351de-69f9-40c3-9298-5d6321f33a1d", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Requirement already satisfied: flask in c:\\users\\vinis\\anaconda3\\lib\\site-packages (2.2.5)\n", - "Requirement already satisfied: requests in c:\\users\\vinis\\anaconda3\\lib\\site-packages (2.31.0)\n", - "Requirement already satisfied: Werkzeug>=2.2.2 in c:\\users\\vinis\\anaconda3\\lib\\site-packages (from flask) (2.2.3)\n", - "Requirement already satisfied: Jinja2>=3.0 in c:\\users\\vinis\\anaconda3\\lib\\site-packages (from flask) (3.1.3)\n", - "Requirement already satisfied: itsdangerous>=2.0 in c:\\users\\vinis\\anaconda3\\lib\\site-packages (from flask) (2.0.1)\n", - "Requirement already satisfied: click>=8.0 in c:\\users\\vinis\\anaconda3\\lib\\site-packages (from flask) (8.1.7)\n", - "Requirement already satisfied: charset-normalizer<4,>=2 in c:\\users\\vinis\\anaconda3\\lib\\site-packages (from requests) (2.0.4)\n", - "Requirement already satisfied: idna<4,>=2.5 in c:\\users\\vinis\\anaconda3\\lib\\site-packages (from requests) (3.4)\n", - "Requirement already satisfied: urllib3<3,>=1.21.1 in c:\\users\\vinis\\anaconda3\\lib\\site-packages (from requests) (2.0.7)\n", - "Requirement already satisfied: certifi>=2017.4.17 in c:\\users\\vinis\\anaconda3\\lib\\site-packages (from requests) (2024.8.30)\n", - "Requirement already satisfied: colorama in c:\\users\\vinis\\anaconda3\\lib\\site-packages (from click>=8.0->flask) (0.4.6)\n", - "Requirement already satisfied: MarkupSafe>=2.0 in c:\\users\\vinis\\anaconda3\\lib\\site-packages (from Jinja2>=3.0->flask) (2.1.3)\n", - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], - "source": [ - "pip install flask requests\n" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "564f36a1-de43-4eb8-b0f4-b3e259f980c2", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - " * Serving Flask app '__main__'\n", - " * Debug mode: off\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.\n", - " * Running on http://127.0.0.1:5000\n", - "Press CTRL+C to quit\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "āœ… Server started at http://127.0.0.1:5000\n", - "\n", - "šŸ“„ Received request: {'tool': 'example_tool', 'input': {'message': 'Hello via SSE!'}}\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "127.0.0.1 - - [28/Apr/2025 17:31:39] \"POST /mcp HTTP/1.1\" 200 -\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "šŸš€ Connecting to SSE Server...\n", - "\n", - "data: {\"message\": \"Update 1\"}\n", - ": keep-alive\n", - "data: {\"message\": \"Update 2\"}\n", - "\n", - "šŸŽÆ Successfully received all SSE events!\n" - ] - } - ], - "source": [ - "import threading\n", - "import time\n", - "from flask import Flask, Response, request\n", - "import requests\n", - "\n", - "# --- 1. SETUP FLASK MOCK SERVER ---\n", - "\n", - "app = Flask(__name__)\n", - "\n", - "@app.route('/mcp', methods=['POST'])\n", - "def mcp_handler():\n", - " data = request.json\n", - " print(\"\\nšŸ“„ Received request:\", data)\n", - "\n", - " def stream():\n", - " for i in range(3):\n", - " time.sleep(1)\n", - " yield f\"data: {{\\\"message\\\": \\\"Update {i+1}\\\"}}\\n\\n\"\n", - " yield \": keep-alive\\n\\n\" # Force immediate flush\n", - "\n", - " return Response(stream(), mimetype='text/event-stream')\n", - "\n", - "@app.route('/')\n", - "def home():\n", - " return \"šŸ  MCP Mock Server Running!\"\n", - "\n", - "def start_server():\n", - " app.run(port=5000)\n", - "\n", - "# --- 2. START SERVER IN BACKGROUND THREAD ---\n", - "server_thread = threading.Thread(target=start_server)\n", - "server_thread.daemon = True\n", - "server_thread.start()\n", - "\n", - "# --- 3. WAIT FOR SERVER TO BE READY ---\n", - "time.sleep(3) # Give Flask server a few seconds to boot\n", - "\n", - "print(\"\\nāœ… Server started at http://127.0.0.1:5000\")\n", - "\n", - "# --- 4. CLIENT CODE: CONNECT TO SERVER ---\n", - "\n", - "try:\n", - " SERVER_URL = \"http://127.0.0.1:5000\"\n", - "\n", - " response = requests.post(f\"{SERVER_URL}/mcp\", stream=True, json={\n", - " \"tool\": \"example_tool\",\n", - " \"input\": {\"message\": \"Hello via SSE!\"}\n", - " })\n", - "\n", - " print(\"\\nšŸš€ Connecting to SSE Server...\\n\")\n", - " event_count = 0\n", - " for line in response.iter_lines():\n", - " if line:\n", - " decoded_line = line.decode('utf-8')\n", - " print(decoded_line)\n", - " event_count += 1\n", - " if event_count >= 3:\n", - " break # Stop after 3 messages\n", - "\n", - " print(\"\\nšŸŽÆ Successfully received all SSE events!\")\n", - "\n", - "except Exception as e:\n", - " print(\"āŒ Connection failed:\", e)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9f9497e8-4354-44e7-b79a-105091ee479b", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.10" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} From a73cffe6cdcc1aaf09a080220605e0857ccc02e1 Mon Sep 17 00:00:00 2001 From: Vinisha Date: Sun, 4 May 2025 13:46:49 -0500 Subject: [PATCH 5/5] Update test_sse_client_server.py --- tests/test_sse_client_server.py | 72 +-------------------------------- 1 file changed, 2 insertions(+), 70 deletions(-) diff --git a/tests/test_sse_client_server.py b/tests/test_sse_client_server.py index 12453b011..e31c797d5 100644 --- a/tests/test_sse_client_server.py +++ b/tests/test_sse_client_server.py @@ -1,74 +1,6 @@ -#!/usr/bin/env python -# coding: utf-8 - -# In[1]: - - -get_ipython().run_line_magic('cd', 'C:/Users/vinis/Documents/python-sdk') - - -# In[2]: - - -import os -print(os.getcwd()) - - -# In[3]: - - -import sys, os -sys.path.append(os.path.abspath("src")) - - -# In[4]: - - -pip install fastapi uvicorn httpx httpx-sse sse-starlette anyio - - -# In[5]: - - -pip install -e . - - -# In[6]: - - -import os - -for root, dirs, files in os.walk("C:/Users/vinis/Documents/python-sdk"): - for file in files: - if file == "sse.py": - print(os.path.join(root, file)) - - -# In[7]: - - -import sys, os -sys.path.append(os.path.abspath("src")) - -import mcp.client.sse as sse_module - -print("Top-level definitions in mcp.client.sse:") -print(dir(sse_module)) - - -# In[8]: - - -import inspect -from mcp.client.sse import aconnect_sse - -print(inspect.signature(aconnect_sse)) - - -# In[9]: - - import asyncio +from typing import AsyncGenerator, List + from fastapi import FastAPI from starlette.responses import StreamingResponse import uvicorn