mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Python: fix(declarative): Fix MCP tool connection not passed from YAML to Azure AI agent creation API (#3248)
* fix(declarative): Fix MCP tool connection not passed from YAML * Add samples to README * Fix mypy * Fix mypy again * Address PR comments
This commit is contained in:
committed by
GitHub
Unverified
parent
83e8965c8e
commit
e0b9be7e08
@@ -33,7 +33,7 @@ from azure.core.credentials_async import AsyncTokenCredential
|
||||
from azure.core.exceptions import ResourceNotFoundError
|
||||
from pydantic import ValidationError
|
||||
|
||||
from ._shared import AzureAISettings, create_text_format_config
|
||||
from ._shared import AzureAISettings, _extract_project_connection_id, create_text_format_config
|
||||
|
||||
if sys.version_info >= (3, 13):
|
||||
from typing import TypeVar # type: ignore # pragma: no cover
|
||||
@@ -510,6 +510,17 @@ class AzureAIClient(OpenAIBaseResponsesClient[TAzureAIClientOptions], Generic[TA
|
||||
"""Get MCP tool from HostedMCPTool."""
|
||||
mcp = MCPTool(server_label=tool.name.replace(" ", "_"), server_url=str(tool.url))
|
||||
|
||||
if tool.description:
|
||||
mcp["server_description"] = tool.description
|
||||
|
||||
# Check for project_connection_id in additional_properties (for Azure AI Foundry connections)
|
||||
project_connection_id = _extract_project_connection_id(tool.additional_properties)
|
||||
if project_connection_id:
|
||||
mcp["project_connection_id"] = project_connection_id
|
||||
elif tool.headers:
|
||||
# Only use headers if no project_connection_id is available
|
||||
mcp["headers"] = tool.headers
|
||||
|
||||
if tool.allowed_tools:
|
||||
mcp["allowed_tools"] = list(tool.allowed_tools)
|
||||
|
||||
|
||||
@@ -87,6 +87,37 @@ class AzureAISettings(AFBaseSettings):
|
||||
model_deployment_name: str | None = None
|
||||
|
||||
|
||||
def _extract_project_connection_id(additional_properties: dict[str, Any] | None) -> str | None:
|
||||
"""Extract project_connection_id from HostedMCPTool additional_properties.
|
||||
|
||||
Checks for both direct 'project_connection_id' key (programmatic usage)
|
||||
and 'connection.name' structure (declarative/YAML usage).
|
||||
|
||||
Args:
|
||||
additional_properties: The additional_properties dict from a HostedMCPTool.
|
||||
|
||||
Returns:
|
||||
The project_connection_id if found, None otherwise.
|
||||
"""
|
||||
if not additional_properties:
|
||||
return None
|
||||
|
||||
# Check for direct project_connection_id (programmatic usage)
|
||||
project_connection_id = additional_properties.get("project_connection_id")
|
||||
if isinstance(project_connection_id, str):
|
||||
return project_connection_id
|
||||
|
||||
# Check for connection.name structure (declarative/YAML usage)
|
||||
if "connection" in additional_properties:
|
||||
conn = additional_properties["connection"]
|
||||
if isinstance(conn, dict):
|
||||
name = conn.get("name")
|
||||
if isinstance(name, str):
|
||||
return name
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def to_azure_ai_agent_tools(
|
||||
tools: Sequence[ToolProtocol | MutableMapping[str, Any]] | None,
|
||||
run_options: dict[str, Any] | None = None,
|
||||
@@ -322,6 +353,11 @@ def from_azure_ai_tools(tools: Sequence[Tool | dict[str, Any]] | None) -> list[T
|
||||
if "never" in require_approval:
|
||||
approval_mode["never_require_approval"] = set(require_approval["never"].get("tool_names", [])) # type: ignore
|
||||
|
||||
# Preserve project_connection_id in additional_properties
|
||||
additional_props: dict[str, Any] | None = None
|
||||
if project_connection_id := mcp_tool.get("project_connection_id"):
|
||||
additional_props = {"connection": {"name": project_connection_id}}
|
||||
|
||||
agent_tools.append(
|
||||
HostedMCPTool(
|
||||
name=mcp_tool.get("server_label", "").replace("_", " "),
|
||||
@@ -330,6 +366,7 @@ def from_azure_ai_tools(tools: Sequence[Tool | dict[str, Any]] | None) -> list[T
|
||||
headers=mcp_tool.get("headers"),
|
||||
allowed_tools=mcp_tool.get("allowed_tools"),
|
||||
approval_mode=approval_mode, # type: ignore
|
||||
additional_properties=additional_props,
|
||||
)
|
||||
)
|
||||
elif tool_type == "code_interpreter":
|
||||
@@ -466,7 +503,13 @@ def _prepare_mcp_tool_for_azure_ai(tool: HostedMCPTool) -> MCPTool:
|
||||
if tool.description:
|
||||
mcp["server_description"] = tool.description
|
||||
|
||||
if tool.headers:
|
||||
# Check for project_connection_id in additional_properties (for Azure AI Foundry connections)
|
||||
project_connection_id = _extract_project_connection_id(tool.additional_properties)
|
||||
if project_connection_id:
|
||||
mcp["project_connection_id"] = project_connection_id
|
||||
elif tool.headers:
|
||||
# Only use headers if no project_connection_id is available
|
||||
# Note: Azure AI Agent Service may reject headers with sensitive info
|
||||
mcp["headers"] = tool.headers
|
||||
|
||||
if tool.allowed_tools:
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from collections.abc import Callable, Mapping
|
||||
from pathlib import Path
|
||||
from typing import Any, Literal, TypedDict
|
||||
from typing import Any, Literal, TypedDict, cast
|
||||
|
||||
import yaml
|
||||
from agent_framework import (
|
||||
@@ -89,6 +89,11 @@ PROVIDER_TYPE_OBJECT_MAPPING: dict[str, ProviderTypeMapping] = {
|
||||
"name": "AzureAIClient",
|
||||
"model_id_field": "model_deployment_name",
|
||||
},
|
||||
"AzureAI.ProjectProvider": {
|
||||
"package": "agent_framework.azure",
|
||||
"name": "AzureAIProjectAgentProvider",
|
||||
"model_id_field": "model",
|
||||
},
|
||||
"Anthropic.Chat": {
|
||||
"package": "agent_framework.anthropic",
|
||||
"name": "AnthropicChatClient",
|
||||
@@ -448,6 +453,175 @@ class AgentFactory:
|
||||
**chat_options,
|
||||
)
|
||||
|
||||
async def create_agent_from_yaml_path_async(self, yaml_path: str | Path) -> ChatAgent:
|
||||
"""Async version: Create a ChatAgent from a YAML file path.
|
||||
|
||||
Use this method when the provider requires async initialization, such as
|
||||
AzureAI.ProjectProvider which creates agents on the Azure AI Agent Service.
|
||||
|
||||
Args:
|
||||
yaml_path: Path to the YAML file representation of a PromptAgent.
|
||||
|
||||
Returns:
|
||||
The ``ChatAgent`` instance created from the YAML file.
|
||||
|
||||
Examples:
|
||||
.. code-block:: python
|
||||
|
||||
from agent_framework_declarative import AgentFactory
|
||||
|
||||
factory = AgentFactory(
|
||||
client_kwargs={"credential": credential},
|
||||
default_provider="AzureAI.ProjectProvider",
|
||||
)
|
||||
agent = await factory.create_agent_from_yaml_path_async("agent.yaml")
|
||||
"""
|
||||
if not isinstance(yaml_path, Path):
|
||||
yaml_path = Path(yaml_path)
|
||||
if not yaml_path.exists():
|
||||
raise DeclarativeLoaderError(f"YAML file not found at path: {yaml_path}")
|
||||
yaml_str = yaml_path.read_text()
|
||||
return await self.create_agent_from_yaml_async(yaml_str)
|
||||
|
||||
async def create_agent_from_yaml_async(self, yaml_str: str) -> ChatAgent:
|
||||
"""Async version: Create a ChatAgent from a YAML string.
|
||||
|
||||
Use this method when the provider requires async initialization, such as
|
||||
AzureAI.ProjectProvider which creates agents on the Azure AI Agent Service.
|
||||
|
||||
Args:
|
||||
yaml_str: YAML string representation of a PromptAgent.
|
||||
|
||||
Returns:
|
||||
The ``ChatAgent`` instance created from the YAML string.
|
||||
|
||||
Examples:
|
||||
.. code-block:: python
|
||||
|
||||
from agent_framework_declarative import AgentFactory
|
||||
|
||||
yaml_content = '''
|
||||
kind: Prompt
|
||||
name: MyAgent
|
||||
instructions: You are a helpful assistant.
|
||||
model:
|
||||
id: gpt-4o
|
||||
provider: AzureAI.ProjectProvider
|
||||
'''
|
||||
|
||||
factory = AgentFactory(client_kwargs={"credential": credential})
|
||||
agent = await factory.create_agent_from_yaml_async(yaml_content)
|
||||
"""
|
||||
return await self.create_agent_from_dict_async(yaml.safe_load(yaml_str))
|
||||
|
||||
async def create_agent_from_dict_async(self, agent_def: dict[str, Any]) -> ChatAgent:
|
||||
"""Async version: Create a ChatAgent from a dictionary definition.
|
||||
|
||||
Use this method when the provider requires async initialization, such as
|
||||
AzureAI.ProjectProvider which creates agents on the Azure AI Agent Service.
|
||||
|
||||
Args:
|
||||
agent_def: Dictionary representation of a PromptAgent.
|
||||
|
||||
Returns:
|
||||
The ``ChatAgent`` instance created from the dictionary.
|
||||
|
||||
Examples:
|
||||
.. code-block:: python
|
||||
|
||||
from agent_framework_declarative import AgentFactory
|
||||
|
||||
agent_def = {
|
||||
"kind": "Prompt",
|
||||
"name": "MyAgent",
|
||||
"instructions": "You are a helpful assistant.",
|
||||
"model": {
|
||||
"id": "gpt-4o",
|
||||
"provider": "AzureAI.ProjectProvider",
|
||||
},
|
||||
}
|
||||
|
||||
factory = AgentFactory(client_kwargs={"credential": credential})
|
||||
agent = await factory.create_agent_from_dict_async(agent_def)
|
||||
"""
|
||||
# Set safe_mode context before parsing YAML to control PowerFx environment variable access
|
||||
_safe_mode_context.set(self.safe_mode)
|
||||
prompt_agent = agent_schema_dispatch(agent_def)
|
||||
if not isinstance(prompt_agent, PromptAgent):
|
||||
raise DeclarativeLoaderError("Only definitions for a PromptAgent are supported for agent creation.")
|
||||
|
||||
# Check if we're using a provider-based approach (like AzureAIProjectAgentProvider)
|
||||
mapping = self._retrieve_provider_configuration(prompt_agent.model) if prompt_agent.model else None
|
||||
if mapping and mapping["name"] == "AzureAIProjectAgentProvider":
|
||||
return await self._create_agent_with_provider(prompt_agent, mapping)
|
||||
|
||||
# Fall back to standard ChatClient approach
|
||||
client = self._get_client(prompt_agent)
|
||||
chat_options = self._parse_chat_options(prompt_agent.model)
|
||||
if tools := self._parse_tools(prompt_agent.tools):
|
||||
chat_options["tools"] = tools
|
||||
if output_schema := prompt_agent.outputSchema:
|
||||
chat_options["response_format"] = _create_model_from_json_schema("agent", output_schema.to_json_schema())
|
||||
return ChatAgent(
|
||||
chat_client=client,
|
||||
name=prompt_agent.name,
|
||||
description=prompt_agent.description,
|
||||
instructions=prompt_agent.instructions,
|
||||
**chat_options,
|
||||
)
|
||||
|
||||
async def _create_agent_with_provider(self, prompt_agent: PromptAgent, mapping: ProviderTypeMapping) -> ChatAgent:
|
||||
"""Create a ChatAgent using AzureAIProjectAgentProvider.
|
||||
|
||||
This method handles the special case where we use a provider that creates
|
||||
agents on a remote service (like Azure AI Agent Service) and returns
|
||||
ChatAgent instances directly.
|
||||
"""
|
||||
# Import the provider class
|
||||
module_name = mapping["package"]
|
||||
class_name = mapping["name"]
|
||||
module = __import__(module_name, fromlist=[class_name])
|
||||
provider_class = getattr(module, class_name)
|
||||
|
||||
# Build provider kwargs from client_kwargs and connection info
|
||||
provider_kwargs: dict[str, Any] = {}
|
||||
provider_kwargs.update(self.client_kwargs)
|
||||
|
||||
# Handle connection settings for the model
|
||||
if prompt_agent.model and prompt_agent.model.connection:
|
||||
match prompt_agent.model.connection:
|
||||
case RemoteConnection() | AnonymousConnection():
|
||||
if prompt_agent.model.connection.endpoint:
|
||||
provider_kwargs["project_endpoint"] = prompt_agent.model.connection.endpoint
|
||||
case ApiKeyConnection():
|
||||
if prompt_agent.model.connection.endpoint:
|
||||
provider_kwargs["project_endpoint"] = prompt_agent.model.connection.endpoint
|
||||
|
||||
# Create the provider and use it to create the agent
|
||||
provider = provider_class(**provider_kwargs)
|
||||
|
||||
# Parse tools
|
||||
tools = self._parse_tools(prompt_agent.tools) if prompt_agent.tools else None
|
||||
|
||||
# Parse response format
|
||||
response_format = None
|
||||
if prompt_agent.outputSchema:
|
||||
response_format = _create_model_from_json_schema("agent", prompt_agent.outputSchema.to_json_schema())
|
||||
|
||||
# Create the agent using the provider
|
||||
# The provider's create_agent returns a ChatAgent directly
|
||||
return cast(
|
||||
ChatAgent,
|
||||
await provider.create_agent(
|
||||
name=prompt_agent.name,
|
||||
model=prompt_agent.model.id if prompt_agent.model else None,
|
||||
instructions=prompt_agent.instructions,
|
||||
description=prompt_agent.description,
|
||||
tools=tools,
|
||||
response_format=response_format,
|
||||
),
|
||||
)
|
||||
|
||||
def _get_client(self, prompt_agent: PromptAgent) -> ChatClientProtocol:
|
||||
"""Create the ChatClientProtocol instance based on the PromptAgent model."""
|
||||
if not prompt_agent.model:
|
||||
@@ -594,12 +768,46 @@ class AgentFactory:
|
||||
)
|
||||
if not approval_mode:
|
||||
approval_mode = None
|
||||
|
||||
# Handle connection settings
|
||||
headers: dict[str, str] | None = None
|
||||
additional_properties: dict[str, Any] | None = None
|
||||
|
||||
if tool_resource.connection is not None:
|
||||
match tool_resource.connection:
|
||||
case ApiKeyConnection():
|
||||
if tool_resource.connection.apiKey:
|
||||
headers = {"Authorization": f"Bearer {tool_resource.connection.apiKey}"}
|
||||
case RemoteConnection():
|
||||
additional_properties = {
|
||||
"connection": {
|
||||
"kind": tool_resource.connection.kind,
|
||||
"name": tool_resource.connection.name,
|
||||
"authenticationMode": tool_resource.connection.authenticationMode,
|
||||
"endpoint": tool_resource.connection.endpoint,
|
||||
}
|
||||
}
|
||||
case ReferenceConnection():
|
||||
additional_properties = {
|
||||
"connection": {
|
||||
"kind": tool_resource.connection.kind,
|
||||
"name": tool_resource.connection.name,
|
||||
"authenticationMode": tool_resource.connection.authenticationMode,
|
||||
}
|
||||
}
|
||||
case AnonymousConnection():
|
||||
pass
|
||||
case _:
|
||||
raise ValueError(f"Unsupported connection kind: {tool_resource.connection.kind}")
|
||||
|
||||
return HostedMCPTool(
|
||||
name=tool_resource.name, # type: ignore
|
||||
description=tool_resource.description,
|
||||
url=tool_resource.url, # type: ignore
|
||||
allowed_tools=tool_resource.allowedTools,
|
||||
approval_mode=approval_mode,
|
||||
headers=headers,
|
||||
additional_properties=additional_properties,
|
||||
)
|
||||
case _:
|
||||
raise ValueError(f"Unsupported tool kind: {tool_resource.kind}")
|
||||
|
||||
@@ -692,3 +692,230 @@ model:
|
||||
assert result.model.connection.apiKey == "secret-key-123"
|
||||
finally:
|
||||
_safe_mode_context.reset(token)
|
||||
|
||||
|
||||
class TestAgentFactoryMcpToolConnection:
|
||||
"""Tests for MCP tool connection handling in AgentFactory._parse_tool."""
|
||||
|
||||
def _get_mcp_tools(self, agent):
|
||||
"""Helper to get MCP tools from agent's default_options."""
|
||||
from agent_framework import HostedMCPTool
|
||||
|
||||
tools = agent.default_options.get("tools", [])
|
||||
return [t for t in tools if isinstance(t, HostedMCPTool)]
|
||||
|
||||
def test_mcp_tool_with_api_key_connection_sets_headers(self):
|
||||
"""Test that MCP tool with ApiKeyConnection sets headers correctly."""
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from agent_framework_declarative import AgentFactory
|
||||
|
||||
yaml_content = """
|
||||
kind: Prompt
|
||||
name: TestAgent
|
||||
instructions: Test agent
|
||||
tools:
|
||||
- kind: mcp
|
||||
name: my-mcp-tool
|
||||
url: https://api.example.com/mcp
|
||||
connection:
|
||||
kind: key
|
||||
apiKey: my-secret-api-key
|
||||
"""
|
||||
|
||||
mock_client = MagicMock()
|
||||
mock_client.create_agent.return_value = MagicMock()
|
||||
|
||||
factory = AgentFactory(chat_client=mock_client)
|
||||
agent = factory.create_agent_from_yaml(yaml_content)
|
||||
|
||||
# Find the MCP tool in the agent's tools
|
||||
mcp_tools = self._get_mcp_tools(agent)
|
||||
assert len(mcp_tools) == 1
|
||||
mcp_tool = mcp_tools[0]
|
||||
|
||||
# Verify headers are set with the API key
|
||||
assert mcp_tool.headers is not None
|
||||
assert mcp_tool.headers == {"Authorization": "Bearer my-secret-api-key"}
|
||||
|
||||
def test_mcp_tool_with_remote_connection_sets_additional_properties(self):
|
||||
"""Test that MCP tool with RemoteConnection sets additional_properties correctly."""
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from agent_framework_declarative import AgentFactory
|
||||
|
||||
yaml_content = """
|
||||
kind: Prompt
|
||||
name: TestAgent
|
||||
instructions: Test agent
|
||||
tools:
|
||||
- kind: mcp
|
||||
name: github-mcp
|
||||
url: https://api.githubcopilot.com/mcp
|
||||
connection:
|
||||
kind: remote
|
||||
authenticationMode: oauth
|
||||
name: github-mcp-oauth-connection
|
||||
"""
|
||||
|
||||
mock_client = MagicMock()
|
||||
mock_client.create_agent.return_value = MagicMock()
|
||||
|
||||
factory = AgentFactory(chat_client=mock_client)
|
||||
agent = factory.create_agent_from_yaml(yaml_content)
|
||||
|
||||
# Find the MCP tool in the agent's tools
|
||||
mcp_tools = self._get_mcp_tools(agent)
|
||||
assert len(mcp_tools) == 1
|
||||
mcp_tool = mcp_tools[0]
|
||||
|
||||
# Verify additional_properties are set with connection info
|
||||
assert mcp_tool.additional_properties is not None
|
||||
assert "connection" in mcp_tool.additional_properties
|
||||
conn = mcp_tool.additional_properties["connection"]
|
||||
assert conn["kind"] == "remote"
|
||||
assert conn["authenticationMode"] == "oauth"
|
||||
assert conn["name"] == "github-mcp-oauth-connection"
|
||||
|
||||
def test_mcp_tool_with_reference_connection_sets_additional_properties(self):
|
||||
"""Test that MCP tool with ReferenceConnection sets additional_properties correctly."""
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from agent_framework_declarative import AgentFactory
|
||||
|
||||
yaml_content = """
|
||||
kind: Prompt
|
||||
name: TestAgent
|
||||
instructions: Test agent
|
||||
tools:
|
||||
- kind: mcp
|
||||
name: ref-mcp-tool
|
||||
url: https://api.example.com/mcp
|
||||
connection:
|
||||
kind: reference
|
||||
name: my-connection-ref
|
||||
target: /connections/my-connection
|
||||
"""
|
||||
|
||||
mock_client = MagicMock()
|
||||
mock_client.create_agent.return_value = MagicMock()
|
||||
|
||||
factory = AgentFactory(chat_client=mock_client)
|
||||
agent = factory.create_agent_from_yaml(yaml_content)
|
||||
|
||||
# Find the MCP tool in the agent's tools
|
||||
mcp_tools = self._get_mcp_tools(agent)
|
||||
assert len(mcp_tools) == 1
|
||||
mcp_tool = mcp_tools[0]
|
||||
|
||||
# Verify additional_properties are set with connection info
|
||||
assert mcp_tool.additional_properties is not None
|
||||
assert "connection" in mcp_tool.additional_properties
|
||||
conn = mcp_tool.additional_properties["connection"]
|
||||
assert conn["kind"] == "reference"
|
||||
assert conn["name"] == "my-connection-ref"
|
||||
|
||||
def test_mcp_tool_with_anonymous_connection_no_headers_or_properties(self):
|
||||
"""Test that MCP tool with AnonymousConnection doesn't set headers or additional_properties."""
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from agent_framework_declarative import AgentFactory
|
||||
|
||||
yaml_content = """
|
||||
kind: Prompt
|
||||
name: TestAgent
|
||||
instructions: Test agent
|
||||
tools:
|
||||
- kind: mcp
|
||||
name: anon-mcp-tool
|
||||
url: https://api.example.com/mcp
|
||||
connection:
|
||||
kind: anonymous
|
||||
"""
|
||||
|
||||
mock_client = MagicMock()
|
||||
mock_client.create_agent.return_value = MagicMock()
|
||||
|
||||
factory = AgentFactory(chat_client=mock_client)
|
||||
agent = factory.create_agent_from_yaml(yaml_content)
|
||||
|
||||
# Find the MCP tool in the agent's tools
|
||||
mcp_tools = self._get_mcp_tools(agent)
|
||||
assert len(mcp_tools) == 1
|
||||
mcp_tool = mcp_tools[0]
|
||||
|
||||
# Verify no headers or additional_properties are set
|
||||
assert mcp_tool.headers is None
|
||||
assert mcp_tool.additional_properties is None
|
||||
|
||||
def test_mcp_tool_without_connection_preserves_existing_behavior(self):
|
||||
"""Test that MCP tool without connection works as before (no headers or additional_properties)."""
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from agent_framework_declarative import AgentFactory
|
||||
|
||||
yaml_content = """
|
||||
kind: Prompt
|
||||
name: TestAgent
|
||||
instructions: Test agent
|
||||
tools:
|
||||
- kind: mcp
|
||||
name: simple-mcp-tool
|
||||
url: https://api.example.com/mcp
|
||||
approvalMode: never
|
||||
"""
|
||||
|
||||
mock_client = MagicMock()
|
||||
mock_client.create_agent.return_value = MagicMock()
|
||||
|
||||
factory = AgentFactory(chat_client=mock_client)
|
||||
agent = factory.create_agent_from_yaml(yaml_content)
|
||||
|
||||
# Find the MCP tool in the agent's tools
|
||||
mcp_tools = self._get_mcp_tools(agent)
|
||||
assert len(mcp_tools) == 1
|
||||
mcp_tool = mcp_tools[0]
|
||||
|
||||
# Verify tool is created correctly without connection
|
||||
assert mcp_tool.name == "simple-mcp-tool"
|
||||
assert str(mcp_tool.url) == "https://api.example.com/mcp"
|
||||
assert mcp_tool.approval_mode == "never_require"
|
||||
assert mcp_tool.headers is None
|
||||
assert mcp_tool.additional_properties is None
|
||||
|
||||
def test_mcp_tool_with_remote_connection_with_endpoint(self):
|
||||
"""Test that MCP tool with RemoteConnection including endpoint sets it in additional_properties."""
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from agent_framework_declarative import AgentFactory
|
||||
|
||||
yaml_content = """
|
||||
kind: Prompt
|
||||
name: TestAgent
|
||||
instructions: Test agent
|
||||
tools:
|
||||
- kind: mcp
|
||||
name: endpoint-mcp-tool
|
||||
url: https://api.example.com/mcp
|
||||
connection:
|
||||
kind: remote
|
||||
authenticationMode: oauth
|
||||
name: my-oauth-connection
|
||||
endpoint: https://auth.example.com
|
||||
"""
|
||||
|
||||
mock_client = MagicMock()
|
||||
mock_client.create_agent.return_value = MagicMock()
|
||||
|
||||
factory = AgentFactory(chat_client=mock_client)
|
||||
agent = factory.create_agent_from_yaml(yaml_content)
|
||||
|
||||
# Find the MCP tool in the agent's tools
|
||||
mcp_tools = self._get_mcp_tools(agent)
|
||||
assert len(mcp_tools) == 1
|
||||
mcp_tool = mcp_tools[0]
|
||||
|
||||
# Verify additional_properties include endpoint
|
||||
assert mcp_tool.additional_properties is not None
|
||||
conn = mcp_tool.additional_properties["connection"]
|
||||
assert conn["endpoint"] == "https://auth.example.com"
|
||||
|
||||
Reference in New Issue
Block a user