diff --git a/litellm/llms/bedrock/chat/invoke_transformations/base_invoke_transformation.py b/litellm/llms/bedrock/chat/invoke_transformations/base_invoke_transformation.py index 8fc2375c224..9650a63b208 100644 --- a/litellm/llms/bedrock/chat/invoke_transformations/base_invoke_transformation.py +++ b/litellm/llms/bedrock/chat/invoke_transformations/base_invoke_transformation.py @@ -8,7 +8,10 @@ import httpx import litellm from litellm._logging import verbose_logger -from litellm.litellm_core_utils.core_helpers import map_finish_reason +from litellm.litellm_core_utils.core_helpers import ( + filter_internal_params, + map_finish_reason, +) from litellm.litellm_core_utils.logging_utils import track_llm_api_timing from litellm.litellm_core_utils.prompt_templates.factory import ( cohere_message_pt, @@ -162,12 +165,13 @@ class AmazonInvokeConfig(BaseConfig, BaseAWSLLM): provider=provider, custom_prompt_dict=custom_prompt_dict, ) - inference_params = copy.deepcopy(optional_params) - inference_params = { - k: v - for k, v in inference_params.items() - if k not in self.aws_authentication_params - } + inference_params = filter_internal_params( + { + k: v + for k, v in copy.deepcopy(optional_params).items() + if k not in self.aws_authentication_params + } + ) request_data: dict = {} if provider == "cohere": if model.startswith("cohere.command-r"): diff --git a/tests/test_litellm/llms/bedrock/chat/invoke_transformations/test_base_invoke_transformation.py b/tests/test_litellm/llms/bedrock/chat/invoke_transformations/test_base_invoke_transformation.py index aff89f02ff2..c812248aa30 100644 --- a/tests/test_litellm/llms/bedrock/chat/invoke_transformations/test_base_invoke_transformation.py +++ b/tests/test_litellm/llms/bedrock/chat/invoke_transformations/test_base_invoke_transformation.py @@ -39,3 +39,38 @@ def test_transform_request_drops_stream_chunk_size(config, model): ) assert "stream_chunk_size" not in json.dumps(request_body) + + +@pytest.mark.parametrize( + "model", + [ + "mistral.mistral-7b-instruct-v0:2", + "amazon.titan-text-express-v1", + "ai21.j2-ultra-v1", + "meta.llama3-8b-instruct-v1:0", + ], +) +def test_transform_request_drops_internal_mcp_params(model): + """skip_mcp_handler, _skip_mcp_handler and mcp_handler_context are + LiteLLM-internal MCP control flags, not Bedrock inference parameters. The + invoke path splats inference_params straight into the request body, so + without filtering they reach AWS and the request is rejected with + 'extraneous key is not permitted'. Regression for + https://github.com/BerriAI/litellm/issues/30371.""" + request_body = AmazonInvokeConfig().transform_request( + model=model, + messages=[{"role": "user", "content": "hi"}], + optional_params={ + "max_tokens": 10, + "skip_mcp_handler": True, + "_skip_mcp_handler": True, + "mcp_handler_context": {"server": "x"}, + }, + litellm_params={}, + headers={}, + ) + + serialized = json.dumps(request_body) + assert "skip_mcp_handler" not in serialized + assert "mcp_handler_context" not in serialized + assert "max_tokens" in serialized