DEV Community

Cover image for Building a Lightweight Remote Gradio MCP Server with DuckDuckGo Search
0xkoji
0xkoji

Posted on

Building a Lightweight Remote Gradio MCP Server with DuckDuckGo Search

Gradio now lets you spin up an MCP (Machine Control Plane) server with almost no boilerplate, so I decided to give it a try.
The official guide is here: https://www.gradio.app/guides/building-mcp-server-with-gradio

Running everything locally worked great—but because Gradio lives in the Hugging Face ecosystem, I wondered if I could deploy the exact same code to a Space and treat it as a lightweight remote MCP server. Spoiler: you can!


1 Create a project

uv init gradio-mcp-server cd gradio-mcp-server 
Enter fullscreen mode Exit fullscreen mode

uv is the Rust-powered Python package/build tool from Astral. (Any env manager works; uv just makes everything reproducible.)


2 Install Gradio + DuckDuckGo Search

uv add "gradio[mcp]" duckduckgo-search 
Enter fullscreen mode Exit fullscreen mode

The [mcp] extra pulls in the small Gradio extension that exposes the MCP interface.


3 Write the code

Building an MCP server is identical to making a regular Gradio app—the only difference is passing mcp_server=True to launch().

Below is the exact main.py generated by uv init, with a single helper function added for news search.

import gradio as gr from duckduckgo_search import DDGS def news( keywords: str, region: str = "wt-wt", safesearch: str = "moderate", timelimit: str | None = None, max_results: int | None = None, ) -> list[dict[str, str]]: """ DuckDuckGo News search. Query-string reference: https://duckduckgo.com/params Args: keywords: Search query. region: e.g. wt-wt, us-en, uk-en. Defaults to "wt-wt". safesearch: on | moderate | off. Defaults to "moderate". timelimit: d | w | m. If None, no date filter. max_results: Max rows to return. None = first batch only. Returns: List of news result dictionaries. """ # Note: use the incoming `keywords`, not a hard-coded string  results = DDGS().news( keywords=keywords, region=region, safesearch=safesearch, timelimit=timelimit, max_results=max_results, ) return list(results) # The generator → list  demo = gr.Interface( fn=news, inputs=[ gr.Textbox(label="Keywords"), gr.Dropdown(["wt-wt", "us-en", "uk-en"], label="Region", value="wt-wt"), gr.Dropdown(["on", "moderate", "off"], label="Safe Search", value="moderate"), gr.Dropdown([None, "d", "w", "m"], label="Time Limit", value=None), gr.Number(label="Max Results", value=10), ], outputs=gr.JSON(), title="News Search", description="Search news via DuckDuckGo", ) demo.launch(mcp_server=True) 
Enter fullscreen mode Exit fullscreen mode

4 Run the server locally

uv run python main.py 
Enter fullscreen mode Exit fullscreen mode

Typical output:

* Running on local URL: http://127.0.0.1:7860 🔨 MCP server (SSE) at: http://127.0.0.1:7860/gradio_api/mcp/sse 
Enter fullscreen mode Exit fullscreen mode

Open http://127.0.0.1:7860 in a browser—Gradio’s built-in UI acts as a handy debug dashboard.


5 Add the server to an MCP client

I’m using Cursor as the client. Copy–paste the following snippet into settings.json (adjust the URL if you changed ports):

"gradio": { "command": "npx", "args": [ "mcp-remote", "http://127.0.0.1:7860/gradio_api/mcp/sse", "--transport", "sse-only" ] } 
Enter fullscreen mode Exit fullscreen mode

Cursor immediately detected the server:

Cursor MCP target


6 Quick test

In Cursor’s chat pane, type:

Tell me the latest news about Real Madrid. 
Enter fullscreen mode Exit fullscreen mode

cursor_test

Even with max_results=10, DuckDuckGo sometimes returns fewer hits (I got four). But the round-trip works, and the Gradio interface logs every request.


7 Deploy to Hugging Face Spaces

I initially created a fresh Space but the build kept failing.
Forking wjlgatech/mcp-demo1 and pushing my code worked out of the box:

https://huggingface.co/spaces/baxin/news-search-mcp

It’s noticeably slower than localhost, and occasionally the client fails to detect the endpoint on the first try. Still, it’s perfectly usable.

"gradio": { "command": "npx", "args": [ "mcp-remote", "https://baxin-newssearch-mcp.hf.space/gradio_api/mcp/sse", "--transport", "sse-only" ] } 
Enter fullscreen mode Exit fullscreen mode

Remote MCP server


Closing thoughts

Compared with the TypeScript version I wrote a while back, this Python + Gradio approach needs far less boilerplate, and the built-in debug UI is a pleasant bonus. For small one-off tools, turning any Gradio app into a portable MCP server feels almost too easy.

If you run into flaky connections on Spaces, try:

  • Enabling share=True in launch() (sometimes steadies SSE handshakes).
  • Switching from SSE-only to websocket transport.
  • Restarting the Space—you won’t lose your files.

Happy hacking!

Top comments (0)