microsoft-agents-a365-tooling-extensions-azureaifoundry 0.2.1.dev0__py3-none-any.whl → 0.2.1.dev4__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.
- microsoft_agents_a365/tooling/extensions/azureaifoundry/services/mcp_tool_registration_service.py +256 -3
- {microsoft_agents_a365_tooling_extensions_azureaifoundry-0.2.1.dev0.dist-info → microsoft_agents_a365_tooling_extensions_azureaifoundry-0.2.1.dev4.dist-info}/METADATA +1 -1
- {microsoft_agents_a365_tooling_extensions_azureaifoundry-0.2.1.dev0.dist-info → microsoft_agents_a365_tooling_extensions_azureaifoundry-0.2.1.dev4.dist-info}/RECORD +5 -5
- {microsoft_agents_a365_tooling_extensions_azureaifoundry-0.2.1.dev0.dist-info → microsoft_agents_a365_tooling_extensions_azureaifoundry-0.2.1.dev4.dist-info}/top_level.txt +0 -1
- {microsoft_agents_a365_tooling_extensions_azureaifoundry-0.2.1.dev0.dist-info → microsoft_agents_a365_tooling_extensions_azureaifoundry-0.2.1.dev4.dist-info}/WHEEL +0 -0
microsoft_agents_a365/tooling/extensions/azureaifoundry/services/mcp_tool_registration_service.py
CHANGED
|
@@ -11,18 +11,21 @@ servers to agents.
|
|
|
11
11
|
|
|
12
12
|
# Standard library imports
|
|
13
13
|
import logging
|
|
14
|
-
from typing import Optional,
|
|
14
|
+
from typing import List, Optional, Sequence, Tuple
|
|
15
15
|
|
|
16
16
|
# Third-party imports - Azure AI
|
|
17
|
+
from azure.ai.agents import AgentsClient
|
|
18
|
+
from azure.ai.agents.models import McpTool, ThreadMessage, ToolResources
|
|
17
19
|
from azure.ai.projects import AIProjectClient
|
|
18
20
|
from azure.identity import DefaultAzureCredential
|
|
19
|
-
from azure.ai.agents.models import McpTool, ToolResources
|
|
20
21
|
from microsoft_agents.hosting.core import Authorization, TurnContext
|
|
22
|
+
|
|
23
|
+
from microsoft_agents_a365.runtime import OperationError, OperationResult
|
|
21
24
|
from microsoft_agents_a365.runtime.utility import Utility
|
|
25
|
+
from microsoft_agents_a365.tooling.models import ChatHistoryMessage, ToolOptions
|
|
22
26
|
from microsoft_agents_a365.tooling.services.mcp_tool_server_configuration_service import (
|
|
23
27
|
McpToolServerConfigurationService,
|
|
24
28
|
)
|
|
25
|
-
from microsoft_agents_a365.tooling.models import ToolOptions
|
|
26
29
|
from microsoft_agents_a365.tooling.utils.constants import Constants
|
|
27
30
|
from microsoft_agents_a365.tooling.utils.utility import get_mcp_platform_authentication_scope
|
|
28
31
|
|
|
@@ -217,3 +220,253 @@ class McpToolRegistrationService:
|
|
|
217
220
|
)
|
|
218
221
|
|
|
219
222
|
return (tool_definitions, combined_tool_resources)
|
|
223
|
+
|
|
224
|
+
# ============================================================================
|
|
225
|
+
# Public Methods - Chat History API
|
|
226
|
+
# ============================================================================
|
|
227
|
+
|
|
228
|
+
async def send_chat_history_messages(
|
|
229
|
+
self,
|
|
230
|
+
turn_context: TurnContext,
|
|
231
|
+
messages: Sequence[ThreadMessage],
|
|
232
|
+
tool_options: Optional[ToolOptions] = None,
|
|
233
|
+
) -> OperationResult:
|
|
234
|
+
"""
|
|
235
|
+
Send Azure AI Foundry chat history messages to the MCP platform.
|
|
236
|
+
|
|
237
|
+
This method accepts a sequence of Azure AI Foundry ThreadMessage objects,
|
|
238
|
+
converts them to ChatHistoryMessage format, and sends them to the MCP
|
|
239
|
+
platform for real-time threat protection.
|
|
240
|
+
|
|
241
|
+
Args:
|
|
242
|
+
turn_context: TurnContext from the Agents SDK containing conversation info.
|
|
243
|
+
messages: Sequence of Azure AI Foundry ThreadMessage objects to send.
|
|
244
|
+
tool_options: Optional configuration for the request.
|
|
245
|
+
|
|
246
|
+
Returns:
|
|
247
|
+
OperationResult indicating success or failure.
|
|
248
|
+
|
|
249
|
+
Raises:
|
|
250
|
+
ValueError: If turn_context or messages is None.
|
|
251
|
+
|
|
252
|
+
Example:
|
|
253
|
+
>>> service = McpToolRegistrationService()
|
|
254
|
+
>>> messages = await agents_client.messages.list(thread_id=thread_id)
|
|
255
|
+
>>> result = await service.send_chat_history_messages(
|
|
256
|
+
... turn_context, list(messages)
|
|
257
|
+
... )
|
|
258
|
+
>>> if result.succeeded:
|
|
259
|
+
... print("Chat history sent successfully")
|
|
260
|
+
"""
|
|
261
|
+
# Input validation
|
|
262
|
+
if turn_context is None:
|
|
263
|
+
raise ValueError("turn_context cannot be None")
|
|
264
|
+
if messages is None:
|
|
265
|
+
raise ValueError("messages cannot be None")
|
|
266
|
+
|
|
267
|
+
self._logger.info(f"Sending {len(messages)} Azure AI Foundry messages as chat history")
|
|
268
|
+
|
|
269
|
+
# Set default options with orchestrator name
|
|
270
|
+
if tool_options is None:
|
|
271
|
+
tool_options = ToolOptions(orchestrator_name=self._orchestrator_name)
|
|
272
|
+
elif tool_options.orchestrator_name is None:
|
|
273
|
+
tool_options.orchestrator_name = self._orchestrator_name
|
|
274
|
+
|
|
275
|
+
try:
|
|
276
|
+
# Convert ThreadMessage objects to ChatHistoryMessage format
|
|
277
|
+
chat_history_messages = self._convert_thread_messages_to_chat_history(messages)
|
|
278
|
+
|
|
279
|
+
self._logger.debug(
|
|
280
|
+
f"Converted {len(chat_history_messages)} messages to ChatHistoryMessage format"
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
# Delegate to core service
|
|
284
|
+
result = await self._mcp_server_configuration_service.send_chat_history(
|
|
285
|
+
turn_context=turn_context,
|
|
286
|
+
chat_history_messages=chat_history_messages,
|
|
287
|
+
options=tool_options,
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
if result.succeeded:
|
|
291
|
+
self._logger.info(
|
|
292
|
+
f"Chat history sent successfully with {len(chat_history_messages)} messages"
|
|
293
|
+
)
|
|
294
|
+
else:
|
|
295
|
+
self._logger.error(f"Failed to send chat history: {result}")
|
|
296
|
+
|
|
297
|
+
return result
|
|
298
|
+
|
|
299
|
+
except ValueError:
|
|
300
|
+
# Re-raise validation errors from the core service
|
|
301
|
+
raise
|
|
302
|
+
except Exception as ex:
|
|
303
|
+
self._logger.error(f"Failed to send chat history messages: {ex}")
|
|
304
|
+
return OperationResult.failed(OperationError(ex))
|
|
305
|
+
|
|
306
|
+
async def send_chat_history(
|
|
307
|
+
self,
|
|
308
|
+
agents_client: AgentsClient,
|
|
309
|
+
thread_id: str,
|
|
310
|
+
turn_context: TurnContext,
|
|
311
|
+
tool_options: Optional[ToolOptions] = None,
|
|
312
|
+
) -> OperationResult:
|
|
313
|
+
"""
|
|
314
|
+
Retrieve and send chat history from Azure AI Foundry to the MCP platform.
|
|
315
|
+
|
|
316
|
+
This method retrieves messages from the Azure AI Foundry Agents API using
|
|
317
|
+
the provided client and thread ID, converts them to ChatHistoryMessage
|
|
318
|
+
format, and sends them to the MCP platform.
|
|
319
|
+
|
|
320
|
+
Args:
|
|
321
|
+
agents_client: The Azure AI Foundry AgentsClient instance.
|
|
322
|
+
thread_id: The thread ID containing the messages to send.
|
|
323
|
+
turn_context: TurnContext from the Agents SDK containing conversation info.
|
|
324
|
+
tool_options: Optional configuration for the request.
|
|
325
|
+
|
|
326
|
+
Returns:
|
|
327
|
+
OperationResult indicating success or failure.
|
|
328
|
+
|
|
329
|
+
Raises:
|
|
330
|
+
ValueError: If agents_client, thread_id, or turn_context is None/empty.
|
|
331
|
+
|
|
332
|
+
Example:
|
|
333
|
+
>>> from azure.ai.agents import AgentsClient
|
|
334
|
+
>>> from azure.identity import DefaultAzureCredential
|
|
335
|
+
>>>
|
|
336
|
+
>>> client = AgentsClient(endpoint, credential=DefaultAzureCredential())
|
|
337
|
+
>>> service = McpToolRegistrationService()
|
|
338
|
+
>>> result = await service.send_chat_history(
|
|
339
|
+
... client, thread_id, turn_context
|
|
340
|
+
... )
|
|
341
|
+
"""
|
|
342
|
+
# Input validation
|
|
343
|
+
if agents_client is None:
|
|
344
|
+
raise ValueError("agents_client cannot be None")
|
|
345
|
+
if thread_id is None or not thread_id.strip():
|
|
346
|
+
raise ValueError("thread_id cannot be empty")
|
|
347
|
+
if turn_context is None:
|
|
348
|
+
raise ValueError("turn_context cannot be None")
|
|
349
|
+
|
|
350
|
+
try:
|
|
351
|
+
# Retrieve messages from the thread
|
|
352
|
+
messages: List[ThreadMessage] = []
|
|
353
|
+
async for message in agents_client.messages.list(thread_id=thread_id):
|
|
354
|
+
messages.append(message)
|
|
355
|
+
|
|
356
|
+
self._logger.info(f"Retrieved {len(messages)} messages from thread {thread_id}")
|
|
357
|
+
|
|
358
|
+
# Delegate to send_chat_history_messages
|
|
359
|
+
return await self.send_chat_history_messages(
|
|
360
|
+
turn_context=turn_context,
|
|
361
|
+
messages=messages,
|
|
362
|
+
tool_options=tool_options,
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
except ValueError:
|
|
366
|
+
# Re-raise validation errors
|
|
367
|
+
raise
|
|
368
|
+
except Exception as ex:
|
|
369
|
+
self._logger.error(f"Failed to send chat history from thread {thread_id}: {ex}")
|
|
370
|
+
return OperationResult.failed(OperationError(ex))
|
|
371
|
+
|
|
372
|
+
# ============================================================================
|
|
373
|
+
# Private Methods - Message Conversion Helpers
|
|
374
|
+
# ============================================================================
|
|
375
|
+
|
|
376
|
+
def _convert_thread_messages_to_chat_history(
|
|
377
|
+
self,
|
|
378
|
+
messages: Sequence[ThreadMessage],
|
|
379
|
+
) -> List[ChatHistoryMessage]:
|
|
380
|
+
"""
|
|
381
|
+
Convert Azure AI Foundry ThreadMessage objects to ChatHistoryMessage format.
|
|
382
|
+
|
|
383
|
+
This internal helper method transforms Azure AI Foundry's native ThreadMessage
|
|
384
|
+
objects into the ChatHistoryMessage format expected by the MCP platform's
|
|
385
|
+
real-time threat protection endpoint.
|
|
386
|
+
|
|
387
|
+
Args:
|
|
388
|
+
messages: Sequence of ThreadMessage objects to convert.
|
|
389
|
+
|
|
390
|
+
Returns:
|
|
391
|
+
List of ChatHistoryMessage objects ready for the MCP platform.
|
|
392
|
+
|
|
393
|
+
Note:
|
|
394
|
+
- Messages with None id, None role, or empty content are filtered out
|
|
395
|
+
- Role is extracted via the .value property of the MessageRole enum
|
|
396
|
+
- Timestamp is taken from message.created_at
|
|
397
|
+
"""
|
|
398
|
+
history_messages: List[ChatHistoryMessage] = []
|
|
399
|
+
|
|
400
|
+
for message in messages:
|
|
401
|
+
# Skip None messages
|
|
402
|
+
if message is None:
|
|
403
|
+
self._logger.warning("Skipping null message")
|
|
404
|
+
continue
|
|
405
|
+
|
|
406
|
+
# Skip messages with None id
|
|
407
|
+
if message.id is None:
|
|
408
|
+
self._logger.warning("Skipping message with null ID")
|
|
409
|
+
continue
|
|
410
|
+
|
|
411
|
+
# Skip messages with None role
|
|
412
|
+
if message.role is None:
|
|
413
|
+
self._logger.warning(f"Skipping message with null role (ID: {message.id})")
|
|
414
|
+
continue
|
|
415
|
+
|
|
416
|
+
# Extract content from message
|
|
417
|
+
content = self._extract_content_from_message(message)
|
|
418
|
+
|
|
419
|
+
# Skip messages with empty content
|
|
420
|
+
if not content or not content.strip():
|
|
421
|
+
self._logger.warning(f"Skipping message {message.id} with empty content")
|
|
422
|
+
continue
|
|
423
|
+
|
|
424
|
+
# Convert role enum to lowercase string
|
|
425
|
+
role_value = message.role.value if hasattr(message.role, "value") else str(message.role)
|
|
426
|
+
role = role_value.lower()
|
|
427
|
+
|
|
428
|
+
# Create ChatHistoryMessage
|
|
429
|
+
history_message = ChatHistoryMessage(
|
|
430
|
+
id=message.id,
|
|
431
|
+
role=role,
|
|
432
|
+
content=content,
|
|
433
|
+
timestamp=message.created_at,
|
|
434
|
+
)
|
|
435
|
+
history_messages.append(history_message)
|
|
436
|
+
|
|
437
|
+
self._logger.debug(
|
|
438
|
+
f"Converted message {message.id} with role '{role}' to ChatHistoryMessage"
|
|
439
|
+
)
|
|
440
|
+
|
|
441
|
+
if len(history_messages) == 0 and len(messages) > 0:
|
|
442
|
+
self._logger.warning("All messages were filtered out during conversion")
|
|
443
|
+
|
|
444
|
+
return history_messages
|
|
445
|
+
|
|
446
|
+
def _extract_content_from_message(self, message: ThreadMessage) -> str:
|
|
447
|
+
"""
|
|
448
|
+
Extract text content from a ThreadMessage's content items.
|
|
449
|
+
|
|
450
|
+
This method iterates through the message's content list and extracts
|
|
451
|
+
text from MessageTextContent items, concatenating them with spaces.
|
|
452
|
+
|
|
453
|
+
Args:
|
|
454
|
+
message: Azure AI Foundry ThreadMessage object.
|
|
455
|
+
|
|
456
|
+
Returns:
|
|
457
|
+
Concatenated text content as string, or empty string if no text found.
|
|
458
|
+
"""
|
|
459
|
+
if message.content is None or len(message.content) == 0:
|
|
460
|
+
return ""
|
|
461
|
+
|
|
462
|
+
text_parts: List[str] = []
|
|
463
|
+
|
|
464
|
+
for content_item in message.content:
|
|
465
|
+
# Check for MessageTextContent by duck typing (has text attribute with value)
|
|
466
|
+
# This handles both real SDK types and mock objects in tests
|
|
467
|
+
if hasattr(content_item, "text") and content_item.text is not None:
|
|
468
|
+
text_value = getattr(content_item.text, "value", None)
|
|
469
|
+
if text_value is not None and text_value:
|
|
470
|
+
text_parts.append(text_value)
|
|
471
|
+
|
|
472
|
+
return " ".join(text_parts)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
microsoft_agents_a365/tooling/extensions/azureaifoundry/__init__.py,sha256=kK4Q6Sa0vMEwPvI_nnIUJj9rCVfLxsW8Ecghy3tWYlI,487
|
|
2
2
|
microsoft_agents_a365/tooling/extensions/azureaifoundry/services/__init__.py,sha256=i6HP6TSmCi9XQkm0xZ304xwy7B0GE4RDB4u5d8K9V_k,392
|
|
3
|
-
microsoft_agents_a365/tooling/extensions/azureaifoundry/services/mcp_tool_registration_service.py,sha256=
|
|
4
|
-
microsoft_agents_a365_tooling_extensions_azureaifoundry-0.2.1.
|
|
5
|
-
microsoft_agents_a365_tooling_extensions_azureaifoundry-0.2.1.
|
|
6
|
-
microsoft_agents_a365_tooling_extensions_azureaifoundry-0.2.1.
|
|
7
|
-
microsoft_agents_a365_tooling_extensions_azureaifoundry-0.2.1.
|
|
3
|
+
microsoft_agents_a365/tooling/extensions/azureaifoundry/services/mcp_tool_registration_service.py,sha256=lvnlGH7DX-QSFXzhdhioYZ0JRGTWH-8uE3WbpitZwE8,18949
|
|
4
|
+
microsoft_agents_a365_tooling_extensions_azureaifoundry-0.2.1.dev4.dist-info/METADATA,sha256=rNeqFgUqWujpTN8zOHVRcAzy3s_JNX8ktcbzbVOPGOg,3418
|
|
5
|
+
microsoft_agents_a365_tooling_extensions_azureaifoundry-0.2.1.dev4.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
6
|
+
microsoft_agents_a365_tooling_extensions_azureaifoundry-0.2.1.dev4.dist-info/top_level.txt,sha256=G3c2_4sy5_EM_BWO67SbK2tKj4G8XFn-QXRbh8g9Lgk,22
|
|
7
|
+
microsoft_agents_a365_tooling_extensions_azureaifoundry-0.2.1.dev4.dist-info/RECORD,,
|