From 2218a6f52a2e5ba89d9b1705e2fbe2d8ea6de08d Mon Sep 17 00:00:00 2001 From: Jeremiah Lowin <153965+jlowin@users.noreply.github.com> Date: Fri, 20 Feb 2026 19:31:38 -0500 Subject: [PATCH] Use max_completion_tokens instead of deprecated max_tokens in OpenAI handler (#3254) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with Claude Code Co-authored-by: Marvin Context Protocol <41898282+Marvin Context Protocol@users.noreply.github.com> Co-authored-by: Jeremiah Lowin --- .../client/sampling/handlers/openai.py | 2 +- .../sampling/handlers/test_openai_handler.py | 36 ++++++++++++++++++- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/fastmcp/client/sampling/handlers/openai.py b/src/fastmcp/client/sampling/handlers/openai.py index 3ddcadaca..362e05905 100644 --- a/src/fastmcp/client/sampling/handlers/openai.py +++ b/src/fastmcp/client/sampling/handlers/openai.py @@ -84,7 +84,7 @@ class OpenAISamplingHandler: kwargs: dict[str, Any] = { "model": model, "messages": openai_messages, - "max_tokens": params.maxTokens, + "max_completion_tokens": params.maxTokens, } if params.temperature is not None: kwargs["temperature"] = params.temperature diff --git a/tests/client/sampling/handlers/test_openai_handler.py b/tests/client/sampling/handlers/test_openai_handler.py index d5b263a81..29f12d749 100644 --- a/tests/client/sampling/handlers/test_openai_handler.py +++ b/tests/client/sampling/handlers/test_openai_handler.py @@ -1,7 +1,8 @@ -from unittest.mock import MagicMock +from unittest.mock import AsyncMock, MagicMock import pytest from mcp.types import ( + CreateMessageRequestParams, CreateMessageResult, ModelHint, ModelPreferences, @@ -72,6 +73,39 @@ def test_select_model_from_preferences(prefs, expected): assert handler._select_model_from_preferences(prefs) == expected +async def test_handler_passes_max_completion_tokens(): + """Verify the handler uses max_completion_tokens (not max_tokens).""" + mock_client = MagicMock(spec=AsyncOpenAI) + mock_client.chat = MagicMock() + mock_client.chat.completions = MagicMock() + mock_client.chat.completions.create = AsyncMock( + return_value=ChatCompletion( + id="123", + created=123, + model="gpt-4o-mini", + object="chat.completion", + choices=[ + Choice( + message=ChatCompletionMessage(content="hi", role="assistant"), + finish_reason="stop", + index=0, + ) + ], + ) + ) + handler = OpenAISamplingHandler(default_model="gpt-4o-mini", client=mock_client) + messages = [ + SamplingMessage(role="user", content=TextContent(type="text", text="hello")) + ] + params = CreateMessageRequestParams(messages=messages, maxTokens=300) + await handler(messages, params, context=None) # type: ignore[arg-type] + + call_kwargs = mock_client.chat.completions.create.call_args + assert "max_completion_tokens" in call_kwargs.kwargs + assert call_kwargs.kwargs["max_completion_tokens"] == 300 + assert "max_tokens" not in call_kwargs.kwargs + + async def test_chat_completion_to_create_message_result(): mock_client = MagicMock(spec=AsyncOpenAI) handler = OpenAISamplingHandler(default_model="fallback-model", client=mock_client) # type: ignore[arg-type]