diff --git a/CHANGELOG.md b/CHANGELOG.md index 4eb2bde..8d9644b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.3.4] + +Updates the `mcp` dependency to `1.8.1`. + ## [0.3.3] Fixes the broken release from 0.3.2. diff --git a/fastapi_mcp/transport/sse.py b/fastapi_mcp/transport/sse.py index c39f9cc..6b33b8e 100644 --- a/fastapi_mcp/transport/sse.py +++ b/fastapi_mcp/transport/sse.py @@ -5,6 +5,7 @@ from anyio.streams.memory import MemoryObjectSendStream from fastapi import Request, Response, BackgroundTasks, HTTPException from fastapi.responses import JSONResponse +from mcp.shared.message import SessionMessage from pydantic import ValidationError from mcp.server.sse import SseServerTransport from mcp.types import JSONRPCMessage, JSONRPCError, ErrorData @@ -87,7 +88,7 @@ async def handle_fastapi_post_message(self, request: Request) -> Response: # Create background task to send message background_tasks = BackgroundTasks() - background_tasks.add_task(self._send_message_safely, writer, message) + background_tasks.add_task(self._send_message_safely, writer, SessionMessage(message)) logger.debug("Accepting message, will send in background") # Return response with background task @@ -96,7 +97,7 @@ async def handle_fastapi_post_message(self, request: Request) -> Response: return response async def _send_message_safely( - self, writer: MemoryObjectSendStream[JSONRPCMessage], message: Union[JSONRPCMessage, ValidationError] + self, writer: MemoryObjectSendStream[SessionMessage], message: Union[SessionMessage, ValidationError] ): """Send a message to the writer, avoiding ASGI race conditions""" @@ -115,7 +116,7 @@ async def _send_message_safely( id="unknown", # We don't know the ID from the invalid request error=error_data, ) - error_message = JSONRPCMessage(root=json_rpc_error) + error_message = SessionMessage(JSONRPCMessage(root=json_rpc_error)) await writer.send(error_message) else: await writer.send(message) diff --git a/pyproject.toml b/pyproject.toml index 2f49e46..bea9fd7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "fastapi-mcp" -version = "0.3.3" +version = "0.3.4" description = "Automatic MCP server generator for FastAPI applications - converts FastAPI endpoints to MCP tools for LLM integration" readme = "README.md" requires-python = ">=3.10" @@ -29,7 +29,7 @@ dependencies = [ "fastapi>=0.100.0", "typer>=0.9.0", "rich>=13.0.0", - "mcp>=1.6.0", + "mcp>=1.8.1", "pydantic>=2.0.0", "pydantic-settings>=2.5.2", "uvicorn>=0.20.0", diff --git a/tests/test_sse_mock_transport.py b/tests/test_sse_mock_transport.py index 090655e..e833e5d 100644 --- a/tests/test_sse_mock_transport.py +++ b/tests/test_sse_mock_transport.py @@ -3,6 +3,7 @@ from uuid import UUID from unittest.mock import AsyncMock, MagicMock, patch from fastapi import HTTPException, Request +from mcp.shared.message import SessionMessage from pydantic import ValidationError from anyio.streams.memory import MemoryObjectSendStream @@ -145,9 +146,10 @@ async def test_send_message_safely_with_validation_error( # Verify that the writer.send was called with a JSONRPCError assert mock_writer.send.called sent_message = mock_writer.send.call_args[0][0] - assert isinstance(sent_message, JSONRPCMessage) - assert isinstance(sent_message.root, JSONRPCError) - assert sent_message.root.error.code == -32700 # Parse error code + assert isinstance(sent_message, SessionMessage) + assert isinstance(sent_message.message, JSONRPCMessage) + assert isinstance(sent_message.message.root, JSONRPCError) + assert sent_message.message.root.error.code == -32700 # Parse error code @pytest.mark.anyio @@ -156,7 +158,9 @@ async def test_send_message_safely_with_jsonrpc_message( ) -> None: """Test sending a JSONRPCMessage safely.""" # Create a JSONRPCMessage - message = JSONRPCMessage.model_validate({"jsonrpc": "2.0", "id": "123", "method": "test_method", "params": {}}) + message = SessionMessage( + JSONRPCMessage.model_validate({"jsonrpc": "2.0", "id": "123", "method": "test_method", "params": {}}) + ) # Call the function await mock_transport._send_message_safely(mock_writer, message) @@ -176,7 +180,9 @@ async def test_send_message_safely_exception_handling( mock_writer.send.side_effect = Exception("Test exception") # Create a message - message = JSONRPCMessage.model_validate({"jsonrpc": "2.0", "id": "123", "method": "test_method", "params": {}}) + message = SessionMessage( + JSONRPCMessage.model_validate({"jsonrpc": "2.0", "id": "123", "method": "test_method", "params": {}}) + ) # Call the function - it should not raise an exception await mock_transport._send_message_safely(mock_writer, message) diff --git a/uv.lock b/uv.lock index 1f9071a..7ee9756 100644 --- a/uv.lock +++ b/uv.lock @@ -1,4 +1,5 @@ version = 1 +revision = 2 requires-python = ">=3.10" [[package]] @@ -326,7 +327,7 @@ wheels = [ [[package]] name = "fastapi-mcp" -version = "0.3.3" +version = "0.3.4" source = { editable = "." } dependencies = [ { name = "fastapi" }, @@ -358,7 +359,7 @@ dev = [ requires-dist = [ { name = "fastapi", specifier = ">=0.100.0" }, { name = "httpx", specifier = ">=0.24.0" }, - { name = "mcp", specifier = ">=1.6.0" }, + { name = "mcp", specifier = ">=1.8.1" }, { name = "pydantic", specifier = ">=2.0.0" }, { name = "pydantic-settings", specifier = ">=2.5.2" }, { name = "requests", specifier = ">=2.25.0" }, @@ -477,7 +478,7 @@ wheels = [ [[package]] name = "mcp" -version = "1.6.0" +version = "1.8.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -485,13 +486,14 @@ dependencies = [ { name = "httpx-sse" }, { name = "pydantic" }, { name = "pydantic-settings" }, + { name = "python-multipart" }, { name = "sse-starlette" }, { name = "starlette" }, - { name = "uvicorn" }, + { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/95/d2/f587cb965a56e992634bebc8611c5b579af912b74e04eb9164bd49527d21/mcp-1.6.0.tar.gz", hash = "sha256:d9324876de2c5637369f43161cd71eebfd803df5a95e46225cab8d280e366723", size = 200031 } +sdist = { url = "https://files.pythonhosted.org/packages/7c/13/16b712e8a3be6a736b411df2fc6b4e75eb1d3e99b1cd57a3a1decf17f612/mcp-1.8.1.tar.gz", hash = "sha256:ec0646271d93749f784d2316fb5fe6102fb0d1be788ec70a9e2517e8f2722c0e", size = 265605 } wheels = [ - { url = "https://files.pythonhosted.org/packages/10/30/20a7f33b0b884a9d14dd3aa94ff1ac9da1479fe2ad66dd9e2736075d2506/mcp-1.6.0-py3-none-any.whl", hash = "sha256:7bd24c6ea042dbec44c754f100984d186620d8b841ec30f1b19eda9b93a634d0", size = 76077 }, + { url = "https://files.pythonhosted.org/packages/1c/5d/91cf0d40e40ae9ecf8d4004e0f9611eea86085aa0b5505493e0ff53972da/mcp-1.8.1-py3-none-any.whl", hash = "sha256:948e03783859fa35abe05b9b6c0a1d5519be452fc079dc8d7f682549591c1770", size = 119761 }, ] [[package]] @@ -782,6 +784,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a", size = 19863 }, ] +[[package]] +name = "python-multipart" +version = "0.0.20" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13", size = 37158 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546 }, +] + [[package]] name = "pyyaml" version = "6.0.2"