microsoft-agents-a365-tooling-extensions-agentframework 0.2.0.dev5__py3-none-any.whl → 0.2.1.dev2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,5 @@
1
- # Copyright (c) Microsoft. All rights reserved.
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
2
3
 
3
4
  """
4
5
  Agent 365 Tooling Agent Framework Extensions
@@ -1,4 +1,5 @@
1
- # Copyright (c) Microsoft. All rights reserved.
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
2
3
 
3
4
  """
4
5
  Services module for Agent Framework tooling.
@@ -1,20 +1,24 @@
1
- # Copyright (c) Microsoft. All rights reserved.
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
2
3
 
3
- from typing import Optional, List, Any, Union
4
4
  import logging
5
+ import uuid
6
+ from datetime import datetime, timezone
7
+ from typing import Any, List, Optional, Sequence, Union
5
8
 
6
- from agent_framework import ChatAgent, MCPStreamableHTTPTool
9
+ from agent_framework import ChatAgent, ChatMessage, ChatMessageStoreProtocol, MCPStreamableHTTPTool
7
10
  from agent_framework.azure import AzureOpenAIChatClient
8
11
  from agent_framework.openai import OpenAIChatClient
9
12
 
10
13
  from microsoft_agents.hosting.core import Authorization, TurnContext
11
14
 
15
+ from microsoft_agents_a365.runtime import OperationResult
12
16
  from microsoft_agents_a365.runtime.utility import Utility
17
+ from microsoft_agents_a365.tooling.models import ChatHistoryMessage, ToolOptions
13
18
  from microsoft_agents_a365.tooling.services.mcp_tool_server_configuration_service import (
14
19
  McpToolServerConfigurationService,
15
20
  )
16
21
  from microsoft_agents_a365.tooling.utils.constants import Constants
17
-
18
22
  from microsoft_agents_a365.tooling.utils.utility import (
19
23
  get_mcp_platform_authentication_scope,
20
24
  )
@@ -28,6 +32,8 @@ class McpToolRegistrationService:
28
32
  tool servers with Agent Framework agents.
29
33
  """
30
34
 
35
+ _orchestrator_name: str = "AgentFramework"
36
+
31
37
  def __init__(self, logger: Optional[logging.Logger] = None):
32
38
  """
33
39
  Initialize the MCP Tool Registration Service for Agent Framework.
@@ -77,10 +83,13 @@ class McpToolRegistrationService:
77
83
 
78
84
  self._logger.info(f"Listing MCP tool servers for agent {agentic_app_id}")
79
85
 
86
+ options = ToolOptions(orchestrator_name=self._orchestrator_name)
87
+
80
88
  # Get MCP server configurations
81
89
  server_configs = await self._mcp_server_configuration_service.list_tool_servers(
82
90
  agentic_app_id=agentic_app_id,
83
91
  auth_token=auth_token,
92
+ options=options,
84
93
  )
85
94
 
86
95
  self._logger.info(f"Loaded {len(server_configs)} MCP server configurations")
@@ -90,14 +99,10 @@ class McpToolRegistrationService:
90
99
 
91
100
  # Add servers as MCPStreamableHTTPTool instances
92
101
  for config in server_configs:
93
- try:
94
- server_url = getattr(config, "server_url", None) or getattr(
95
- config, "mcp_server_unique_name", None
96
- )
97
- if not server_url:
98
- self._logger.warning(f"MCP server config missing server_url: {config}")
99
- continue
102
+ # Use mcp_server_name if available (not None or empty), otherwise fall back to mcp_server_unique_name
103
+ server_name = config.mcp_server_name or config.mcp_server_unique_name
100
104
 
105
+ try:
101
106
  # Prepare auth headers
102
107
  headers = {}
103
108
  if auth_token:
@@ -105,18 +110,20 @@ class McpToolRegistrationService:
105
110
  f"{Constants.Headers.BEARER_PREFIX} {auth_token}"
106
111
  )
107
112
 
108
- server_name = getattr(config, "mcp_server_name", "Unknown")
113
+ headers[Constants.Headers.USER_AGENT] = Utility.get_user_agent_header(
114
+ self._orchestrator_name
115
+ )
109
116
 
110
117
  # Create and configure MCPStreamableHTTPTool
111
118
  mcp_tools = MCPStreamableHTTPTool(
112
119
  name=server_name,
113
- url=server_url,
120
+ url=config.url,
114
121
  headers=headers,
115
122
  description=f"MCP tools from {server_name}",
116
123
  )
117
124
 
118
125
  # Let Agent Framework handle the connection automatically
119
- self._logger.info(f"Created MCP plugin for '{server_name}' at {server_url}")
126
+ self._logger.info(f"Created MCP plugin for '{server_name}' at {config.url}")
120
127
 
121
128
  all_tools.append(mcp_tools)
122
129
  self._connected_servers.append(mcp_tools)
@@ -124,7 +131,6 @@ class McpToolRegistrationService:
124
131
  self._logger.info(f"Added MCP plugin '{server_name}' to agent tools")
125
132
 
126
133
  except Exception as tool_ex:
127
- server_name = getattr(config, "mcp_server_name", "Unknown")
128
134
  self._logger.warning(
129
135
  f"Failed to create MCP plugin for {server_name}: {tool_ex}"
130
136
  )
@@ -144,6 +150,192 @@ class McpToolRegistrationService:
144
150
  self._logger.error(f"Failed to add tool servers to agent: {ex}")
145
151
  raise
146
152
 
153
+ def _convert_chat_messages_to_history(
154
+ self,
155
+ chat_messages: Sequence[ChatMessage],
156
+ ) -> List[ChatHistoryMessage]:
157
+ """
158
+ Convert Agent Framework ChatMessage objects to ChatHistoryMessage format.
159
+
160
+ This internal helper method transforms Agent Framework's native ChatMessage
161
+ objects into the ChatHistoryMessage format expected by the MCP platform's
162
+ real-time threat protection endpoint.
163
+
164
+ Args:
165
+ chat_messages: Sequence of ChatMessage objects to convert.
166
+
167
+ Returns:
168
+ List of ChatHistoryMessage objects ready for the MCP platform.
169
+
170
+ Note:
171
+ - If message_id is None, a new UUID is generated
172
+ - Role is extracted via the .value property of the Role object
173
+ - Timestamp is set to current UTC time (ChatMessage has no timestamp)
174
+ - Messages with empty or whitespace-only content are filtered out and
175
+ logged at WARNING level. This is because ChatHistoryMessage requires
176
+ non-empty content for validation. The filtered messages will not be
177
+ sent to the MCP platform.
178
+ """
179
+ history_messages: List[ChatHistoryMessage] = []
180
+ current_time = datetime.now(timezone.utc)
181
+
182
+ for msg in chat_messages:
183
+ message_id = msg.message_id if msg.message_id is not None else str(uuid.uuid4())
184
+ if msg.role is None:
185
+ self._logger.warning(
186
+ "Skipping message %s with missing role during conversion", message_id
187
+ )
188
+ continue
189
+ # Defensive handling: use .value if role is an enum, otherwise convert to string
190
+ role = msg.role.value if hasattr(msg.role, "value") else str(msg.role)
191
+ content = msg.text if msg.text is not None else ""
192
+
193
+ # Skip messages with empty content as ChatHistoryMessage validates non-empty content
194
+ if not content.strip():
195
+ self._logger.warning(
196
+ "Skipping message %s with empty content during conversion", message_id
197
+ )
198
+ continue
199
+
200
+ history_message = ChatHistoryMessage(
201
+ id=message_id,
202
+ role=role,
203
+ content=content,
204
+ timestamp=current_time,
205
+ )
206
+ history_messages.append(history_message)
207
+
208
+ self._logger.debug(
209
+ "Converted message %s with role '%s' to ChatHistoryMessage", message_id, role
210
+ )
211
+
212
+ return history_messages
213
+
214
+ async def send_chat_history_messages(
215
+ self,
216
+ chat_messages: Sequence[ChatMessage],
217
+ turn_context: TurnContext,
218
+ tool_options: Optional[ToolOptions] = None,
219
+ ) -> OperationResult:
220
+ """
221
+ Send chat history messages to the MCP platform for real-time threat protection.
222
+
223
+ This is the primary implementation method that handles message conversion
224
+ and delegation to the core tooling service.
225
+
226
+ Args:
227
+ 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.
230
+ turn_context: TurnContext from the Agents SDK containing conversation info.
231
+ tool_options: Optional configuration for the request. Defaults to
232
+ AgentFramework-specific options if not provided.
233
+
234
+ Returns:
235
+ OperationResult indicating success or failure of the operation.
236
+
237
+ Raises:
238
+ ValueError: If chat_messages or turn_context is None.
239
+
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
+
246
+ Example:
247
+ >>> service = McpToolRegistrationService()
248
+ >>> messages = [ChatMessage(role=Role.USER, text="Hello")]
249
+ >>> result = await service.send_chat_history_messages(messages, turn_context)
250
+ >>> if result.succeeded:
251
+ ... print("Chat history sent successfully")
252
+ """
253
+ # Input validation
254
+ if chat_messages is None:
255
+ raise ValueError("chat_messages cannot be None")
256
+
257
+ if turn_context is None:
258
+ raise ValueError("turn_context cannot be None")
259
+
260
+ self._logger.info(f"Send chat history initiated with {len(chat_messages)} messages")
261
+
262
+ # Use default options if not provided
263
+ if tool_options is None:
264
+ tool_options = ToolOptions(orchestrator_name=self._orchestrator_name)
265
+
266
+ # Convert messages to ChatHistoryMessage format
267
+ history_messages = self._convert_chat_messages_to_history(chat_messages)
268
+
269
+ # Call core service even with empty history_messages to register
270
+ # the user message from turn_context.activity.text in the MCP platform.
271
+ if len(history_messages) == 0:
272
+ self._logger.info(
273
+ "Empty history messages (either no input or all filtered), "
274
+ "still sending to register user message"
275
+ )
276
+
277
+ # Delegate to core service
278
+ result = await self._mcp_server_configuration_service.send_chat_history(
279
+ turn_context=turn_context,
280
+ chat_history_messages=history_messages,
281
+ options=tool_options,
282
+ )
283
+
284
+ if result.succeeded:
285
+ self._logger.info(
286
+ f"Chat history sent successfully with {len(history_messages)} messages"
287
+ )
288
+ else:
289
+ self._logger.error(f"Failed to send chat history: {result}")
290
+
291
+ return result
292
+
293
+ async def send_chat_history_from_store(
294
+ self,
295
+ chat_message_store: ChatMessageStoreProtocol,
296
+ turn_context: TurnContext,
297
+ tool_options: Optional[ToolOptions] = None,
298
+ ) -> OperationResult:
299
+ """
300
+ Send chat history from a ChatMessageStore to the MCP platform.
301
+
302
+ This is a convenience method that extracts messages from the store
303
+ and delegates to send_chat_history_messages().
304
+
305
+ Args:
306
+ chat_message_store: ChatMessageStore containing the conversation history.
307
+ turn_context: TurnContext from the Agents SDK containing conversation info.
308
+ tool_options: Optional configuration for the request.
309
+
310
+ Returns:
311
+ OperationResult indicating success or failure of the operation.
312
+
313
+ Raises:
314
+ ValueError: If chat_message_store or turn_context is None.
315
+
316
+ Example:
317
+ >>> service = McpToolRegistrationService()
318
+ >>> result = await service.send_chat_history_from_store(
319
+ ... thread.chat_message_store, turn_context
320
+ ... )
321
+ """
322
+ # Input validation
323
+ if chat_message_store is None:
324
+ raise ValueError("chat_message_store cannot be None")
325
+
326
+ if turn_context is None:
327
+ raise ValueError("turn_context cannot be None")
328
+
329
+ # Extract messages from the store
330
+ messages = await chat_message_store.list_messages()
331
+
332
+ # Delegate to the primary implementation
333
+ return await self.send_chat_history_messages(
334
+ chat_messages=messages,
335
+ turn_context=turn_context,
336
+ tool_options=tool_options,
337
+ )
338
+
147
339
  async def cleanup(self):
148
340
  """Clean up any resources used by the service."""
149
341
  try:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: microsoft-agents-a365-tooling-extensions-agentframework
3
- Version: 0.2.0.dev5
3
+ Version: 0.2.1.dev2
4
4
  Summary: Agent Framework integration tools for Agent 365 AI agent tooling
5
5
  Author-email: Microsoft <support@microsoft.com>
6
6
  License: MIT
@@ -0,0 +1,7 @@
1
+ microsoft_agents_a365/tooling/extensions/agentframework/__init__.py,sha256=f_v_Uqi-rG_ramG91wrw-u8hzANbn5qS2wbp62S_bNw,905
2
+ microsoft_agents_a365/tooling/extensions/agentframework/services/__init__.py,sha256=Xw_MZvaAiiPCFnTTU59FnG6AS6FxVj5qN2vxazB1I50,363
3
+ microsoft_agents_a365/tooling/extensions/agentframework/services/mcp_tool_registration_service.py,sha256=cKIEJFW8xRI74MF_SY67mxr8Bk2XWP5C7OnUuQXEB9Y,14053
4
+ microsoft_agents_a365_tooling_extensions_agentframework-0.2.1.dev2.dist-info/METADATA,sha256=4yEAdTEKGJlJaVDN0ntMja3LHFEbKvpad1i6n_qTEmc,3492
5
+ microsoft_agents_a365_tooling_extensions_agentframework-0.2.1.dev2.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
6
+ microsoft_agents_a365_tooling_extensions_agentframework-0.2.1.dev2.dist-info/top_level.txt,sha256=G3c2_4sy5_EM_BWO67SbK2tKj4G8XFn-QXRbh8g9Lgk,22
7
+ microsoft_agents_a365_tooling_extensions_agentframework-0.2.1.dev2.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,7 +0,0 @@
1
- microsoft_agents_a365/tooling/extensions/agentframework/__init__.py,sha256=IPaHio1uop6iVK6KYeI5ERhTm3uPQRO6__21SYQqpQ4,880
2
- microsoft_agents_a365/tooling/extensions/agentframework/services/__init__.py,sha256=TcxL8IstydK8bZ2H3hFVKRvO67VYDqY7JOTBEA9ylXc,338
3
- microsoft_agents_a365/tooling/extensions/agentframework/services/mcp_tool_registration_service.py,sha256=Ic_yrUMQZs-Vc5IcSKMMdZCX1oZVJZw1EkDzyX14Q2c,6237
4
- microsoft_agents_a365_tooling_extensions_agentframework-0.2.0.dev5.dist-info/METADATA,sha256=uBTGpOFgmKdHrFzOXY-tT7Va8rejcGQYl43wuF___RE,3492
5
- microsoft_agents_a365_tooling_extensions_agentframework-0.2.0.dev5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
6
- microsoft_agents_a365_tooling_extensions_agentframework-0.2.0.dev5.dist-info/top_level.txt,sha256=G3c2_4sy5_EM_BWO67SbK2tKj4G8XFn-QXRbh8g9Lgk,22
7
- microsoft_agents_a365_tooling_extensions_agentframework-0.2.0.dev5.dist-info/RECORD,,