chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:39:17 +08:00
commit 4ed4e9ff99
1368 changed files with 334957 additions and 0 deletions
+26
View File
@@ -0,0 +1,26 @@
# MCP Filesystem Example
This example uses the [filesystem MCP server](https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem), running locally via `npx`.
Run it via:
```
uv run python examples/mcp/filesystem_example/main.py
```
## Details
The example uses the `MCPServerStdio` class from `agents.mcp`, with the command:
```bash
npx -y "@modelcontextprotocol/server-filesystem" <samples_directory>
```
It's only given access to the `sample_files` directory adjacent to the example, which contains some sample data.
Under the hood:
1. The server is spun up in a subprocess, and exposes a bunch of tools like `list_directory()`, `read_file()`, etc.
2. We add the server instance to the Agent via `mcp_agents`.
3. Each time the agent runs, we call out to the MCP server to fetch the list of tools via `server.list_tools()`.
4. If the LLM chooses to use an MCP tool, we call the MCP server to run the tool via `server.run_tool()`.
+57
View File
@@ -0,0 +1,57 @@
import asyncio
import os
import shutil
from agents import Agent, Runner, gen_trace_id, trace
from agents.mcp import MCPServer, MCPServerStdio
async def run(mcp_server: MCPServer):
agent = Agent(
name="Assistant",
instructions="Use the tools to read the filesystem and answer questions based on those files.",
mcp_servers=[mcp_server],
)
# List the files it can read
message = "Read the files and list them."
print(f"Running: {message}")
result = await Runner.run(starting_agent=agent, input=message)
print(result.final_output)
# Ask about books
message = "Read favorite_books.txt and tell me my #1 favorite book."
print(f"\n\nRunning: {message}")
result = await Runner.run(starting_agent=agent, input=message)
print(result.final_output)
# Ask a question that reads then reasons.
message = "Read favorite_songs.txt and suggest one new song that I might like."
print(f"\n\nRunning: {message}")
result = await Runner.run(starting_agent=agent, input=message)
print(result.final_output)
async def main():
current_dir = os.path.dirname(os.path.abspath(__file__))
samples_dir = os.path.join(current_dir, "sample_files")
async with MCPServerStdio(
name="Filesystem Server, via npx",
params={
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", samples_dir],
},
) as server:
trace_id = gen_trace_id()
with trace(workflow_name="MCP Filesystem Example", trace_id=trace_id):
print(f"View trace: https://platform.openai.com/traces/trace?trace_id={trace_id}\n")
await run(server)
if __name__ == "__main__":
# Let's make sure the user has npx installed
if not shutil.which("npx"):
raise RuntimeError("npx is not installed. Please 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 Sorcerers Stone J.K. Rowling
6. The Great Gatsby F. Scott Fitzgerald
7. Charlottes 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 LEngle
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,4 @@
- In the summer, I love visiting London.
- In the winter, Tokyo is great.
- In the spring, San Francisco.
- In the fall, New York is the best.
@@ -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. "Dont Stop Believin" Journey
8. "Dancing Queen" ABBA
9. "Happy" Pharrell Williams
10. "Wonderwall" Oasis
@@ -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 Sorcerers Stone J.K. Rowling
6. The Great Gatsby F. Scott Fitzgerald
7. Charlottes 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 LEngle
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. "Dont Stop Believin" Journey
8. "Dancing Queen" ABBA
9. "Happy" Pharrell Williams
10. "Wonderwall" Oasis
+26
View File
@@ -0,0 +1,26 @@
# MCP Git Example
This example uses the [git MCP server](https://github.com/modelcontextprotocol/servers/tree/main/src/git), running locally via `uvx`.
Run it via:
```
uv run python examples/mcp/git_example/main.py
```
## Details
The example uses the `MCPServerStdio` class from `agents.mcp`, with the command:
```bash
uvx mcp-server-git
```
Prior to running the agent, the user is prompted to provide a local directory path to their git repo. Using that, the Agent can invoke Git MCP tools like `git_log` to inspect the git commit log.
Under the hood:
1. The server is spun up in a subprocess, and exposes a bunch of tools like `git_log()`
2. We add the server instance to the Agent via `mcp_agents`.
3. Each time the agent runs, we call out to the MCP server to fetch the list of tools via `server.list_tools()`. The result is cached.
4. If the LLM chooses to use an MCP tool, we call the MCP server to run the tool via `server.run_tool()`.
+48
View File
@@ -0,0 +1,48 @@
import asyncio
import shutil
from agents import Agent, Runner, trace
from agents.mcp import MCPServer, MCPServerStdio
from examples.auto_mode import input_with_fallback
async def run(mcp_server: MCPServer, directory_path: str):
agent = Agent(
name="Assistant",
instructions=f"Answer questions about the git repository at {directory_path}, use that for repo_path",
mcp_servers=[mcp_server],
)
message = "Who's the most frequent contributor?"
print("\n" + "-" * 40)
print(f"Running: {message}")
result = await Runner.run(starting_agent=agent, input=message)
print(result.final_output)
message = "Summarize the last change in the repository."
print("\n" + "-" * 40)
print(f"Running: {message}")
result = await Runner.run(starting_agent=agent, input=message)
print(result.final_output)
async def main():
# Ask the user for the directory path
directory_path = input_with_fallback(
"Please enter the path to the git repository: ",
".",
)
async with MCPServerStdio(
cache_tools_list=True, # Cache the tools list, for demonstration
params={"command": "uvx", "args": ["mcp-server-git"]},
) as server:
with trace(workflow_name="MCP Git Example"):
await run(server, directory_path)
if __name__ == "__main__":
if not shutil.which("uvx"):
raise RuntimeError("uvx is not installed. Please install it with `pip install uvx`.")
asyncio.run(main())
+78
View File
@@ -0,0 +1,78 @@
# MCP Manager Example (FastAPI)
This example shows how to use `MCPServerManager` to keep MCP server lifecycle management in a single task inside a FastAPI app with the Streamable HTTP transport.
## Run the MCP server (Streamable HTTP)
```
uv run python examples/mcp/manager_example/mcp_server.py
```
The server listens at `http://localhost:8000/mcp` by default.
You can override the host/port with:
```
export STREAMABLE_HTTP_HOST=127.0.0.1
export STREAMABLE_HTTP_PORT=8000
```
This example also configures an inactive MCP server at `http://localhost:8001/mcp` to demonstrate how the manager drops failed
servers. You can override it with:
```
export INACTIVE_MCP_SERVER_URL=http://localhost:8001/mcp
```
## Run the FastAPI app
```
uv run python examples/mcp/manager_example/app.py
```
The app listens at `http://127.0.0.1:9001`.
## Run the smoke test
To verify the MCP manager and app integration without calling a model:
```
uv run python -m examples.mcp.manager_example.smoke_test
```
The smoke test starts the local MCP server on a temporary port, points both app MCP server settings at that server, and checks `/health`, `/tools`, and `/add`.
## Toggle MCP manager usage
By default, the app uses `MCPServerManager`. To disable it:
```
export USE_MCP_MANAGER=0
```
## Try the endpoints
```
curl http://127.0.0.1:9001/health
curl http://127.0.0.1:9001/tools
curl -X POST http://127.0.0.1:9001/add \
-H 'Content-Type: application/json' \
-d '{"a": 2, "b": 3}'
```
Reconnect failed MCP servers (manager must be enabled):
```
curl -X POST http://127.0.0.1:9001/reconnect \
-H 'Content-Type: application/json' \
-d '{"failed_only": true}'
```
To use `/run`, set `OPENAI_API_KEY`:
```
export OPENAI_API_KEY=...
curl -X POST http://127.0.0.1:9001/run \
-H 'Content-Type: application/json' \
-d '{"input": "Add 4 and 9."}'
```
+130
View File
@@ -0,0 +1,130 @@
import os
from contextlib import asynccontextmanager
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from agents import Agent, Runner
from agents.mcp import MCPServer, MCPServerManager, MCPServerStreamableHttp
from agents.model_settings import ModelSettings
MCP_SERVER_URL = os.getenv("MCP_SERVER_URL", "http://localhost:8000/mcp")
INACTIVE_MCP_SERVER_URL = os.getenv("INACTIVE_MCP_SERVER_URL", "http://localhost:8001/mcp")
APP_HOST = "127.0.0.1"
APP_PORT = 9001
USE_MCP_MANAGER = os.getenv("USE_MCP_MANAGER", "1") != "0"
class AddRequest(BaseModel):
a: int
b: int
class RunRequest(BaseModel):
input: str
class ReconnectRequest(BaseModel):
failed_only: bool = True
@asynccontextmanager
async def lifespan(app: FastAPI):
server = MCPServerStreamableHttp({"url": MCP_SERVER_URL})
inactive_server = MCPServerStreamableHttp({"url": INACTIVE_MCP_SERVER_URL})
servers = [server, inactive_server]
if USE_MCP_MANAGER:
async with MCPServerManager(
servers=servers,
connect_in_parallel=True,
) as manager:
app.state.mcp_manager = manager
app.state.mcp_servers = servers
yield
return
await server.connect()
app.state.mcp_servers = servers
app.state.active_servers = [server]
try:
yield
finally:
await server.cleanup()
app = FastAPI(lifespan=lifespan)
@app.get("/health")
async def health() -> dict[str, object]:
if USE_MCP_MANAGER:
manager: MCPServerManager = app.state.mcp_manager
return {
"connected_servers": [server.name for server in manager.active_servers],
"failed_servers": [server.name for server in manager.failed_servers],
}
active_servers = _get_active_servers()
return {
"connected_servers": [server.name for server in active_servers],
"failed_servers": [],
}
@app.get("/tools")
async def list_tools() -> dict[str, object]:
active_servers = _get_active_servers()
if not active_servers:
return {"tools": []}
tools = await active_servers[0].list_tools()
return {"tools": [tool.name for tool in tools]}
@app.post("/add")
async def add(req: AddRequest) -> dict[str, object]:
active_servers = _get_active_servers()
if not active_servers:
raise HTTPException(status_code=503, detail="No MCP servers available")
result = await active_servers[0].call_tool("add", {"a": req.a, "b": req.b})
return {"result": result.model_dump(mode="json")}
@app.post("/run")
async def run_agent(req: RunRequest) -> dict[str, object]:
if not os.getenv("OPENAI_API_KEY"):
raise HTTPException(status_code=400, detail="OPENAI_API_KEY is required")
servers = _get_active_servers()
if not servers:
raise HTTPException(status_code=503, detail="No MCP servers available")
agent = Agent(
name="FastAPI Agent",
instructions="Use the MCP tools when needed.",
mcp_servers=servers,
model_settings=ModelSettings(tool_choice="auto"),
)
result = await Runner.run(starting_agent=agent, input=req.input)
return {"output": result.final_output}
@app.post("/reconnect")
async def reconnect(req: ReconnectRequest) -> dict[str, object]:
if not USE_MCP_MANAGER:
raise HTTPException(status_code=400, detail="MCPServerManager is disabled")
manager: MCPServerManager = app.state.mcp_manager
servers = await manager.reconnect(failed_only=req.failed_only)
return {"connected_servers": [server.name for server in servers]}
def _get_active_servers() -> list[MCPServer]:
if USE_MCP_MANAGER:
manager: MCPServerManager = app.state.mcp_manager
return list(manager.active_servers)
return list(app.state.active_servers)
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host=APP_HOST, port=APP_PORT)
@@ -0,0 +1,26 @@
import os
from mcp.server.fastmcp import FastMCP
STREAMABLE_HTTP_HOST = os.getenv("STREAMABLE_HTTP_HOST", "127.0.0.1")
STREAMABLE_HTTP_PORT = int(os.getenv("STREAMABLE_HTTP_PORT", "8000"))
mcp = FastMCP(
"FastAPI Example Server",
host=STREAMABLE_HTTP_HOST,
port=STREAMABLE_HTTP_PORT,
)
@mcp.tool()
def add(a: int, b: int) -> int:
return a + b
@mcp.tool()
def echo(message: str) -> str:
return f"echo: {message}"
if __name__ == "__main__":
mcp.run(transport="streamable-http")
+144
View File
@@ -0,0 +1,144 @@
"""Smoke test for the MCP manager example app.
This script starts the sibling Streamable HTTP MCP server on a temporary local
port, loads the app with matching environment variables, and verifies the
manager-backed endpoints without calling a model.
"""
from __future__ import annotations
import asyncio
import importlib
import os
import socket
import subprocess
import sys
import time
from typing import Any, cast
import httpx
HOST = "127.0.0.1"
PORT_WAIT_SECONDS = 10.0
def _free_port() -> int:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.bind((HOST, 0))
return int(sock.getsockname()[1])
def _wait_for_port(process: subprocess.Popen[str], port: int) -> None:
deadline = time.monotonic() + PORT_WAIT_SECONDS
while time.monotonic() < deadline:
if process.poll() is not None:
output, _ = process.communicate(timeout=1)
raise RuntimeError(f"MCP server exited before it was ready:\n{output}")
try:
with socket.create_connection((HOST, port), timeout=0.2):
return
except OSError:
time.sleep(0.1)
raise RuntimeError(f"MCP server did not listen on {HOST}:{port}.")
def _stop_process(process: subprocess.Popen[str]) -> str:
if process.poll() is not None:
output, _ = process.communicate(timeout=1)
return output
process.terminate()
try:
output, _ = process.communicate(timeout=5)
except subprocess.TimeoutExpired:
process.kill()
output, _ = process.communicate(timeout=5)
return output
def _start_mcp_server(port: int) -> subprocess.Popen[str]:
env = {
**os.environ,
"STREAMABLE_HTTP_HOST": HOST,
"STREAMABLE_HTTP_PORT": str(port),
}
return subprocess.Popen(
[sys.executable, "-u", "-m", "examples.mcp.manager_example.mcp_server"],
env=env,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
)
def _load_app_module(mcp_port: int) -> Any:
mcp_server_url = f"http://{HOST}:{mcp_port}/mcp"
os.environ["MCP_SERVER_URL"] = mcp_server_url
# Point both configured MCP servers at the same temporary server so this
# smoke test stays on the clean app integration path.
os.environ["INACTIVE_MCP_SERVER_URL"] = mcp_server_url
os.environ["USE_MCP_MANAGER"] = "1"
module_name = "examples.mcp.manager_example.app"
if module_name in sys.modules:
module = importlib.reload(sys.modules[module_name])
else:
module = importlib.import_module(module_name)
return cast(Any, module)
def _require(condition: bool, message: str) -> None:
if not condition:
raise RuntimeError(message)
async def _exercise_app(mcp_port: int) -> None:
app_module = _load_app_module(mcp_port)
app = app_module.app
expected_server_url = f"http://{HOST}:{mcp_port}/mcp"
async with app.router.lifespan_context(app):
transport = httpx.ASGITransport(app=app)
async with httpx.AsyncClient(transport=transport, base_url="http://test") as client:
health_response = await client.get("/health")
health_response.raise_for_status()
health = health_response.json()
_require(
any(expected_server_url in name for name in health["connected_servers"]),
f"Expected connected MCP server in health response: {health}",
)
_require(
health["failed_servers"] == [],
f"Expected no failed MCP servers in health response: {health}",
)
tools_response = await client.get("/tools")
tools_response.raise_for_status()
tools = tools_response.json()["tools"]
_require({"add", "echo"} <= set(tools), f"Expected add and echo tools: {tools}")
add_response = await client.post("/add", json={"a": 2, "b": 3})
add_response.raise_for_status()
add_result = add_response.json()["result"]
texts = [
item.get("text")
for item in add_result.get("content", [])
if item.get("type") == "text"
]
_require("5" in texts, f"Expected add tool result to include 5: {add_result}")
async def main() -> None:
mcp_port = _free_port()
process = _start_mcp_server(mcp_port)
try:
_wait_for_port(process, mcp_port)
await _exercise_app(mcp_port)
finally:
_stop_process(process)
print("MCP manager example smoke test completed successfully.")
if __name__ == "__main__":
asyncio.run(main())
+29
View File
@@ -0,0 +1,29 @@
# MCP Prompt Server Example
This example uses a local MCP prompt server in [server.py](server.py).
Run the example via:
```
uv run python examples/mcp/prompt_server/main.py
```
## Details
The example uses the `MCPServerStreamableHttp` class from `agents.mcp`. The script auto-selects an open localhost port (or honors `STREAMABLE_HTTP_PORT`) and runs the server at `http://<host>:<port>/mcp`, providing user-controlled prompts that generate agent instructions. If you need a specific address, set `STREAMABLE_HTTP_PORT` and `STREAMABLE_HTTP_HOST`.
The server exposes prompts like `generate_code_review_instructions` that take parameters such as focus area and programming language. The agent calls these prompts to dynamically generate its system instructions based on user-provided parameters.
## Workflow
The example demonstrates two key functions:
1. **`show_available_prompts`** - Lists all available prompts on the MCP server, showing users what prompts they can select from. This demonstrates the discovery aspect of MCP prompts.
2. **`demo_code_review`** - Shows the complete user-controlled prompt workflow:
- Calls `generate_code_review_instructions` with specific parameters (focus: "security vulnerabilities", language: "python")
- Uses the generated instructions to create an Agent with specialized code review capabilities
- Runs the agent against vulnerable sample code (command injection via `os.system`)
- The agent analyzes the code and provides security-focused feedback using available tools
This pattern allows users to dynamically configure agent behavior through MCP prompts rather than hardcoded instructions.
+131
View File
@@ -0,0 +1,131 @@
import asyncio
import os
import shutil
import socket
import subprocess
import time
from typing import Any, cast
from agents import Agent, Runner, gen_trace_id, trace
from agents.mcp import MCPServer, MCPServerStreamableHttp
from agents.model_settings import ModelSettings
STREAMABLE_HTTP_HOST = os.getenv("STREAMABLE_HTTP_HOST", "127.0.0.1")
def _choose_port() -> int:
env_port = os.getenv("STREAMABLE_HTTP_PORT")
if env_port:
return int(env_port)
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind((STREAMABLE_HTTP_HOST, 0))
address = cast(tuple[str, int], s.getsockname())
return address[1]
STREAMABLE_HTTP_PORT = _choose_port()
os.environ.setdefault("STREAMABLE_HTTP_PORT", str(STREAMABLE_HTTP_PORT))
STREAMABLE_HTTP_URL = f"http://{STREAMABLE_HTTP_HOST}:{STREAMABLE_HTTP_PORT}/mcp"
async def get_instructions_from_prompt(mcp_server: MCPServer, prompt_name: str, **kwargs) -> str:
"""Get agent instructions by calling MCP prompt endpoint (user-controlled)"""
print(f"Getting instructions from prompt: {prompt_name}")
try:
prompt_result = await mcp_server.get_prompt(prompt_name, kwargs)
content = prompt_result.messages[0].content
if hasattr(content, "text"):
instructions = content.text
else:
instructions = str(content)
print("Generated instructions")
return instructions
except Exception as e:
print(f"Failed to get instructions: {e}")
return f"You are a helpful assistant. Error: {e}"
async def demo_code_review(mcp_server: MCPServer):
"""Demo: Code review with user-selected prompt"""
print("=== CODE REVIEW DEMO ===")
# User explicitly selects prompt and parameters
instructions = await get_instructions_from_prompt(
mcp_server,
"generate_code_review_instructions",
focus="security vulnerabilities",
language="python",
)
agent = Agent(
name="Code Reviewer Agent",
instructions=instructions, # Instructions from MCP prompt
model_settings=ModelSettings(tool_choice="auto"),
)
message = """Please review this code:
def process_user_input(user_input):
command = f"echo {user_input}"
os.system(command)
return "Command executed"
"""
print(f"Running: {message[:60]}...")
result = await Runner.run(starting_agent=agent, input=message)
print(result.final_output)
print("\n" + "=" * 50 + "\n")
async def show_available_prompts(mcp_server: MCPServer):
"""Show available prompts for user selection"""
print("=== AVAILABLE PROMPTS ===")
prompts_result = await mcp_server.list_prompts()
print("User can select from these prompts:")
for i, prompt in enumerate(prompts_result.prompts, 1):
print(f" {i}. {prompt.name} - {prompt.description}")
print()
async def main():
async with MCPServerStreamableHttp(
name="Simple Prompt Server",
params={"url": STREAMABLE_HTTP_URL},
) as server:
trace_id = gen_trace_id()
with trace(workflow_name="Simple Prompt Demo", trace_id=trace_id):
print(f"Trace: https://platform.openai.com/traces/trace?trace_id={trace_id}\n")
await show_available_prompts(server)
await demo_code_review(server)
if __name__ == "__main__":
if not shutil.which("uv"):
raise RuntimeError("uv is not installed")
process: subprocess.Popen[Any] | None = None
try:
this_dir = os.path.dirname(os.path.abspath(__file__))
server_file = os.path.join(this_dir, "server.py")
print(f"Starting Simple Prompt Server at {STREAMABLE_HTTP_URL} ...")
env = os.environ.copy()
env.setdefault("STREAMABLE_HTTP_HOST", STREAMABLE_HTTP_HOST)
env.setdefault("STREAMABLE_HTTP_PORT", str(STREAMABLE_HTTP_PORT))
process = subprocess.Popen(["uv", "run", server_file], env=env)
time.sleep(3)
print("Server started\n")
except Exception as e:
print(f"Error starting server: {e}")
exit(1)
try:
asyncio.run(main())
finally:
if process:
process.terminate()
print("Server terminated.")
+42
View File
@@ -0,0 +1,42 @@
import os
from mcp.server.fastmcp import FastMCP
STREAMABLE_HTTP_HOST = os.getenv("STREAMABLE_HTTP_HOST", "127.0.0.1")
STREAMABLE_HTTP_PORT = int(os.getenv("STREAMABLE_HTTP_PORT", "18080"))
# Create server
mcp = FastMCP("Prompt Server", host=STREAMABLE_HTTP_HOST, port=STREAMABLE_HTTP_PORT)
# Instruction-generating prompts (user-controlled)
@mcp.prompt()
def generate_code_review_instructions(
focus: str = "general code quality", language: str = "python"
) -> str:
"""Generate agent instructions for code review tasks"""
print(f"[debug-server] generate_code_review_instructions({focus}, {language})")
return f"""You are a senior {language} code review specialist. Your role is to provide comprehensive code analysis with focus on {focus}.
INSTRUCTIONS:
- Analyze code for quality, security, performance, and best practices
- Provide specific, actionable feedback with examples
- Identify potential bugs, vulnerabilities, and optimization opportunities
- Suggest improvements with code examples when applicable
- Be constructive and educational in your feedback
- Focus particularly on {focus} aspects
RESPONSE FORMAT:
1. Overall Assessment
2. Specific Issues Found
3. Security Considerations
4. Performance Notes
5. Recommended Improvements
6. Best Practices Suggestions
Use the available tools to check current time if you need timestamps for your analysis."""
if __name__ == "__main__":
mcp.run(transport="streamable-http")
+13
View File
@@ -0,0 +1,13 @@
# MCP SSE Example
This example uses a local SSE server in [server.py](server.py).
Run the example via:
```
uv run python examples/mcp/sse_example/main.py
```
## Details
The example uses the `MCPServerSse` class from `agents.mcp`. The server runs in a sub-process at `https://localhost:8000/sse`.
+104
View File
@@ -0,0 +1,104 @@
import asyncio
import os
import shutil
import socket
import subprocess
import time
from typing import Any, cast
from agents import Agent, Runner, gen_trace_id, trace
from agents.mcp import MCPServer, MCPServerSse
from agents.model_settings import ModelSettings
SSE_HOST = os.getenv("SSE_HOST", "127.0.0.1")
def _choose_port() -> int:
env_port = os.getenv("SSE_PORT")
if env_port:
return int(env_port)
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind((SSE_HOST, 0))
address = cast(tuple[str, int], s.getsockname())
return address[1]
SSE_PORT = _choose_port()
os.environ.setdefault("SSE_PORT", str(SSE_PORT))
SSE_URL = f"http://{SSE_HOST}:{SSE_PORT}/sse"
async def run(mcp_server: MCPServer):
agent = Agent(
name="Assistant",
instructions="Use the tools to answer the questions.",
mcp_servers=[mcp_server],
model_settings=ModelSettings(tool_choice="required"),
)
# Use the `add` tool to add two numbers
message = "Add these numbers: 7 and 22."
print(f"Running: {message}")
result = await Runner.run(starting_agent=agent, input=message)
print(result.final_output)
# Run the `get_weather` tool
message = "What's the weather in Tokyo?"
print(f"\n\nRunning: {message}")
result = await Runner.run(starting_agent=agent, input=message)
print(result.final_output)
# Run the `get_secret_word` tool
message = "What's the secret word?"
print(f"\n\nRunning: {message}")
result = await Runner.run(starting_agent=agent, input=message)
print(result.final_output)
async def main():
async with MCPServerSse(
name="SSE Python Server",
params={
"url": SSE_URL,
},
) as server:
trace_id = gen_trace_id()
with trace(workflow_name="SSE Example", trace_id=trace_id):
print(f"View trace: https://platform.openai.com/traces/trace?trace_id={trace_id}\n")
await run(server)
if __name__ == "__main__":
# Let's make sure the user has uv installed
if not shutil.which("uv"):
raise RuntimeError(
"uv is not installed. Please install it: https://docs.astral.sh/uv/getting-started/installation/"
)
# We'll run the SSE server in a subprocess. Usually this would be a remote server, but for this
# demo, we'll run it locally at SSE_URL.
process: subprocess.Popen[Any] | None = None
try:
this_dir = os.path.dirname(os.path.abspath(__file__))
server_file = os.path.join(this_dir, "server.py")
print(f"Starting SSE server at {SSE_URL} ...")
# Run `uv run server.py` to start the SSE server
env = os.environ.copy()
env.setdefault("SSE_HOST", SSE_HOST)
env.setdefault("SSE_PORT", str(SSE_PORT))
process = subprocess.Popen(["uv", "run", server_file], env=env)
# Give it 3 seconds to start
time.sleep(3)
print("SSE server started. Running example...\n\n")
except Exception as e:
print(f"Error starting SSE server: {e}")
exit(1)
try:
asyncio.run(main())
finally:
if process:
process.terminate()
+42
View File
@@ -0,0 +1,42 @@
import os
import random
from mcp.server.fastmcp import FastMCP
SSE_HOST = os.getenv("SSE_HOST", "127.0.0.1")
SSE_PORT = int(os.getenv("SSE_PORT", "8000"))
# Create server
mcp = FastMCP("Echo Server", host=SSE_HOST, port=SSE_PORT)
@mcp.tool()
def add(a: int, b: int) -> int:
"""Add two numbers"""
print(f"[debug-server] add({a}, {b})")
return a + b
@mcp.tool()
def get_secret_word() -> str:
print("[debug-server] get_secret_word()")
return random.choice(["apple", "banana", "cherry"])
@mcp.tool()
def get_current_weather(city: str) -> str:
print(f"[debug-server] get_current_weather({city})")
# Keep tool output deterministic so this example is stable in CI and offline environments.
weather_by_city = {
"tokyo": "sunny with a light breeze and 20°C",
"san francisco": "cool and foggy with 14°C",
"new york": "partly cloudy with 18°C",
}
forecast = weather_by_city.get(city.strip().lower())
if forecast:
return f"The weather in {city} is {forecast}."
return f"The weather data for {city} is unavailable in this demo."
if __name__ == "__main__":
mcp.run(transport="sse")
+13
View File
@@ -0,0 +1,13 @@
# MCP SSE Remote Example
Python port of the JS `examples/mcp/sse-example.ts`. By default it starts the bundled local SSE MCP server and lets the agent use those tools. Set `MCP_SSE_REMOTE_URL` to try a compatible remote SSE server instead.
Run it with:
```bash
uv run python examples/mcp/sse_remote_example/main.py
```
Prerequisites:
- `OPENAI_API_KEY` set for the model calls.
+100
View File
@@ -0,0 +1,100 @@
import asyncio
import os
import shutil
import socket
import subprocess
import time
from collections.abc import Iterator
from contextlib import contextmanager
from pathlib import Path
from typing import Any, cast
from agents import Agent, Runner, gen_trace_id, trace
from agents.mcp import MCPServerSse
from agents.model_settings import ModelSettings
SSE_HOST = os.getenv("SSE_HOST", "127.0.0.1")
REMOTE_SSE_URL = os.getenv("MCP_SSE_REMOTE_URL")
def _choose_port() -> int:
env_port = os.getenv("SSE_PORT")
if env_port:
return int(env_port)
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.bind((SSE_HOST, 0))
address = cast(tuple[str, int], sock.getsockname())
return address[1]
@contextmanager
def local_sse_server() -> Iterator[str]:
if not shutil.which("uv"):
raise RuntimeError(
"uv is not installed. Please install it: "
"https://docs.astral.sh/uv/getting-started/installation/"
)
sse_port = _choose_port()
sse_url = f"http://{SSE_HOST}:{sse_port}/sse"
server_file = Path(__file__).resolve().parents[1] / "sse_example" / "server.py"
print(f"Starting local SSE server at {sse_url} ...", flush=True)
env = os.environ.copy()
env.setdefault("SSE_HOST", SSE_HOST)
env["SSE_PORT"] = str(sse_port)
process: subprocess.Popen[Any] | None = None
try:
process = subprocess.Popen(["uv", "run", str(server_file)], env=env)
time.sleep(3)
yield sse_url
finally:
if process is not None:
process.terminate()
try:
process.wait(timeout=5)
except subprocess.TimeoutExpired:
process.kill()
process.wait()
async def run(url: str, name: str) -> None:
async with MCPServerSse(
name=name,
params={
"url": url,
"timeout": 5,
"sse_read_timeout": 30,
},
) as server:
agent = Agent(
name="SSE Assistant",
instructions="Use the available MCP tools to answer the user.",
mcp_servers=[server],
model_settings=ModelSettings(tool_choice="required"),
)
trace_id = gen_trace_id()
with trace(workflow_name="SSE MCP Server Example", trace_id=trace_id):
print(f"View trace: https://platform.openai.com/traces/trace?trace_id={trace_id}\n")
result = await Runner.run(agent, "Use the MCP add tool to add 7 and 22.")
print(result.final_output)
async def main() -> None:
if REMOTE_SSE_URL:
print(f"Connecting to remote SSE server at {REMOTE_SSE_URL} ...", flush=True)
await run(REMOTE_SSE_URL, "Remote SSE Server")
return
print(
"MCP_SSE_REMOTE_URL is not set; using the bundled local SSE server for this demo.",
flush=True,
)
with local_sse_server() as url:
await run(url, "Local SSE Server")
if __name__ == "__main__":
asyncio.run(main())
@@ -0,0 +1,13 @@
# MCP Streamable HTTP Remote Example
Python port of the JS `examples/mcp/streamable-http-example.ts`. It connects to DeepWiki over the Streamable HTTP transport (`https://mcp.deepwiki.com/mcp`) and lets the agent use those tools.
Run it with:
```bash
uv run python examples/mcp/streamable_http_remote_example/main.py
```
Prerequisites:
- `OPENAI_API_KEY` set for the model calls.
@@ -0,0 +1,38 @@
import asyncio
from agents import Agent, Runner, gen_trace_id, trace
from agents.mcp import MCPServerStreamableHttp
async def main():
async with MCPServerStreamableHttp(
name="DeepWiki MCP Streamable HTTP Server",
params={
"url": "https://mcp.deepwiki.com/mcp",
# Allow more time for remote tool responses.
"timeout": 15,
"sse_read_timeout": 300,
},
# Retry slow/unstable remote calls a couple of times.
max_retry_attempts=2,
retry_backoff_seconds_base=2.0,
client_session_timeout_seconds=15,
) as server:
agent = Agent(
name="DeepWiki Assistant",
instructions="Use the tools to respond to user requests.",
mcp_servers=[server],
)
trace_id = gen_trace_id()
with trace(workflow_name="DeepWiki Streamable HTTP Example", trace_id=trace_id):
print(f"View trace: https://platform.openai.com/traces/trace?trace_id={trace_id}\n")
result = await Runner.run(
agent,
"For the repository openai/codex, tell me the primary programming language.",
)
print(result.final_output)
if __name__ == "__main__":
asyncio.run(main())
@@ -0,0 +1,63 @@
# Custom HTTP Client Factory Example
This example demonstrates how to use the new `httpx_client_factory` parameter in `MCPServerStreamableHttp` to configure custom HTTP client behavior for MCP StreamableHTTP connections.
## Features Demonstrated
- **Custom SSL Configuration**: Configure SSL certificates and verification settings
- **Custom Headers**: Add custom headers to all HTTP requests
- **Custom Timeouts**: Set custom timeout values for requests
- **Proxy Configuration**: Configure HTTP proxy settings
- **Custom Retry Logic**: Set up custom retry behavior (through httpx configuration)
## Running the Example
1. Make sure you have `uv` installed: https://docs.astral.sh/uv/getting-started/installation/
2. Run the example:
```bash
cd examples/mcp/streamablehttp_custom_client_example
uv run main.py
```
## Code Examples
### Basic Custom Client
```python
import httpx
from agents.mcp import MCPServerStreamableHttp
def create_custom_http_client() -> httpx.AsyncClient:
return httpx.AsyncClient(
verify=False, # Disable SSL verification for testing
timeout=httpx.Timeout(60.0, read=120.0),
headers={"X-Custom-Client": "my-app"},
)
async with MCPServerStreamableHttp(
name="Custom Client Server",
params={
"url": "http://localhost:<port>/mcp",
"httpx_client_factory": create_custom_http_client,
},
) as server:
# Use the server...
```
## Use Cases
- **Corporate Networks**: Configure proxy settings for corporate environments
- **SSL/TLS Requirements**: Use custom SSL certificates for secure connections
- **Custom Authentication**: Add custom headers for API authentication
- **Network Optimization**: Configure timeouts and connection pooling
- **Debugging**: Disable SSL verification for development environments
## Benefits
- **Flexibility**: Configure HTTP client behavior to match your network requirements
- **Security**: Use custom SSL certificates and authentication methods
- **Performance**: Optimize timeouts and connection settings for your use case
- **Compatibility**: Work with corporate proxies and network restrictions
This example will auto-pick a free localhost port unless you set `STREAMABLE_HTTP_PORT`; use `STREAMABLE_HTTP_HOST` to change the bind address.
@@ -0,0 +1,137 @@
"""Example demonstrating custom httpx_client_factory for MCPServerStreamableHttp.
This example shows how to configure custom HTTP client behavior for MCP StreamableHTTP
connections, including SSL certificates, proxy settings, and custom timeouts.
"""
import asyncio
import os
import shutil
import socket
import subprocess
import time
from typing import Any, cast
import httpx
from agents import Agent, Runner, gen_trace_id, trace
from agents.mcp import MCPServer, MCPServerStreamableHttp
from agents.model_settings import ModelSettings
STREAMABLE_HTTP_HOST = os.getenv("STREAMABLE_HTTP_HOST", "127.0.0.1")
def _choose_port() -> int:
env_port = os.getenv("STREAMABLE_HTTP_PORT")
if env_port:
return int(env_port)
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind((STREAMABLE_HTTP_HOST, 0))
address = cast(tuple[str, int], s.getsockname())
return address[1]
STREAMABLE_HTTP_PORT = _choose_port()
os.environ.setdefault("STREAMABLE_HTTP_PORT", str(STREAMABLE_HTTP_PORT))
STREAMABLE_HTTP_URL = f"http://{STREAMABLE_HTTP_HOST}:{STREAMABLE_HTTP_PORT}/mcp"
def create_custom_http_client(
headers: dict[str, str] | None = None,
timeout: httpx.Timeout | None = None,
auth: httpx.Auth | None = None,
) -> httpx.AsyncClient:
"""Create a custom HTTP client with specific configurations.
This function demonstrates how to configure:
- Custom SSL verification settings
- Custom timeouts
- Custom headers
- Proxy settings (commented out)
"""
if headers is None:
headers = {
"X-Custom-Client": "agents-mcp-example",
"User-Agent": "OpenAI-Agents-MCP/1.0",
}
if timeout is None:
timeout = httpx.Timeout(60.0, read=120.0)
if auth is None:
auth = None
return httpx.AsyncClient(
# Disable SSL verification for testing (not recommended for production)
verify=False,
# Set custom timeout
timeout=httpx.Timeout(60.0, read=120.0),
# Add custom headers that will be sent with every request
headers=headers,
)
async def run_with_custom_client(mcp_server: MCPServer):
"""Run the agent with a custom HTTP client configuration."""
agent = Agent(
name="Assistant",
instructions="Use the tools to answer the questions.",
mcp_servers=[mcp_server],
model_settings=ModelSettings(tool_choice="required"),
)
# Use the `add` tool to add two numbers
message = "Add these numbers: 7 and 22."
print(f"Running: {message}")
result = await Runner.run(starting_agent=agent, input=message)
print(result.final_output)
async def main():
"""Main function demonstrating different HTTP client configurations."""
print("=== Example: Custom HTTP Client with SSL disabled and custom headers ===")
async with MCPServerStreamableHttp(
name="Streamable HTTP with Custom Client",
params={
"url": STREAMABLE_HTTP_URL,
"httpx_client_factory": create_custom_http_client,
},
) as server:
trace_id = gen_trace_id()
with trace(workflow_name="Custom HTTP Client Example", trace_id=trace_id):
print(f"View trace: https://platform.openai.com/logs/trace?trace_id={trace_id}\n")
await run_with_custom_client(server)
if __name__ == "__main__":
# Let's make sure the user has uv installed
if not shutil.which("uv"):
raise RuntimeError(
"uv is not installed. Please install it: https://docs.astral.sh/uv/getting-started/installation/"
)
# We'll run the Streamable HTTP server in a subprocess. Usually this would be a remote server, but for this
# demo, we'll run it locally at STREAMABLE_HTTP_URL
process: subprocess.Popen[Any] | None = None
try:
this_dir = os.path.dirname(os.path.abspath(__file__))
server_file = os.path.join(this_dir, "server.py")
print(f"Starting Streamable HTTP server at {STREAMABLE_HTTP_URL} ...")
# Run `uv run server.py` to start the Streamable HTTP server
env = os.environ.copy()
env.setdefault("STREAMABLE_HTTP_HOST", STREAMABLE_HTTP_HOST)
env.setdefault("STREAMABLE_HTTP_PORT", str(STREAMABLE_HTTP_PORT))
process = subprocess.Popen(["uv", "run", server_file], env=env)
# Give it 3 seconds to start
time.sleep(3)
print("Streamable HTTP server started. Running example...\n\n")
except Exception as e:
print(f"Error starting Streamable HTTP server: {e}")
exit(1)
try:
asyncio.run(main())
finally:
if process:
process.terminate()
@@ -0,0 +1,27 @@
import os
import random
from mcp.server.fastmcp import FastMCP
STREAMABLE_HTTP_HOST = os.getenv("STREAMABLE_HTTP_HOST", "127.0.0.1")
STREAMABLE_HTTP_PORT = int(os.getenv("STREAMABLE_HTTP_PORT", "18080"))
# Create server
mcp = FastMCP("Echo Server", host=STREAMABLE_HTTP_HOST, port=STREAMABLE_HTTP_PORT)
@mcp.tool()
def add(a: int, b: int) -> int:
"""Add two numbers"""
print(f"[debug-server] add({a}, {b})")
return a + b
@mcp.tool()
def get_secret_word() -> str:
print("[debug-server] get_secret_word()")
return random.choice(["apple", "banana", "cherry"])
if __name__ == "__main__":
mcp.run(transport="streamable-http")
@@ -0,0 +1,13 @@
# MCP Streamable HTTP Example
This example uses a local Streamable HTTP server in [server.py](server.py).
Run the example via:
```
uv run python examples/mcp/streamablehttp_example/main.py
```
## Details
The example uses the `MCPServerStreamableHttp` class from `agents.mcp`. The script picks an open localhost port automatically (or honors `STREAMABLE_HTTP_PORT` if you set it) and starts the server at `http://<host>:<port>/mcp`. Set `STREAMABLE_HTTP_HOST` if you need a different bind address.
+104
View File
@@ -0,0 +1,104 @@
import asyncio
import os
import shutil
import socket
import subprocess
import time
from typing import Any, cast
from agents import Agent, Runner, gen_trace_id, trace
from agents.mcp import MCPServer, MCPServerStreamableHttp
from agents.model_settings import ModelSettings
STREAMABLE_HTTP_HOST = os.getenv("STREAMABLE_HTTP_HOST", "127.0.0.1")
def _choose_port() -> int:
env_port = os.getenv("STREAMABLE_HTTP_PORT")
if env_port:
return int(env_port)
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind((STREAMABLE_HTTP_HOST, 0))
address = cast(tuple[str, int], s.getsockname())
return address[1]
STREAMABLE_HTTP_PORT = _choose_port()
os.environ.setdefault("STREAMABLE_HTTP_PORT", str(STREAMABLE_HTTP_PORT))
STREAMABLE_HTTP_URL = f"http://{STREAMABLE_HTTP_HOST}:{STREAMABLE_HTTP_PORT}/mcp"
async def run(mcp_server: MCPServer):
agent = Agent(
name="Assistant",
instructions="Use the tools to answer the questions.",
mcp_servers=[mcp_server],
model_settings=ModelSettings(tool_choice="required"),
)
# Use the `add` tool to add two numbers
message = "Add these numbers: 7 and 22."
print(f"Running: {message}")
result = await Runner.run(starting_agent=agent, input=message)
print(result.final_output)
# Run the `get_weather` tool
message = "What's the weather in Tokyo?"
print(f"\n\nRunning: {message}")
result = await Runner.run(starting_agent=agent, input=message)
print(result.final_output)
# Run the `get_secret_word` tool
message = "What's the secret word?"
print(f"\n\nRunning: {message}")
result = await Runner.run(starting_agent=agent, input=message)
print(result.final_output)
async def main():
async with MCPServerStreamableHttp(
name="Streamable HTTP Python Server",
params={
"url": STREAMABLE_HTTP_URL,
},
) as server:
trace_id = gen_trace_id()
with trace(workflow_name="Streamable HTTP Example", trace_id=trace_id):
print(f"View trace: https://platform.openai.com/traces/trace?trace_id={trace_id}\n")
await run(server)
if __name__ == "__main__":
# Let's make sure the user has uv installed
if not shutil.which("uv"):
raise RuntimeError(
"uv is not installed. Please install it: https://docs.astral.sh/uv/getting-started/installation/"
)
# We'll run the Streamable HTTP server in a subprocess. Usually this would be a remote server, but for this
# demo, we'll run it locally at STREAMABLE_HTTP_URL
process: subprocess.Popen[Any] | None = None
try:
this_dir = os.path.dirname(os.path.abspath(__file__))
server_file = os.path.join(this_dir, "server.py")
print(f"Starting Streamable HTTP server at {STREAMABLE_HTTP_URL} ...")
# Run `uv run server.py` to start the Streamable HTTP server
env = os.environ.copy()
env.setdefault("STREAMABLE_HTTP_HOST", STREAMABLE_HTTP_HOST)
env.setdefault("STREAMABLE_HTTP_PORT", str(STREAMABLE_HTTP_PORT))
process = subprocess.Popen(["uv", "run", server_file], env=env)
# Give it 3 seconds to start
time.sleep(3)
print("Streamable HTTP server started. Running example...\n\n")
except Exception as e:
print(f"Error starting Streamable HTTP server: {e}")
exit(1)
try:
asyncio.run(main())
finally:
if process:
process.terminate()
@@ -0,0 +1,43 @@
import os
import random
import requests
from mcp.server.fastmcp import FastMCP
STREAMABLE_HTTP_HOST = os.getenv("STREAMABLE_HTTP_HOST", "127.0.0.1")
STREAMABLE_HTTP_PORT = int(os.getenv("STREAMABLE_HTTP_PORT", "18080"))
# Create server
mcp = FastMCP("Echo Server", host=STREAMABLE_HTTP_HOST, port=STREAMABLE_HTTP_PORT)
@mcp.tool()
def add(a: int, b: int) -> int:
"""Add two numbers"""
print(f"[debug-server] add({a}, {b})")
return a + b
@mcp.tool()
def get_secret_word() -> str:
print("[debug-server] get_secret_word()")
return random.choice(["apple", "banana", "cherry"])
@mcp.tool()
def get_current_weather(city: str) -> str:
print(f"[debug-server] get_current_weather({city})")
# Avoid slow or flaky network calls during automated runs.
try:
endpoint = "https://wttr.in"
response = requests.get(f"{endpoint}/{city}", timeout=2)
if response.ok:
return response.text
except Exception:
pass
# Fallback keeps the tool responsive even when offline.
return f"Weather data unavailable right now; assume clear skies in {city}."
if __name__ == "__main__":
mcp.run(transport="streamable-http")
@@ -0,0 +1,19 @@
# MCP Tool Filter Example
Python port of the JS `examples/mcp/tool-filter-example.ts`. It shows how to:
- Run the filesystem MCP server locally via `npx`.
- Apply a static tool filter so only specific tools are exposed to the model.
- Observe that blocked tools are not available.
- Enable `require_approval="always"` and auto-approve interruptions in code so the HITL path is exercised.
Run it with:
```bash
uv run python examples/mcp/tool_filter_example/main.py
```
Prerequisites:
- `npx` available on your PATH.
- `OPENAI_API_KEY` set for the model calls.
+75
View File
@@ -0,0 +1,75 @@
import asyncio
import os
import shutil
from typing import Any, cast
from agents import Agent, Runner, gen_trace_id, trace
from agents.mcp import MCPServerStdio
from agents.mcp.util import create_static_tool_filter
async def run_with_auto_approval(agent: Agent[Any], message: str) -> str | None:
"""Run and auto-approve interruptions."""
result = await Runner.run(agent, message)
while result.interruptions:
state = result.to_state()
for interruption in result.interruptions:
print(f"Approving a tool call... (name: {interruption.name})")
state.approve(interruption, always_approve=True)
result = await Runner.run(agent, state)
return cast(str | None, result.final_output)
async def main():
current_dir = os.path.dirname(os.path.abspath(__file__))
samples_dir = os.path.join(current_dir, "sample_files")
target_path = os.path.join(samples_dir, "test.txt")
async with MCPServerStdio(
name="Filesystem Server with filter",
params={
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", samples_dir],
"cwd": samples_dir,
},
require_approval="always",
tool_filter=create_static_tool_filter(
allowed_tool_names=["read_file", "list_directory"],
blocked_tool_names=["write_file"],
),
) as server:
agent = Agent(
name="MCP Assistant",
instructions=(
"Use only the available filesystem tools. "
"All file paths should be absolute paths inside the allowed directory. "
"If a user asks for an action that requires an unavailable tool, "
"explicitly explain that it is blocked by the tool filter."
),
mcp_servers=[server],
)
trace_id = gen_trace_id()
with trace(workflow_name="MCP Tool Filter Example", trace_id=trace_id):
print(f"View trace: https://platform.openai.com/traces/trace?trace_id={trace_id}\n")
result = await run_with_auto_approval(
agent, f"List the files in this allowed directory: {samples_dir}"
)
print(result)
blocked_result = await run_with_auto_approval(
agent,
(
f'Create a file at "{target_path}" with the text "hello". '
"If you cannot, explain that write operations are blocked by the tool filter."
),
)
print("\nAttempting to write a file (should be blocked):")
print(blocked_result)
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 Sorcerers Stone J.K. Rowling
6. The Great Gatsby F. Scott Fitzgerald
7. Charlottes 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 LEngle
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. "Dont Stop Believin" Journey
8. "Dancing Queen" ABBA
9. "Happy" Pharrell Williams
10. "Wonderwall" Oasis