Shrijeeth-Suresh
commited on
Commit
·
3e53b8b
1
Parent(s):
7aa98fe
feat: create web search MCP with DuckDuckGo integration and Gradio UI
Browse files- .gitignore +7 -0
- Makefile +11 -0
- app.py +25 -0
- requirements.txt +8 -0
- search_engines/__init__.py +0 -0
- search_engines/duckduckgo.py +30 -0
- utils/__init__.py +0 -0
- utils/helpers.py +21 -0
.gitignore
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.venv
|
2 |
+
poetry.lock
|
3 |
+
.env
|
4 |
+
.ruff_cache
|
5 |
+
*.egg-info
|
6 |
+
build
|
7 |
+
__pycache__
|
Makefile
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
install:
|
2 |
+
python -m pip install .
|
3 |
+
|
4 |
+
format:
|
5 |
+
python -m ruff format .
|
6 |
+
|
7 |
+
lint:
|
8 |
+
python -m ruff check --select I,RUF022 --fix .
|
9 |
+
|
10 |
+
check:
|
11 |
+
python -m ruff check .
|
app.py
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
|
3 |
+
from search_engines.duckduckgo import duckduckgo_search
|
4 |
+
|
5 |
+
duckduckgo_interface = gr.Interface(
|
6 |
+
fn=duckduckgo_search,
|
7 |
+
inputs=[
|
8 |
+
gr.Textbox(label="Search Query"),
|
9 |
+
gr.Slider(minimum=1, maximum=10, step=1, value=5, label="Number of Results"),
|
10 |
+
],
|
11 |
+
outputs=gr.Dataframe(label="Search Results", headers=["title", "body", "link"]),
|
12 |
+
title="DuckDuckGo Search",
|
13 |
+
description="Search the web using DuckDuckGo Search Engine.",
|
14 |
+
)
|
15 |
+
|
16 |
+
app = gr.TabbedInterface(
|
17 |
+
interface_list=[duckduckgo_interface],
|
18 |
+
tab_names=["DuckDuckGo Search"],
|
19 |
+
)
|
20 |
+
|
21 |
+
|
22 |
+
if __name__ == "__main__":
|
23 |
+
app.launch(
|
24 |
+
mcp_server=True,
|
25 |
+
)
|
requirements.txt
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
python-dotenv==1.1.0
|
2 |
+
gradio==5.33.0
|
3 |
+
pydantic==2.11.5
|
4 |
+
pydantic_settings==2.9.1
|
5 |
+
langchain-community==0.3.24
|
6 |
+
duckduckgo-search==8.0.2
|
7 |
+
pandas==2.2.3
|
8 |
+
ruff==0.11.12
|
search_engines/__init__.py
ADDED
File without changes
|
search_engines/duckduckgo.py
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import pandas as pd
|
2 |
+
from langchain_community.tools import DuckDuckGoSearchResults
|
3 |
+
from langchain_community.utilities.duckduckgo_search import DuckDuckGoSearchAPIWrapper
|
4 |
+
|
5 |
+
from utils.helpers import map_results
|
6 |
+
|
7 |
+
|
8 |
+
async def duckduckgo_search(query: str, max_results: int = 5) -> pd.DataFrame:
|
9 |
+
"""
|
10 |
+
Given a search query, returns the search results from DuckDuckGo.
|
11 |
+
|
12 |
+
Args:
|
13 |
+
query (str): The search query.
|
14 |
+
max_results (int, optional): The number of maximum results to return. Defaults to 5.
|
15 |
+
|
16 |
+
Returns:
|
17 |
+
str: The search results from DuckDuckGo Search Engine.
|
18 |
+
"""
|
19 |
+
ddg = DuckDuckGoSearchResults(
|
20 |
+
output_format="list",
|
21 |
+
)
|
22 |
+
ddg.max_results = max_results
|
23 |
+
results = await ddg.ainvoke(query)
|
24 |
+
mapping = {
|
25 |
+
"title": "title",
|
26 |
+
"link": "link",
|
27 |
+
"snippet": "body",
|
28 |
+
}
|
29 |
+
results = await map_results(results, mapping)
|
30 |
+
return results
|
utils/__init__.py
ADDED
File without changes
|
utils/helpers.py
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import pandas as pd
|
2 |
+
|
3 |
+
|
4 |
+
async def map_results(results: list[dict], mapping: dict[str, str]) -> pd.DataFrame:
|
5 |
+
values = list(mapping.values())
|
6 |
+
if "title" not in values:
|
7 |
+
raise ValueError("title is required in the mapping")
|
8 |
+
if "link" not in values:
|
9 |
+
raise ValueError("link is required in the mapping")
|
10 |
+
if "body" not in values:
|
11 |
+
raise ValueError("body is required in the mapping")
|
12 |
+
mapped_data = [
|
13 |
+
{mapping.get(key, key): value for key, value in result.items()}
|
14 |
+
for result in results
|
15 |
+
]
|
16 |
+
result = {"title": [], "link": [], "body": []}
|
17 |
+
for data in mapped_data:
|
18 |
+
result["title"].append(data["title"])
|
19 |
+
result["link"].append(data["link"])
|
20 |
+
result["body"].append(data["body"])
|
21 |
+
return pd.DataFrame(result)
|