diff --git a/python/packages/foundry/tests/test_foundry_chat_client.py b/python/packages/foundry/tests/test_foundry_chat_client.py index d2bd6e6c3c..5696af9d81 100644 --- a/python/packages/foundry/tests/test_foundry_chat_client.py +++ b/python/packages/foundry/tests/test_foundry_chat_client.py @@ -18,6 +18,7 @@ from agent_framework import ( FunctionCallContent, FunctionResultContent, HostedCodeInterpreterTool, + MCPStreamableHTTPTool, Role, TextContent, UriContent, @@ -880,6 +881,27 @@ async def test_foundry_chat_client_agent_code_interpreter(): assert "120" in response.text or "factorial" in response.text.lower() +@skip_if_foundry_integration_tests_disabled +async def test_foundry_chat_client_agent_with_mcp_tools() -> None: + """Test MCP tools defined at agent creation with FoundryChatClient.""" + async with ChatAgent( + chat_client=FoundryChatClient(async_credential=AzureCliCredential()), + name="DocsAgent", + instructions="You are a helpful assistant that can help with microsoft documentation questions.", + tools=MCPStreamableHTTPTool( + name="Microsoft Learn MCP", + url="https://learn.microsoft.com/api/mcp", + ), + ) as agent: + # Test that the agent can use MCP tools to answer questions + response = await agent.run("What is Azure App Service?") + + assert isinstance(response, AgentRunResponse) + assert response.text is not None + # Verify the response contains relevant information about Azure App Service + assert any(term in response.text.lower() for term in ["app service", "azure", "web", "application"]) + + @skip_if_foundry_integration_tests_disabled async def test_foundry_chat_client_agent_level_tool_persistence(): """Test that agent-level tools persist across multiple runs with FoundryChatClient.""" diff --git a/python/samples/getting_started/agents/foundry/README.md b/python/samples/getting_started/agents/foundry/README.md index 2a2981f2b8..39d8c9aa6e 100644 --- a/python/samples/getting_started/agents/foundry/README.md +++ b/python/samples/getting_started/agents/foundry/README.md @@ -11,6 +11,7 @@ This folder contains examples demonstrating different ways to create and use age | [`foundry_with_existing_agent.py`](foundry_with_existing_agent.py) | Shows how to work with a pre-existing agent by providing the agent ID to the Foundry chat client. This example also demonstrates proper cleanup of manually created agents. | | [`foundry_with_function_tools.py`](foundry_with_function_tools.py) | Demonstrates how to use function tools with agents. Shows both agent-level tools (defined when creating the agent) and query-level tools (provided with specific queries). | | [`foundry_with_code_interpreter.py`](foundry_with_code_interpreter.py) | Shows how to use the HostedCodeInterpreterTool with Foundry agents to write and execute Python code. Includes helper methods for accessing code interpreter data from response chunks. | +| [`foundry_with_local_mcp.py`](foundry_with_local_mcp.py) | Shows how to integrate Foundry agents with Model Context Protocol (MCP) servers for enhanced functionality and tool integration. Demonstrates both agent-level and run-level tool configuration. | | [`foundry_with_thread.py`](foundry_with_thread.py) | Demonstrates thread management with Foundry agents, including automatic thread creation for stateless conversations and explicit thread management for maintaining conversation context across multiple interactions. | ## Environment Variables diff --git a/python/samples/getting_started/agents/foundry/foundry_with_local_mcp.py b/python/samples/getting_started/agents/foundry/foundry_with_local_mcp.py new file mode 100644 index 0000000000..401a62f2a7 --- /dev/null +++ b/python/samples/getting_started/agents/foundry/foundry_with_local_mcp.py @@ -0,0 +1,81 @@ +# Copyright (c) Microsoft. All rights reserved. + +import asyncio + +from agent_framework import ChatAgent, MCPStreamableHTTPTool +from agent_framework.foundry import FoundryChatClient +from azure.identity.aio import AzureCliCredential + + +async def mcp_tools_on_run_level() -> None: + """Example showing MCP tools defined when running the agent.""" + print("=== Tools Defined on Run Level ===") + + # Tools are provided when running the agent + # This means we have to ensure we connect to the MCP server before running the agent + # and pass the tools to the run method. + async with ( + AzureCliCredential() as credential, + MCPStreamableHTTPTool( + name="Microsoft Learn MCP", + url="https://learn.microsoft.com/api/mcp", + ) as mcp_server, + ChatAgent( + chat_client=FoundryChatClient(async_credential=credential), + name="DocsAgent", + instructions="You are a helpful assistant that can help with microsoft documentation questions.", + ) as agent, + ): + # First query + query1 = "How to create an Azure storage account using az cli?" + print(f"User: {query1}") + result1 = await agent.run(query1, tools=mcp_server) + print(f"{agent.name}: {result1}\n") + print("\n=======================================\n") + # Second query + query2 = "What is Microsoft Semantic Kernel?" + print(f"User: {query2}") + result2 = await agent.run(query2, tools=mcp_server) + print(f"{agent.name}: {result2}\n") + + +async def mcp_tools_on_agent_level() -> None: + """Example showing tools defined when creating the agent.""" + print("=== Tools Defined on Agent Level ===") + + # Tools are provided when creating the agent + # The agent can use these tools for any query during its lifetime + # The agent will connect to the MCP server through its context manager. + async with ( + AzureCliCredential() as credential, + FoundryChatClient(async_credential=credential).create_agent( + name="DocsAgent", + instructions="You are a helpful assistant that can help with microsoft documentation questions.", + tools=MCPStreamableHTTPTool( # Tools defined at agent creation + name="Microsoft Learn MCP", + url="https://learn.microsoft.com/api/mcp", + ), + ) as agent, + ): + # First query + query1 = "How to create an Azure storage account using az cli?" + print(f"User: {query1}") + result1 = await agent.run(query1) + print(f"{agent.name}: {result1}\n") + print("\n=======================================\n") + # Second query + query2 = "What is Microsoft Semantic Kernel?" + print(f"User: {query2}") + result2 = await agent.run(query2) + print(f"{agent.name}: {result2}\n") + + +async def main() -> None: + print("=== Foundry Chat Client Agent with MCP Tools Examples ===\n") + + await mcp_tools_on_agent_level() + await mcp_tools_on_run_level() + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/python/tests/samples/getting_started/test_agents.py b/python/tests/samples/getting_started/test_agents.py index 5e04367061..c4a94f10ff 100644 --- a/python/tests/samples/getting_started/test_agents.py +++ b/python/tests/samples/getting_started/test_agents.py @@ -68,6 +68,9 @@ from samples.getting_started.agents.foundry.foundry_with_explicit_settings impor from samples.getting_started.agents.foundry.foundry_with_function_tools import ( main as foundry_with_function_tools, ) +from samples.getting_started.agents.foundry.foundry_with_local_mcp import ( + main as foundry_with_local_mcp, +) from samples.getting_started.agents.foundry.foundry_with_thread import ( main as foundry_with_thread, ) @@ -337,6 +340,15 @@ agent_samples = [ pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), ], ), + param( + foundry_with_local_mcp, + [], # Non-interactive sample + id="foundry_with_local_mcp", + marks=[ + pytest.mark.foundry, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), # OpenAI Assistants Agent samples param( openai_assistants_basic, diff --git a/user-documentation-python/user-guide/agent-types.md b/user-documentation-python/user-guide/agent-types.md index 55dfc0637d..7364795c23 100644 --- a/user-documentation-python/user-guide/agent-types.md +++ b/user-documentation-python/user-guide/agent-types.md @@ -120,6 +120,53 @@ async with ( For code interpreter examples, see: - [Foundry with code interpreter](../../../python/samples/getting_started/agents/foundry/foundry_with_code_interpreter.py) +### Model Context Protocol (MCP) Tools + +Foundry agents support Model Context Protocol (MCP) tools for connecting to external services and data sources. + +Learn more about MCP tools in the [Azure AI Foundry documentation](https://learn.microsoft.com/en-us/azure/ai-foundry/agents/how-to/tools/model-context-protocol). + +```python +from agent_framework import ChatAgent, MCPStreamableHTTPTool +from agent_framework.foundry import FoundryChatClient +from azure.identity.aio import AzureCliCredential + +# Tools can be defined at agent creation +async with ( + AzureCliCredential() as credential, + FoundryChatClient(async_credential=credential).create_agent( + name="DocsAgent", + instructions="You are a helpful assistant that can help with microsoft documentation questions.", + tools=MCPStreamableHTTPTool( + name="Microsoft Learn MCP", + url="https://learn.microsoft.com/api/mcp", + ), + ) as agent +): + response = await agent.run("How to create an Azure storage account using az cli?") +``` + +You can also provide MCP tools when running the agent: + +```python +async with ( + AzureCliCredential() as credential, + MCPStreamableHTTPTool( + name="Microsoft Learn MCP", + url="https://learn.microsoft.com/api/mcp", + ) as mcp_server, + ChatAgent( + chat_client=FoundryChatClient(async_credential=credential), + name="DocsAgent", + instructions="You are a helpful assistant that can help with microsoft documentation questions.", + ) as agent, +): + response = await agent.run("What is Microsoft Semantic Kernel?", tools=mcp_server) +``` + +For complete MCP examples, see: +- [Foundry with MCP tools](../../../python/samples/getting_started/agents/foundry/foundry_with_local_mcp.py) + ## Custom agents It is also possible to create fully custom agents that are not just wrappers around a chat client.