Skip to main content
Glama
alexcong

Gemini DeepSearch MCP

by alexcong

deep_search

Perform advanced web research using Google Gemini models and Google Search to investigate queries with citation-rich answers and adjustable effort levels.

Instructions

Perform a deep search on a given query using an advanced web research agent.

Args: query: The research question or topic to investigate. effort: The amount of effect for the research, low, medium or hight (default: low).

Returns: A dictionary containing the file path to a JSON file with the answer and sources.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
queryYesSearch query string
effortNoSearch effortlow

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • Asynchronous handler for the 'deep_search' MCP tool. Registers the tool with FastMCP, handles input validation via Annotated types, configures the research graph based on effort level, invokes it asynchronously, and returns answer and sources.
    @mcp.tool()
    async def deep_search(
        query: Annotated[str, Field(description="Search query string")],
        effort: Annotated[
            Literal["low", "medium", "high"], Field(description="Search effort")
        ] = "low",
    ) -> dict:
        """Perform a deep search on a given query using an advanced web research agent.
    
        Args:
            query: The research question or topic to investigate.
            effort: The amount of effect for the research, low, medium or hight (default: low).
    
        Returns:
            A dictionary containing the answer to the query and a list of sources used.
        """
        # Set search query count, research loops and reasoning model based on effort level
        if effort == "low":
            initial_search_query_count = 1
            max_research_loops = 1
            reasoning_model = "gemini-2.5-flash"
        elif effort == "medium":
            initial_search_query_count = 3
            max_research_loops = 2
            reasoning_model = "gemini-2.5-flash"
        else:  # high effort
            initial_search_query_count = 5
            max_research_loops = 3
            reasoning_model = "gemini-2.5-pro"
    
        # Prepare the input state with the user's query
        input_state = {
            "messages": [HumanMessage(content=query)],
            "search_query": [],
            "web_research_result": [],
            "sources_gathered": [],
            "initial_search_query_count": initial_search_query_count,
            "max_research_loops": max_research_loops,
            "reasoning_model": reasoning_model,
        }
    
        query_generator_model: str = "gemini-2.5-flash"
        web_search_model: str = "gemini-2.5-flash-lite-preview-06-17"
        reflection_model: str = "gemini-2.5-flash"
        answer_model: str = "gemini-2.5-pro"
    
        # Configuration for the agent
        config = {
            "configurable": {
                "query_generator_model": query_generator_model,
                "web_search_model": web_search_model,
                "reflection_model": reflection_model,
                "answer_model": answer_model,
            }
        }
    
        # Run the agent graph to process the query in a separate thread to avoid blocking
        result = await asyncio.to_thread(graph.invoke, input_state, config)
    
        # Extract the final answer and sources from the result
        answer = (
            result["messages"][-1].content if result["messages"] else "No answer generated."
        )
        sources = result["sources_gathered"]
    
        return {"answer": answer, "sources": sources}
  • Synchronous handler for the 'deep_search' MCP tool (stdio server). Similar to app.py version but synchronous, writes result to a temporary JSON file, and returns the file path.
    @mcp.tool()
    def deep_search(
        query: Annotated[str, Field(description="Search query string")],
        effort: Annotated[
            Literal["low", "medium", "high"], Field(description="Search effort")
        ] = "low",
    ) -> dict:
        """Perform a deep search on a given query using an advanced web research agent.
    
        Args:
            query: The research question or topic to investigate.
            effort: The amount of effect for the research, low, medium or hight (default: low).
    
        Returns:
            A dictionary containing the file path to a JSON file with the answer and sources.
        """
        # Set search query count, research loops and reasoning model based on effort level
        if effort == "low":
            initial_search_query_count = 1
            max_research_loops = 1
            reasoning_model = "gemini-2.5-flash-preview-05-20"
        elif effort == "medium":
            initial_search_query_count = 3
            max_research_loops = 2
            reasoning_model = "gemini-2.5-flash-preview-05-20"
        else:  # high effort
            initial_search_query_count = 5
            max_research_loops = 3
            reasoning_model = "gemini-2.5-pro-preview-06-05"
    
        # Prepare the input state with the user's query
        input_state = {
            "messages": [HumanMessage(content=query)],
            "search_query": [],
            "web_research_result": [],
            "sources_gathered": [],
            "initial_search_query_count": initial_search_query_count,
            "max_research_loops": max_research_loops,
            "reasoning_model": reasoning_model,
        }
    
        query_generator_model: str = "gemini-2.5-flash-preview-05-20"
        reflection_model: str = "gemini-2.5-flash-preview-05-20"
        answer_model: str = "gemini-2.5-pro-preview-06-05"
    
        # Configuration for the agent
        config = {
            "configurable": {
                "query_generator_model": query_generator_model,
                "reflection_model": reflection_model,
                "answer_model": answer_model,
            }
        }
    
        # Run the agent graph to process the query in a separate thread to avoid blocking
        result = graph.invoke(input_state, config)
    
        # Extract the final answer and sources from the result
        answer = (
            result["messages"][-1].content if result["messages"] else "No answer generated."
        )
        sources = result["sources_gathered"]
    
        # Create filename from first few characters of query (spaces replaced with underscores)
        sanitized_query = re.sub(r'[^\w\s-]', '', query)[:20]  # Remove special chars, keep first 20 chars
        filename = re.sub(r'\s+', '_', sanitized_query.strip()) + '.json'
        file_path = os.path.join(tempfile.gettempdir(), filename)
        
        # Write answer and sources to JSON file
        result_data = {"answer": answer, "sources": sources}
        with open(file_path, 'w', encoding='utf-8') as f:
            json.dump(result_data, f, ensure_ascii=False, indent=2)
        
        return {"file_path": file_path}
  • Pydantic schemas used within the research graph for structured LLM outputs: SearchQueryList for generating search queries and Reflection for evaluating research sufficiency.
    class SearchQueryList(BaseModel):
        query: List[str] = Field(
            description="A list of search queries to be used for web research."
        )
        rationale: str = Field(
            description="A brief explanation of why these queries are relevant to the research topic."
        )
    
    
    class Reflection(BaseModel):
        is_sufficient: bool = Field(
            description="Whether the provided summaries are sufficient to answer the user's question."
        )
        knowledge_gap: str = Field(
            description="A description of what information is missing or needs clarification."
        )
        follow_up_queries: List[str] = Field(
            description="A list of follow-up queries to address the knowledge gap."
        )
  • LangGraph definition invoked by deep_search handlers. Defines nodes for query generation, web research, reflection, and answer finalization, with edges controlling the research loop.
    builder = StateGraph(OverallState, config_schema=Configuration)
    
    # Define the nodes we will cycle between
    builder.add_node("generate_query", generate_query)
    builder.add_node("web_research", web_research)
    builder.add_node("reflection", reflection)
    builder.add_node("finalize_answer", finalize_answer)
    
    # Set the entrypoint as `generate_query`
    # This means that this node is the first one called
    builder.add_edge(START, "generate_query")
    # Add conditional edge to continue with search queries in a parallel branch
    builder.add_conditional_edges(
        "generate_query", continue_to_web_research, ["web_research"]
    )
    # Reflect on the web research
    builder.add_edge("web_research", "reflection")
    # Evaluate the research
    builder.add_conditional_edges(
        "reflection", evaluate_research, ["web_research", "finalize_answer"]
    )
    # Finalize the answer
    builder.add_edge("finalize_answer", END)
    
    graph = builder.compile(name="pro-search-agent")
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden of behavioral disclosure. It mentions 'advanced web research agent' and returns a file path to JSON, but doesn't cover critical aspects like rate limits, authentication needs, execution time, or what 'deep' entails operationally. For a tool with no annotation coverage, this leaves significant behavioral traits undisclosed.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is appropriately sized and front-loaded with the core purpose. The 'Args' and 'Returns' sections are structured but slightly verbose (e.g., 'low, medium or hight' has a typo). Overall, it's efficient with minimal waste, though not perfectly polished.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (web research with effort levels), no annotations, and an output schema present (implied by 'Returns' statement), the description is moderately complete. It covers the basic operation and return format but lacks details on behavioral traits and usage context, making it adequate but with clear gaps.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The description adds minimal semantics beyond the input schema. It explains 'query' as 'the research question or topic to investigate' and 'effort' as 'the amount of effect for the research', but the schema already has 100% coverage with clear descriptions. The description's param info is redundant, so it meets the baseline of 3 without adding significant value.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'Perform a deep search on a given query using an advanced web research agent.' It specifies the verb ('perform a deep search') and resource ('web research'), though it doesn't differentiate from siblings since none exist. The purpose is specific and actionable, not vague or tautological.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. It mentions 'advanced web research agent' but doesn't explain what makes it 'deep' or in what contexts it's preferred over other search methods. With no sibling tools, the lack of explicit usage context is a notable gap.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/alexcong/gemini-deepsearch-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server