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
6 changes: 5 additions & 1 deletion python/agent-framework/sample-agent/.env.template
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@
OPENAI_API_KEY=
MCP_SERVER_HOST=
MCP_PLATFORM_ENDPOINT=
# Logging

# Authentication Handler Configuration
# Set to "AGENTIC" for production agentic auth, or leave empty for no auth handler
AUTH_HANDLER_NAME=

# Logging
LOG_LEVEL=INFO

# Observability Configuration
Expand Down
5 changes: 1 addition & 4 deletions python/agent-framework/sample-agent/ToolingManifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@
"mcpServers": [
{
"mcpServerName": "mcp_MailTools",
"mcpServerUniqueName": "mcp_MailTools",
"url": "https://agent365.svc.cloud.microsoft/agents/servers/mcp_MailTools",
"scope": "McpServers.Mail.All",
"audience": "ea9ffc3e-8a23-4a7d-836d-234d7c7565c1"
"mcpServerUniqueName": "mcp_MailTools"
Comment thread
rahuldevikar761 marked this conversation as resolved.
}
]
}
19 changes: 15 additions & 4 deletions python/agent-framework/sample-agent/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import asyncio
import logging
import os
from typing import Optional

from dotenv import load_dotenv

Expand Down Expand Up @@ -120,6 +121,7 @@ def _create_chat_client(self):
endpoint = os.getenv("AZURE_OPENAI_ENDPOINT")
deployment = os.getenv("AZURE_OPENAI_DEPLOYMENT")
api_version = os.getenv("AZURE_OPENAI_API_VERSION")
api_key = os.getenv("AZURE_OPENAI_API_KEY")

if not endpoint:
raise ValueError("AZURE_OPENAI_ENDPOINT environment variable is required")
Expand All @@ -130,9 +132,18 @@ def _create_chat_client(self):
"AZURE_OPENAI_API_VERSION environment variable is required"
)

# Use API key if provided, otherwise fall back to Azure CLI credential
if api_key:
from azure.core.credentials import AzureKeyCredential
credential = AzureKeyCredential(api_key)
logger.info("Using API key authentication for Azure OpenAI")
else:
credential = AzureCliCredential()
logger.info("Using Azure CLI authentication for Azure OpenAI")

Comment thread
rahuldevikar761 marked this conversation as resolved.
self.chat_client = AzureOpenAIChatClient(
endpoint=endpoint,
credential=AzureCliCredential(),
credential=credential,
deployment_name=deployment,
api_version=api_version,
)
Expand Down Expand Up @@ -193,7 +204,7 @@ def _initialize_services(self):
logger.warning(f"⚠️ MCP tool service failed: {e}")
self.tool_service = None

async def setup_mcp_servers(self, auth: Authorization, auth_handler_name: str, context: TurnContext):
async def setup_mcp_servers(self, auth: Authorization, auth_handler_name: Optional[str], context: TurnContext):
"""Set up MCP server connections"""
if self.mcp_servers_initialized:
return
Expand Down Expand Up @@ -246,7 +257,7 @@ async def initialize(self):
logger.info("Agent initialized")

async def process_user_message(
self, message: str, auth: Authorization, auth_handler_name: str, context: TurnContext
self, message: str, auth: Authorization, auth_handler_name: Optional[str], context: TurnContext
) -> str:
"""Process user message using the AgentFramework SDK"""
try:
Expand All @@ -265,7 +276,7 @@ async def process_user_message(
# <NotificationHandling>

async def handle_agent_notification_activity(
self, notification_activity, auth: Authorization, auth_handler_name: str, context: TurnContext
self, notification_activity, auth: Authorization, auth_handler_name: Optional[str], context: TurnContext
) -> str:
"""Handle agent notification activities (email, Word mentions, etc.)"""
try:
Expand Down
4 changes: 3 additions & 1 deletion python/agent-framework/sample-agent/agent_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
"""

from abc import ABC, abstractmethod
from typing import Optional

from microsoft_agents.hosting.core import Authorization, TurnContext


Expand All @@ -24,7 +26,7 @@ async def initialize(self) -> None:

@abstractmethod
async def process_user_message(
self, message: str, auth: Authorization, auth_handler_name: str, context: TurnContext
self, message: str, auth: Authorization, auth_handler_name: Optional[str], context: TurnContext
) -> str:
"""Process a user message and return a response."""
pass
Expand Down
34 changes: 28 additions & 6 deletions python/agent-framework/sample-agent/host_agent_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,13 @@ def __init__(self, agent_class: type[AgentInterface], *agent_args, **agent_kwarg
f"Agent class {agent_class.__name__} must inherit from AgentInterface"
)

self.auth_handler_name = "AGENTIC"
# Auth handler name can be configured via environment
# Defaults to empty (no auth handler) - set AUTH_HANDLER_NAME=AGENTIC for production agentic auth
self.auth_handler_name = os.getenv("AUTH_HANDLER_NAME", "") or None
Comment thread
rahuldevikar761 marked this conversation as resolved.
Comment thread
rahuldevikar761 marked this conversation as resolved.
if self.auth_handler_name:
logger.info(f"🔐 Using auth handler: {self.auth_handler_name}")
else:
logger.info("🔓 No auth handler configured (AUTH_HANDLER_NAME not set)")

self.agent_class = agent_class
self.agent_args = agent_args
Expand All @@ -118,19 +124,34 @@ def __init__(self, agent_class: type[AgentInterface], *agent_args, **agent_kwarg
async def _setup_observability_token(
self, context: TurnContext, tenant_id: str, agent_id: str
):
# Only attempt token exchange when auth handler is configured
if not self.auth_handler_name:
logger.debug("Skipping observability token exchange (no auth handler)")
return

try:
logger.info(
f"🔐 Attempting token exchange for observability... "
f"(tenant_id={tenant_id}, agent_id={agent_id})"
)
exaau_token = await self.agent_app.auth.exchange_token(
context,
scopes=get_observability_authentication_scope(),
auth_handler_id=self.auth_handler_name,
)
cache_agentic_token(tenant_id, agent_id, exaau_token.token)
logger.info(
f"✅ Token exchange successful "
f"(tenant_id={tenant_id}, agent_id={agent_id})"
)
except Exception as e:
logger.warning(f"⚠️ Failed to cache observability token: {e}")

async def _validate_agent_and_setup_context(self, context: TurnContext):
logger.info("🔍 Validating agent and setting up context...")
tenant_id = context.activity.recipient.tenant_id
agent_id = context.activity.recipient.agentic_app_id
logger.info(f"🔍 tenant_id={tenant_id}, agent_id={agent_id}")

if not self.agent_instance:
logger.error("Agent not available")
Expand All @@ -143,18 +164,19 @@ async def _validate_agent_and_setup_context(self, context: TurnContext):
# --- Handlers (Messages & Notifications) ---
def _setup_handlers(self):
"""Setup message and notification handlers"""
handler = [self.auth_handler_name]
# Configure auth handlers - only required when auth_handler_name is set
handler_config = {"auth_handlers": [self.auth_handler_name]} if self.auth_handler_name else {}

async def help_handler(context: TurnContext, _: TurnState):
await context.send_activity(
f"👋 **Hi there!** I'm **{self.agent_class.__name__}**, your AI assistant.\n\n"
"How can I help you today?"
)

self.agent_app.conversation_update("membersAdded", auth_handlers=handler)(help_handler)
self.agent_app.message("/help", auth_handlers=handler)(help_handler)
self.agent_app.conversation_update("membersAdded", **handler_config)(help_handler)
self.agent_app.message("/help", **handler_config)(help_handler)

@self.agent_app.activity("message", auth_handlers=handler)
@self.agent_app.activity("message", **handler_config)
async def on_message(context: TurnContext, _: TurnState):
try:
result = await self._validate_agent_and_setup_context(context)
Expand All @@ -179,7 +201,7 @@ async def on_message(context: TurnContext, _: TurnState):

@self.agent_notification.on_agent_notification(
channel_id=ChannelId(channel="agents", sub_channel="*"),
auth_handlers=handler,
**handler_config,
)
async def on_notification(
context: TurnContext,
Expand Down