ai-agent-book 精选快照(<2MB 代码与文档,来自 github.com/bojieli/ai-agent-book)
Build latest book artifacts / build (push) Canceled after 0s
dependency resolution / resolve (3.11) (push) Canceled after 0s
dependency resolution / resolve (3.13) (push) Canceled after 0s
deploy-pages / build (push) Canceled after 0s
deploy-pages / deploy (push) Canceled after 0s
i18n consistency check / check (push) Canceled after 0s
provider adoption tests / test (chapter2/context-compression) (push) Canceled after 0s
provider adoption tests / test (chapter2/prompt-injection) (push) Canceled after 0s
provider adoption tests / test (chapter2/system-hint) (push) Canceled after 0s
provider adoption tests / test (chapter3/log-sanitization) (push) Canceled after 0s
web-search-agent tests / test (push) Canceled after 0s
web-search-agent tests / agentbook (push) Canceled after 0s

This commit is contained in:
2026-08-20 13:12:50 +00:00
commit b119135836
10275 changed files with 3284984 additions and 0 deletions
@@ -0,0 +1,111 @@
"""
Advanced example: Using multiple MCP servers together.
This example demonstrates how to:
1. Connect multiple MCP servers (Gmail + Filesystem) to browser-use
2. Sign up for a new account on a website
3. Save registration details to a file
4. Retrieve the verification link from Gmail
5. Complete the verification process
"""
import asyncio
import os
from browser_use import Agent, Tools
from browser_use.llm.openai.chat import ChatOpenAI
from browser_use.mcp.client import MCPClient
async def main():
"""Sign up for account, save details, and verify via Gmail."""
# Initialize tools
tools = Tools()
# Connect to Gmail MCP Server
# Requires Gmail API credentials - see: https://github.com/GongRzhe/Gmail-MCP-Server#setup
gmail_env = {}
if client_id := os.getenv('GMAIL_CLIENT_ID'):
gmail_env['GMAIL_CLIENT_ID'] = client_id
if client_secret := os.getenv('GMAIL_CLIENT_SECRET'):
gmail_env['GMAIL_CLIENT_SECRET'] = client_secret
if refresh_token := os.getenv('GMAIL_REFRESH_TOKEN'):
gmail_env['GMAIL_REFRESH_TOKEN'] = refresh_token
gmail_client = MCPClient(server_name='gmail', command='npx', args=['gmail-mcp-server'], env=gmail_env)
# Connect to Filesystem MCP Server for saving registration details
filesystem_client = MCPClient(
server_name='filesystem',
command='npx',
args=['-y', '@modelcontextprotocol/server-filesystem', os.path.expanduser('~/Desktop')],
)
# Connect and register tools from both servers
print('Connecting to Gmail MCP server...')
await gmail_client.connect()
await gmail_client.register_to_tools(tools)
print('Connecting to Filesystem MCP server...')
await filesystem_client.connect()
await filesystem_client.register_to_tools(tools)
# Create agent with extended system prompt for using multiple MCP servers
agent = Agent(
task='Sign up for a new Anthropic account using the email example@gmail.com, save the registration details to a file',
llm=ChatOpenAI(model='gpt-4.1-mini'),
tools=tools,
extend_system_message="""
You have access to both Gmail and Filesystem tools through MCP servers. When signing up for accounts:
1. Fill out registration forms with the provided email address
2. Use the filesystem tools to create a file called 'anthropic_registration.txt' on the Desktop containing:
- Email used for registration
- Timestamp of registration
- Any username or account details
3. After submitting the registration, use the Gmail MCP tools to check for verification emails
4. Search for recent emails (within the last 5 minutes) from the service you're signing up for
5. Look for verification links or codes in those emails
6. Append the verification details to the registration file
7. Use any verification links or codes found to complete the account setup
8. Update the file with the final account status
Available tools include:
Gmail tools:
- search_emails: Search for emails by query (e.g., "from:noreply@anthropic.com")
- get_email: Get full email content by ID
- list_emails: List recent emails
Filesystem tools:
- read_file: Read content from a file
- write_file: Write content to a file
- list_directory: List files in a directory
Always wait a few seconds after submitting a form before checking Gmail to allow the email to arrive.
""",
)
# Run the agent
result = await agent.run()
print('\nTask completed!')
print(f'Result: {result}')
# Disconnect both MCP clients
await gmail_client.disconnect()
await filesystem_client.disconnect()
if __name__ == '__main__':
# Prerequisites:
# 1. Install both MCP servers:
# npm install -g gmail-mcp-server
# npm install -g @modelcontextprotocol/server-filesystem
# 2. Set up Gmail API credentials following: https://github.com/GongRzhe/Gmail-MCP-Server#setup
# 3. Set these environment variables:
# export GMAIL_CLIENT_ID="your-client-id"
# export GMAIL_CLIENT_SECRET="your-client-secret"
# export GMAIL_REFRESH_TOKEN="your-refresh-token"
asyncio.run(main())
@@ -0,0 +1,324 @@
"""
Advanced example of building an AI assistant that uses browser-use MCP server.
This example shows how to build a more sophisticated MCP client that:
- Connects to multiple MCP servers (browser-use + filesystem)
- Orchestrates complex multi-step workflows
- Handles errors and retries
- Provides a conversational interface
Prerequisites:
1. Install required packages:
pip install 'browser-use[cli]'
2. Start the browser-use MCP server:
uvx 'browser-use[cli]' --mcp
3. Run this example:
python advanced_server.py
This demonstrates real-world usage patterns for the MCP protocol.
"""
import asyncio
import json
from dataclasses import dataclass
from datetime import datetime
from typing import Any
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from mcp.types import TextContent, Tool
@dataclass
class TaskResult:
"""Result of executing a task."""
success: bool
data: Any
error: str | None = None
timestamp: datetime | None = None
def __post_init__(self):
if self.timestamp is None:
self.timestamp = datetime.now()
class AIAssistant:
"""An AI assistant that uses MCP servers to perform complex tasks."""
def __init__(self):
self.servers: dict[str, ClientSession] = {}
self.tools: dict[str, Tool] = {}
self.history: list[TaskResult] = []
async def connect_server(self, name: str, command: str, args: list[str], env: dict[str, str] | None = None):
"""Connect to an MCP server and discover its tools."""
print(f'\n🔌 Connecting to {name} server...')
server_params = StdioServerParameters(command=command, args=args, env=env or {})
try:
# Create connection
read, write = await stdio_client(server_params).__aenter__()
session = ClientSession(read, write)
await session.__aenter__()
await session.initialize()
# Store session
self.servers[name] = session
# Discover tools
tools_result = await session.list_tools()
tools = tools_result.tools
for tool in tools:
# Prefix tool names with server name to avoid conflicts
prefixed_name = f'{name}.{tool.name}'
self.tools[prefixed_name] = tool
print(f' ✓ Discovered: {prefixed_name}')
print(f'✅ Connected to {name} with {len(tools)} tools')
except Exception as e:
print(f'❌ Failed to connect to {name}: {e}')
raise
async def disconnect_all(self):
"""Disconnect from all MCP servers."""
for name, session in self.servers.items():
try:
await session.__aexit__(None, None, None)
print(f'📴 Disconnected from {name}')
except Exception as e:
print(f'⚠️ Error disconnecting from {name}: {e}')
async def call_tool(self, tool_name: str, arguments: dict[str, Any]) -> TaskResult:
"""Call a tool on the appropriate MCP server."""
# Parse server and tool name
if '.' not in tool_name:
return TaskResult(False, None, "Invalid tool name format. Use 'server.tool'")
server_name, actual_tool_name = tool_name.split('.', 1)
# Check if server is connected
if server_name not in self.servers:
return TaskResult(False, None, f"Server '{server_name}' not connected")
# Call the tool
try:
session = self.servers[server_name]
result = await session.call_tool(actual_tool_name, arguments)
# Extract text content
text_content = [c.text for c in result.content if isinstance(c, TextContent)]
data = text_content[0] if text_content else str(result.content)
task_result = TaskResult(True, data)
self.history.append(task_result)
return task_result
except Exception as e:
error_result = TaskResult(False, None, str(e))
self.history.append(error_result)
return error_result
async def search_and_save(self, query: str, output_file: str) -> TaskResult:
"""Search for information and save results to a file."""
print(f'\n🔍 Searching for: {query}')
# Step 1: Navigate to search engine
print(' 1️⃣ Opening DuckDuckGo...')
nav_result = await self.call_tool('browser.browser_navigate', {'url': f'https://duckduckgo.com/?q={query}'})
if not nav_result.success:
return nav_result
await asyncio.sleep(2) # Wait for page load
# Step 2: Get search results
print(' 2️⃣ Extracting search results...')
extract_result = await self.call_tool(
'browser.browser_extract_content',
{'query': 'Extract the top 5 search results with titles and descriptions', 'extract_links': True},
)
if not extract_result.success:
return extract_result
# Step 3: Save to file (if filesystem server is connected)
if 'filesystem' in self.servers:
print(f' 3️⃣ Saving results to {output_file}...')
save_result = await self.call_tool(
'filesystem.write_file',
{'path': output_file, 'content': f'Search Query: {query}\n\nResults:\n{extract_result.data}'},
)
if save_result.success:
print(f' ✅ Results saved to {output_file}')
else:
print(' ⚠️ Filesystem server not connected, skipping save')
return extract_result
async def monitor_page_changes(self, url: str, duration: int = 10, interval: int = 2):
"""Monitor a webpage for changes over time."""
print(f'\n📊 Monitoring {url} for {duration} seconds...')
# Navigate to page
await self.call_tool('browser.browser_navigate', {'url': url})
await asyncio.sleep(2)
changes = []
start_time = datetime.now()
while (datetime.now() - start_time).seconds < duration:
# Get current state
state_result = await self.call_tool('browser.browser_get_state', {'include_screenshot': False})
if state_result.success:
state = json.loads(state_result.data)
changes.append(
{
'timestamp': datetime.now().isoformat(),
'title': state.get('title', ''),
'element_count': len(state.get('interactive_elements', [])),
}
)
print(f' 📸 Captured state at {changes[-1]["timestamp"]}')
await asyncio.sleep(interval)
return TaskResult(True, changes)
async def fill_form_workflow(self, form_url: str, form_data: dict[str, str]):
"""Navigate to a form and fill it out."""
print(f'\n📝 Form filling workflow for {form_url}')
# Step 1: Navigate to form
print(' 1️⃣ Navigating to form...')
nav_result = await self.call_tool('browser.browser_navigate', {'url': form_url})
if not nav_result.success:
return nav_result
await asyncio.sleep(2)
# Step 2: Get form elements
print(' 2️⃣ Analyzing form elements...')
state_result = await self.call_tool('browser.browser_get_state', {'include_screenshot': False})
if not state_result.success:
return state_result
state = json.loads(state_result.data)
# Step 3: Fill form fields
print(' 3️⃣ Filling form fields...')
filled_fields = []
for element in state.get('interactive_elements', []):
# Look for input fields
if element.get('tag') in ['input', 'textarea']:
# Try to match field by placeholder or nearby text
for field_name, field_value in form_data.items():
element_text = str(element).lower()
if field_name.lower() in element_text:
print(f' ✏️ Filling {field_name}...')
type_result = await self.call_tool(
'browser.browser_type', {'index': element['index'], 'text': field_value}
)
if type_result.success:
filled_fields.append(field_name)
await asyncio.sleep(0.5)
break
return TaskResult(True, {'filled_fields': filled_fields, 'form_data': form_data, 'url': form_url})
async def main():
"""Main demonstration of advanced MCP client usage."""
print('Browser-Use MCP Client - Advanced Example')
print('=' * 50)
assistant = AIAssistant()
try:
# Connect to browser-use MCP server
await assistant.connect_server(name='browser', command='uvx', args=['browser-use[cli]', '--mcp'])
# Optionally connect to filesystem server
# Note: Uncomment to enable file operations
# await assistant.connect_server(
# name="filesystem",
# command="npx",
# args=["@modelcontextprotocol/server-filesystem", "."]
# )
print('\n' + '=' * 50)
print('Starting demonstration workflows...')
print('=' * 50)
# Demo 1: Search and extract
print('\n📌 Demo 1: Web Search and Extraction')
search_result = await assistant.search_and_save(query='MCP protocol browser automation', output_file='search_results.txt')
print(f'Search completed: {"" if search_result.success else ""}')
# Demo 2: Multi-tab comparison
print('\n📌 Demo 2: Multi-tab News Comparison')
news_sites = [('BBC News', 'https://bbc.com/news'), ('CNN', 'https://cnn.com'), ('Reuters', 'https://reuters.com')]
for i, (name, url) in enumerate(news_sites):
print(f'\n 📰 Opening {name}...')
await assistant.call_tool('browser.browser_navigate', {'url': url, 'new_tab': i > 0})
await asyncio.sleep(2)
# List all tabs
tabs_result = await assistant.call_tool('browser.browser_list_tabs', {})
if tabs_result.success:
tabs = json.loads(tabs_result.data)
print(f'\n 📑 Opened {len(tabs)} news sites:')
for tab in tabs:
print(f' - Tab {tab["index"]}: {tab["title"]}')
# Demo 3: Form filling
print('\n📌 Demo 3: Automated Form Filling')
form_result = await assistant.fill_form_workflow(
form_url='https://httpbin.org/forms/post',
form_data={
'custname': 'AI Assistant',
'custtel': '555-0123',
'custemail': 'ai@example.com',
'comments': 'Testing MCP browser automation',
},
)
if form_result.success:
print(f' ✅ Filled {len(form_result.data["filled_fields"])} fields')
# Demo 4: Page monitoring
print('\n📌 Demo 4: Dynamic Page Monitoring')
monitor_result = await assistant.monitor_page_changes(url='https://time.is/', duration=10, interval=3)
if monitor_result.success:
print(f' 📊 Collected {len(monitor_result.data)} snapshots')
# Summary
print('\n' + '=' * 50)
print('📊 Session Summary')
print('=' * 50)
success_count = sum(1 for r in assistant.history if r.success)
total_count = len(assistant.history)
print(f'Total operations: {total_count}')
print(f'Successful: {success_count}')
print(f'Failed: {total_count - success_count}')
print(f'Success rate: {success_count / total_count * 100:.1f}%')
except Exception as e:
print(f'\n❌ Fatal error: {e}')
finally:
# Always disconnect
print('\n🧹 Cleaning up...')
await assistant.disconnect_all()
print('✨ Demo complete!')
if __name__ == '__main__':
asyncio.run(main())
@@ -0,0 +1,44 @@
"""
Simple example of using MCP client with browser-use.
This example shows how to connect to an MCP server and use its tools with an agent.
"""
import asyncio
import os
from browser_use import Agent, Tools
from browser_use.llm.openai.chat import ChatOpenAI
from browser_use.mcp.client import MCPClient
async def main():
# Initialize tools
tools = Tools()
# Connect to a filesystem MCP server
# This server provides tools to read/write files in a directory
mcp_client = MCPClient(
server_name='filesystem', command='npx', args=['@modelcontextprotocol/server-filesystem', os.path.expanduser('~/Desktop')]
)
# Connect and register MCP tools
await mcp_client.connect()
await mcp_client.register_to_tools(tools)
# Create agent with MCP-enabled tools
agent = Agent(
task='List all files on the Desktop and read the content of any .txt files you find',
llm=ChatOpenAI(model='gpt-4.1-mini'),
tools=tools,
)
# Run the agent - it now has access to filesystem tools
await agent.run()
# Disconnect when done
await mcp_client.disconnect()
if __name__ == '__main__':
asyncio.run(main())
@@ -0,0 +1,138 @@
"""
Simple example of connecting to browser-use MCP server as a client.
This example demonstrates how to use the MCP client library to connect to
a running browser-use MCP server and call its browser automation tools.
Prerequisites:
1. Install required packages:
pip install 'browser-use[cli]'
2. Start the browser-use MCP server in a separate terminal:
uvx browser-use --mcp
3. Run this client example:
python simple_server.py
This shows the actual MCP protocol flow between a client and the browser-use server.
"""
import asyncio
import json
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from mcp.types import TextContent
async def run_simple_browser_automation():
"""Connect to browser-use MCP server and perform basic browser automation."""
# Create connection parameters for the browser-use MCP server
server_params = StdioServerParameters(command='uvx', args=['browser-use', '--mcp'], env={})
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
# Initialize the connection
await session.initialize()
print('✅ Connected to browser-use MCP server')
# List available tools
tools_result = await session.list_tools()
tools = tools_result.tools
print(f'\n📋 Available tools: {len(tools)}')
for tool in tools:
print(f' - {tool.name}: {tool.description}')
# Example 1: Navigate to a website
print('\n🌐 Navigating to example.com...')
result = await session.call_tool('browser_navigate', arguments={'url': 'https://example.com'})
# Handle different content types
content = result.content[0]
if isinstance(content, TextContent):
print(f'Result: {content.text}')
else:
print(f'Result: {content}')
# Example 2: Get the current browser state
print('\n🔍 Getting browser state...')
result = await session.call_tool('browser_get_state', arguments={'include_screenshot': False})
# Handle different content types
content = result.content[0]
if isinstance(content, TextContent):
state = json.loads(content.text)
else:
state = json.loads(str(content))
print(f'Page title: {state["title"]}')
print(f'URL: {state["url"]}')
print(f'Interactive elements found: {len(state["interactive_elements"])}')
# Example 3: Open a new tab
print('\n📑 Opening Python.org in a new tab...')
result = await session.call_tool('browser_navigate', arguments={'url': 'https://python.org', 'new_tab': True})
# Handle different content types
content = result.content[0]
if isinstance(content, TextContent):
print(f'Result: {content.text}')
else:
print(f'Result: {content}')
# Example 4: List all open tabs
print('\n📋 Listing all tabs...')
result = await session.call_tool('browser_list_tabs', arguments={})
# Handle different content types
content = result.content[0]
if isinstance(content, TextContent):
tabs = json.loads(content.text)
else:
tabs = json.loads(str(content))
for tab in tabs:
print(f' Tab {tab["index"]}: {tab["title"]} - {tab["url"]}')
# Example 5: Click on an element
print('\n👆 Looking for clickable elements...')
state_result = await session.call_tool('browser_get_state', arguments={'include_screenshot': False})
# Handle different content types
content = state_result.content[0]
if isinstance(content, TextContent):
state = json.loads(content.text)
else:
state = json.loads(str(content))
# Find a link to click
link_element = None
for elem in state['interactive_elements']:
if elem['tag'] == 'a' and elem.get('href'):
link_element = elem
break
if link_element:
print(f'Clicking on link: {link_element.get("text", "unnamed")[:50]}...')
result = await session.call_tool('browser_click', arguments={'index': link_element['index']})
# Handle different content types
content = result.content[0]
if isinstance(content, TextContent):
print(f'Result: {content.text}')
else:
print(f'Result: {content}')
print('\n✨ Simple browser automation demo complete!')
async def main():
"""Main entry point."""
print('Browser-Use MCP Client - Simple Example')
print('=' * 50)
print('\nConnecting to browser-use MCP server...\n')
try:
await run_simple_browser_automation()
except Exception as e:
print(f'\n❌ Error: {e}')
print('\nMake sure the browser-use MCP server is running:')
print(" uvx 'browser-use[cli]' --mcp")
if __name__ == '__main__':
asyncio.run(main())