I am building a web app using Microsoft Agent Framework AG-UI and CopilotKit.
I want Human‑in‑the‑Loop approvals for MCP tool calls. However, after approval, the MCP tools are not called.
In contrast, standard tools (python function) execute correctly.
Could you please look into this? Thank you.
from __future__ import annotations
import logging
import os
from typing import Any
import uvicorn
from agent_framework import ChatAgent, MCPStreamableHTTPTool, ai_function
from agent_framework.azure import AzureOpenAIChatClient
from agent_framework_ag_ui import (AgentFrameworkAgent,
add_agent_framework_fastapi_endpoint)
from app.config import settings
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
chat_client = AzureOpenAIChatClient(credential=AzureCliCredential())
app = FastAPI(title="CopilotKit + Microsoft Agent Framework (Python)")
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@ai_function(
name="get_weather",
description="Get the current weather",
approval_mode="always_require",
)
def get_weather() -> str:
"""Get the current weather."""
print(">>> get_weather called")
return "Today is sunny."
get_datetime_mcp = MCPStreamableHTTPTool(
name="get-datetime-mcp",
url="http://localhost:8001/mcp",
approval_mode="always_require",
)
base_agent = ChatAgent(
name="MyAgent",
instructions="You are a helpful AI Agent.",
chat_client=chat_client,
tools=[
get_weather,
get_datetime_mcp,
],
)
add_agent_framework_fastapi_endpoint(
app=app,
agent=AgentFrameworkAgent(
agent=base_agent,
name="MyAgent",
description="Helpful AI Agent",
require_confirmation=True,
),
path="/api/v1/agents/my-agent",
)
if __name__ == "__main__":
host = os.getenv("AGENT_HOST", "0.0.0.0")
port = int(os.getenv("AGENT_PORT", "8000"))
uvicorn.run("sample3:app", host=host, port=port, reload=True)
'use client';
import { useState } from 'react';
import { CopilotKit, useHumanInTheLoop } from '@copilotkit/react-core';
import { CopilotChat } from '@copilotkit/react-ui';
const HumanInTheLoop = () => {
return (
<CopilotKit runtimeUrl="/master-agent/api/copilotkit" agent="my_agent">
<CopilotKitPage />
</CopilotKit>
);
};
const CopilotKitPage = () => {
useHumanInTheLoop({
name: 'get_datetime',
description: 'Get the current date and time',
parameters: [],
render: ({ status, respond }) => {
return <ApprovalCard title="Would you like to allow the date and time retrieval tool to run?" status={status} respond={respond} />;
},
});
useHumanInTheLoop({
name: 'get_weather',
description: 'Get the current weather',
parameters: [],
render: ({ status, respond }) => {
return <ApprovalCard title="Would you like to allow the weather retrieval tool to run?" status={status} respond={respond} />;
},
});
return (
<div className="flex h-full w-full flex-1 items-center justify-center" data-testid="background-container">
<div className="h-full w-full rounded-lg">
<CopilotChat
className="mx-auto h-full max-w-6xl rounded-2xl"
labels={{ initial: "Hi, I'm an agent specialized in helping you with your tasks. How can I help you?" }}
></CopilotChat>
</div>
</div>
);
};
interface ApprovalCardProps {
title: string;
status: 'inProgress' | 'executing' | 'complete';
respond?: (response: { accepted: boolean }) => void;
}
const ApprovalCard = ({ title, status, respond }: ApprovalCardProps) => {
const [decision, setDecision] = useState<'allowed' | 'aborted' | null>(null);
const handleAllow = () => {
setDecision('allowed');
respond?.({ accepted: true });
};
const handleAbort = () => {
setDecision('aborted');
respond?.({ accepted: false });
};
return (
<div className="mt-6 w-full max-w-md rounded-2xl bg-cyan-500 shadow-xl">
<div className="w-full rounded-2xl bg-white/20 p-8 backdrop-blur-md">
{decision === 'allowed' ? (
<div className="text-center">
<h2 className="mb-2 text-2xl font-bold text-white">🚀 Allowed</h2>
</div>
) : decision === 'aborted' ? (
<div className="text-center">
<h2 className="mb-2 text-2xl font-bold text-white">✋ Aborted</h2>
</div>
) : (
<>
<div className="mb-6 text-center">
<h2 className="mb-2 text-2xl font-bold text-white">{title}</h2>
</div>
{status === 'executing' && (
<div className="flex gap-3">
<button
onClick={handleAllow}
className="flex-1 rounded-xl bg-white px-6 py-4 font-bold text-black shadow-lg transition-all hover:scale-105 hover:shadow-xl active:scale-95"
>
🚀 Allow!
</button>
<button
onClick={handleAbort}
className="flex-1 rounded-xl border-2 border-white/30 bg-black/20 px-6 py-4 font-bold text-white shadow-lg transition-all hover:scale-105 hover:bg-black/30 active:scale-95"
>
✋ Abort
</button>
</div>
)}
</>
)}
</div>
</div>
);
};
export default HumanInTheLoop;
INFO:httpx:HTTP Request: POST http://localhost:8001/mcp "HTTP/1.1 200 OK"
DEBUG:httpcore.http11:receive_response_body.started request=<Request [b'POST']>
DEBUG:mcp.client.streamable_http:SSE message: root=JSONRPCResponse(jsonrpc='2.0', id=8, result={'content': [{'type': 'text', 'text': "1 validation error for call[get_datetime]\noptions\n Unexpected keyword argument [type=unexpected_keyword_argument, input_value={'metadata': {'ag_ui_thre...8d13c1'}, 'store': True}, input_type=dict]\n For further information visit https://errors.pydantic.dev/2.12/v/unexpected_keyword_argument"}], 'isError': True})
DEBUG:httpcore.http11:response_closed.started
DEBUG:httpcore.http11:response_closed.complete
INFO:agent_framework:Function get_datetime succeeded.
DEBUG:agent_framework:Function result: [<agent_framework._types.TextContent object at 0x769b3c3ed160>]
DEBUG:openai._base_client:Request options: {'method': 'post', 'url': '/chat/completions', 'headers': {'api-key': '<redacted>'}, 'files': None, 'idempotency_key': 'stainless-python-retry-99582d9d-5a66-4a77-b91e-60060fac54aa', 'json_data': {'messages': [{'role': 'system', 'content': [{'type': 'text', 'text': 'You are a helpful AI Agent.'}]}, {'role': 'user', 'content': [{'type': 'text', 'text': 'Please tell me the weather.'}]}, {'role': 'assistant', 'tool_calls': [{'id': 'call_7f61P2Z8N49FgToGE7zVNEYj', 'type': 'function', 'function': {'name': 'get_weather', 'arguments': '{}'}}, {'id': '80058605-e3d5-49d0-9124-35cd9d472e0c', 'type': 'function', 'function': {'name': 'confirm_changes', 'arguments': '{"function_name": "get_weather", "function_call_id": "call_7f61P2Z8N49FgToGE7zVNEYj", "function_arguments": {}, "steps": [{"description": "Execute get_weather", "status": "enabled"}]}'}}], 'content': ''}, {'role': 'tool', 'tool_call_id': '80058605-e3d5-49d0-9124-35cd9d472e0c', 'content': 'Confirmed'}, {'role': 'tool', 'tool_call_id': 'call_7f61P2Z8N49FgToGE7zVNEYj', 'content': 'Today is sunny.'}, {'role': 'assistant', 'content': [{'type': 'text', 'text': 'I can look up the current weather for any location — what city or ZIP code should I check? \n\nIf you meant a general update: today is sunny.'}]}, {'role': 'user', 'content': [{'type': 'text', 'text': 'Please tell me the current time.'}]}, {'role': 'assistant', 'tool_calls': [{'id': 'call_NEZhIt4eCJeqRZkjGMdFxUSo', 'type': 'function', 'function': {'name': 'get_datetime', 'arguments': '{}'}}, {'id': '06b716ed-510f-4517-92e8-6397ab1688ea', 'type': 'function', 'function': {'name': 'confirm_changes', 'arguments': '{"function_name": "get_datetime", "function_call_id": "call_NEZhIt4eCJeqRZkjGMdFxUSo", "function_arguments": {}, "steps": [{"description": "Execute get_datetime", "status": "enabled"}]}'}}], 'content': ''}, {'role': 'tool', 'tool_call_id': '06b716ed-510f-4517-92e8-6397ab1688ea', 'content': 'Confirmed'}, {'role': 'tool', 'tool_call_id': 'call_NEZhIt4eCJeqRZkjGMdFxUSo', 'content': '[{"type": "text", "text": "1 validation error for call[get_datetime]\\noptions\\n Unexpected keyword argument [type=unexpected_keyword_argument, input_value={\'metadata\': {\'ag_ui_thre...8d13c1\'}, \'store\': True}, input_type=dict]\\n For further information visit https://errors.pydantic.dev/2.12/v/unexpected_keyword_argument"}]'}], 'model': 'openai5-mini', 'metadata': {'ag_ui_thread_id': '7bf6120e-70e2-4991-9ed4-57524365c3c4', 'ag_ui_run_id': '66cfad04-c957-4e4d-8c0b-beff288d13c1'}, 'store': True, 'stream': True, 'stream_options': {'include_usage': True}, 'tool_choice': 'auto', 'tools': [{'type': 'function', 'function': {'name': 'get_weather', 'description': 'Get the current weather', 'parameters': {'properties': {}, 'title': 'get_weather_input', 'type': 'object'}}}, {'type': 'function', 'function': {'name': 'get_datetime', 'description': 'Get the current date and time.', 'parameters': {'properties': {}, 'title': 'get_datetime_input', 'type': 'object'}}}]}}
Thank you in advance.
I am building a web app using Microsoft Agent Framework AG-UI and CopilotKit.
I want Human‑in‑the‑Loop approvals for MCP tool calls. However, after approval, the MCP tools are not called.
In contrast, standard tools (python function) execute correctly.
Could you please look into this? Thank you.
Source code (Backend)
Source code (Frontend)
Log
Screenshots
Version information
Python 3.13.10
agent-framework-ag-ui==1.0.0b260116
agent-framework-core==1.0.0b260116
Thank you in advance.