Skip to content

MCP tool approval in multi-step workflow with Anthropic model #47996

Description

@FriedrichRober
  • Package Name: azure-ai-projects, azure-identity, openai
  • Package Version:
    • azure-ai-projects version: 2.3.0
    • azure-identity version: 1.25.3
    • openai version: 2.44.0
  • Operating System: Windows 11
  • Python Version: Python 3.14.5

Describe the bug
When using Azure AI Foundry Agents with an MCP tool configured with require_approval="always" and the claude-sonnet-4-6 model, the first MCP approval succeeds, but a subsequent MCP approval request in the same response chain fails with HTTP 400.

The same workflow succeeds when require_approval="never" is used.

The issue appears when Claude performs a multi-step MCP interaction:

  1. Request MCP approval.
  2. Execute MCP tool call.
  3. Request approval for a second MCP tool call.
  4. Approval response for the second request returns HTTP 400.

The identical approval payload format works for the first approval request.

To Reproduce
I was following https://github.com/MicrosoftLearning/mslearn-ai-agents/tree/main/Labfiles/03-mcp-integration/Python and adjusted the code for agent.py slightly to handle multi-step approvals.

Steps to reproduce the behavior:

agent.py
import os
from dotenv import load_dotenv
import json

# Add references
import azure.identity as azure_identity
import azure.ai.projects as azure_ai_projects
import openai as openai

from azure.identity import DefaultAzureCredential
from azure.ai.projects import AIProjectClient
from azure.ai.projects.models import PromptAgentDefinition, MCPTool
from openai.types.responses.response_input_param import (
    McpApprovalResponse,
    ResponseInputParam,
)
from openai import BadRequestError

# Load environment variables from .env file
load_dotenv()
project_endpoint = os.getenv("PROJECT_ENDPOINT")
model_deployment = os.getenv("MODEL_DEPLOYMENT_NAME")

print("=" * 80)
print(f"azure-ai-projects version: {azure_ai_projects.__version__}")
print(f"azure-identity version: {azure_identity.__version__}")
print(f"openai version: {openai.__version__}")
print("=" * 80)

# Connect to the agents client
with (
    DefaultAzureCredential() as credential,
    AIProjectClient(endpoint=project_endpoint, credential=credential) as project_client,
    project_client.get_openai_client() as openai_client,
):

    # Initialize agent MCP tool
    mcp_tool = MCPTool(
        server_label="api-specs",
        server_url="https://learn.microsoft.com/api/mcp",
        require_approval="always",
    )

    # Create a new agent with the MCP tool
    agent = project_client.agents.create_version(
        agent_name="MyAgent",
        definition=PromptAgentDefinition(
            model=model_deployment,
            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.",
            tools=[mcp_tool],
        ),
    )
    print(
        f"Agent created (id: {agent.id}, name: {agent.name}, version: {agent.version})"
    )

    # Create conversation thread
    conversation = openai_client.conversations.create()
    print(f"Created conversation (id: {conversation.id})")

    # Send initial request that will trigger the MCP tool
    response = openai_client.responses.create(
        conversation=conversation.id,
        input="Give me the Azure CLI commands to create an Azure Container App with a managed identity.",
        extra_body={"agent_reference": {"name": agent.name, "type": "agent_reference"}},
    )

    response_nr = 1

    # Process any MCP approval requests that were generated
    # The agent may issue several tool calls, each needing its own approval,
    # so we loop until there are none left.
    # # I had to change this from the instructions,
    # # because my model iterated over tools instead of providing a list of tool calls.
    while not response.output_text:

        print("=" * 80)
        print(
            f"Response {response_nr}: {json.dumps(response.model_dump(), indent=2, sort_keys=True)}"
        )
        print("=" * 80)

        # Collect any MCP approval requests from the latest response
        input_list: ResponseInputParam = []
        item_nr = 1
        for item in response.output:
            print(
                f"Input Item {response_nr}.{item_nr}: {json.dumps(item.model_dump(), indent=2, sort_keys=True)}"
            )
            if item.type == "mcp_approval_request":
                if item.server_label == "api-specs" and item.id:
                    # Automatically approve the MCP request to allow the agent to proceed
                    result = McpApprovalResponse(
                        type="mcp_approval_response",
                        approve=True,
                        approval_request_id=item.id,
                    )
                    print(
                        f"Output Item {response_nr}.{item_nr}: {json.dumps(result, indent=2, sort_keys=True)}"
                    )
                    input_list.append(result)
            item_nr += 1

        response_nr += 1
        if not input_list:
            break

        try:
            response = openai_client.responses.create(
                input=input_list,
                previous_response_id=response.id,
                extra_body={
                    "agent_reference": {"name": agent.name, "type": "agent_reference"}
                },
            )

        except BadRequestError as e:
            print("Status:", e.status_code)

            if hasattr(e, "response") and e.response:
                print("Headers:", e.response.headers)

                try:
                    print("JSON:", e.response.json())
                except Exception:
                    print("TEXT:", e.response.text)

            raise

    print(f"\nAgent response: {response.output_text}")

    # Clean up resources by deleting the agent version
    project_client.agents.delete_version(
        agent_name=agent.name, agent_version=agent.version
    )
    print("Agent deleted")
output structure
Response 1: {...}
Input Item 1.1: {type: mcp_list_tools}
Input Item 1.2: {type: mcp_approval_request}
Output Item 1.2: {type: mcp_approval_response}
Response 2: {...}
Input Item 2.1: {type: mcp_call}
Input Item 2.2: {type: mcp_approval_request}
Output Item 2.2: {type: mcp_approval_response}
...
openai.BadRequestError: Error code: 400 - {'error': {'message': 'There was an issue with your request. Please check your inputs and try again', 'type': 'invalid_request_error', 'param': None, 'code': None}}

Expected behavior
The second MCP approval should be accepted and the agent should continue execution, eventually producing a final response.

Metadata

Metadata

Assignees

No one assigned

    Labels

    AI ProjectsClientThis issue points to a problem in the data-plane of the library.customer-reportedIssues that are reported by GitHub users external to the Azure organization.needs-team-triageWorkflow: This issue needs the team to triage.questionThe issue doesn't require a change to the product in order to be resolved. Most issues start as that

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions