Files
2026-07-13 13:31:35 +08:00

212 lines
7.3 KiB
Python

#!/usr/bin/env python3
"""
Web Search MCP Client
This client demonstrates how to interact with the Web Search MCP Server
by calling its various tools for retrieving real-time web data.
The client connects to the MCP server via stdio communication and
shows how to call each of the four specialized search tools:
- general_search: For broad web search results
- news_search: For recent news articles
- product_search: For e-commerce product information
- qna: For direct question-answer snippets
Quick Start:
1. Get a free API key from SerpAPI (https://serpapi.com/), then create a `.env` file in this folder with:
SERPAPI_KEY=your_serpapi_key_here
2. Install dependencies:
pip install -r ../../requirements.txt
3. Start the server:
python server.py
4. In a new terminal, run the client:
python client.py
# Or for interactive mode:
python client.py --interactive
"""
import asyncio
import argparse
import logging
import sys
from typing import Dict, Any
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from mcp.types import TextContent, TextResourceContents
from dotenv import load_dotenv
# Configure logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger(__name__)
async def call_tool(session: ClientSession, tool_name: str, params: Dict[str, Any]) -> None:
"""
Call a tool on the MCP server and print the results.
Args:
session: MCP client session
tool_name: Name of the tool to call
params: Parameters to pass to the tool
"""
logger.info(f"Calling {tool_name} with params: {params}")
try:
result = await session.call_tool(tool_name, arguments=params)
if result and result.content:
text_content = next((content for content in result.content
if isinstance(content, TextContent)), None)
if text_content:
print(f"\n{'='*50}")
print(f"{tool_name.upper()} RESULTS:")
print(f"{'='*50}")
print(text_content.text)
print(f"{'='*50}\n")
logger.info(f"Successfully called {tool_name}")
else:
logger.warning(f"No text content returned from {tool_name}")
print(f"No text content returned from {tool_name}")
else:
logger.warning(f"No content returned from {tool_name}")
print(f"No content returned from {tool_name}")
except Exception as e:
logger.error(f"Error calling {tool_name}: {e}")
print(f"Error calling {tool_name}: {e}")
async def run_interactive_demo(session: ClientSession) -> None:
"""
Run an interactive demo that lets the user try different search tools.
Args:
session: MCP client session
"""
print("\n=== Web Search MCP Interactive Demo ===\n")
try:
# Get list of available tools
logger.info("Getting list of tools")
tools_response = await session.list_tools()
tool_names = [tool.name for tool in tools_response.tools]
print(f"Connected to server")
print(f"Available tools: {', '.join(tool_names)}")
while True:
print("\nOptions:")
print("1. General Web Search")
print("2. News Search")
print("3. Product Search")
print("4. Question & Answer")
print("5. Exit")
choice = input("\nEnter your choice (1-5): ")
if choice == "5":
print("Exiting demo. Goodbye!")
break
query = input("Enter your search query: ")
if not query:
print("Query cannot be empty. Please try again.")
continue
if choice == "1":
await call_tool(session, "general_search", {"query": query})
elif choice == "2":
await call_tool(session, "news_search", {"query": query})
elif choice == "3": await call_tool(session, "product_search", {"query": query})
elif choice == "4":
await call_tool(session, "qna", {"question": query})
else:
print("Invalid choice. Please enter a number between 1 and 5.")
except Exception as e:
logger.error(f"Error in interactive demo: {e}")
print(f"Error: {e}")
async def run_all_tools_demo(session: ClientSession) -> None:
"""Run a demonstration of all available tools with preset queries."""
print("\n=== Running All Tools Demo ===\n")
try:
# Get the README resource
logger.info("Getting README resource")
readme_response = await session.read_resource("readme://")
if readme_response and readme_response.contents:
text_resource = next((content for content in readme_response.contents
if isinstance(content, TextResourceContents)), None)
if text_resource:
print(f"\n{'='*50}")
print(f"README RESOURCE:")
print(f"{'='*50}")
print(text_resource.text)
print(f"{'='*50}\n")
# Test the general_search tool
await call_tool(session, "general_search", {"query": "latest AI trends 2025", "num_results": 3})
# Test the news_search tool
await call_tool(session, "news_search", {"query": "AI policy updates", "num_results": 3})
# Test the product_search tool
await call_tool(session, "product_search", {"query": "best AI gadgets 2025", "num_results": 3})
# Test the qna tool
await call_tool(session, "qna", {"question": "what is artificial intelligence"})
except Exception as e:
logger.error(f"Error running tools demo: {e}")
print(f"Error: {e}")
async def main() -> None:
"""Main entry point for the client."""
# Load environment variables if dotenv module is available
try:
load_dotenv()
except:
pass
parser = argparse.ArgumentParser(description="Web Search MCP Client")
parser.add_argument(
"--interactive",
action="store_true",
help="Run in interactive mode"
)
args = parser.parse_args()
logger.info("Starting Web Search MCP client")
server_params = StdioServerParameters(
command="python",
args=["server.py"]
)
try:
logger.info("Connecting to server...")
async with stdio_client(server_params) as (reader, writer):
async with ClientSession(reader, writer) as session:
logger.info("Initializing session")
await session.initialize()
if args.interactive:
await run_interactive_demo(session)
else:
await run_all_tools_demo(session)
except KeyboardInterrupt:
print("\nClient terminated by user.")
except Exception as e:
logger.error(f"Error during client execution: {e}")
import traceback
logger.error(f"Traceback: {traceback.format_exc()}")
print(f"Error: {e}")
sys.exit(1)
if __name__ == "__main__":
asyncio.run(main())