Skip to content

Commit 637f690

Browse files
Copilotpontemonti
andauthored
Fix send_chat_history_messages to send empty lists to MCP platform (#145)
* Initial plan * Fix send_chat_history_messages to send empty lists to MCP platform Co-authored-by: pontemonti <7850950+pontemonti@users.noreply.github.com> * Address code review: make comments more concise Co-authored-by: pontemonti <7850950+pontemonti@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: pontemonti <7850950+pontemonti@users.noreply.github.com>
1 parent accc8a3 commit 637f690

File tree

5 files changed

+64
-29
lines changed

5 files changed

+64
-29
lines changed

libraries/microsoft-agents-a365-tooling-extensions-agentframework/microsoft_agents_a365/tooling/extensions/agentframework/services/mcp_tool_registration_service.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,8 @@ async def send_chat_history_messages(
225225
226226
Args:
227227
chat_messages: Sequence of Agent Framework ChatMessage objects to send.
228+
Can be empty - the request will still be sent to register
229+
the user message from turn_context.activity.text.
228230
turn_context: TurnContext from the Agents SDK containing conversation info.
229231
tool_options: Optional configuration for the request. Defaults to
230232
AgentFramework-specific options if not provided.
@@ -235,6 +237,12 @@ async def send_chat_history_messages(
235237
Raises:
236238
ValueError: If chat_messages or turn_context is None.
237239
240+
Note:
241+
Even if chat_messages is empty or all messages are filtered during
242+
conversion, the request will still be sent to the MCP platform. This
243+
ensures the user message from turn_context.activity.text is registered
244+
correctly for real-time threat protection.
245+
238246
Example:
239247
>>> service = McpToolRegistrationService()
240248
>>> messages = [ChatMessage(role=Role.USER, text="Hello")]
@@ -249,11 +257,6 @@ async def send_chat_history_messages(
249257
if turn_context is None:
250258
raise ValueError("turn_context cannot be None")
251259

252-
# Handle empty messages - return success with warning
253-
if len(chat_messages) == 0:
254-
self._logger.warning("Empty message list provided to send_chat_history_messages")
255-
return OperationResult.success()
256-
257260
self._logger.info(f"Send chat history initiated with {len(chat_messages)} messages")
258261

259262
# Use default options if not provided
@@ -263,10 +266,13 @@ async def send_chat_history_messages(
263266
# Convert messages to ChatHistoryMessage format
264267
history_messages = self._convert_chat_messages_to_history(chat_messages)
265268

266-
# Check if all messages were filtered out during conversion
269+
# Call core service even with empty history_messages to register
270+
# the user message from turn_context.activity.text in the MCP platform.
267271
if len(history_messages) == 0:
268-
self._logger.warning("All messages were filtered out during conversion (empty content)")
269-
return OperationResult.success()
272+
self._logger.info(
273+
"Empty history messages (either no input or all filtered), "
274+
"still sending to register user message"
275+
)
270276

271277
# Delegate to core service
272278
result = await self._mcp_server_configuration_service.send_chat_history(

libraries/microsoft-agents-a365-tooling-extensions-openai/microsoft_agents_a365/tooling/extensions/openai/mcp_tool_registration_service.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,8 @@ async def send_chat_history_messages(
347347
and activity.text.
348348
messages: List of OpenAI TResponseInputItem messages to send. Supports
349349
UserMessage, AssistantMessage, SystemMessage, and other OpenAI
350-
message types.
350+
message types. Can be empty - the request will still be sent to
351+
register the user message from turn_context.activity.text.
351352
options: Optional ToolOptions for customization. If not provided,
352353
uses default options with orchestrator_name="OpenAI".
353354
@@ -359,6 +360,12 @@ async def send_chat_history_messages(
359360
Raises:
360361
ValueError: If turn_context is None or messages is None.
361362
363+
Note:
364+
Even if messages is empty or all messages are filtered during conversion,
365+
the request will still be sent to the MCP platform. This ensures the user
366+
message from turn_context.activity.text is registered correctly for
367+
real-time threat protection.
368+
362369
Example:
363370
>>> from microsoft_agents_a365.tooling.extensions.openai import (
364371
... McpToolRegistrationService
@@ -382,11 +389,6 @@ async def send_chat_history_messages(
382389
if messages is None:
383390
raise ValueError("messages cannot be None")
384391

385-
# Handle empty list as no-op
386-
if len(messages) == 0:
387-
self._logger.info("Empty message list provided, returning success")
388-
return OperationResult.success()
389-
390392
self._logger.info(f"Sending {len(messages)} OpenAI messages as chat history")
391393

392394
# Set default options
@@ -399,9 +401,13 @@ async def send_chat_history_messages(
399401
# Convert OpenAI messages to ChatHistoryMessage format
400402
chat_history_messages = self._convert_openai_messages_to_chat_history(messages)
401403

404+
# Call core service even with empty chat_history_messages to register
405+
# the user message from turn_context.activity.text in the MCP platform.
402406
if len(chat_history_messages) == 0:
403-
self._logger.warning("No messages could be converted to chat history format")
404-
return OperationResult.success()
407+
self._logger.info(
408+
"Empty chat history messages (either no input or all filtered), "
409+
"still sending to register user message"
410+
)
405411

406412
self._logger.debug(
407413
f"Converted {len(chat_history_messages)} messages to ChatHistoryMessage format"

libraries/microsoft-agents-a365-tooling/microsoft_agents_a365/tooling/services/mcp_tool_server_configuration_service.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,11 @@ async def send_chat_history(
583583
turn_context.activity is None, or any of the required fields
584584
(conversation.id, activity.id, activity.text) are missing or empty.
585585
586+
Note:
587+
Even if chat_history_messages is empty, the request will still be sent to
588+
the MCP platform. This ensures the user message from turn_context.activity.text
589+
is registered correctly for real-time threat protection.
590+
586591
Example:
587592
>>> from datetime import datetime, timezone
588593
>>> from microsoft_agents_a365.tooling.models import ChatHistoryMessage

tests/tooling/extensions/agentframework/services/test_send_chat_history.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -120,17 +120,20 @@ async def test_send_chat_history_from_store_validates_turn_context_none(
120120

121121
@pytest.mark.asyncio
122122
@pytest.mark.unit
123-
async def test_send_chat_history_messages_empty_messages_returns_success(
123+
async def test_send_chat_history_messages_empty_messages_calls_core_service(
124124
self, service, mock_turn_context
125125
):
126-
"""Test that empty message list returns success with warning log."""
126+
"""Test that empty message list still calls core service to register user message."""
127127
# Act
128128
result = await service.send_chat_history_messages([], mock_turn_context)
129129

130130
# Assert
131131
assert result.succeeded is True
132-
# Core service should not be called for empty messages
133-
service._mcp_server_configuration_service.send_chat_history.assert_not_called()
132+
# Core service SHOULD be called even for empty messages to register the user message
133+
service._mcp_server_configuration_service.send_chat_history.assert_called_once()
134+
# Verify empty list was passed
135+
call_args = service._mcp_server_configuration_service.send_chat_history.call_args
136+
assert call_args.kwargs["chat_history_messages"] == []
134137

135138
@pytest.mark.asyncio
136139
@pytest.mark.unit
@@ -492,10 +495,10 @@ async def test_send_chat_history_messages_skips_messages_with_none_role(
492495

493496
@pytest.mark.asyncio
494497
@pytest.mark.unit
495-
async def test_send_chat_history_messages_all_filtered_returns_success(
498+
async def test_send_chat_history_messages_all_filtered_still_calls_core(
496499
self, service, mock_turn_context, mock_role
497500
):
498-
"""Test that all messages filtered out returns success without calling core (CRM-006)."""
501+
"""Test that all messages filtered out still calls core service to register user message."""
499502
# Arrange - all messages have empty content
500503
msg1 = Mock()
501504
msg1.message_id = "msg-1"
@@ -517,8 +520,11 @@ async def test_send_chat_history_messages_all_filtered_returns_success(
517520

518521
# Assert
519522
assert result.succeeded is True
520-
# Core service should not be called when all messages are filtered out
521-
service._mcp_server_configuration_service.send_chat_history.assert_not_called()
523+
# Core service SHOULD be called even when all messages are filtered out to register user message
524+
service._mcp_server_configuration_service.send_chat_history.assert_called_once()
525+
# Verify empty list was passed (all messages filtered)
526+
call_args = service._mcp_server_configuration_service.send_chat_history.call_args
527+
assert call_args.kwargs["chat_history_messages"] == []
522528

523529
@pytest.mark.asyncio
524530
@pytest.mark.unit

tests/tooling/extensions/openai/test_send_chat_history.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,26 @@ async def test_send_chat_history_messages_validates_messages_none(
4545
# UV-03
4646
@pytest.mark.asyncio
4747
@pytest.mark.unit
48-
async def test_send_chat_history_messages_empty_list_returns_success(
48+
async def test_send_chat_history_messages_empty_list_calls_core_service(
4949
self, service, mock_turn_context
5050
):
51-
"""Test that empty message list returns success (no-op)."""
52-
result = await service.send_chat_history_messages(mock_turn_context, [])
51+
"""Test that empty message list still calls core service to register user message."""
52+
with patch.object(
53+
service.config_service,
54+
"send_chat_history",
55+
new_callable=AsyncMock,
56+
) as mock_send:
57+
mock_send.return_value = OperationResult.success()
5358

54-
assert result.succeeded is True
55-
assert len(result.errors) == 0
59+
result = await service.send_chat_history_messages(mock_turn_context, [])
60+
61+
assert result.succeeded is True
62+
assert len(result.errors) == 0
63+
# Core service SHOULD be called even for empty messages to register the user message
64+
mock_send.assert_called_once()
65+
# Verify empty list was passed
66+
call_args = mock_send.call_args
67+
assert call_args.kwargs["chat_history_messages"] == []
5668

5769
# UV-04
5870
@pytest.mark.asyncio

0 commit comments

Comments
 (0)