1# pylint: disable=line-too-long,useless-suppression
2# ------------------------------------
3# Copyright (c) Microsoft Corporation.
4# Licensed under the MIT License.
5# ------------------------------------
6
7"""
8DESCRIPTION:
9 This sample demonstrates how to use agent operations with multiple
10 Model Context Protocol (MCP) servers from the Azure Agents service using a synchronous client.
11 To learn more about Model Context Protocol, visit https://modelcontextprotocol.io/
12
13USAGE:
14 python sample_agents_multiple_mcp.py
15
16 Before running the sample:
17
18 pip install azure-ai-projects azure-ai-agents>=1.2.0b3 azure-identity --pre
19
20 Set these environment variables with your own values:
21 1) PROJECT_ENDPOINT - The Azure AI Project endpoint, as found in the Overview
22 page of your Azure AI Foundry portal.
23 2) MODEL_DEPLOYMENT_NAME - The deployment name of the AI model, as found under the "Name" column in
24 the "Models + endpoints" tab in your Azure AI Foundry project.
25 3) MCP_SERVER_URL_1 - The URL of your first MCP server endpoint.
26 4) MCP_SERVER_LABEL_1 - A label for your first MCP server.
27 5) MCP_SERVER_URL_2 - The URL of your second MCP server endpoint.
28 6) MCP_SERVER_LABEL_2 - A label for your second MCP server.
29"""
30
31import os, time
32from azure.ai.projects import AIProjectClient
33from azure.identity import DefaultAzureCredential
34from azure.ai.agents.models import (
35 ListSortOrder,
36 McpTool,
37 RequiredMcpToolCall,
38 RunStepActivityDetails,
39 SubmitToolApprovalAction,
40 ToolApproval,
41)
42
43# Get MCP server configuration from environment variables
44mcp_server_url_1 = os.environ.get("MCP_SERVER_URL_1", "https://gitmcp.io/Azure/azure-rest-api-specs")
45mcp_server_label_1 = os.environ.get("MCP_SERVER_LABEL_1", "github")
46mcp_server_url_2 = os.environ.get("MCP_SERVER_URL_2", "https://learn.microsoft.com/api/mcp")
47mcp_server_label_2 = os.environ.get("MCP_SERVER_LABEL_2", "microsoft_learn")
48
49project_client = AIProjectClient(
50 endpoint=os.environ["PROJECT_ENDPOINT"],
51 credential=DefaultAzureCredential(),
52)
53
54# Initialize agent MCP tool
55mcp_tool1 = McpTool(
56 server_label=mcp_server_label_1,
57 server_url=mcp_server_url_1,
58 allowed_tools=[], # Optional: specify allowed tools
59)
60
61# You can also add or remove allowed tools dynamically
62search_api_code = "search_azure_rest_api_code"
63mcp_tool1.allow_tool(search_api_code)
64print(f"Allowed tools: {mcp_tool1.allowed_tools}")
65
66mcp_tool2 = McpTool(
67 server_label=mcp_server_label_2,
68 server_url=mcp_server_url_2,
69 allowed_tools=["microsoft_docs_search"], # Optional: specify allowed tools
70)
71
72mcp_tools = [mcp_tool1, mcp_tool2]
73
74# Create agent with MCP tool and process agent run
75with project_client:
76 agents_client = project_client.agents
77
78 # Create a new agent.
79 # NOTE: To reuse existing agent, fetch it with get_agent(agent_id)
80 agent = agents_client.create_agent(
81 model=os.environ["MODEL_DEPLOYMENT_NAME"],
82 name="my-mcp-agent",
83 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.",
84 tools=[tool.definitions[0] for tool in mcp_tools],
85 )
86
87 print(f"Created agent, ID: {agent.id}")
88 print(f"MCP Server 1: {mcp_tool1.server_label} at {mcp_tool1.server_url}")
89 print(f"MCP Server 2: {mcp_tool2.server_label} at {mcp_tool2.server_url}")
90
91 # Create thread for communication
92 thread = agents_client.threads.create()
93 print(f"Created thread, ID: {thread.id}")
94
95 # Create message to thread
96 message = agents_client.messages.create(
97 thread_id=thread.id,
98 role="user",
99 content="Please summarize the Azure REST API specifications Readme and Give me the Azure CLI commands to create an Azure Container App with a managed identity",
100 )
101 print(f"Created message, ID: {message.id}")
102
103 # Create and process agent run in thread with MCP tools
104 mcp_tool1.update_headers("SuperSecret", "123456")
105 mcp_tool2.set_approval_mode("never") # Disable approval for MS Learn MCP tool
106 tool_resources = McpTool.merge_resources(mcp_tools)
107 print(tool_resources)
108 run = agents_client.runs.create(thread_id=thread.id, agent_id=agent.id, tool_resources=tool_resources)
109 print(f"Created run, ID: {run.id}")
110
111 while run.status in ["queued", "in_progress", "requires_action"]:
112 time.sleep(1)
113 run = agents_client.runs.get(thread_id=thread.id, run_id=run.id)
114
115 if run.status == "requires_action" and isinstance(run.required_action, SubmitToolApprovalAction):
116 tool_calls = run.required_action.submit_tool_approval.tool_calls
117 if not tool_calls:
118 print("No tool calls provided - cancelling run")
119 agents_client.runs.cancel(thread_id=thread.id, run_id=run.id)
120 break
121
122 tool_approvals = []
123 for tool_call in tool_calls:
124 if isinstance(tool_call, RequiredMcpToolCall):
125 try:
126 print(f"Approving tool call: {tool_call}")
127 tool_approvals.append(
128 ToolApproval(
129 tool_call_id=tool_call.id,
130 approve=True,
131 headers=mcp_tool1.headers,
132 )
133 )
134 except Exception as e:
135 print(f"Error approving tool_call {tool_call.id}: {e}")
136
137 print(f"tool_approvals: {tool_approvals}")
138 if tool_approvals:
139 agents_client.runs.submit_tool_outputs(
140 thread_id=thread.id, run_id=run.id, tool_approvals=tool_approvals
141 )
142
143 print(f"Current run status: {run.status}")
144
145 print(f"Run completed with status: {run.status}")
146 if run.status == "failed":
147 print(f"Run failed: {run.last_error}")
148
149 # Display run steps and tool calls
150 run_steps = agents_client.run_steps.list(thread_id=thread.id, run_id=run.id)
151
152 # Loop through each step
153 for step in run_steps:
154 print(f"Step {step['id']} status: {step['status']}")
155
156 # Check if there are tool calls in the step details
157 step_details = step.get("step_details", {})
158 tool_calls = step_details.get("tool_calls", [])
159
160 if tool_calls:
161 print(" MCP Tool calls:")
162 for call in tool_calls:
163 print(f" Tool Call ID: {call.get('id')}")
164 print(f" Type: {call.get('type')}")
165
166 if isinstance(step_details, RunStepActivityDetails):
167 for activity in step_details.activities:
168 for function_name, function_definition in activity.tools.items():
169 print(
170 f' The function {function_name} with description "{function_definition.description}" will be called.:'
171 )
172 if len(function_definition.parameters) > 0:
173 print(" Function parameters:")
174 for argument, func_argument in function_definition.parameters.properties.items():
175 print(f" {argument}")
176 print(f" Type: {func_argument.type}")
177 print(f" Description: {func_argument.description}")
178 else:
179 print("This function has no parameters")
180
181 print() # add an extra newline between steps
182
183 # Fetch and log all messages
184 messages = agents_client.messages.list(thread_id=thread.id, order=ListSortOrder.ASCENDING)
185 print("\nConversation:")
186 print("-" * 50)
187 for msg in messages:
188 if msg.text_messages:
189 last_text = msg.text_messages[-1]
190 print(f"{msg.role.upper()}: {last_text.text.value}")
191 print("-" * 50)
192
193 # Example of dynamic tool management
194 print(f"\nDemonstrating dynamic tool management:")
195 print(f"Current allowed tools: {mcp_tool1.allowed_tools}")
196
197 # Remove a tool
198 try:
199 mcp_tool1.disallow_tool(search_api_code)
200 print(f"After removing {search_api_code}: {mcp_tool1.allowed_tools}")
201 except ValueError as e:
202 print(f"Error removing tool: {e}")
203
204 # Clean-up and delete the agent once the run is finished.
205 # NOTE: Comment out this line if you plan to reuse the agent later.
206 agents_client.delete_agent(agent.id)
207 print("Deleted agent")