Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 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 @@ -86,7 +86,7 @@ def resolve_agent_identity(context: Any, auth_token: Optional[str]) -> str:
return Utility.get_app_id_from_token(auth_token)

@staticmethod
def get_user_agent_header(orchestrator: str = "") -> str:
def get_user_agent_header(orchestrator: Optional[str] = None) -> str:
"""
Generates a User-Agent header string for SDK requests.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from microsoft_agents_a365.tooling.services.mcp_tool_server_configuration_service import (
McpToolServerConfigurationService,
)
from microsoft_agents_a365.tooling.models import ToolOptions
from microsoft_agents_a365.tooling.utils.constants import Constants

from microsoft_agents_a365.tooling.utils.utility import (
Expand All @@ -28,6 +29,8 @@ class McpToolRegistrationService:
tool servers with Agent Framework agents.
"""

_orchestrator_name: str = "AgentFramework"

def __init__(self, logger: Optional[logging.Logger] = None):
"""
Initialize the MCP Tool Registration Service for Agent Framework.
Expand Down Expand Up @@ -77,10 +80,13 @@ async def add_tool_servers_to_agent(

self._logger.info(f"Listing MCP tool servers for agent {agentic_app_id}")

options = ToolOptions(orchestrator_name=self._orchestrator_name)

# Get MCP server configurations
server_configs = await self._mcp_server_configuration_service.list_tool_servers(
agentic_app_id=agentic_app_id,
auth_token=auth_token,
options=options,
)

self._logger.info(f"Loaded {len(server_configs)} MCP server configurations")
Expand All @@ -105,6 +111,10 @@ async def add_tool_servers_to_agent(
f"{Constants.Headers.BEARER_PREFIX} {auth_token}"
)

headers[Constants.Headers.USER_AGENT] = Utility.get_user_agent_header(
self._orchestrator_name
)

server_name = getattr(config, "mcp_server_name", "Unknown")

# Create and configure MCPStreamableHTTPTool
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from microsoft_agents_a365.tooling.services.mcp_tool_server_configuration_service import (
McpToolServerConfigurationService,
)
from microsoft_agents_a365.tooling.Models import ToolOptions
from microsoft_agents_a365.tooling.utils.constants import Constants
from microsoft_agents_a365.tooling.utils.utility import get_mcp_platform_authentication_scope

Expand All @@ -45,6 +46,8 @@ class McpToolRegistrationService:
>>> service.add_tool_servers_to_agent(project_client, agent_id, token)
"""

_orchestrator_name: str = "AzureAIFoundry"

def __init__(
self,
logger: Optional[logging.Logger] = None,
Expand Down Expand Up @@ -139,9 +142,10 @@ async def _get_mcp_tool_definitions_and_resources(
return ([], None)

# Get MCP server configurations
options = ToolOptions(orchestrator_name=self._orchestrator_name)
try:
servers = await self._mcp_server_configuration_service.list_tool_servers(
agentic_app_id, auth_token
agentic_app_id, auth_token, options
)
except Exception as ex:
self._logger.error(
Expand Down Expand Up @@ -189,6 +193,10 @@ async def _get_mcp_tool_definitions_and_resources(
)
mcp_tool.update_headers(Constants.Headers.AUTHORIZATION, header_value)

mcp_tool.update_headers(
Constants.Headers.USER_AGENT, Utility.get_user_agent_header(self._orchestrator_name)
)

# Add to collections
tool_definitions.extend(mcp_tool.definitions)
if mcp_tool.resources and mcp_tool.resources.mcp:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
McpToolServerConfigurationService,
)

from microsoft_agents_a365.tooling.models import ToolOptions
from microsoft_agents_a365.tooling.utils.constants import Constants
from microsoft_agents_a365.tooling.utils.utility import (
get_mcp_platform_authentication_scope,
)
Expand All @@ -38,6 +40,8 @@ class MCPServerInfo:
class McpToolRegistrationService:
"""Service for managing MCP tools and servers for an agent"""

_orchestrator_name: str = "OpenAI"

def __init__(self, logger: Optional[logging.Logger] = None):
"""
Initialize the MCP Tool Registration Service for OpenAI.
Expand Down Expand Up @@ -83,11 +87,13 @@ async def add_tool_servers_to_agent(
# mcp_server_configs = []
# TODO: radevika: Update once the common project is merged.

options = ToolOptions(orchestrator_name=self._orchestrator_name)
agentic_app_id = Utility.resolve_agent_identity(context, auth_token)
self._logger.info(f"Listing MCP tool servers for agent {agentic_app_id}")
mcp_server_configs = await self.config_service.list_tool_servers(
agentic_app_id=agentic_app_id,
auth_token=auth_token,
options=options,
)

self._logger.info(f"Loaded {len(mcp_server_configs)} MCP server configurations")
Expand Down Expand Up @@ -132,7 +138,13 @@ async def add_tool_servers_to_agent(
# Prepare headers with authorization
headers = si.headers or {}
if auth_token:
headers["Authorization"] = f"Bearer {auth_token}"
headers[Constants.Headers.AUTHORIZATION] = (
f"{Constants.Headers.BEARER_PREFIX} {auth_token}"
)

headers[Constants.Headers.USER_AGENT] = Utility.get_user_agent_header(
self._orchestrator_name
)

# Create MCPServerStreamableHttpParams with proper configuration
params = MCPServerStreamableHttpParams(url=si.url, headers=headers)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from microsoft_agents_a365.tooling.services.mcp_tool_server_configuration_service import (
McpToolServerConfigurationService,
)
from microsoft_agents_a365.tooling.models.mcp_server_config import MCPServerConfig
from microsoft_agents_a365.tooling.models import MCPServerConfig, ToolOptions
from microsoft_agents_a365.tooling.utils.constants import Constants
from microsoft_agents_a365.tooling.utils.utility import (
get_tools_mode,
Expand All @@ -36,6 +36,8 @@ class McpToolRegistrationService:
tool servers with Semantic Kernel agents.
"""

_orchestrator_name: str = "SemanticKernel"

def __init__(
self,
logger: Optional[logging.Logger] = None,
Expand Down Expand Up @@ -107,8 +109,9 @@ async def add_tool_servers_to_agent(
self._validate_inputs(kernel, agentic_app_id, auth_token)

# Get and process servers
options = ToolOptions(orchestrator_name=self._orchestrator_name)
servers = await self._mcp_server_configuration_service.list_tool_servers(
agentic_app_id, auth_token
agentic_app_id, auth_token, options
)
self._logger.info(f"🔧 Adding MCP tools from {len(servers)} servers")

Expand All @@ -132,6 +135,10 @@ async def add_tool_servers_to_agent(
Constants.Headers.AUTHORIZATION: f"{Constants.Headers.BEARER_PREFIX} {auth_token}",
}

headers[Constants.Headers.USER_AGENT] = Utility.get_user_agent_header(
self._orchestrator_name
)

plugin = MCPStreamableHttpPlugin(
name=server.mcp_server_name,
url=server.mcp_server_unique_name,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@

from .mcp_server_config import MCPServerConfig

__all__ = ["MCPServerConfig"]
__all__ = ["MCPServerConfig", "ToolOptions"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Copyright (c) Microsoft. All rights reserved.

"""
Tooling Options model.
"""

from dataclasses import dataclass
from typing import Optional


@dataclass
class ToolOptions:
"""Configuration options for tooling operations."""

#: Gets or sets the name of the orchestrator.
orchestrator_name: Optional[str]
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,13 @@
import aiohttp

# Local imports
from ..models import MCPServerConfig
from ..models import MCPServerConfig, ToolOptions
from ..utils import Constants
from ..utils.utility import get_tooling_gateway_for_digital_worker, build_mcp_server_url

# Runtime Imports
from microsoft_agents_a365.runtime.utility import Utility as RuntimeUtility


# ==============================================================================
# MAIN SERVICE CLASS
Expand Down Expand Up @@ -66,14 +69,15 @@ def __init__(self, logger: Optional[logging.Logger] = None):
# --------------------------------------------------------------------------

async def list_tool_servers(
self, agentic_app_id: str, auth_token: str
self, agentic_app_id: str, auth_token: str, options: Optional[ToolOptions] = None
) -> List[MCPServerConfig]:
"""
Gets the list of MCP Servers that are configured for the agent.

Args:
agentic_app_id: Agentic App ID for the agent.
auth_token: Authentication token to access the MCP servers.
options: Optional ToolOptions instance containing optional parameters.

Returns:
List[MCPServerConfig]: Returns the list of MCP Servers that are configured.
Expand All @@ -85,13 +89,17 @@ async def list_tool_servers(
# Validate input parameters
self._validate_input_parameters(agentic_app_id, auth_token)

# Use default options if none provided
if options is None:
options = ToolOptions(orchestrator_name=None)

self._logger.info(f"Listing MCP tool servers for agent {agentic_app_id}")

# Determine configuration source based on environment
if self._is_development_scenario():
return self._load_servers_from_manifest()
else:
return await self._load_servers_from_gateway(agentic_app_id, auth_token)
return await self._load_servers_from_gateway(agentic_app_id, auth_token, options)

# --------------------------------------------------------------------------
# ENVIRONMENT DETECTION
Expand Down Expand Up @@ -275,14 +283,15 @@ def _log_manifest_search_failure(self) -> None:
# --------------------------------------------------------------------------

async def _load_servers_from_gateway(
self, agentic_app_id: str, auth_token: str
self, agentic_app_id: str, auth_token: str, options: ToolOptions
) -> List[MCPServerConfig]:
"""
Reads MCP server configurations from tooling gateway endpoint for production scenario.

Args:
agentic_app_id: Agentic App ID for the agent.
auth_token: Authentication token to access the tooling gateway.
orchestrator_name: Optional orchestrator name to include in User-Agent header.

Returns:
List[MCPServerConfig]: List of MCP server configurations from tooling gateway.
Expand All @@ -294,7 +303,7 @@ async def _load_servers_from_gateway(

try:
config_endpoint = get_tooling_gateway_for_digital_worker(agentic_app_id)
headers = self._prepare_gateway_headers(auth_token)
headers = self._prepare_gateway_headers(auth_token, options)

self._logger.info(f"Calling tooling gateway endpoint: {config_endpoint}")

Expand Down Expand Up @@ -323,18 +332,22 @@ async def _load_servers_from_gateway(

return mcp_servers

def _prepare_gateway_headers(self, auth_token: str) -> Dict[str, str]:
def _prepare_gateway_headers(self, auth_token: str, options: ToolOptions) -> Dict[str, str]:
"""
Prepares headers for tooling gateway requests.

Args:
auth_token: Authentication token.
orchestrator_name: Optional orchestrator name to include in User-Agent header.

Returns:
Dictionary of HTTP headers.
"""
return {
"Authorization": f"{Constants.Headers.BEARER_PREFIX} {auth_token}",
Constants.Headers.AUTHORIZATION: f"{Constants.Headers.BEARER_PREFIX} {auth_token}",
Constants.Headers.USER_AGENT: RuntimeUtility.get_user_agent_header(
options.orchestrator_name
),
}

async def _parse_gateway_response(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,6 @@ class Headers:

#: The prefix used for Bearer authentication tokens in HTTP headers.
BEARER_PREFIX = "Bearer"

#: The header name for User-Agent information.
USER_AGENT = "User-Agent"
Loading