chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
import asyncio
|
||||
import os
|
||||
import shutil
|
||||
from typing import Any
|
||||
|
||||
from agents import Agent, Runner, gen_trace_id, trace
|
||||
from agents.mcp import MCPServer, MCPServerStdio
|
||||
from agents.mcp.util import MCPUtil, create_static_tool_filter
|
||||
from agents.run_context import RunContextWrapper
|
||||
from examples.auto_mode import confirm_with_fallback, is_auto_mode
|
||||
|
||||
|
||||
async def list_tools(server: MCPServer, *, convert_to_strict: bool) -> list[Any]:
|
||||
"""Fetch all MCP tools from the server."""
|
||||
|
||||
run_context: RunContextWrapper[dict[str, str]] = RunContextWrapper(context={})
|
||||
agent = Agent(name="ToolFetcher", instructions="Prefetch MCP tools.", mcp_servers=[server])
|
||||
|
||||
return await MCPUtil.get_all_function_tools(
|
||||
[server],
|
||||
convert_schemas_to_strict=convert_to_strict,
|
||||
run_context=run_context,
|
||||
agent=agent,
|
||||
)
|
||||
|
||||
|
||||
def prompt_user_approval(interruption_name: str) -> bool:
|
||||
"""Ask the user to approve a tool call and return the decision."""
|
||||
if is_auto_mode():
|
||||
return confirm_with_fallback(
|
||||
f"Approve tool call '{interruption_name}'? (y/n): ",
|
||||
default=True,
|
||||
)
|
||||
while True:
|
||||
user_input = input(f"Approve tool call '{interruption_name}'? (y/n): ").strip().lower()
|
||||
if user_input == "y":
|
||||
return True
|
||||
if user_input == "n":
|
||||
return False
|
||||
print("Please enter 'y' or 'n'.")
|
||||
|
||||
|
||||
async def resolve_interruptions(agent: Agent, result: Any) -> Any:
|
||||
"""Prompt for approvals until no interruptions remain."""
|
||||
current_result = result
|
||||
while current_result.interruptions:
|
||||
state = current_result.to_state()
|
||||
# Human in the loop: prompt for approval on each tool call.
|
||||
for interruption in current_result.interruptions:
|
||||
if prompt_user_approval(interruption.name):
|
||||
print(f"Approving a tool call... (name: {interruption.name})")
|
||||
state.approve(interruption)
|
||||
else:
|
||||
print(f"Rejecting a tool call... (name: {interruption.name})")
|
||||
state.reject(interruption)
|
||||
current_result = await Runner.run(agent, state)
|
||||
return current_result
|
||||
|
||||
|
||||
async def main():
|
||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
samples_dir = os.path.join(current_dir, "sample_files")
|
||||
blocked_path = os.path.join(samples_dir, "test.txt")
|
||||
|
||||
async with MCPServerStdio(
|
||||
name="Filesystem Server",
|
||||
params={
|
||||
"command": "npx",
|
||||
"args": ["-y", "@modelcontextprotocol/server-filesystem", samples_dir],
|
||||
"cwd": samples_dir,
|
||||
},
|
||||
require_approval={"always": {"tool_names": ["read_text_file"]}},
|
||||
) as server:
|
||||
trace_id = gen_trace_id()
|
||||
with trace(workflow_name="MCP get_all_mcp_tools Example", trace_id=trace_id):
|
||||
print(f"View trace: https://platform.openai.com/traces/trace?trace_id={trace_id}\n")
|
||||
|
||||
print("=== Fetching all tools with strict schemas ===")
|
||||
all_tools = await list_tools(server, convert_to_strict=True)
|
||||
print(f"Found {len(all_tools)} tool(s):")
|
||||
for tool in all_tools:
|
||||
description = getattr(tool, "description", "") or ""
|
||||
print(f"- {tool.name}: {description}")
|
||||
|
||||
# Build an agent that uses the prefetched tools instead of mcp_servers.
|
||||
prefetched_agent = Agent(
|
||||
name="Prefetched MCP Assistant",
|
||||
instructions=(
|
||||
"Use the prefetched tools to help with file questions. "
|
||||
"When using path arguments, prefer absolute paths in the allowed directory."
|
||||
),
|
||||
tools=all_tools,
|
||||
)
|
||||
message = (
|
||||
f"List files in this allowed directory: {samples_dir}. "
|
||||
"Then read one of those files."
|
||||
)
|
||||
print(f"\nRunning: {message}\n")
|
||||
result = await Runner.run(prefetched_agent, message)
|
||||
result = await resolve_interruptions(prefetched_agent, result)
|
||||
print(result.final_output)
|
||||
|
||||
# Apply a static tool filter and refetch tools.
|
||||
server.tool_filter = create_static_tool_filter(
|
||||
allowed_tool_names=["read_file", "list_directory"]
|
||||
)
|
||||
filtered_tools = await list_tools(server, convert_to_strict=False)
|
||||
|
||||
print("\n=== After applying tool filter ===")
|
||||
print(f"Found {len(filtered_tools)} tool(s):")
|
||||
for tool in filtered_tools:
|
||||
print(f"- {tool.name}")
|
||||
|
||||
filtered_agent = Agent(
|
||||
name="Filtered MCP Assistant",
|
||||
instructions=(
|
||||
"Use the filtered tools to respond. "
|
||||
"If a request requires a missing tool, explain that the capability is not "
|
||||
"available."
|
||||
),
|
||||
tools=filtered_tools,
|
||||
)
|
||||
blocked_message = (
|
||||
f'Create a file named "{blocked_path}" with the text "hello". '
|
||||
"If the available tools cannot create files, explain that clearly."
|
||||
)
|
||||
print(f"\nRunning: {blocked_message}\n")
|
||||
filtered_result = await Runner.run(filtered_agent, blocked_message)
|
||||
filtered_result = await resolve_interruptions(filtered_agent, filtered_result)
|
||||
print(filtered_result.final_output)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if not shutil.which("npx"):
|
||||
raise RuntimeError("npx is required. Install it with `npm install -g npx`.")
|
||||
|
||||
asyncio.run(main())
|
||||
Reference in New Issue
Block a user