493 lines
15 KiB
Plaintext
493 lines
15 KiB
Plaintext
{
|
||
"cells": [
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "deb94027",
|
||
"metadata": {},
|
||
"source": [
|
||
"# Azure AI Agents with Model Context Protocol (MCP) Support\n",
|
||
"\n",
|
||
"This notebook demonstrates how to use Azure AI Agents with Model Context Protocol (MCP) tools to create an intelligent agent that can leverage external MCP servers for enhanced capabilities."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "c65b1772",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Install Required NuGet Packages\n",
|
||
"\n",
|
||
"First, we need to install the Azure AI Agents Persistent package which provides the core functionality for working with Azure AI Agents."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "000d6659",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Keyless Authentication Benefits\n",
|
||
"\n",
|
||
"This notebook demonstrates **keyless authentication** which provides several advantages:\n",
|
||
"- ✅ **No API keys to manage** - Uses Azure identity-based authentication\n",
|
||
"- ✅ **Enhanced security** - No secrets stored in code or configuration\n",
|
||
"- ✅ **Automatic credential rotation** - Azure handles credential lifecycle\n",
|
||
"- ✅ **Role-based access** - Uses Azure RBAC for fine-grained permissions\n",
|
||
"\n",
|
||
"The `DefaultAzureCredential` will automatically use the best available credential source:\n",
|
||
"1. Managed Identity (when running in Azure)\n",
|
||
"2. Azure CLI credentials (during development)\n",
|
||
"3. Visual Studio credentials\n",
|
||
"4. Environment variables (if configured)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 1,
|
||
"id": "ba8d7dfe",
|
||
"metadata": {
|
||
"language_info": {
|
||
"name": "polyglot-notebook"
|
||
},
|
||
"polyglot_notebook": {
|
||
"kernelName": "csharp"
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"text/html": [
|
||
"<div><div></div><div></div><div><strong>Installed Packages</strong><ul><li><span>Azure.AI.Agents.Persistent, 1.1.0-beta.4</span></li></ul></div></div>"
|
||
]
|
||
},
|
||
"metadata": {},
|
||
"output_type": "display_data"
|
||
}
|
||
],
|
||
"source": [
|
||
"#r \"nuget: Azure.AI.Agents.Persistent, 1.1.0-beta.4\""
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "832540a4",
|
||
"metadata": {},
|
||
"source": [
|
||
"Install the Azure Identity package for authentication with Azure services using DefaultAzureCredential."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 2,
|
||
"id": "836f34ef",
|
||
"metadata": {
|
||
"language_info": {
|
||
"name": "polyglot-notebook"
|
||
},
|
||
"polyglot_notebook": {
|
||
"kernelName": "csharp"
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"text/html": [
|
||
"<div><div></div><div></div><div><strong>Installed Packages</strong><ul><li><span>Azure.Identity, 1.14.2</span></li></ul></div></div>"
|
||
]
|
||
},
|
||
"metadata": {},
|
||
"output_type": "display_data"
|
||
}
|
||
],
|
||
"source": [
|
||
"#r \"nuget: Azure.Identity, 1.14.2\""
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "c871f7c3",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Import Required Namespaces\n",
|
||
"\n",
|
||
"Import the necessary namespaces for Azure AI Agents and Azure Identity."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 3,
|
||
"id": "6f4467f6",
|
||
"metadata": {
|
||
"language_info": {
|
||
"name": "polyglot-notebook"
|
||
},
|
||
"polyglot_notebook": {
|
||
"kernelName": "csharp"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"using Azure.AI.Agents.Persistent;\n",
|
||
"using Azure.Identity;"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "531fd5c4",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Configure Azure AI Agent Client (Keyless Authentication)\n",
|
||
"\n",
|
||
"Set up the configuration variables and create the PersistentAgentsClient using **keyless authentication**:\n",
|
||
"- **projectEndpoint**: The Azure AI Foundry project endpoint\n",
|
||
"- **modelDeploymentName**: The name of the deployed AI model (GPT-4.1 nano)\n",
|
||
"- **mcpServerUrl**: The URL of the MCP server (Microsoft Learn API)\n",
|
||
"- **mcpServerLabel**: A label to identify the MCP server\n",
|
||
"- **DefaultAzureCredential**: Uses managed identity, Azure CLI, or other credential sources (no API keys required)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "6aee3e93",
|
||
"metadata": {
|
||
"language_info": {
|
||
"name": "polyglot-notebook"
|
||
},
|
||
"polyglot_notebook": {
|
||
"kernelName": "csharp"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"var projectEndpoint = \"Your Azure AI Foundry Project Endpoint\";\n",
|
||
"var modelDeploymentName = \"Your Azure OpenAI Model Deployment Name\";\n",
|
||
"var mcpServerUrl = \"https://learn.microsoft.com/api/mcp\";\n",
|
||
"var mcpServerLabel = \"mslearn\";\n",
|
||
"PersistentAgentsClient agentClient = new(projectEndpoint, new DefaultAzureCredential());"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "3fcd686c",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Create MCP Tool Definition\n",
|
||
"\n",
|
||
"Create an MCP tool definition that connects to the Microsoft Learn MCP server. This will allow the agent to access Microsoft Learn content and documentation."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "678a4a25",
|
||
"metadata": {
|
||
"language_info": {
|
||
"name": "polyglot-notebook"
|
||
},
|
||
"polyglot_notebook": {
|
||
"kernelName": "csharp"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"MCPToolDefinition mcpTool = new(mcpServerLabel, mcpServerUrl);"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "5304cefe",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Create the AI Agent\n",
|
||
"\n",
|
||
"Create a persistent AI agent with the specified model and MCP tools. The agent is configured with:\n",
|
||
"- The GPT-4.1 nano model\n",
|
||
"- Instructions to use MCP tools for assistance\n",
|
||
"- Access to the Microsoft Learn MCP server"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "cf7cb7d0",
|
||
"metadata": {
|
||
"language_info": {
|
||
"name": "polyglot-notebook"
|
||
},
|
||
"polyglot_notebook": {
|
||
"kernelName": "csharp"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"PersistentAgent agent = await agentClient.Administration.CreateAgentAsync(\n",
|
||
" model: modelDeploymentName,\n",
|
||
" name: \"my-learn-agent\",\n",
|
||
" instructions: \"You are a helpful agent that can use MCP tools to assist users. Use the available MCP tools to answer questions and perform tasks.\",\n",
|
||
" tools: [mcpTool]\n",
|
||
" );"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "8e82cb78",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Create Thread and Send Message\n",
|
||
"\n",
|
||
"Create a conversation thread and send a user message asking about the difference between Azure OpenAI and OpenAI. This will test the agent's ability to use the MCP tools to provide accurate information."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 7,
|
||
"id": "c3e5fe54",
|
||
"metadata": {
|
||
"language_info": {
|
||
"name": "polyglot-notebook"
|
||
},
|
||
"polyglot_notebook": {
|
||
"kernelName": "csharp"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"PersistentAgentThread thread = await agentClient.Threads.CreateThreadAsync();\n",
|
||
"\n",
|
||
"// Create message to thread\n",
|
||
"PersistentThreadMessage message = await agentClient.Messages.CreateMessageAsync(\n",
|
||
" thread.Id,\n",
|
||
" MessageRole.User,\n",
|
||
" \"What's difference between Azure OpenAI and OpenAI?\");"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "c6caf33e",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Configure MCP Tool Resources (Keyless)\n",
|
||
"\n",
|
||
"Set up the MCP tool resources. For a truly keyless approach, you can remove custom headers if the MCP server supports Azure identity-based authentication. The example below shows how to add headers if needed, but for keyless scenarios, these may not be required."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "b3dbe829",
|
||
"metadata": {
|
||
"language_info": {
|
||
"name": "polyglot-notebook"
|
||
},
|
||
"polyglot_notebook": {
|
||
"kernelName": "csharp"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"// Option 1: Completely keyless (if MCP server supports Azure identity)\n",
|
||
"MCPToolResource mcpToolResource = new(mcpServerLabel);\n",
|
||
"ToolResources toolResources = mcpToolResource.ToToolResources();\n",
|
||
"\n",
|
||
"// Option 2: With custom headers (if still needed for specific MCP servers)\n",
|
||
"// MCPToolResource mcpToolResource = new(mcpServerLabel);\n",
|
||
"// mcpToolResource.UpdateHeader(\"Authorization\", \"Bearer <your-token>\");\n",
|
||
"// ToolResources toolResources = mcpToolResource.ToToolResources();"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "56469935",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Start Agent Run\n",
|
||
"\n",
|
||
"Create and start a run to process the user's message. The agent will use the configured MCP tools and resources to generate a response."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 9,
|
||
"id": "609f145c",
|
||
"metadata": {
|
||
"language_info": {
|
||
"name": "polyglot-notebook"
|
||
},
|
||
"polyglot_notebook": {
|
||
"kernelName": "csharp"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"ThreadRun run = await agentClient.Runs.CreateRunAsync(thread, agent, toolResources);"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "80501bff",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Monitor Run and Handle Tool Approvals (Keyless)\n",
|
||
"\n",
|
||
"Monitor the agent run status and handle any required tool approvals. This loop:\n",
|
||
"1. Waits for the run to complete or require action\n",
|
||
"2. Automatically approves MCP tool calls when required\n",
|
||
"3. For keyless authentication, headers may not be needed if the MCP server supports Azure identity"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "14056ceb",
|
||
"metadata": {
|
||
"language_info": {
|
||
"name": "polyglot-notebook"
|
||
},
|
||
"polyglot_notebook": {
|
||
"kernelName": "csharp"
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Approving MCP tool call: microsoft_docs_search\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"while (run.Status == RunStatus.Queued || run.Status == RunStatus.InProgress || run.Status == RunStatus.RequiresAction)\n",
|
||
"{\n",
|
||
" await Task.Delay(TimeSpan.FromMilliseconds(1000));\n",
|
||
" run = await agentClient.Runs.GetRunAsync(thread.Id, run.Id);\n",
|
||
"\n",
|
||
" if (run.Status == RunStatus.RequiresAction && run.RequiredAction is SubmitToolApprovalAction toolApprovalAction)\n",
|
||
" {\n",
|
||
" var toolApprovals = new List<ToolApproval>();\n",
|
||
" foreach (var toolCall in toolApprovalAction.SubmitToolApproval.ToolCalls)\n",
|
||
" {\n",
|
||
" if (toolCall is RequiredMcpToolCall mcpToolCall)\n",
|
||
" {\n",
|
||
" Console.WriteLine($\"Approving MCP tool call: {mcpToolCall.Name}\");\n",
|
||
" \n",
|
||
" // Option 1: Keyless approval (no headers needed)\n",
|
||
" toolApprovals.Add(new ToolApproval(mcpToolCall.Id, approve: true));\n",
|
||
" \n",
|
||
" // Option 2: With headers (if required by specific MCP server)\n",
|
||
" // toolApprovals.Add(new ToolApproval(mcpToolCall.Id, approve: true)\n",
|
||
" // {\n",
|
||
" // Headers = { [\"Authorization\"] = \"Bearer <your-token>\" }\n",
|
||
" // });\n",
|
||
" }\n",
|
||
" }\n",
|
||
"\n",
|
||
" if (toolApprovals.Count > 0)\n",
|
||
" {\n",
|
||
" run = await agentClient.Runs.SubmitToolOutputsToRunAsync(thread.Id, run.Id, toolApprovals: toolApprovals);\n",
|
||
" }\n",
|
||
" }\n",
|
||
"}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "a87e050f",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Display Conversation Results\n",
|
||
"\n",
|
||
"Retrieve and display all messages in the thread, showing both the user's question and the agent's response. The messages are displayed in chronological order with timestamps and role indicators."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 12,
|
||
"id": "f08a5dbe",
|
||
"metadata": {
|
||
"language_info": {
|
||
"name": "polyglot-notebook"
|
||
},
|
||
"polyglot_notebook": {
|
||
"kernelName": "csharp"
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"2025-07-16 06:39:43 - user: What's difference between Azure OpenAI and OpenAI?\n",
|
||
"2025-07-16 06:39:51 - assistant: The main difference between Azure OpenAI and OpenAI lies in their deployment, management, and integration options:\n",
|
||
"\n",
|
||
"1. **Azure OpenAI**:\n",
|
||
" - A cloud service offered through Microsoft Azure.\n",
|
||
" - Provides access to OpenAI models with additional enterprise features like security, compliance, and scale.\n",
|
||
" - Allows integration with other Azure services, enabling seamless use within existing Azure-based solutions.\n",
|
||
" - Offers managed deployment, monitoring, and support within the Azure ecosystem.\n",
|
||
" - Suitable for organizations looking for enterprise-grade security, compliance, and regional availability.\n",
|
||
"\n",
|
||
"2. **OpenAI (OpenAI API)**:\n",
|
||
" - A standalone API service provided directly by OpenAI.\n",
|
||
" - Accessible via the OpenAI platform without the need for Azure.\n",
|
||
" - Focused on providing GPT models, DALL-E, etc., primarily for developers and researchers.\n",
|
||
" - Suitable for individual developers, startups, and organizations preferring a direct connection to OpenAI’s models.\n",
|
||
"\n",
|
||
"In summary, **Azure OpenAI** is essentially OpenAI models accessed via Microsoft Azure, with additional enterprise offerings and integrations, whereas **OpenAI API** provides direct access to OpenAI models without Azure integration.\n",
|
||
"\n",
|
||
"Would you like more detailed technical differences or usage scenarios?\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"using Azure;\n",
|
||
"\n",
|
||
"AsyncPageable<PersistentThreadMessage> messages = agentClient.Messages.GetMessagesAsync(\n",
|
||
" threadId: thread.Id,\n",
|
||
" order: ListSortOrder.Ascending\n",
|
||
");\n",
|
||
"\n",
|
||
"await foreach (PersistentThreadMessage threadMessage in messages)\n",
|
||
"{\n",
|
||
" Console.Write($\"{threadMessage.CreatedAt:yyyy-MM-dd HH:mm:ss} - {threadMessage.Role,10}: \");\n",
|
||
" foreach (MessageContent contentItem in threadMessage.ContentItems)\n",
|
||
" {\n",
|
||
" if (contentItem is MessageTextContent textItem)\n",
|
||
" {\n",
|
||
" Console.Write(textItem.Text);\n",
|
||
" }\n",
|
||
" else if (contentItem is MessageImageFileContent imageFileItem)\n",
|
||
" {\n",
|
||
" Console.Write($\"<image from ID: {imageFileItem.FileId}>\");\n",
|
||
" }\n",
|
||
" Console.WriteLine();\n",
|
||
" }\n",
|
||
"}"
|
||
]
|
||
}
|
||
],
|
||
"metadata": {
|
||
"kernelspec": {
|
||
"display_name": ".NET (C#)",
|
||
"language": "C#",
|
||
"name": ".net-csharp"
|
||
},
|
||
"language_info": {
|
||
"name": "polyglot-notebook"
|
||
},
|
||
"polyglot_notebook": {
|
||
"kernelInfo": {
|
||
"defaultKernelName": "csharp",
|
||
"items": [
|
||
{
|
||
"aliases": [],
|
||
"name": "csharp"
|
||
}
|
||
]
|
||
}
|
||
}
|
||
},
|
||
"nbformat": 4,
|
||
"nbformat_minor": 5
|
||
}
|