Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from datetime import datetime, timezone
from typing import Any, List, Optional, Sequence, Union

from agent_framework import ChatAgent, ChatMessage, ChatMessageStoreProtocol, MCPStreamableHTTPTool
from agent_framework import RawAgent, Message, BaseHistoryProvider, MCPStreamableHTTPTool
from agent_framework.azure import AzureOpenAIChatClient
from agent_framework.openai import OpenAIChatClient
import httpx
Expand Down Expand Up @@ -62,9 +62,9 @@ async def add_tool_servers_to_agent(
auth_handler_name: str,
turn_context: TurnContext,
auth_token: Optional[str] = None,
) -> Optional[ChatAgent]:
) -> RawAgent:
"""
Add MCP tool servers to a chat agent (mirrors .NET implementation).
Add MCP tool servers to a RawAgent (mirrors .NET implementation).

Args:
chat_client: The chat client instance (Union[OpenAIChatClient, AzureOpenAIChatClient])
Expand All @@ -76,7 +76,10 @@ async def add_tool_servers_to_agent(
auth_token: Optional bearer token for authentication

Returns:
ChatAgent instance with MCP tools registered, or None if creation failed
RawAgent instance with MCP tools registered.

Raises:
Exception: If agent creation fails.
"""
try:
# Exchange token if not provided
Expand Down Expand Up @@ -148,9 +151,9 @@ async def add_tool_servers_to_agent(
)
continue

# Create the ChatAgent
agent = ChatAgent(
chat_client=chat_client,
# Create the RawAgent
agent = RawAgent(
client=chat_client,
tools=all_tools,
instructions=agent_instructions,
)
Expand All @@ -164,25 +167,25 @@ async def add_tool_servers_to_agent(

def _convert_chat_messages_to_history(
self,
chat_messages: Sequence[ChatMessage],
chat_messages: Sequence[Message],
) -> List[ChatHistoryMessage]:
"""
Convert Agent Framework ChatMessage objects to ChatHistoryMessage format.
Convert Agent Framework Message objects to ChatHistoryMessage format.

This internal helper method transforms Agent Framework's native ChatMessage
This internal helper method transforms Agent Framework's native Message
objects into the ChatHistoryMessage format expected by the MCP platform's
real-time threat protection endpoint.

Args:
chat_messages: Sequence of ChatMessage objects to convert.
chat_messages: Sequence of Message objects to convert.

Returns:
List of ChatHistoryMessage objects ready for the MCP platform.

Note:
- If message_id is None, a new UUID is generated
- Role is extracted via the .value property of the Role object
- Timestamp is set to current UTC time (ChatMessage has no timestamp)
- Timestamp is set to current UTC time (Message has no timestamp)
- Messages with empty or whitespace-only content are filtered out and
logged at WARNING level. This is because ChatHistoryMessage requires
non-empty content for validation. The filtered messages will not be
Expand Down Expand Up @@ -225,7 +228,7 @@ def _convert_chat_messages_to_history(

async def send_chat_history_messages(
self,
chat_messages: Sequence[ChatMessage],
chat_messages: Sequence[Message],
turn_context: TurnContext,
tool_options: Optional[ToolOptions] = None,
) -> OperationResult:
Expand All @@ -236,7 +239,7 @@ async def send_chat_history_messages(
and delegation to the core tooling service.

Args:
chat_messages: Sequence of Agent Framework ChatMessage objects to send.
chat_messages: Sequence of Agent Framework Message objects to send.
Can be empty - the request will still be sent to register
the user message from turn_context.activity.text.
turn_context: TurnContext from the Agents SDK containing conversation info.
Expand All @@ -257,7 +260,7 @@ async def send_chat_history_messages(

Example:
>>> service = McpToolRegistrationService()
>>> messages = [ChatMessage(role=Role.USER, text="Hello")]
>>> messages = [Message(role="user", text="Hello")]
>>> result = await service.send_chat_history_messages(messages, turn_context)
>>> if result.succeeded:
... print("Chat history sent successfully")
Expand Down Expand Up @@ -304,18 +307,18 @@ async def send_chat_history_messages(

async def send_chat_history_from_store(
self,
chat_message_store: ChatMessageStoreProtocol,
chat_message_store: BaseHistoryProvider,
turn_context: TurnContext,
tool_options: Optional[ToolOptions] = None,
) -> OperationResult:
"""
Send chat history from a ChatMessageStore to the MCP platform.
Send chat history from a BaseHistoryProvider to the MCP platform.

This is a convenience method that extracts messages from the store
and delegates to send_chat_history_messages().

Args:
chat_message_store: ChatMessageStore containing the conversation history.
chat_message_store: BaseHistoryProvider containing the conversation history.
turn_context: TurnContext from the Agents SDK containing conversation info.
tool_options: Optional configuration for the request.

Expand All @@ -339,7 +342,7 @@ async def send_chat_history_from_store(
raise ValueError("turn_context cannot be None")

# Extract messages from the store
messages = await chat_message_store.list_messages()
messages = await chat_message_store.get_messages()

# Delegate to the primary implementation
return await self.send_chat_history_messages(
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ constraint-dependencies = [
"azure-monitor-opentelemetry-exporter >= 1.0.0b39",

# --- AI Frameworks ---
"agent-framework-azure-ai >= 1.0.0b251114",
"agent-framework-azure-ai >= 1.0.0rc4",
"langchain >= 0.1.0",
"langchain-core >= 0.1.0",
"openai-agents >= 0.2.6",
Expand All @@ -103,7 +103,7 @@ constraint-dependencies = [
# --- Development & Testing ---
"black >= 23.0.0",
"mypy >= 1.0.0",
"openai >= 1.0.0",
"openai >= 2.0.0",
"pytest >= 7.0.0",
"pytest-asyncio >= 0.21.0",
"pytest-cov>=7.0.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

# AgentFramework SDK
try:
from agent_framework import ChatAgent, ai_function
from agent_framework import RawAgent, ai_function
from agent_framework.azure import AzureOpenAIChatClient
from agent_framework.observability import setup_observability
from azure.identity import AzureCliCredential
Expand Down Expand Up @@ -80,8 +80,8 @@ def test_agentframework_trace_processor_integration(self, azure_openai_config, a
)

# Create agent framework agent
agent = ChatAgent(
chat_client=chat_client,
agent = RawAgent(
client=chat_client,
instructions="You are a helpful assistant.",
tools=[],
)
Expand Down Expand Up @@ -149,8 +149,8 @@ def test_agentframework_trace_processor_with_tool_calls(
)

# Create agent framework agent
agent = ChatAgent(
chat_client=chat_client,
agent = RawAgent(
client=chat_client,
instructions="You are a helpful agent framework assistant.",
tools=[add_numbers],
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ async def test_httpx_client_has_authorization_header(
"microsoft_agents_a365.tooling.extensions.agentframework.services.mcp_tool_registration_service.MCPStreamableHTTPTool"
),
patch(
"microsoft_agents_a365.tooling.extensions.agentframework.services.mcp_tool_registration_service.ChatAgent"
"microsoft_agents_a365.tooling.extensions.agentframework.services.mcp_tool_registration_service.RawAgent"
),
patch(
"microsoft_agents_a365.tooling.extensions.agentframework.services.mcp_tool_registration_service.Utility.resolve_agent_identity",
Expand Down Expand Up @@ -156,7 +156,7 @@ async def test_httpx_client_has_user_agent_header(
"microsoft_agents_a365.tooling.extensions.agentframework.services.mcp_tool_registration_service.MCPStreamableHTTPTool"
),
patch(
"microsoft_agents_a365.tooling.extensions.agentframework.services.mcp_tool_registration_service.ChatAgent"
"microsoft_agents_a365.tooling.extensions.agentframework.services.mcp_tool_registration_service.RawAgent"
),
patch(
"microsoft_agents_a365.tooling.extensions.agentframework.services.mcp_tool_registration_service.Utility.resolve_agent_identity",
Expand Down Expand Up @@ -214,7 +214,7 @@ async def test_httpx_client_has_correct_timeout(
"microsoft_agents_a365.tooling.extensions.agentframework.services.mcp_tool_registration_service.MCPStreamableHTTPTool"
),
patch(
"microsoft_agents_a365.tooling.extensions.agentframework.services.mcp_tool_registration_service.ChatAgent"
"microsoft_agents_a365.tooling.extensions.agentframework.services.mcp_tool_registration_service.RawAgent"
),
patch(
"microsoft_agents_a365.tooling.extensions.agentframework.services.mcp_tool_registration_service.Utility.resolve_agent_identity",
Expand Down Expand Up @@ -277,7 +277,7 @@ async def test_mcp_tool_receives_http_client_not_headers(
"microsoft_agents_a365.tooling.extensions.agentframework.services.mcp_tool_registration_service.MCPStreamableHTTPTool"
) as mock_mcp_tool,
patch(
"microsoft_agents_a365.tooling.extensions.agentframework.services.mcp_tool_registration_service.ChatAgent"
"microsoft_agents_a365.tooling.extensions.agentframework.services.mcp_tool_registration_service.RawAgent"
),
patch(
"microsoft_agents_a365.tooling.extensions.agentframework.services.mcp_tool_registration_service.Utility.resolve_agent_identity",
Expand Down Expand Up @@ -339,7 +339,7 @@ async def test_httpx_client_added_to_internal_list_for_cleanup(
"microsoft_agents_a365.tooling.extensions.agentframework.services.mcp_tool_registration_service.MCPStreamableHTTPTool"
),
patch(
"microsoft_agents_a365.tooling.extensions.agentframework.services.mcp_tool_registration_service.ChatAgent"
"microsoft_agents_a365.tooling.extensions.agentframework.services.mcp_tool_registration_service.RawAgent"
),
patch(
"microsoft_agents_a365.tooling.extensions.agentframework.services.mcp_tool_registration_service.Utility.resolve_agent_identity",
Expand Down Expand Up @@ -498,7 +498,7 @@ async def test_full_client_lifecycle_single_server(
"microsoft_agents_a365.tooling.extensions.agentframework.services.mcp_tool_registration_service.MCPStreamableHTTPTool"
),
patch(
"microsoft_agents_a365.tooling.extensions.agentframework.services.mcp_tool_registration_service.ChatAgent"
"microsoft_agents_a365.tooling.extensions.agentframework.services.mcp_tool_registration_service.RawAgent"
),
patch(
"microsoft_agents_a365.tooling.extensions.agentframework.services.mcp_tool_registration_service.Utility.resolve_agent_identity",
Expand Down Expand Up @@ -582,7 +582,7 @@ async def test_full_client_lifecycle_multiple_servers(
"microsoft_agents_a365.tooling.extensions.agentframework.services.mcp_tool_registration_service.MCPStreamableHTTPTool"
),
patch(
"microsoft_agents_a365.tooling.extensions.agentframework.services.mcp_tool_registration_service.ChatAgent"
"microsoft_agents_a365.tooling.extensions.agentframework.services.mcp_tool_registration_service.RawAgent"
),
patch(
"microsoft_agents_a365.tooling.extensions.agentframework.services.mcp_tool_registration_service.Utility.resolve_agent_identity",
Expand Down Expand Up @@ -678,7 +678,7 @@ async def test_cleanup_called_twice_after_creating_clients(
"microsoft_agents_a365.tooling.extensions.agentframework.services.mcp_tool_registration_service.MCPStreamableHTTPTool"
),
patch(
"microsoft_agents_a365.tooling.extensions.agentframework.services.mcp_tool_registration_service.ChatAgent"
"microsoft_agents_a365.tooling.extensions.agentframework.services.mcp_tool_registration_service.RawAgent"
),
patch(
"microsoft_agents_a365.tooling.extensions.agentframework.services.mcp_tool_registration_service.Utility.resolve_agent_identity",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ def sample_chat_messages(self, mock_role, mock_assistant_role):

@pytest.fixture
def mock_chat_message_store(self, sample_chat_messages):
"""Create a mock ChatMessageStoreProtocol."""
"""Create a mock BaseHistoryProvider."""
store = AsyncMock()
store.list_messages = AsyncMock(return_value=sample_chat_messages)
store.get_messages = AsyncMock(return_value=sample_chat_messages)
return store

@pytest.fixture
Expand Down Expand Up @@ -238,7 +238,7 @@ async def test_send_chat_history_from_store_with_store_success(

# Assert
assert result.succeeded is True
mock_chat_message_store.list_messages.assert_called_once()
mock_chat_message_store.get_messages.assert_called_once()
service._mcp_server_configuration_service.send_chat_history.assert_called_once()

@pytest.mark.asyncio
Expand All @@ -260,7 +260,7 @@ async def test_send_chat_history_from_store_delegates_to_messages_async(

# Assert
assert result.succeeded is True
mock_chat_message_store.list_messages.assert_called_once()
mock_chat_message_store.get_messages.assert_called_once()
mock_messages_method.assert_called_once_with(
chat_messages=sample_chat_messages,
turn_context=mock_turn_context,
Expand Down Expand Up @@ -424,10 +424,10 @@ async def test_send_chat_history_messages_role_value_conversion(
async def test_send_chat_history_from_store_propagates_store_exception(
self, service, mock_turn_context
):
"""Test that exceptions from chat_message_store.list_messages() propagate (CRM-001)."""
"""Test that exceptions from chat_message_store.get_messages() propagate (CRM-001)."""
# Arrange
mock_store = AsyncMock()
mock_store.list_messages = AsyncMock(side_effect=RuntimeError("Store connection failed"))
mock_store.get_messages = AsyncMock(side_effect=RuntimeError("Store connection failed"))

# Act & Assert
with pytest.raises(RuntimeError, match="Store connection failed"):
Expand Down
Loading
Loading