chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
# MCP get_all_mcp_tools Example
|
||||
|
||||
Python port of the JS `examples/mcp/get-all-mcp-tools-example.ts`. It demonstrates:
|
||||
|
||||
- Spinning up a local filesystem MCP server via `npx`.
|
||||
- Prefetching all MCP tools with `MCPUtil.get_all_function_tools`.
|
||||
- Building an agent that uses those prefetched tools instead of `mcp_servers`.
|
||||
- Applying a static tool filter and refetching tools.
|
||||
- Enabling `require_approval="always"` on the server and auto-approving interruptions in code to exercise the HITL path.
|
||||
|
||||
Run it with:
|
||||
|
||||
```bash
|
||||
uv run python examples/mcp/get_all_mcp_tools_example/main.py
|
||||
```
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- `npx` available on your PATH.
|
||||
- `OPENAI_API_KEY` set for the model calls.
|
||||
@@ -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())
|
||||
@@ -0,0 +1,20 @@
|
||||
1. To Kill a Mockingbird – Harper Lee
|
||||
2. Pride and Prejudice – Jane Austen
|
||||
3. 1984 – George Orwell
|
||||
4. The Hobbit – J.R.R. Tolkien
|
||||
5. Harry Potter and the Sorcerer’s Stone – J.K. Rowling
|
||||
6. The Great Gatsby – F. Scott Fitzgerald
|
||||
7. Charlotte’s Web – E.B. White
|
||||
8. Anne of Green Gables – Lucy Maud Montgomery
|
||||
9. The Alchemist – Paulo Coelho
|
||||
10. Little Women – Louisa May Alcott
|
||||
11. The Catcher in the Rye – J.D. Salinger
|
||||
12. Animal Farm – George Orwell
|
||||
13. The Chronicles of Narnia: The Lion, the Witch, and the Wardrobe – C.S. Lewis
|
||||
14. The Book Thief – Markus Zusak
|
||||
15. A Wrinkle in Time – Madeleine L’Engle
|
||||
16. The Secret Garden – Frances Hodgson Burnett
|
||||
17. Moby-Dick – Herman Melville
|
||||
18. Fahrenheit 451 – Ray Bradbury
|
||||
19. Jane Eyre – Charlotte Brontë
|
||||
20. The Little Prince – Antoine de Saint-Exupéry
|
||||
@@ -0,0 +1,10 @@
|
||||
1. "Here Comes the Sun" – The Beatles
|
||||
2. "Imagine" – John Lennon
|
||||
3. "Bohemian Rhapsody" – Queen
|
||||
4. "Shake It Off" – Taylor Swift
|
||||
5. "Billie Jean" – Michael Jackson
|
||||
6. "Uptown Funk" – Mark Ronson ft. Bruno Mars
|
||||
7. "Don’t Stop Believin’" – Journey
|
||||
8. "Dancing Queen" – ABBA
|
||||
9. "Happy" – Pharrell Williams
|
||||
10. "Wonderwall" – Oasis
|
||||
Reference in New Issue
Block a user