From dd07a47aa0338e0a28ecc83b1596b0540920cb2f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 5 Jan 2026 19:25:39 +0000 Subject: [PATCH 01/10] Fix MCPStreamableHTTPTool to use new streamable_http_client API with proper httpx client cleanup Co-authored-by: eavanvalkenburg <13749212+eavanvalkenburg@users.noreply.github.com> --- python/packages/core/agent_framework/_mcp.py | 45 ++++++---- python/packages/core/tests/core/test_mcp.py | 88 ++++++++++++++++++-- 2 files changed, 112 insertions(+), 21 deletions(-) diff --git a/python/packages/core/agent_framework/_mcp.py b/python/packages/core/agent_framework/_mcp.py index a25f359a59..05319f16c0 100644 --- a/python/packages/core/agent_framework/_mcp.py +++ b/python/packages/core/agent_framework/_mcp.py @@ -10,10 +10,11 @@ from functools import partial from typing import TYPE_CHECKING, Any, Literal +import httpx from mcp import types from mcp.client.session import ClientSession from mcp.client.stdio import StdioServerParameters, stdio_client -from mcp.client.streamable_http import streamablehttp_client +from mcp.client.streamable_http import streamable_http_client, streamablehttp_client from mcp.client.websocket import websocket_client from mcp.shared.context import RequestContext from mcp.shared.exceptions import McpError @@ -978,6 +979,7 @@ def __init__( self.sse_read_timeout = sse_read_timeout self.terminate_on_close = terminate_on_close self._client_kwargs = kwargs + self._httpx_client: httpx.AsyncClient | None = None def get_mcp_client(self) -> _AsyncGeneratorContextManager[Any, None]: """Get an MCP streamable HTTP client. @@ -985,20 +987,33 @@ def get_mcp_client(self) -> _AsyncGeneratorContextManager[Any, None]: Returns: An async context manager for the streamable HTTP client transport. """ - args: dict[str, Any] = { - "url": self.url, - } - if self.headers: - args["headers"] = self.headers - if self.timeout is not None: - args["timeout"] = self.timeout - if self.sse_read_timeout is not None: - args["sse_read_timeout"] = self.sse_read_timeout - if self.terminate_on_close is not None: - args["terminate_on_close"] = self.terminate_on_close - if self._client_kwargs: - args.update(self._client_kwargs) - return streamablehttp_client(**args) + timeout_value = self.timeout if self.timeout is not None else 30.0 + sse_timeout_value = self.sse_read_timeout if self.sse_read_timeout is not None else 300.0 + + # Create and track httpx client + # Note: _client_kwargs are not passed to AsyncClient as they were specific to the old API + self._httpx_client = httpx.AsyncClient( + headers=self.headers, + timeout=httpx.Timeout(timeout_value, read=sse_timeout_value), + ) + + # Use new API instead of deprecated streamablehttp_client() + return streamable_http_client( + url=self.url, + http_client=self._httpx_client, + terminate_on_close=self.terminate_on_close if self.terminate_on_close is not None else True, + ) + + async def close(self) -> None: + """Disconnect from the MCP server and close httpx client. + + Closes the connection and cleans up resources including the httpx client. + """ + await super().close() + + if self._httpx_client is not None: + await self._httpx_client.aclose() + self._httpx_client = None class MCPWebsocketTool(MCPTool): diff --git a/python/packages/core/tests/core/test_mcp.py b/python/packages/core/tests/core/test_mcp.py index 18c90d64b3..72f6bb56a2 100644 --- a/python/packages/core/tests/core/test_mcp.py +++ b/python/packages/core/tests/core/test_mcp.py @@ -5,6 +5,7 @@ from typing import Any from unittest.mock import AsyncMock, Mock, patch +import httpx import pytest from mcp import types from mcp.client.session import ClientSession @@ -1519,17 +1520,29 @@ def test_mcp_streamable_http_tool_get_mcp_client_all_params(): custom_param="test", ) - with patch("agent_framework._mcp.streamablehttp_client") as mock_http_client: + with patch("agent_framework._mcp.streamable_http_client") as mock_http_client, patch( + "agent_framework._mcp.httpx.AsyncClient" + ) as mock_async_client: + # Create a mock httpx client instance + mock_client_instance = Mock() + mock_async_client.return_value = mock_client_instance + tool.get_mcp_client() - # Verify all parameters were passed + # Verify httpx.AsyncClient was created with correct parameters + mock_async_client.assert_called_once() + call_kwargs = mock_async_client.call_args.kwargs + assert call_kwargs["headers"] == {"Auth": "token"} + assert isinstance(call_kwargs["timeout"], httpx.Timeout) + # The timeout constructor sets connect/write/pool to the first arg, and read can be overridden + assert call_kwargs["timeout"].connect == 30.0 + assert call_kwargs["timeout"].read == 10.0 + + # Verify streamable_http_client was called with new API parameters mock_http_client.assert_called_once_with( url="http://example.com", - headers={"Auth": "token"}, - timeout=30.0, - sse_read_timeout=10.0, + http_client=mock_client_instance, terminate_on_close=True, - custom_param="test", ) @@ -1692,3 +1705,66 @@ async def test_load_prompts_prevents_multiple_calls(): tool._prompts_loaded = True assert mock_session.list_prompts.call_count == 1 # Still 1, not incremented + + +@pytest.mark.asyncio +async def test_mcp_streamable_http_tool_httpx_client_cleanup(): + """Test that MCPStreamableHTTPTool properly closes httpx client and creates new instances.""" + from unittest.mock import AsyncMock, Mock, patch + + from agent_framework import MCPStreamableHTTPTool + + # Mock the streamable_http_client to avoid actual connections + with patch("agent_framework._mcp.streamable_http_client") as mock_client, patch( + "agent_framework._mcp.ClientSession" + ) as mock_session_class: + # Setup mock context manager for streamable_http_client + mock_transport = (Mock(), Mock()) + mock_context_manager = Mock() + mock_context_manager.__aenter__ = AsyncMock(return_value=mock_transport) + mock_context_manager.__aexit__ = AsyncMock(return_value=None) + mock_client.return_value = mock_context_manager + + # Setup mock session + mock_session = Mock() + mock_session.initialize = AsyncMock() + mock_session_class.return_value.__aenter__ = AsyncMock(return_value=mock_session) + mock_session_class.return_value.__aexit__ = AsyncMock(return_value=None) + + # Create and use first tool + tool1 = MCPStreamableHTTPTool( + name="test", + url="http://localhost:8081/mcp", + load_tools=False, + load_prompts=False, + terminate_on_close=False, + timeout=30, + ) + await tool1.connect() + client1 = tool1._httpx_client + assert client1 is not None, "First httpx client should be created" + client1_id = id(client1) + + # Close first tool + await tool1.close() + assert tool1._httpx_client is None, "First httpx client should be cleared after close" + + # Create and use second tool + tool2 = MCPStreamableHTTPTool( + name="test", + url="http://localhost:8081/mcp", + load_tools=False, + load_prompts=False, + terminate_on_close=False, + timeout=30, + ) + await tool2.connect() + client2 = tool2._httpx_client + assert client2 is not None, "Second httpx client should be created" + client2_id = id(client2) + + # Verify different httpx client instances (no reuse) + assert client1_id != client2_id, "httpx clients should not be reused between tool instances" + + await tool2.close() + assert tool2._httpx_client is None, "Second httpx client should be cleared after close" From 4cf37b0f25f088a7ed2ca03e9a0e2dbb22275188 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 5 Jan 2026 19:27:37 +0000 Subject: [PATCH 02/10] Update docstring to reflect new streamable_http_client API usage Co-authored-by: eavanvalkenburg <13749212+eavanvalkenburg@users.noreply.github.com> --- python/packages/core/agent_framework/_mcp.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/python/packages/core/agent_framework/_mcp.py b/python/packages/core/agent_framework/_mcp.py index 05319f16c0..d35c93f4a5 100644 --- a/python/packages/core/agent_framework/_mcp.py +++ b/python/packages/core/agent_framework/_mcp.py @@ -931,10 +931,9 @@ def __init__( """Initialize the MCP streamable HTTP tool. Note: - The arguments are used to create a streamable HTTP client. - See ``mcp.client.streamable_http.streamablehttp_client`` for more details. - Any extra arguments passed to the constructor will be passed to the - streamable HTTP client constructor. + The arguments are used to create a streamable HTTP client using the + new ``mcp.client.streamable_http.streamable_http_client`` API. + The tool manages its own httpx.AsyncClient instance for proper session isolation. Args: name: The name of the tool. @@ -955,11 +954,11 @@ def __init__( allowed_tools: A list of tools that are allowed to use this tool. additional_properties: Additional properties. headers: The headers to send with the request. - timeout: The timeout for the request. - sse_read_timeout: The timeout for reading from the SSE stream. + timeout: The timeout for the request (default: 30.0 seconds). + sse_read_timeout: The timeout for reading from the SSE stream (default: 300.0 seconds). terminate_on_close: Close the transport when the MCP client is terminated. chat_client: The chat client to use for sampling. - kwargs: Any extra arguments to pass to the SSE client. + kwargs: Any extra arguments (currently not used but preserved for future compatibility). """ super().__init__( name=name, From 878d1bdbc0f1c34c08d5d819c30d54fb6205f841 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 6 Jan 2026 08:59:42 +0000 Subject: [PATCH 03/10] Refactor MCPStreamableHTTPTool to accept optional http_client parameter and delegate client creation to streamable_http_client Co-authored-by: eavanvalkenburg <13749212+eavanvalkenburg@users.noreply.github.com> --- python/packages/core/agent_framework/_mcp.py | 32 +++++------- python/packages/core/tests/core/test_mcp.py | 55 ++++++++------------ 2 files changed, 36 insertions(+), 51 deletions(-) diff --git a/python/packages/core/agent_framework/_mcp.py b/python/packages/core/agent_framework/_mcp.py index d35c93f4a5..a36ee7cf8b 100644 --- a/python/packages/core/agent_framework/_mcp.py +++ b/python/packages/core/agent_framework/_mcp.py @@ -14,7 +14,7 @@ from mcp import types from mcp.client.session import ClientSession from mcp.client.stdio import StdioServerParameters, stdio_client -from mcp.client.streamable_http import streamable_http_client, streamablehttp_client +from mcp.client.streamable_http import streamable_http_client from mcp.client.websocket import websocket_client from mcp.shared.context import RequestContext from mcp.shared.exceptions import McpError @@ -926,6 +926,7 @@ def __init__( terminate_on_close: bool | None = None, chat_client: "ChatClientProtocol | None" = None, additional_properties: dict[str, Any] | None = None, + http_client: httpx.AsyncClient | None = None, **kwargs: Any, ) -> None: """Initialize the MCP streamable HTTP tool. @@ -933,7 +934,8 @@ def __init__( Note: The arguments are used to create a streamable HTTP client using the new ``mcp.client.streamable_http.streamable_http_client`` API. - The tool manages its own httpx.AsyncClient instance for proper session isolation. + If an httpx.AsyncClient is provided, it will be used; otherwise, the + streamable_http_client will create and manage its own client. Args: name: The name of the tool. @@ -958,6 +960,8 @@ def __init__( sse_read_timeout: The timeout for reading from the SSE stream (default: 300.0 seconds). terminate_on_close: Close the transport when the MCP client is terminated. chat_client: The chat client to use for sampling. + http_client: Optional httpx.AsyncClient to use. If not provided, the client + will be created and managed by streamable_http_client. kwargs: Any extra arguments (currently not used but preserved for future compatibility). """ super().__init__( @@ -978,7 +982,7 @@ def __init__( self.sse_read_timeout = sse_read_timeout self.terminate_on_close = terminate_on_close self._client_kwargs = kwargs - self._httpx_client: httpx.AsyncClient | None = None + self._httpx_client: httpx.AsyncClient | None = http_client def get_mcp_client(self) -> _AsyncGeneratorContextManager[Any, None]: """Get an MCP streamable HTTP client. @@ -986,17 +990,7 @@ def get_mcp_client(self) -> _AsyncGeneratorContextManager[Any, None]: Returns: An async context manager for the streamable HTTP client transport. """ - timeout_value = self.timeout if self.timeout is not None else 30.0 - sse_timeout_value = self.sse_read_timeout if self.sse_read_timeout is not None else 300.0 - - # Create and track httpx client - # Note: _client_kwargs are not passed to AsyncClient as they were specific to the old API - self._httpx_client = httpx.AsyncClient( - headers=self.headers, - timeout=httpx.Timeout(timeout_value, read=sse_timeout_value), - ) - - # Use new API instead of deprecated streamablehttp_client() + # Pass the http_client if provided, otherwise let streamable_http_client handle it return streamable_http_client( url=self.url, http_client=self._httpx_client, @@ -1006,13 +1000,15 @@ def get_mcp_client(self) -> _AsyncGeneratorContextManager[Any, None]: async def close(self) -> None: """Disconnect from the MCP server and close httpx client. - Closes the connection and cleans up resources including the httpx client. + Closes the connection and cleans up resources. If an httpx client was + provided by the user, it is their responsibility to close it. """ await super().close() - if self._httpx_client is not None: - await self._httpx_client.aclose() - self._httpx_client = None + # Only close the client if we created it (user didn't provide one) + # Note: We can't distinguish between user-provided and self-created clients, + # so we don't close any clients to avoid closing user-provided ones + self._httpx_client = None class MCPWebsocketTool(MCPTool): diff --git a/python/packages/core/tests/core/test_mcp.py b/python/packages/core/tests/core/test_mcp.py index 72f6bb56a2..5e94be7347 100644 --- a/python/packages/core/tests/core/test_mcp.py +++ b/python/packages/core/tests/core/test_mcp.py @@ -1520,28 +1520,14 @@ def test_mcp_streamable_http_tool_get_mcp_client_all_params(): custom_param="test", ) - with patch("agent_framework._mcp.streamable_http_client") as mock_http_client, patch( - "agent_framework._mcp.httpx.AsyncClient" - ) as mock_async_client: - # Create a mock httpx client instance - mock_client_instance = Mock() - mock_async_client.return_value = mock_client_instance - + with patch("agent_framework._mcp.streamable_http_client") as mock_http_client: tool.get_mcp_client() - # Verify httpx.AsyncClient was created with correct parameters - mock_async_client.assert_called_once() - call_kwargs = mock_async_client.call_args.kwargs - assert call_kwargs["headers"] == {"Auth": "token"} - assert isinstance(call_kwargs["timeout"], httpx.Timeout) - # The timeout constructor sets connect/write/pool to the first arg, and read can be overridden - assert call_kwargs["timeout"].connect == 30.0 - assert call_kwargs["timeout"].read == 10.0 - - # Verify streamable_http_client was called with new API parameters + # Verify streamable_http_client was called with None for http_client + # (since we didn't provide one, let the API handle client creation) mock_http_client.assert_called_once_with( url="http://example.com", - http_client=mock_client_instance, + http_client=None, terminate_on_close=True, ) @@ -1709,7 +1695,7 @@ async def test_load_prompts_prevents_multiple_calls(): @pytest.mark.asyncio async def test_mcp_streamable_http_tool_httpx_client_cleanup(): - """Test that MCPStreamableHTTPTool properly closes httpx client and creates new instances.""" + """Test that MCPStreamableHTTPTool allows user-provided httpx clients.""" from unittest.mock import AsyncMock, Mock, patch from agent_framework import MCPStreamableHTTPTool @@ -1731,7 +1717,7 @@ async def test_mcp_streamable_http_tool_httpx_client_cleanup(): mock_session_class.return_value.__aenter__ = AsyncMock(return_value=mock_session) mock_session_class.return_value.__aexit__ = AsyncMock(return_value=None) - # Create and use first tool + # Test 1: Tool without provided client (delegates to streamable_http_client) tool1 = MCPStreamableHTTPTool( name="test", url="http://localhost:8081/mcp", @@ -1741,15 +1727,14 @@ async def test_mcp_streamable_http_tool_httpx_client_cleanup(): timeout=30, ) await tool1.connect() - client1 = tool1._httpx_client - assert client1 is not None, "First httpx client should be created" - client1_id = id(client1) + # When no client is provided, _httpx_client should be None + assert tool1._httpx_client is None, "httpx client should be None when not provided" - # Close first tool await tool1.close() - assert tool1._httpx_client is None, "First httpx client should be cleared after close" + assert tool1._httpx_client is None, "httpx client should remain None after close" - # Create and use second tool + # Test 2: Tool with user-provided client + user_client = Mock(spec=httpx.AsyncClient) tool2 = MCPStreamableHTTPTool( name="test", url="http://localhost:8081/mcp", @@ -1757,14 +1742,18 @@ async def test_mcp_streamable_http_tool_httpx_client_cleanup(): load_prompts=False, terminate_on_close=False, timeout=30, + http_client=user_client, ) await tool2.connect() - client2 = tool2._httpx_client - assert client2 is not None, "Second httpx client should be created" - client2_id = id(client2) - - # Verify different httpx client instances (no reuse) - assert client1_id != client2_id, "httpx clients should not be reused between tool instances" + + # Verify the user-provided client was stored + assert tool2._httpx_client is user_client, "User-provided client should be stored" + + # Verify streamable_http_client was called with the user's client + # Get the last call (should be from tool2.connect()) + call_args = mock_client.call_args + assert call_args.kwargs["http_client"] is user_client, "User's client should be passed through" await tool2.close() - assert tool2._httpx_client is None, "Second httpx client should be cleared after close" + # After close, the reference is cleared but user is responsible for closing + assert tool2._httpx_client is None, "Client reference should be cleared after close" From e13d6b51c4f0fcf61ba365aa5e399b598048bb9d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 6 Jan 2026 09:18:39 +0000 Subject: [PATCH 04/10] Update mcp package minimum version to 1.24.0 for streamable_http_client API support Co-authored-by: eavanvalkenburg <13749212+eavanvalkenburg@users.noreply.github.com> --- python/packages/core/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/packages/core/pyproject.toml b/python/packages/core/pyproject.toml index 1b245277c0..bf3974eeb5 100644 --- a/python/packages/core/pyproject.toml +++ b/python/packages/core/pyproject.toml @@ -34,7 +34,7 @@ dependencies = [ # connectors and functions "openai>=1.99.0", "azure-identity>=1,<2", - "mcp[ws]>=1.23", + "mcp[ws]>=1.24.0", "packaging>=24.1", ] From 5067391ba8ad937791340e77718bb371cc5abe23 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 6 Jan 2026 09:27:11 +0000 Subject: [PATCH 05/10] Fix critical bugs: apply headers/timeout/sse_read_timeout when creating httpx client, add version constraint <2, and properly manage client lifecycle Co-authored-by: eavanvalkenburg <13749212+eavanvalkenburg@users.noreply.github.com> --- python/packages/core/agent_framework/_mcp.py | 41 +++++++++++------ python/packages/core/pyproject.toml | 2 +- python/packages/core/tests/core/test_mcp.py | 48 +++++++++++++++----- 3 files changed, 65 insertions(+), 26 deletions(-) diff --git a/python/packages/core/agent_framework/_mcp.py b/python/packages/core/agent_framework/_mcp.py index a36ee7cf8b..51a0327155 100644 --- a/python/packages/core/agent_framework/_mcp.py +++ b/python/packages/core/agent_framework/_mcp.py @@ -934,8 +934,9 @@ def __init__( Note: The arguments are used to create a streamable HTTP client using the new ``mcp.client.streamable_http.streamable_http_client`` API. - If an httpx.AsyncClient is provided, it will be used; otherwise, the - streamable_http_client will create and manage its own client. + If an httpx.AsyncClient is provided via ``http_client``, it will be used directly. + Otherwise, an httpx.AsyncClient will be created automatically using the + ``headers``, ``timeout``, and ``sse_read_timeout`` parameters. Args: name: The name of the tool. @@ -955,13 +956,14 @@ def __init__( A tool should not be listed in both, if so, it will require approval. allowed_tools: A list of tools that are allowed to use this tool. additional_properties: Additional properties. - headers: The headers to send with the request. - timeout: The timeout for the request (default: 30.0 seconds). + headers: The headers to send with the request. Only used if ``http_client`` is not provided. + timeout: The timeout for the request (default: 30.0 seconds). Only used if ``http_client`` is not provided. sse_read_timeout: The timeout for reading from the SSE stream (default: 300.0 seconds). + Only used if ``http_client`` is not provided. terminate_on_close: Close the transport when the MCP client is terminated. chat_client: The chat client to use for sampling. - http_client: Optional httpx.AsyncClient to use. If not provided, the client - will be created and managed by streamable_http_client. + http_client: Optional httpx.AsyncClient to use. If not provided, one will be created + automatically using the ``headers``, ``timeout``, and ``sse_read_timeout`` parameters. kwargs: Any extra arguments (currently not used but preserved for future compatibility). """ super().__init__( @@ -983,6 +985,7 @@ def __init__( self.terminate_on_close = terminate_on_close self._client_kwargs = kwargs self._httpx_client: httpx.AsyncClient | None = http_client + self._owns_client = http_client is None # Track if we need to close the client def get_mcp_client(self) -> _AsyncGeneratorContextManager[Any, None]: """Get an MCP streamable HTTP client. @@ -990,7 +993,17 @@ def get_mcp_client(self) -> _AsyncGeneratorContextManager[Any, None]: Returns: An async context manager for the streamable HTTP client transport. """ - # Pass the http_client if provided, otherwise let streamable_http_client handle it + # If no client was provided, create one with the configured parameters + if self._httpx_client is None: + timeout_value = self.timeout if self.timeout is not None else 30.0 + sse_timeout_value = self.sse_read_timeout if self.sse_read_timeout is not None else 300.0 + + self._httpx_client = httpx.AsyncClient( + headers=self.headers, + timeout=httpx.Timeout(timeout_value, read=sse_timeout_value), + ) + + # Pass the http_client to streamable_http_client return streamable_http_client( url=self.url, http_client=self._httpx_client, @@ -998,16 +1011,18 @@ def get_mcp_client(self) -> _AsyncGeneratorContextManager[Any, None]: ) async def close(self) -> None: - """Disconnect from the MCP server and close httpx client. + """Disconnect from the MCP server and close httpx client if we created it. - Closes the connection and cleans up resources. If an httpx client was - provided by the user, it is their responsibility to close it. + Closes the MCP connection and cleans up internal resources. If this tool + created an httpx client (because none was provided), it will be closed. + If the caller provided their own client, they remain responsible for closing it. """ await super().close() - # Only close the client if we created it (user didn't provide one) - # Note: We can't distinguish between user-provided and self-created clients, - # so we don't close any clients to avoid closing user-provided ones + # Only close the client if we created it + if self._httpx_client is not None and self._owns_client: + await self._httpx_client.aclose() + self._httpx_client = None diff --git a/python/packages/core/pyproject.toml b/python/packages/core/pyproject.toml index bf3974eeb5..942f9d8919 100644 --- a/python/packages/core/pyproject.toml +++ b/python/packages/core/pyproject.toml @@ -34,7 +34,7 @@ dependencies = [ # connectors and functions "openai>=1.99.0", "azure-identity>=1,<2", - "mcp[ws]>=1.24.0", + "mcp[ws]>=1.24.0,<2", "packaging>=24.1", ] diff --git a/python/packages/core/tests/core/test_mcp.py b/python/packages/core/tests/core/test_mcp.py index 5e94be7347..551c075738 100644 --- a/python/packages/core/tests/core/test_mcp.py +++ b/python/packages/core/tests/core/test_mcp.py @@ -1520,14 +1520,28 @@ def test_mcp_streamable_http_tool_get_mcp_client_all_params(): custom_param="test", ) - with patch("agent_framework._mcp.streamable_http_client") as mock_http_client: + with patch("agent_framework._mcp.streamable_http_client") as mock_http_client, patch( + "agent_framework._mcp.httpx.AsyncClient" + ) as mock_async_client: + # Create a mock httpx client instance + mock_client_instance = Mock() + mock_async_client.return_value = mock_client_instance + tool.get_mcp_client() - # Verify streamable_http_client was called with None for http_client - # (since we didn't provide one, let the API handle client creation) + # Verify httpx.AsyncClient was created with correct parameters + mock_async_client.assert_called_once() + call_kwargs = mock_async_client.call_args.kwargs + assert call_kwargs["headers"] == {"Auth": "token"} + assert isinstance(call_kwargs["timeout"], httpx.Timeout) + # The timeout constructor sets connect/write/pool to the first arg, and read can be overridden + assert call_kwargs["timeout"].connect == 30.0 + assert call_kwargs["timeout"].read == 10.0 + + # Verify streamable_http_client was called with the created client mock_http_client.assert_called_once_with( url="http://example.com", - http_client=None, + http_client=mock_client_instance, terminate_on_close=True, ) @@ -1695,7 +1709,7 @@ async def test_load_prompts_prevents_multiple_calls(): @pytest.mark.asyncio async def test_mcp_streamable_http_tool_httpx_client_cleanup(): - """Test that MCPStreamableHTTPTool allows user-provided httpx clients.""" + """Test that MCPStreamableHTTPTool properly manages httpx client lifecycle.""" from unittest.mock import AsyncMock, Mock, patch from agent_framework import MCPStreamableHTTPTool @@ -1703,7 +1717,7 @@ async def test_mcp_streamable_http_tool_httpx_client_cleanup(): # Mock the streamable_http_client to avoid actual connections with patch("agent_framework._mcp.streamable_http_client") as mock_client, patch( "agent_framework._mcp.ClientSession" - ) as mock_session_class: + ) as mock_session_class, patch("agent_framework._mcp.httpx.AsyncClient") as mock_async_client_class: # Setup mock context manager for streamable_http_client mock_transport = (Mock(), Mock()) mock_context_manager = Mock() @@ -1717,7 +1731,11 @@ async def test_mcp_streamable_http_tool_httpx_client_cleanup(): mock_session_class.return_value.__aenter__ = AsyncMock(return_value=mock_session) mock_session_class.return_value.__aexit__ = AsyncMock(return_value=None) - # Test 1: Tool without provided client (delegates to streamable_http_client) + # Test 1: Tool without provided client (creates and manages its own) + mock_created_client = Mock() + mock_created_client.aclose = AsyncMock() + mock_async_client_class.return_value = mock_created_client + tool1 = MCPStreamableHTTPTool( name="test", url="http://localhost:8081/mcp", @@ -1727,14 +1745,18 @@ async def test_mcp_streamable_http_tool_httpx_client_cleanup(): timeout=30, ) await tool1.connect() - # When no client is provided, _httpx_client should be None - assert tool1._httpx_client is None, "httpx client should be None when not provided" + # When no client is provided, one should be created + assert tool1._httpx_client is mock_created_client, "httpx client should be created when not provided" + assert tool1._owns_client is True, "Tool should track that it owns the client" await tool1.close() - assert tool1._httpx_client is None, "httpx client should remain None after close" + # Verify the client was closed since we created it + mock_created_client.aclose.assert_called_once() + assert tool1._httpx_client is None, "httpx client should be cleared after close" # Test 2: Tool with user-provided client - user_client = Mock(spec=httpx.AsyncClient) + user_client = Mock() + user_client.aclose = AsyncMock() tool2 = MCPStreamableHTTPTool( name="test", url="http://localhost:8081/mcp", @@ -1748,6 +1770,7 @@ async def test_mcp_streamable_http_tool_httpx_client_cleanup(): # Verify the user-provided client was stored assert tool2._httpx_client is user_client, "User-provided client should be stored" + assert tool2._owns_client is False, "Tool should track that it doesn't own the client" # Verify streamable_http_client was called with the user's client # Get the last call (should be from tool2.connect()) @@ -1755,5 +1778,6 @@ async def test_mcp_streamable_http_tool_httpx_client_cleanup(): assert call_args.kwargs["http_client"] is user_client, "User's client should be passed through" await tool2.close() - # After close, the reference is cleared but user is responsible for closing + # Verify the user's client was NOT closed (user is responsible) + user_client.aclose.assert_not_called() assert tool2._httpx_client is None, "Client reference should be cleared after close" From 7fe3d5d16c4d651a325371be81bb31d7bb90fb28 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 6 Jan 2026 09:34:40 +0000 Subject: [PATCH 06/10] Simplify implementation: remove headers/timeout/sse_read_timeout params, remove kwargs, remove close() override per feedback Co-authored-by: eavanvalkenburg <13749212+eavanvalkenburg@users.noreply.github.com> --- python/packages/core/agent_framework/_mcp.py | 49 +++-------------- python/packages/core/tests/core/test_mcp.py | 55 ++++---------------- 2 files changed, 15 insertions(+), 89 deletions(-) diff --git a/python/packages/core/agent_framework/_mcp.py b/python/packages/core/agent_framework/_mcp.py index 51a0327155..33f66178ff 100644 --- a/python/packages/core/agent_framework/_mcp.py +++ b/python/packages/core/agent_framework/_mcp.py @@ -920,14 +920,10 @@ def __init__( description: str | None = None, approval_mode: (Literal["always_require", "never_require"] | HostedMCPSpecificApproval | None) = None, allowed_tools: Collection[str] | None = None, - headers: dict[str, Any] | None = None, - timeout: float | None = None, - sse_read_timeout: float | None = None, terminate_on_close: bool | None = None, chat_client: "ChatClientProtocol | None" = None, additional_properties: dict[str, Any] | None = None, http_client: httpx.AsyncClient | None = None, - **kwargs: Any, ) -> None: """Initialize the MCP streamable HTTP tool. @@ -935,8 +931,7 @@ def __init__( The arguments are used to create a streamable HTTP client using the new ``mcp.client.streamable_http.streamable_http_client`` API. If an httpx.AsyncClient is provided via ``http_client``, it will be used directly. - Otherwise, an httpx.AsyncClient will be created automatically using the - ``headers``, ``timeout``, and ``sse_read_timeout`` parameters. + Otherwise, the ``streamable_http_client`` API will create and manage a default client. Args: name: The name of the tool. @@ -956,15 +951,12 @@ def __init__( A tool should not be listed in both, if so, it will require approval. allowed_tools: A list of tools that are allowed to use this tool. additional_properties: Additional properties. - headers: The headers to send with the request. Only used if ``http_client`` is not provided. - timeout: The timeout for the request (default: 30.0 seconds). Only used if ``http_client`` is not provided. - sse_read_timeout: The timeout for reading from the SSE stream (default: 300.0 seconds). - Only used if ``http_client`` is not provided. terminate_on_close: Close the transport when the MCP client is terminated. chat_client: The chat client to use for sampling. - http_client: Optional httpx.AsyncClient to use. If not provided, one will be created - automatically using the ``headers``, ``timeout``, and ``sse_read_timeout`` parameters. - kwargs: Any extra arguments (currently not used but preserved for future compatibility). + http_client: Optional httpx.AsyncClient to use. If not provided, the + ``streamable_http_client`` API will create and manage a default client. + To configure headers, timeouts, or other HTTP client settings, create + and pass your own ``httpx.AsyncClient`` instance. """ super().__init__( name=name, @@ -979,13 +971,8 @@ def __init__( request_timeout=request_timeout, ) self.url = url - self.headers = headers or {} - self.timeout = timeout - self.sse_read_timeout = sse_read_timeout self.terminate_on_close = terminate_on_close - self._client_kwargs = kwargs self._httpx_client: httpx.AsyncClient | None = http_client - self._owns_client = http_client is None # Track if we need to close the client def get_mcp_client(self) -> _AsyncGeneratorContextManager[Any, None]: """Get an MCP streamable HTTP client. @@ -993,37 +980,13 @@ def get_mcp_client(self) -> _AsyncGeneratorContextManager[Any, None]: Returns: An async context manager for the streamable HTTP client transport. """ - # If no client was provided, create one with the configured parameters - if self._httpx_client is None: - timeout_value = self.timeout if self.timeout is not None else 30.0 - sse_timeout_value = self.sse_read_timeout if self.sse_read_timeout is not None else 300.0 - - self._httpx_client = httpx.AsyncClient( - headers=self.headers, - timeout=httpx.Timeout(timeout_value, read=sse_timeout_value), - ) - - # Pass the http_client to streamable_http_client + # Pass the http_client (which may be None) to streamable_http_client return streamable_http_client( url=self.url, http_client=self._httpx_client, terminate_on_close=self.terminate_on_close if self.terminate_on_close is not None else True, ) - async def close(self) -> None: - """Disconnect from the MCP server and close httpx client if we created it. - - Closes the MCP connection and cleans up internal resources. If this tool - created an httpx client (because none was provided), it will be closed. - If the caller provided their own client, they remain responsible for closing it. - """ - await super().close() - - # Only close the client if we created it - if self._httpx_client is not None and self._owns_client: - await self._httpx_client.aclose() - - self._httpx_client = None class MCPWebsocketTool(MCPTool): diff --git a/python/packages/core/tests/core/test_mcp.py b/python/packages/core/tests/core/test_mcp.py index 551c075738..0cc70a0eff 100644 --- a/python/packages/core/tests/core/test_mcp.py +++ b/python/packages/core/tests/core/test_mcp.py @@ -1513,35 +1513,17 @@ def test_mcp_streamable_http_tool_get_mcp_client_all_params(): tool = MCPStreamableHTTPTool( name="test", url="http://example.com", - headers={"Auth": "token"}, - timeout=30.0, - sse_read_timeout=10.0, terminate_on_close=True, - custom_param="test", ) - with patch("agent_framework._mcp.streamable_http_client") as mock_http_client, patch( - "agent_framework._mcp.httpx.AsyncClient" - ) as mock_async_client: - # Create a mock httpx client instance - mock_client_instance = Mock() - mock_async_client.return_value = mock_client_instance - + with patch("agent_framework._mcp.streamable_http_client") as mock_http_client: tool.get_mcp_client() - # Verify httpx.AsyncClient was created with correct parameters - mock_async_client.assert_called_once() - call_kwargs = mock_async_client.call_args.kwargs - assert call_kwargs["headers"] == {"Auth": "token"} - assert isinstance(call_kwargs["timeout"], httpx.Timeout) - # The timeout constructor sets connect/write/pool to the first arg, and read can be overridden - assert call_kwargs["timeout"].connect == 30.0 - assert call_kwargs["timeout"].read == 10.0 - - # Verify streamable_http_client was called with the created client + # Verify streamable_http_client was called with None for http_client + # (since we didn't provide one, the API will create its own) mock_http_client.assert_called_once_with( url="http://example.com", - http_client=mock_client_instance, + http_client=None, terminate_on_close=True, ) @@ -1709,7 +1691,7 @@ async def test_load_prompts_prevents_multiple_calls(): @pytest.mark.asyncio async def test_mcp_streamable_http_tool_httpx_client_cleanup(): - """Test that MCPStreamableHTTPTool properly manages httpx client lifecycle.""" + """Test that MCPStreamableHTTPTool properly passes through httpx clients.""" from unittest.mock import AsyncMock, Mock, patch from agent_framework import MCPStreamableHTTPTool @@ -1717,7 +1699,7 @@ async def test_mcp_streamable_http_tool_httpx_client_cleanup(): # Mock the streamable_http_client to avoid actual connections with patch("agent_framework._mcp.streamable_http_client") as mock_client, patch( "agent_framework._mcp.ClientSession" - ) as mock_session_class, patch("agent_framework._mcp.httpx.AsyncClient") as mock_async_client_class: + ) as mock_session_class: # Setup mock context manager for streamable_http_client mock_transport = (Mock(), Mock()) mock_context_manager = Mock() @@ -1731,53 +1713,34 @@ async def test_mcp_streamable_http_tool_httpx_client_cleanup(): mock_session_class.return_value.__aenter__ = AsyncMock(return_value=mock_session) mock_session_class.return_value.__aexit__ = AsyncMock(return_value=None) - # Test 1: Tool without provided client (creates and manages its own) - mock_created_client = Mock() - mock_created_client.aclose = AsyncMock() - mock_async_client_class.return_value = mock_created_client - + # Test 1: Tool without provided client (passes None to streamable_http_client) tool1 = MCPStreamableHTTPTool( name="test", url="http://localhost:8081/mcp", load_tools=False, load_prompts=False, terminate_on_close=False, - timeout=30, ) await tool1.connect() - # When no client is provided, one should be created - assert tool1._httpx_client is mock_created_client, "httpx client should be created when not provided" - assert tool1._owns_client is True, "Tool should track that it owns the client" - - await tool1.close() - # Verify the client was closed since we created it - mock_created_client.aclose.assert_called_once() - assert tool1._httpx_client is None, "httpx client should be cleared after close" + # When no client is provided, _httpx_client should be None + assert tool1._httpx_client is None, "httpx client should be None when not provided" # Test 2: Tool with user-provided client user_client = Mock() - user_client.aclose = AsyncMock() tool2 = MCPStreamableHTTPTool( name="test", url="http://localhost:8081/mcp", load_tools=False, load_prompts=False, terminate_on_close=False, - timeout=30, http_client=user_client, ) await tool2.connect() # Verify the user-provided client was stored assert tool2._httpx_client is user_client, "User-provided client should be stored" - assert tool2._owns_client is False, "Tool should track that it doesn't own the client" # Verify streamable_http_client was called with the user's client # Get the last call (should be from tool2.connect()) call_args = mock_client.call_args assert call_args.kwargs["http_client"] is user_client, "User's client should be passed through" - - await tool2.close() - # Verify the user's client was NOT closed (user is responsible) - user_client.aclose.assert_not_called() - assert tool2._httpx_client is None, "Client reference should be cleared after close" From 988f9b5a4daf6b97b115e82cb40cb607155c0340 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 6 Jan 2026 09:46:38 +0000 Subject: [PATCH 07/10] Add back **kwargs parameter for backward compatibility (accepted but not used) Co-authored-by: eavanvalkenburg <13749212+eavanvalkenburg@users.noreply.github.com> --- python/packages/core/agent_framework/_mcp.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/python/packages/core/agent_framework/_mcp.py b/python/packages/core/agent_framework/_mcp.py index 33f66178ff..a95186a58e 100644 --- a/python/packages/core/agent_framework/_mcp.py +++ b/python/packages/core/agent_framework/_mcp.py @@ -924,6 +924,7 @@ def __init__( chat_client: "ChatClientProtocol | None" = None, additional_properties: dict[str, Any] | None = None, http_client: httpx.AsyncClient | None = None, + **kwargs: Any, ) -> None: """Initialize the MCP streamable HTTP tool. @@ -957,6 +958,7 @@ def __init__( ``streamable_http_client`` API will create and manage a default client. To configure headers, timeouts, or other HTTP client settings, create and pass your own ``httpx.AsyncClient`` instance. + kwargs: Additional keyword arguments (accepted for backward compatibility but not used). """ super().__init__( name=name, From 43decb4c786b58bf30706bfe44eb20fc0a3bf000 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 6 Jan 2026 10:54:21 +0000 Subject: [PATCH 08/10] Remove unused httpx import from test file Note: The uv.lock file needs to be updated with 'uv sync' to reflect the mcp version constraint change (>=1.24.0,<2) Co-authored-by: eavanvalkenburg <13749212+eavanvalkenburg@users.noreply.github.com> --- python/packages/core/tests/core/test_mcp.py | 1 - 1 file changed, 1 deletion(-) diff --git a/python/packages/core/tests/core/test_mcp.py b/python/packages/core/tests/core/test_mcp.py index 0cc70a0eff..940ff72eb4 100644 --- a/python/packages/core/tests/core/test_mcp.py +++ b/python/packages/core/tests/core/test_mcp.py @@ -5,7 +5,6 @@ from typing import Any from unittest.mock import AsyncMock, Mock, patch -import httpx import pytest from mcp import types from mcp.client.session import ClientSession From 0888069e9ca8835f17b317818a5d44d529b5f0ec Mon Sep 17 00:00:00 2001 From: eavanvalkenburg Date: Thu, 8 Jan 2026 17:30:22 +0100 Subject: [PATCH 09/10] cicd fixes --- python/packages/core/agent_framework/_mcp.py | 1 - python/packages/core/tests/core/test_mcp.py | 11 ++++++----- python/uv.lock | 18 ++++++++++++------ 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/python/packages/core/agent_framework/_mcp.py b/python/packages/core/agent_framework/_mcp.py index a95186a58e..68a2320171 100644 --- a/python/packages/core/agent_framework/_mcp.py +++ b/python/packages/core/agent_framework/_mcp.py @@ -990,7 +990,6 @@ def get_mcp_client(self) -> _AsyncGeneratorContextManager[Any, None]: ) - class MCPWebsocketTool(MCPTool): """MCP tool for connecting to WebSocket-based MCP servers. diff --git a/python/packages/core/tests/core/test_mcp.py b/python/packages/core/tests/core/test_mcp.py index 940ff72eb4..89c3c520fe 100644 --- a/python/packages/core/tests/core/test_mcp.py +++ b/python/packages/core/tests/core/test_mcp.py @@ -1696,9 +1696,10 @@ async def test_mcp_streamable_http_tool_httpx_client_cleanup(): from agent_framework import MCPStreamableHTTPTool # Mock the streamable_http_client to avoid actual connections - with patch("agent_framework._mcp.streamable_http_client") as mock_client, patch( - "agent_framework._mcp.ClientSession" - ) as mock_session_class: + with ( + patch("agent_framework._mcp.streamable_http_client") as mock_client, + patch("agent_framework._mcp.ClientSession") as mock_session_class, + ): # Setup mock context manager for streamable_http_client mock_transport = (Mock(), Mock()) mock_context_manager = Mock() @@ -1735,10 +1736,10 @@ async def test_mcp_streamable_http_tool_httpx_client_cleanup(): http_client=user_client, ) await tool2.connect() - + # Verify the user-provided client was stored assert tool2._httpx_client is user_client, "User-provided client should be stored" - + # Verify streamable_http_client was called with the user's client # Get the last call (should be from tool2.connect()) call_args = mock_client.call_args diff --git a/python/uv.lock b/python/uv.lock index 15d1fd855e..314719e7b3 100644 --- a/python/uv.lock +++ b/python/uv.lock @@ -377,7 +377,7 @@ requires-dist = [ { name = "agent-framework-purview", marker = "extra == 'all'", editable = "packages/purview" }, { name = "agent-framework-redis", marker = "extra == 'all'", editable = "packages/redis" }, { name = "azure-identity", specifier = ">=1,<2" }, - { name = "mcp", extras = ["ws"], specifier = ">=1.23" }, + { name = "mcp", extras = ["ws"], specifier = ">=1.24.0,<2" }, { name = "openai", specifier = ">=1.99.0" }, { name = "opentelemetry-api", specifier = ">=1.39.0" }, { name = "opentelemetry-sdk", specifier = ">=1.39.0" }, @@ -1369,7 +1369,7 @@ name = "clr-loader" version = "0.2.10" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cffi", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "cffi", marker = "(python_full_version < '3.14' and sys_platform == 'darwin') or (python_full_version < '3.14' and sys_platform == 'linux') or (python_full_version < '3.14' and sys_platform == 'win32')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/18/24/c12faf3f61614b3131b5c98d3bf0d376b49c7feaa73edca559aeb2aee080/clr_loader-0.2.10.tar.gz", hash = "sha256:81f114afbc5005bafc5efe5af1341d400e22137e275b042a8979f3feb9fc9446", size = 83605, upload-time = "2026-01-03T23:13:06.984Z" } wheels = [ @@ -1848,7 +1848,7 @@ name = "exceptiongroup" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "(python_full_version < '3.13' and sys_platform == 'darwin') or (python_full_version < '3.13' and sys_platform == 'linux') or (python_full_version < '3.13' and sys_platform == 'win32')" }, + { name = "typing-extensions", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ @@ -2294,6 +2294,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/32/6a/33d1702184d94106d3cdd7bfb788e19723206fce152e303473ca3b946c7b/greenlet-3.3.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:6f8496d434d5cb2dce025773ba5597f71f5410ae499d5dd9533e0653258cdb3d", size = 273658, upload-time = "2025-12-04T14:23:37.494Z" }, { url = "https://files.pythonhosted.org/packages/d6/b7/2b5805bbf1907c26e434f4e448cd8b696a0b71725204fa21a211ff0c04a7/greenlet-3.3.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b96dc7eef78fd404e022e165ec55327f935b9b52ff355b067eb4a0267fc1cffb", size = 574810, upload-time = "2025-12-04T14:50:04.154Z" }, { url = "https://files.pythonhosted.org/packages/94/38/343242ec12eddf3d8458c73f555c084359883d4ddc674240d9e61ec51fd6/greenlet-3.3.0-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:73631cd5cccbcfe63e3f9492aaa664d278fda0ce5c3d43aeda8e77317e38efbd", size = 586248, upload-time = "2025-12-04T14:57:39.35Z" }, + { url = "https://files.pythonhosted.org/packages/f0/d0/0ae86792fb212e4384041e0ef8e7bc66f59a54912ce407d26a966ed2914d/greenlet-3.3.0-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b299a0cb979f5d7197442dccc3aee67fce53500cd88951b7e6c35575701c980b", size = 597403, upload-time = "2025-12-04T15:07:10.831Z" }, { url = "https://files.pythonhosted.org/packages/b6/a8/15d0aa26c0036a15d2659175af00954aaaa5d0d66ba538345bd88013b4d7/greenlet-3.3.0-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7dee147740789a4632cace364816046e43310b59ff8fb79833ab043aefa72fd5", size = 586910, upload-time = "2025-12-04T14:25:59.705Z" }, { url = "https://files.pythonhosted.org/packages/e1/9b/68d5e3b7ccaba3907e5532cf8b9bf16f9ef5056a008f195a367db0ff32db/greenlet-3.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:39b28e339fc3c348427560494e28d8a6f3561c8d2bcf7d706e1c624ed8d822b9", size = 1547206, upload-time = "2025-12-04T15:04:21.027Z" }, { url = "https://files.pythonhosted.org/packages/66/bd/e3086ccedc61e49f91e2cfb5ffad9d8d62e5dc85e512a6200f096875b60c/greenlet-3.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b3c374782c2935cc63b2a27ba8708471de4ad1abaa862ffdb1ef45a643ddbb7d", size = 1613359, upload-time = "2025-12-04T14:27:26.548Z" }, @@ -2301,6 +2302,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1f/cb/48e964c452ca2b92175a9b2dca037a553036cb053ba69e284650ce755f13/greenlet-3.3.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:e29f3018580e8412d6aaf5641bb7745d38c85228dacf51a73bd4e26ddf2a6a8e", size = 274908, upload-time = "2025-12-04T14:23:26.435Z" }, { url = "https://files.pythonhosted.org/packages/28/da/38d7bff4d0277b594ec557f479d65272a893f1f2a716cad91efeb8680953/greenlet-3.3.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a687205fb22794e838f947e2194c0566d3812966b41c78709554aa883183fb62", size = 577113, upload-time = "2025-12-04T14:50:05.493Z" }, { url = "https://files.pythonhosted.org/packages/3c/f2/89c5eb0faddc3ff014f1c04467d67dee0d1d334ab81fadbf3744847f8a8a/greenlet-3.3.0-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4243050a88ba61842186cb9e63c7dfa677ec146160b0efd73b855a3d9c7fcf32", size = 590338, upload-time = "2025-12-04T14:57:41.136Z" }, + { url = "https://files.pythonhosted.org/packages/80/d7/db0a5085035d05134f8c089643da2b44cc9b80647c39e93129c5ef170d8f/greenlet-3.3.0-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:670d0f94cd302d81796e37299bcd04b95d62403883b24225c6b5271466612f45", size = 601098, upload-time = "2025-12-04T15:07:11.898Z" }, { url = "https://files.pythonhosted.org/packages/dc/a6/e959a127b630a58e23529972dbc868c107f9d583b5a9f878fb858c46bc1a/greenlet-3.3.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6cb3a8ec3db4a3b0eb8a3c25436c2d49e3505821802074969db017b87bc6a948", size = 590206, upload-time = "2025-12-04T14:26:01.254Z" }, { url = "https://files.pythonhosted.org/packages/48/60/29035719feb91798693023608447283b266b12efc576ed013dd9442364bb/greenlet-3.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2de5a0b09eab81fc6a382791b995b1ccf2b172a9fec934747a7a23d2ff291794", size = 1550668, upload-time = "2025-12-04T15:04:22.439Z" }, { url = "https://files.pythonhosted.org/packages/0a/5f/783a23754b691bfa86bd72c3033aa107490deac9b2ef190837b860996c9f/greenlet-3.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4449a736606bd30f27f8e1ff4678ee193bc47f6ca810d705981cfffd6ce0d8c5", size = 1615483, upload-time = "2025-12-04T14:27:28.083Z" }, @@ -2308,6 +2310,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f8/0a/a3871375c7b9727edaeeea994bfff7c63ff7804c9829c19309ba2e058807/greenlet-3.3.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:b01548f6e0b9e9784a2c99c5651e5dc89ffcbe870bc5fb2e5ef864e9cc6b5dcb", size = 276379, upload-time = "2025-12-04T14:23:30.498Z" }, { url = "https://files.pythonhosted.org/packages/43/ab/7ebfe34dce8b87be0d11dae91acbf76f7b8246bf9d6b319c741f99fa59c6/greenlet-3.3.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:349345b770dc88f81506c6861d22a6ccd422207829d2c854ae2af8025af303e3", size = 597294, upload-time = "2025-12-04T14:50:06.847Z" }, { url = "https://files.pythonhosted.org/packages/a4/39/f1c8da50024feecd0793dbd5e08f526809b8ab5609224a2da40aad3a7641/greenlet-3.3.0-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e8e18ed6995e9e2c0b4ed264d2cf89260ab3ac7e13555b8032b25a74c6d18655", size = 607742, upload-time = "2025-12-04T14:57:42.349Z" }, + { url = "https://files.pythonhosted.org/packages/77/cb/43692bcd5f7a0da6ec0ec6d58ee7cddb606d055ce94a62ac9b1aa481e969/greenlet-3.3.0-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c024b1e5696626890038e34f76140ed1daf858e37496d33f2af57f06189e70d7", size = 622297, upload-time = "2025-12-04T15:07:13.552Z" }, { url = "https://files.pythonhosted.org/packages/75/b0/6bde0b1011a60782108c01de5913c588cf51a839174538d266de15e4bf4d/greenlet-3.3.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:047ab3df20ede6a57c35c14bf5200fcf04039d50f908270d3f9a7a82064f543b", size = 609885, upload-time = "2025-12-04T14:26:02.368Z" }, { url = "https://files.pythonhosted.org/packages/49/0e/49b46ac39f931f59f987b7cd9f34bfec8ef81d2a1e6e00682f55be5de9f4/greenlet-3.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2d9ad37fc657b1102ec880e637cccf20191581f75c64087a549e66c57e1ceb53", size = 1567424, upload-time = "2025-12-04T15:04:23.757Z" }, { url = "https://files.pythonhosted.org/packages/05/f5/49a9ac2dff7f10091935def9165c90236d8f175afb27cbed38fb1d61ab6b/greenlet-3.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:83cd0e36932e0e7f36a64b732a6f60c2fc2df28c351bae79fbaf4f8092fe7614", size = 1636017, upload-time = "2025-12-04T14:27:29.688Z" }, @@ -2315,6 +2318,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/02/2f/28592176381b9ab2cafa12829ba7b472d177f3acc35d8fbcf3673d966fff/greenlet-3.3.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:a1e41a81c7e2825822f4e068c48cb2196002362619e2d70b148f20a831c00739", size = 275140, upload-time = "2025-12-04T14:23:01.282Z" }, { url = "https://files.pythonhosted.org/packages/2c/80/fbe937bf81e9fca98c981fe499e59a3f45df2a04da0baa5c2be0dca0d329/greenlet-3.3.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9f515a47d02da4d30caaa85b69474cec77b7929b2e936ff7fb853d42f4bf8808", size = 599219, upload-time = "2025-12-04T14:50:08.309Z" }, { url = "https://files.pythonhosted.org/packages/c2/ff/7c985128f0514271b8268476af89aee6866df5eec04ac17dcfbc676213df/greenlet-3.3.0-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7d2d9fd66bfadf230b385fdc90426fcd6eb64db54b40c495b72ac0feb5766c54", size = 610211, upload-time = "2025-12-04T14:57:43.968Z" }, + { url = "https://files.pythonhosted.org/packages/79/07/c47a82d881319ec18a4510bb30463ed6891f2ad2c1901ed5ec23d3de351f/greenlet-3.3.0-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:30a6e28487a790417d036088b3bcb3f3ac7d8babaa7d0139edbaddebf3af9492", size = 624311, upload-time = "2025-12-04T15:07:14.697Z" }, { url = "https://files.pythonhosted.org/packages/fd/8e/424b8c6e78bd9837d14ff7df01a9829fc883ba2ab4ea787d4f848435f23f/greenlet-3.3.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:087ea5e004437321508a8d6f20efc4cfec5e3c30118e1417ea96ed1d93950527", size = 612833, upload-time = "2025-12-04T14:26:03.669Z" }, { url = "https://files.pythonhosted.org/packages/b5/ba/56699ff9b7c76ca12f1cdc27a886d0f81f2189c3455ff9f65246780f713d/greenlet-3.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ab97cf74045343f6c60a39913fa59710e4bd26a536ce7ab2397adf8b27e67c39", size = 1567256, upload-time = "2025-12-04T15:04:25.276Z" }, { url = "https://files.pythonhosted.org/packages/1e/37/f31136132967982d698c71a281a8901daf1a8fbab935dce7c0cf15f942cc/greenlet-3.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5375d2e23184629112ca1ea89a53389dddbffcf417dad40125713d88eb5f96e8", size = 1636483, upload-time = "2025-12-04T14:27:30.804Z" }, @@ -2322,6 +2326,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d7/7c/f0a6d0ede2c7bf092d00bc83ad5bafb7e6ec9b4aab2fbdfa6f134dc73327/greenlet-3.3.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:60c2ef0f578afb3c8d92ea07ad327f9a062547137afe91f38408f08aacab667f", size = 275671, upload-time = "2025-12-04T14:23:05.267Z" }, { url = "https://files.pythonhosted.org/packages/44/06/dac639ae1a50f5969d82d2e3dd9767d30d6dbdbab0e1a54010c8fe90263c/greenlet-3.3.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a5d554d0712ba1de0a6c94c640f7aeba3f85b3a6e1f2899c11c2c0428da9365", size = 646360, upload-time = "2025-12-04T14:50:10.026Z" }, { url = "https://files.pythonhosted.org/packages/e0/94/0fb76fe6c5369fba9bf98529ada6f4c3a1adf19e406a47332245ef0eb357/greenlet-3.3.0-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3a898b1e9c5f7307ebbde4102908e6cbfcb9ea16284a3abe15cab996bee8b9b3", size = 658160, upload-time = "2025-12-04T14:57:45.41Z" }, + { url = "https://files.pythonhosted.org/packages/93/79/d2c70cae6e823fac36c3bbc9077962105052b7ef81db2f01ec3b9bf17e2b/greenlet-3.3.0-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:dcd2bdbd444ff340e8d6bdf54d2f206ccddbb3ccfdcd3c25bf4afaa7b8f0cf45", size = 671388, upload-time = "2025-12-04T15:07:15.789Z" }, { url = "https://files.pythonhosted.org/packages/b8/14/bab308fc2c1b5228c3224ec2bf928ce2e4d21d8046c161e44a2012b5203e/greenlet-3.3.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5773edda4dc00e173820722711d043799d3adb4f01731f40619e07ea2750b955", size = 660166, upload-time = "2025-12-04T14:26:05.099Z" }, { url = "https://files.pythonhosted.org/packages/4b/d2/91465d39164eaa0085177f61983d80ffe746c5a1860f009811d498e7259c/greenlet-3.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ac0549373982b36d5fd5d30beb8a7a33ee541ff98d2b502714a09f1169f31b55", size = 1615193, upload-time = "2025-12-04T15:04:27.041Z" }, { url = "https://files.pythonhosted.org/packages/42/1b/83d110a37044b92423084d52d5d5a3b3a73cafb51b547e6d7366ff62eff1/greenlet-3.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d198d2d977460358c3b3a4dc844f875d1adb33817f0613f663a656f463764ccc", size = 1683653, upload-time = "2025-12-04T14:27:32.366Z" }, @@ -2329,6 +2334,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a0/66/bd6317bc5932accf351fc19f177ffba53712a202f9df10587da8df257c7e/greenlet-3.3.0-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:d6ed6f85fae6cdfdb9ce04c9bf7a08d666cfcfb914e7d006f44f840b46741931", size = 282638, upload-time = "2025-12-04T14:25:20.941Z" }, { url = "https://files.pythonhosted.org/packages/30/cf/cc81cb030b40e738d6e69502ccbd0dd1bced0588e958f9e757945de24404/greenlet-3.3.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d9125050fcf24554e69c4cacb086b87b3b55dc395a8b3ebe6487b045b2614388", size = 651145, upload-time = "2025-12-04T14:50:11.039Z" }, { url = "https://files.pythonhosted.org/packages/9c/ea/1020037b5ecfe95ca7df8d8549959baceb8186031da83d5ecceff8b08cd2/greenlet-3.3.0-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:87e63ccfa13c0a0f6234ed0add552af24cc67dd886731f2261e46e241608bee3", size = 654236, upload-time = "2025-12-04T14:57:47.007Z" }, + { url = "https://files.pythonhosted.org/packages/69/cc/1e4bae2e45ca2fa55299f4e85854606a78ecc37fead20d69322f96000504/greenlet-3.3.0-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2662433acbca297c9153a4023fe2161c8dcfdcc91f10433171cf7e7d94ba2221", size = 662506, upload-time = "2025-12-04T15:07:16.906Z" }, { url = "https://files.pythonhosted.org/packages/57/b9/f8025d71a6085c441a7eaff0fd928bbb275a6633773667023d19179fe815/greenlet-3.3.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3c6e9b9c1527a78520357de498b0e709fb9e2f49c3a513afd5a249007261911b", size = 653783, upload-time = "2025-12-04T14:26:06.225Z" }, { url = "https://files.pythonhosted.org/packages/f6/c7/876a8c7a7485d5d6b5c6821201d542ef28be645aa024cfe1145b35c120c1/greenlet-3.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:286d093f95ec98fdd92fcb955003b8a3d054b4e2cab3e2707a5039e7b50520fd", size = 1614857, upload-time = "2025-12-04T15:04:28.484Z" }, { url = "https://files.pythonhosted.org/packages/4f/dc/041be1dff9f23dac5f48a43323cd0789cb798342011c19a248d9c9335536/greenlet-3.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c10513330af5b8ae16f023e8ddbfb486ab355d04467c4679c5cfe4659975dd9", size = 1676034, upload-time = "2025-12-04T14:27:33.531Z" }, @@ -4505,8 +4511,8 @@ name = "powerfx" version = "0.0.34" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cffi", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pythonnet", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "cffi", marker = "(python_full_version < '3.14' and sys_platform == 'darwin') or (python_full_version < '3.14' and sys_platform == 'linux') or (python_full_version < '3.14' and sys_platform == 'win32')" }, + { name = "pythonnet", marker = "(python_full_version < '3.14' and sys_platform == 'darwin') or (python_full_version < '3.14' and sys_platform == 'linux') or (python_full_version < '3.14' and sys_platform == 'win32')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9f/fb/6c4bf87e0c74ca1c563921ce89ca1c5785b7576bca932f7255cdf81082a7/powerfx-0.0.34.tar.gz", hash = "sha256:956992e7afd272657ed16d80f4cad24ec95d9e4a79fb9dfa4a068a09e136af32", size = 3237555, upload-time = "2025-12-22T15:50:59.682Z" } wheels = [ @@ -5173,7 +5179,7 @@ name = "pythonnet" version = "3.0.5" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "clr-loader", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "clr-loader", marker = "(python_full_version < '3.14' and sys_platform == 'darwin') or (python_full_version < '3.14' and sys_platform == 'linux') or (python_full_version < '3.14' and sys_platform == 'win32')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9a/d6/1afd75edd932306ae9bd2c2d961d603dc2b52fcec51b04afea464f1f6646/pythonnet-3.0.5.tar.gz", hash = "sha256:48e43ca463941b3608b32b4e236db92d8d40db4c58a75ace902985f76dac21cf", size = 239212, upload-time = "2024-12-13T08:30:44.393Z" } wheels = [ From a35b89d730e4f66c9262d711f5e0aa2dd4670b1d Mon Sep 17 00:00:00 2001 From: eavanvalkenburg Date: Fri, 9 Jan 2026 15:49:34 +0100 Subject: [PATCH 10/10] udpated samples with headers examples --- python/packages/core/agent_framework/_mcp.py | 1 - python/pyproject.toml | 2 +- .../getting_started/mcp/mcp_api_key_auth.py | 8 +++-- python/uv.lock | 34 ++++++++----------- 4 files changed, 21 insertions(+), 24 deletions(-) diff --git a/python/packages/core/agent_framework/_mcp.py b/python/packages/core/agent_framework/_mcp.py index 68a2320171..034ed5e9ff 100644 --- a/python/packages/core/agent_framework/_mcp.py +++ b/python/packages/core/agent_framework/_mcp.py @@ -898,7 +898,6 @@ class MCPStreamableHTTPTool(MCPTool): mcp_tool = MCPStreamableHTTPTool( name="web-api", url="https://api.example.com/mcp", - headers={"Authorization": "Bearer token"}, description="Web API operations", ) diff --git a/python/pyproject.toml b/python/pyproject.toml index cbdf3f0d75..b8ea697142 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -146,7 +146,7 @@ ignore = [ "TD003", # allow missing link to todo issue "FIX002", # allow todo "B027", # allow empty non-abstract method in ABC - "RUF067", # allow version detection in __init__.py + "RUF067" # Allow version in __init__.py ] [tool.ruff.lint.per-file-ignores] diff --git a/python/samples/getting_started/mcp/mcp_api_key_auth.py b/python/samples/getting_started/mcp/mcp_api_key_auth.py index f3ec1777e6..d80d92d4fa 100644 --- a/python/samples/getting_started/mcp/mcp_api_key_auth.py +++ b/python/samples/getting_started/mcp/mcp_api_key_auth.py @@ -4,6 +4,7 @@ from agent_framework import ChatAgent, MCPStreamableHTTPTool from agent_framework.openai import OpenAIResponsesClient +from httpx import AsyncClient """ MCP Authentication Example @@ -31,13 +32,16 @@ async def api_key_auth_example() -> None: "Authorization": f"Bearer {api_key}", } - # Create MCP tool with authentication headers + # Create HTTP client with authentication headers + http_client = AsyncClient(headers=auth_headers) + + # Create MCP tool with the configured HTTP client async with ( MCPStreamableHTTPTool( name="MCP tool", description="MCP tool description", url=mcp_server_url, - headers=auth_headers, # Authentication headers + http_client=http_client, # Pass HTTP client with authentication headers ) as mcp_tool, ChatAgent( chat_client=OpenAIResponsesClient(), diff --git a/python/uv.lock b/python/uv.lock index 314719e7b3..26fb2ed57e 100644 --- a/python/uv.lock +++ b/python/uv.lock @@ -1369,7 +1369,7 @@ name = "clr-loader" version = "0.2.10" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cffi", marker = "(python_full_version < '3.14' and sys_platform == 'darwin') or (python_full_version < '3.14' and sys_platform == 'linux') or (python_full_version < '3.14' and sys_platform == 'win32')" }, + { name = "cffi", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/18/24/c12faf3f61614b3131b5c98d3bf0d376b49c7feaa73edca559aeb2aee080/clr_loader-0.2.10.tar.gz", hash = "sha256:81f114afbc5005bafc5efe5af1341d400e22137e275b042a8979f3feb9fc9446", size = 83605, upload-time = "2026-01-03T23:13:06.984Z" } wheels = [ @@ -1848,7 +1848,7 @@ name = "exceptiongroup" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, + { name = "typing-extensions", marker = "(python_full_version < '3.13' and sys_platform == 'darwin') or (python_full_version < '3.13' and sys_platform == 'linux') or (python_full_version < '3.13' and sys_platform == 'win32')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ @@ -2294,7 +2294,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/32/6a/33d1702184d94106d3cdd7bfb788e19723206fce152e303473ca3b946c7b/greenlet-3.3.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:6f8496d434d5cb2dce025773ba5597f71f5410ae499d5dd9533e0653258cdb3d", size = 273658, upload-time = "2025-12-04T14:23:37.494Z" }, { url = "https://files.pythonhosted.org/packages/d6/b7/2b5805bbf1907c26e434f4e448cd8b696a0b71725204fa21a211ff0c04a7/greenlet-3.3.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b96dc7eef78fd404e022e165ec55327f935b9b52ff355b067eb4a0267fc1cffb", size = 574810, upload-time = "2025-12-04T14:50:04.154Z" }, { url = "https://files.pythonhosted.org/packages/94/38/343242ec12eddf3d8458c73f555c084359883d4ddc674240d9e61ec51fd6/greenlet-3.3.0-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:73631cd5cccbcfe63e3f9492aaa664d278fda0ce5c3d43aeda8e77317e38efbd", size = 586248, upload-time = "2025-12-04T14:57:39.35Z" }, - { url = "https://files.pythonhosted.org/packages/f0/d0/0ae86792fb212e4384041e0ef8e7bc66f59a54912ce407d26a966ed2914d/greenlet-3.3.0-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b299a0cb979f5d7197442dccc3aee67fce53500cd88951b7e6c35575701c980b", size = 597403, upload-time = "2025-12-04T15:07:10.831Z" }, { url = "https://files.pythonhosted.org/packages/b6/a8/15d0aa26c0036a15d2659175af00954aaaa5d0d66ba538345bd88013b4d7/greenlet-3.3.0-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7dee147740789a4632cace364816046e43310b59ff8fb79833ab043aefa72fd5", size = 586910, upload-time = "2025-12-04T14:25:59.705Z" }, { url = "https://files.pythonhosted.org/packages/e1/9b/68d5e3b7ccaba3907e5532cf8b9bf16f9ef5056a008f195a367db0ff32db/greenlet-3.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:39b28e339fc3c348427560494e28d8a6f3561c8d2bcf7d706e1c624ed8d822b9", size = 1547206, upload-time = "2025-12-04T15:04:21.027Z" }, { url = "https://files.pythonhosted.org/packages/66/bd/e3086ccedc61e49f91e2cfb5ffad9d8d62e5dc85e512a6200f096875b60c/greenlet-3.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b3c374782c2935cc63b2a27ba8708471de4ad1abaa862ffdb1ef45a643ddbb7d", size = 1613359, upload-time = "2025-12-04T14:27:26.548Z" }, @@ -2302,7 +2301,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1f/cb/48e964c452ca2b92175a9b2dca037a553036cb053ba69e284650ce755f13/greenlet-3.3.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:e29f3018580e8412d6aaf5641bb7745d38c85228dacf51a73bd4e26ddf2a6a8e", size = 274908, upload-time = "2025-12-04T14:23:26.435Z" }, { url = "https://files.pythonhosted.org/packages/28/da/38d7bff4d0277b594ec557f479d65272a893f1f2a716cad91efeb8680953/greenlet-3.3.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a687205fb22794e838f947e2194c0566d3812966b41c78709554aa883183fb62", size = 577113, upload-time = "2025-12-04T14:50:05.493Z" }, { url = "https://files.pythonhosted.org/packages/3c/f2/89c5eb0faddc3ff014f1c04467d67dee0d1d334ab81fadbf3744847f8a8a/greenlet-3.3.0-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4243050a88ba61842186cb9e63c7dfa677ec146160b0efd73b855a3d9c7fcf32", size = 590338, upload-time = "2025-12-04T14:57:41.136Z" }, - { url = "https://files.pythonhosted.org/packages/80/d7/db0a5085035d05134f8c089643da2b44cc9b80647c39e93129c5ef170d8f/greenlet-3.3.0-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:670d0f94cd302d81796e37299bcd04b95d62403883b24225c6b5271466612f45", size = 601098, upload-time = "2025-12-04T15:07:11.898Z" }, { url = "https://files.pythonhosted.org/packages/dc/a6/e959a127b630a58e23529972dbc868c107f9d583b5a9f878fb858c46bc1a/greenlet-3.3.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6cb3a8ec3db4a3b0eb8a3c25436c2d49e3505821802074969db017b87bc6a948", size = 590206, upload-time = "2025-12-04T14:26:01.254Z" }, { url = "https://files.pythonhosted.org/packages/48/60/29035719feb91798693023608447283b266b12efc576ed013dd9442364bb/greenlet-3.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2de5a0b09eab81fc6a382791b995b1ccf2b172a9fec934747a7a23d2ff291794", size = 1550668, upload-time = "2025-12-04T15:04:22.439Z" }, { url = "https://files.pythonhosted.org/packages/0a/5f/783a23754b691bfa86bd72c3033aa107490deac9b2ef190837b860996c9f/greenlet-3.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4449a736606bd30f27f8e1ff4678ee193bc47f6ca810d705981cfffd6ce0d8c5", size = 1615483, upload-time = "2025-12-04T14:27:28.083Z" }, @@ -2310,7 +2308,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f8/0a/a3871375c7b9727edaeeea994bfff7c63ff7804c9829c19309ba2e058807/greenlet-3.3.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:b01548f6e0b9e9784a2c99c5651e5dc89ffcbe870bc5fb2e5ef864e9cc6b5dcb", size = 276379, upload-time = "2025-12-04T14:23:30.498Z" }, { url = "https://files.pythonhosted.org/packages/43/ab/7ebfe34dce8b87be0d11dae91acbf76f7b8246bf9d6b319c741f99fa59c6/greenlet-3.3.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:349345b770dc88f81506c6861d22a6ccd422207829d2c854ae2af8025af303e3", size = 597294, upload-time = "2025-12-04T14:50:06.847Z" }, { url = "https://files.pythonhosted.org/packages/a4/39/f1c8da50024feecd0793dbd5e08f526809b8ab5609224a2da40aad3a7641/greenlet-3.3.0-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e8e18ed6995e9e2c0b4ed264d2cf89260ab3ac7e13555b8032b25a74c6d18655", size = 607742, upload-time = "2025-12-04T14:57:42.349Z" }, - { url = "https://files.pythonhosted.org/packages/77/cb/43692bcd5f7a0da6ec0ec6d58ee7cddb606d055ce94a62ac9b1aa481e969/greenlet-3.3.0-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c024b1e5696626890038e34f76140ed1daf858e37496d33f2af57f06189e70d7", size = 622297, upload-time = "2025-12-04T15:07:13.552Z" }, { url = "https://files.pythonhosted.org/packages/75/b0/6bde0b1011a60782108c01de5913c588cf51a839174538d266de15e4bf4d/greenlet-3.3.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:047ab3df20ede6a57c35c14bf5200fcf04039d50f908270d3f9a7a82064f543b", size = 609885, upload-time = "2025-12-04T14:26:02.368Z" }, { url = "https://files.pythonhosted.org/packages/49/0e/49b46ac39f931f59f987b7cd9f34bfec8ef81d2a1e6e00682f55be5de9f4/greenlet-3.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2d9ad37fc657b1102ec880e637cccf20191581f75c64087a549e66c57e1ceb53", size = 1567424, upload-time = "2025-12-04T15:04:23.757Z" }, { url = "https://files.pythonhosted.org/packages/05/f5/49a9ac2dff7f10091935def9165c90236d8f175afb27cbed38fb1d61ab6b/greenlet-3.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:83cd0e36932e0e7f36a64b732a6f60c2fc2df28c351bae79fbaf4f8092fe7614", size = 1636017, upload-time = "2025-12-04T14:27:29.688Z" }, @@ -2318,7 +2315,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/02/2f/28592176381b9ab2cafa12829ba7b472d177f3acc35d8fbcf3673d966fff/greenlet-3.3.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:a1e41a81c7e2825822f4e068c48cb2196002362619e2d70b148f20a831c00739", size = 275140, upload-time = "2025-12-04T14:23:01.282Z" }, { url = "https://files.pythonhosted.org/packages/2c/80/fbe937bf81e9fca98c981fe499e59a3f45df2a04da0baa5c2be0dca0d329/greenlet-3.3.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9f515a47d02da4d30caaa85b69474cec77b7929b2e936ff7fb853d42f4bf8808", size = 599219, upload-time = "2025-12-04T14:50:08.309Z" }, { url = "https://files.pythonhosted.org/packages/c2/ff/7c985128f0514271b8268476af89aee6866df5eec04ac17dcfbc676213df/greenlet-3.3.0-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7d2d9fd66bfadf230b385fdc90426fcd6eb64db54b40c495b72ac0feb5766c54", size = 610211, upload-time = "2025-12-04T14:57:43.968Z" }, - { url = "https://files.pythonhosted.org/packages/79/07/c47a82d881319ec18a4510bb30463ed6891f2ad2c1901ed5ec23d3de351f/greenlet-3.3.0-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:30a6e28487a790417d036088b3bcb3f3ac7d8babaa7d0139edbaddebf3af9492", size = 624311, upload-time = "2025-12-04T15:07:14.697Z" }, { url = "https://files.pythonhosted.org/packages/fd/8e/424b8c6e78bd9837d14ff7df01a9829fc883ba2ab4ea787d4f848435f23f/greenlet-3.3.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:087ea5e004437321508a8d6f20efc4cfec5e3c30118e1417ea96ed1d93950527", size = 612833, upload-time = "2025-12-04T14:26:03.669Z" }, { url = "https://files.pythonhosted.org/packages/b5/ba/56699ff9b7c76ca12f1cdc27a886d0f81f2189c3455ff9f65246780f713d/greenlet-3.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ab97cf74045343f6c60a39913fa59710e4bd26a536ce7ab2397adf8b27e67c39", size = 1567256, upload-time = "2025-12-04T15:04:25.276Z" }, { url = "https://files.pythonhosted.org/packages/1e/37/f31136132967982d698c71a281a8901daf1a8fbab935dce7c0cf15f942cc/greenlet-3.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5375d2e23184629112ca1ea89a53389dddbffcf417dad40125713d88eb5f96e8", size = 1636483, upload-time = "2025-12-04T14:27:30.804Z" }, @@ -2326,7 +2322,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d7/7c/f0a6d0ede2c7bf092d00bc83ad5bafb7e6ec9b4aab2fbdfa6f134dc73327/greenlet-3.3.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:60c2ef0f578afb3c8d92ea07ad327f9a062547137afe91f38408f08aacab667f", size = 275671, upload-time = "2025-12-04T14:23:05.267Z" }, { url = "https://files.pythonhosted.org/packages/44/06/dac639ae1a50f5969d82d2e3dd9767d30d6dbdbab0e1a54010c8fe90263c/greenlet-3.3.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a5d554d0712ba1de0a6c94c640f7aeba3f85b3a6e1f2899c11c2c0428da9365", size = 646360, upload-time = "2025-12-04T14:50:10.026Z" }, { url = "https://files.pythonhosted.org/packages/e0/94/0fb76fe6c5369fba9bf98529ada6f4c3a1adf19e406a47332245ef0eb357/greenlet-3.3.0-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3a898b1e9c5f7307ebbde4102908e6cbfcb9ea16284a3abe15cab996bee8b9b3", size = 658160, upload-time = "2025-12-04T14:57:45.41Z" }, - { url = "https://files.pythonhosted.org/packages/93/79/d2c70cae6e823fac36c3bbc9077962105052b7ef81db2f01ec3b9bf17e2b/greenlet-3.3.0-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:dcd2bdbd444ff340e8d6bdf54d2f206ccddbb3ccfdcd3c25bf4afaa7b8f0cf45", size = 671388, upload-time = "2025-12-04T15:07:15.789Z" }, { url = "https://files.pythonhosted.org/packages/b8/14/bab308fc2c1b5228c3224ec2bf928ce2e4d21d8046c161e44a2012b5203e/greenlet-3.3.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5773edda4dc00e173820722711d043799d3adb4f01731f40619e07ea2750b955", size = 660166, upload-time = "2025-12-04T14:26:05.099Z" }, { url = "https://files.pythonhosted.org/packages/4b/d2/91465d39164eaa0085177f61983d80ffe746c5a1860f009811d498e7259c/greenlet-3.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ac0549373982b36d5fd5d30beb8a7a33ee541ff98d2b502714a09f1169f31b55", size = 1615193, upload-time = "2025-12-04T15:04:27.041Z" }, { url = "https://files.pythonhosted.org/packages/42/1b/83d110a37044b92423084d52d5d5a3b3a73cafb51b547e6d7366ff62eff1/greenlet-3.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d198d2d977460358c3b3a4dc844f875d1adb33817f0613f663a656f463764ccc", size = 1683653, upload-time = "2025-12-04T14:27:32.366Z" }, @@ -2334,7 +2329,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a0/66/bd6317bc5932accf351fc19f177ffba53712a202f9df10587da8df257c7e/greenlet-3.3.0-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:d6ed6f85fae6cdfdb9ce04c9bf7a08d666cfcfb914e7d006f44f840b46741931", size = 282638, upload-time = "2025-12-04T14:25:20.941Z" }, { url = "https://files.pythonhosted.org/packages/30/cf/cc81cb030b40e738d6e69502ccbd0dd1bced0588e958f9e757945de24404/greenlet-3.3.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d9125050fcf24554e69c4cacb086b87b3b55dc395a8b3ebe6487b045b2614388", size = 651145, upload-time = "2025-12-04T14:50:11.039Z" }, { url = "https://files.pythonhosted.org/packages/9c/ea/1020037b5ecfe95ca7df8d8549959baceb8186031da83d5ecceff8b08cd2/greenlet-3.3.0-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:87e63ccfa13c0a0f6234ed0add552af24cc67dd886731f2261e46e241608bee3", size = 654236, upload-time = "2025-12-04T14:57:47.007Z" }, - { url = "https://files.pythonhosted.org/packages/69/cc/1e4bae2e45ca2fa55299f4e85854606a78ecc37fead20d69322f96000504/greenlet-3.3.0-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2662433acbca297c9153a4023fe2161c8dcfdcc91f10433171cf7e7d94ba2221", size = 662506, upload-time = "2025-12-04T15:07:16.906Z" }, { url = "https://files.pythonhosted.org/packages/57/b9/f8025d71a6085c441a7eaff0fd928bbb275a6633773667023d19179fe815/greenlet-3.3.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3c6e9b9c1527a78520357de498b0e709fb9e2f49c3a513afd5a249007261911b", size = 653783, upload-time = "2025-12-04T14:26:06.225Z" }, { url = "https://files.pythonhosted.org/packages/f6/c7/876a8c7a7485d5d6b5c6821201d542ef28be645aa024cfe1145b35c120c1/greenlet-3.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:286d093f95ec98fdd92fcb955003b8a3d054b4e2cab3e2707a5039e7b50520fd", size = 1614857, upload-time = "2025-12-04T15:04:28.484Z" }, { url = "https://files.pythonhosted.org/packages/4f/dc/041be1dff9f23dac5f48a43323cd0789cb798342011c19a248d9c9335536/greenlet-3.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c10513330af5b8ae16f023e8ddbfb486ab355d04467c4679c5cfe4659975dd9", size = 1676034, upload-time = "2025-12-04T14:27:33.531Z" }, @@ -2601,7 +2595,7 @@ wheels = [ [[package]] name = "huggingface-hub" -version = "1.2.4" +version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, @@ -2615,9 +2609,9 @@ dependencies = [ { name = "typer-slim", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fb/94/42ed2ff780f4bc58acbe4b8cb98eb4574310ad6feba12f76a820e7546120/huggingface_hub-1.2.4.tar.gz", hash = "sha256:7a1d9ec4802e64372d1d152d69fb8e26d943f15a2289096fbc8e09e7b90c21a5", size = 614771, upload-time = "2026-01-06T11:01:29.828Z" } +sdist = { url = "https://files.pythonhosted.org/packages/dd/dd/1cc985c5dda36298b152f75e82a1c81f52243b78fb7e9cad637a29561ad1/huggingface_hub-1.3.1.tar.gz", hash = "sha256:e80e0cfb4a75557c51ab20d575bdea6bb6106c2f97b7c75d8490642f1efb6df5", size = 622356, upload-time = "2026-01-09T14:08:16.888Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/dd/b0/113c4a688e7af9f0b92f5585cb425e71134e04c83a0a4a1e62db90edee20/huggingface_hub-1.2.4-py3-none-any.whl", hash = "sha256:2db69b91877d9d34825f5cd2a63b94f259011a77dcf761b437bf510fbe9522e9", size = 520980, upload-time = "2026-01-06T11:01:27.789Z" }, + { url = "https://files.pythonhosted.org/packages/90/fb/cb8fe5f71d5622427f20bcab9e06a696a5aaf21bfe7bd0a8a0c63c88abf5/huggingface_hub-1.3.1-py3-none-any.whl", hash = "sha256:efbc7f3153cb84e2bb69b62ed90985e21ecc9343d15647a419fc0ee4b85f0ac3", size = 533351, upload-time = "2026-01-09T14:08:14.519Z" }, ] [[package]] @@ -3074,7 +3068,7 @@ wheels = [ [[package]] name = "litellm" -version = "1.80.12" +version = "1.80.13" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, @@ -3092,9 +3086,9 @@ dependencies = [ { name = "tiktoken", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "tokenizers", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d1/fa/29cea38f88a024e835eced4630ede0d65a0d9e0d951b1e17c6921dfce393/litellm-1.80.12.tar.gz", hash = "sha256:75eca39cb00f3ae00f3b5282e34fb304ffc0d36f7d4cfd189c9191b3a17b5815", size = 13264051, upload-time = "2026-01-07T17:50:33.965Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e1/b4/ef75838159aabee15ad93d65ee0e91d04ba0e310784b7b0d3f490cca270c/litellm-1.80.13.tar.gz", hash = "sha256:61ed22dfad633ada3b97dd8a50d8e8d804da0115105006d2f9d77ba3fb247a0b", size = 13277620, upload-time = "2026-01-09T04:37:08.529Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2b/34/57b06609b0b637bd1b2d68ed5282a4dfa1d2c35535202de70d36352ea4d4/litellm-1.80.12-py3-none-any.whl", hash = "sha256:d602add4486cc32f30ebdf1a05c58e8074ef1e87838fb5f1306a862968496ff7", size = 11544878, upload-time = "2026-01-07T17:50:30.776Z" }, + { url = "https://files.pythonhosted.org/packages/a6/90/e8e0ad7f57d3a56c3411b3867e02768f9722b5975a263c8aaaaba6693d91/litellm-1.80.13-py3-none-any.whl", hash = "sha256:43dcdbca010961f17d7a5a6a995a38d1a46101350959b0e8269576cfe913cf0b", size = 11562501, upload-time = "2026-01-09T04:37:05.551Z" }, ] [package.optional-dependencies] @@ -3136,11 +3130,11 @@ wheels = [ [[package]] name = "litellm-proxy-extras" -version = "0.4.18" +version = "0.4.20" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b8/96/ed23a3c3af1913b4a9b4afbdbd5176b8b1e06138f26a9584427f09f652c1/litellm_proxy_extras-0.4.18.tar.gz", hash = "sha256:898b28e3e74acdc29142906b84787ab05a90e30aa3c0c8aee849915e3a16adb3", size = 20676, upload-time = "2026-01-07T09:08:13.903Z" } +sdist = { url = "https://files.pythonhosted.org/packages/44/ab/df85ce715ebf488cacba338607f67d46c4e4db0b717c9d2f904b8dc7de12/litellm_proxy_extras-0.4.20.tar.gz", hash = "sha256:4fcc95db25cc8b75abbc3f00bb79fd6b94edd1b838ad7bb12cf839b39c67923a", size = 21044, upload-time = "2026-01-07T19:11:32.562Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/af/60/2465d2d58625af9ffc80c3933b43cf63e94f1722078b5da197398b0705de/litellm_proxy_extras-0.4.18-py3-none-any.whl", hash = "sha256:c3edee68bf8eb073c6158dcf7df05727dfc829e63c03a617fcb48853d11490df", size = 45338, upload-time = "2026-01-07T09:08:12.886Z" }, + { url = "https://files.pythonhosted.org/packages/d4/f5/eb350c49e7cf09db5b335aaeef410c2094e19c84bfe51733cab8470dc011/litellm_proxy_extras-0.4.20-py3-none-any.whl", hash = "sha256:7737cd693dd1aa0bd25ada6d300b37f42c8c18d1820535aceb0ed38ed21f68f5", size = 46565, upload-time = "2026-01-07T19:11:29.728Z" }, ] [[package]] @@ -4511,8 +4505,8 @@ name = "powerfx" version = "0.0.34" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cffi", marker = "(python_full_version < '3.14' and sys_platform == 'darwin') or (python_full_version < '3.14' and sys_platform == 'linux') or (python_full_version < '3.14' and sys_platform == 'win32')" }, - { name = "pythonnet", marker = "(python_full_version < '3.14' and sys_platform == 'darwin') or (python_full_version < '3.14' and sys_platform == 'linux') or (python_full_version < '3.14' and sys_platform == 'win32')" }, + { name = "cffi", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "pythonnet", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9f/fb/6c4bf87e0c74ca1c563921ce89ca1c5785b7576bca932f7255cdf81082a7/powerfx-0.0.34.tar.gz", hash = "sha256:956992e7afd272657ed16d80f4cad24ec95d9e4a79fb9dfa4a068a09e136af32", size = 3237555, upload-time = "2025-12-22T15:50:59.682Z" } wheels = [ @@ -5179,7 +5173,7 @@ name = "pythonnet" version = "3.0.5" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "clr-loader", marker = "(python_full_version < '3.14' and sys_platform == 'darwin') or (python_full_version < '3.14' and sys_platform == 'linux') or (python_full_version < '3.14' and sys_platform == 'win32')" }, + { name = "clr-loader", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9a/d6/1afd75edd932306ae9bd2c2d961d603dc2b52fcec51b04afea464f1f6646/pythonnet-3.0.5.tar.gz", hash = "sha256:48e43ca463941b3608b32b4e236db92d8d40db4c58a75ace902985f76dac21cf", size = 239212, upload-time = "2024-12-13T08:30:44.393Z" } wheels = [