Build an AI Research Agent with MCP

Orchestrate 3 AI agents with custom tools and MCP to research any topic.

Introduction

30 Second Summary

Think about how you research a topic today. You open multiple tabs, scan articles, pull out key facts, then organize everything into something readable. Now imagine a team of AI agents doing that entire workflow automatically.

In this project, you will build an AI-powered research pipeline that takes any topic and produces a professional intelligence report using live web data. You'll create a custom search tool from scratch, integrate MCP for file access, and orchestrate three specialized agents in a sequential pipeline.

What You'll Build

A multi-agent research system powered by CrewAI and Groq that researches any topic using live web data and outputs a polished markdown report.

By the end of this project, you'll have:

  • A custom web search tool built from scratch using CrewAI's @tool decorator and the ddgs library for live web results.
  • An MCP Filesystem Server integrated into your agent pipeline, giving your agents the ability to read and write files through the Model Context Protocol.
  • A three-agent sequential pipeline (Researcher, Writer, Editor) that collaborates to produce a polished research report saved to your filesystem.
  • Secret Mission: structured Pydantic outputs that make your research data machine-readable for downstream systems.

Are there any prerequisites?

You'll need basic Python knowledge (functions, imports, variables) and a free Groq API key from console.groq.com. No credit card is required.

You'll also need VS Code, Python 3.13, and Node.js installed on your machine. Step 1 walks you through everything else.

Before We Start

Before We Start...

Set Up Your Project Environment

A multi-agent system works best when each component lives in its own file.

In this step, you'll create a dedicated project folder, install the packages your agents need, and configure your API key so CrewAI can talk to Groq's language model.

In this step, get ready to:
  • Create the project folder with dedicated files for tools and orchestration.
  • Install CrewAI, MCP tools, and web search packages.
  • Configure your Groq API key and telemetry settings.
Create the project folder and files

Your project will have two Python files: tools.py for your custom web search tool, and main.py for agent orchestration.

  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type Terminal (macOS) or PowerShell (Windows) and press Enter to open it.
  • Move to your Desktop by running this command:
cd ~/Desktop
  • Create your project folder and move into it by running these commands:
mkdir ai-research-agent
cd ai-research-agent
  • Create the project files by running this command:

macOS

touch tools.py main.py

Windows

New-Item -Path tools.py -ItemType File
New-Item -Path main.py -ItemType File
  • Confirm the files exist by running:
ls

You should see tools.py and main.py in the output.

Now open VS Code and point it at your new project folder.

  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type VS Code and press Enter to open it.
  • Click File in the top menu bar.
  • Click Open Folder.
  • Select the ai-research-agent folder on your Desktop, then click Open.

Don't see the folder on your Desktop?

Make sure you ran cd ~/Desktop before creating the folder. If the folder ended up somewhere else, check your home directory or re-run the commands above.

Help me find my project folder.

Create a virtual environment and install packages

A virtual environment keeps this project's packages isolated from any other Python projects on your machine.

  • Open VS Code's integrated terminal by pressing Ctrl+` (backtick key, top-left of your keyboard).
  • Create and activate your virtual environment by running these commands:

macOS

python3 -m venv .venv
source .venv/bin/activate

Windows

python -m venv .venv
.venv\Scripts\activate

You should see (.venv) appear at the start of your terminal prompt. This confirms the virtual environment is active.

  • Install the project dependencies by running:
pip install 'crewai[litellm]' 'crewai-tools[mcp]' ddgs python-dotenv

What are these packages?

  • crewai[litellm] is the core multi-agent framework. The LiteLLM extra lets CrewAI route requests to Groq's API.
  • crewai-tools[mcp] adds pre-built tool integrations including support for MCP (Model Context Protocol) servers.
  • ddgs provides web search capabilities your custom tool will use to pull live data from the internet.
  • python-dotenv loads environment variables from your .env file so your API key stays out of your code.

This installation may take a minute or two. When it finishes, you should see a success message with no red error text.

Seeing a red error during install?

Make sure your virtual environment is active. You should see (.venv) in your terminal prompt. If not, re-run the activation command from the platform tab above.

Check that you're using Python 3.10 or higher. Run python3 --version (macOS) or python --version (Windows) to confirm.

Help me debug my pip install error.

Create the .env file with your API key

Your agents need a Groq API key to access the language model. Storing it in a .env file keeps the secret out of your code and out of version control.

  • In the VS Code Explorer panel (left sidebar), click the new file icon at the top of the file list.
  • Name the file .env and press Enter.
  • Paste the following into your new .env file:
GROQ_API_KEY=your-groq-api-key-here
OTEL_SDK_DISABLED=true
  • Replace your-groq-api-key-here with your actual Groq API key.

What does OTEL_SDK_DISABLED do?

CrewAI includes OpenTelemetry for telemetry collection. Setting OTEL_SDK_DISABLED=true prevents noisy warning messages from cluttering your terminal output when you run the pipeline.

  • Save .env by pressing Cmd+S (macOS) or Ctrl+S (Windows).
  • Check the VS Code Explorer panel. You should see three files listed: .env, main.py, and tools.py.

Don't see .env in the Explorer?

Files starting with a dot are sometimes hidden by default. Make sure you typed the filename as .env (with the leading dot). If VS Code asks whether to exclude dotfiles, select No.

Help me create my .env file in VS Code.

✔️ Awesome, I've got everything!

Great. Double check you've saved your .env file with your real Groq API key pasted in.

ⓧ I'd like to double check the full code

Here's what your .env file should look like (with your actual key in place of the placeholder):

GROQ_API_KEY=your-groq-api-key-here
OTEL_SDK_DISABLED=true

Your environment is ready. Next up, you'll build a custom web search tool from scratch that your research agent will use to pull live data from the internet.

Build a Custom Web Search Tool

Your agents need a way to access live information from the internet. Without tools, an agent can only work with the knowledge baked into its LLM.

In this step, you'll build a custom web search tool from scratch using CrewAI's @tool decorator and the ddgs library. This is the skill that separates developers who understand AI internals from those who only use pre-built components.

In this step, get ready to:
  • Create the custom web search tool with the @tool decorator.
  • Build the search function that queries the web and formats results.
  • Test your tool in isolation to confirm it returns real data.
Open your tools file in VS Code

The @tool decorator is CrewAI's lightweight way to turn any Python function into a tool that agents can use. You provide a name and a docstring, and CrewAI handles the rest.

  • In VS Code, open tools.py from the Explorer sidebar. This file is currently empty.

Why build a custom tool instead of using a pre-built one?

CrewAI ships pre-built tools like SerperDevTool for web search. Those require paid API keys. Building your own tool with ddgs gives you free web search and shows employers you understand what happens inside a tool, not just how to import one.

Build the web search tool

Your tool needs to accept a search query, hit the web, and return formatted results that an agent can read and reason about.

  • Copy the following code into tools.py:
from crewai.tools import tool
from ddgs import DDGS


@tool("Web Search")
def web_search(query: str) -> str:
    """Search the web for current information on any topic. Returns titles, URLs, and descriptions of the top results."""
    results = DDGS().text(query, max_results=5)
    if not results:
        return "No results found for the query."

    formatted = []
    for r in results:
        formatted.append(
            f"Title: {r.get('title', 'N/A')}\n"
            f"URL: {r.get('href', 'N/A')}\n"
            f"Summary: {r.get('body', 'N/A')}\n"
        )
    return "\n---\n".join(formatted)

What does this code do?

  • from crewai.tools import tool imports the @tool decorator. This is the canonical import path for custom tools in CrewAI.
  • from ddgs import DDGS imports the DuckDuckGo search client. This library was renamed from duckduckgo-search to ddgs in version 9.0.0.
  • @tool("Web Search") registers the function as a CrewAI tool with the display name "Web Search". The function's docstring becomes the tool description that agents read to decide when to use it.
  • DDGS().text(query, max_results=5) performs a web search and returns up to 5 results as a list of dictionaries.
  • The formatting loop extracts title, href, and body from each result and joins them with --- separators. This gives the agent clean, structured data to work with.
  • Save tools.py.

Seeing a red underline on the imports?

  • Make sure your virtual environment is activated. You should see (.venv) at the start of your terminal prompt.
  • If VS Code still shows import errors, press Cmd+Shift+P (macOS) or Ctrl+Shift+P (Windows), type Python: Select Interpreter, and choose the one inside your .venv folder.

Why isn't VS Code recognizing my installed packages?

Test your tool in isolation

Before handing this tool to an agent, you should verify it actually hits the web and returns real data. CrewAI tools expose a .run() method that lets you invoke them directly.

  • In your terminal, test the tool by running this command:
python -c "from tools import web_search; print(web_search.run('AI agents 2026'))"
  • You should see formatted search results with titles, URLs, and summaries printed to your terminal. The exact results will vary because they come from live web data.

What does this command do?

The -c flag tells Python to execute the string that follows as code. It imports your web_search tool, calls .run() with a test query, and prints the result. This is a quick way to verify a tool works without writing a separate test file.

Seeing an error or empty results?

  • If you see ModuleNotFoundError, make sure your virtual environment is activated and you installed ddgs in the previous step.
  • If you get a network error, check your internet connection. The ddgs library needs web access to return results.
  • If the output says "No results found", try a different query string. Some queries may be rate-limited on first use.

Why is my web search tool returning an error?

✔️ Awesome, I've got everything!

Great. Double check you've saved tools.py and confirmed your test returned real search results.

ⓧ I'd like to double check the full code

Here's the complete tools.py file:

from crewai.tools import tool
from ddgs import DDGS


@tool("Web Search")
def web_search(query: str) -> str:
    """Search the web for current information on any topic. Returns titles, URLs, and descriptions of the top results."""
    results = DDGS().text(query, max_results=5)
    if not results:
        return "No results found for the query."

    formatted = []
    for r in results:
        formatted.append(
            f"Title: {r.get('title', 'N/A')}\n"
            f"URL: {r.get('href', 'N/A')}\n"
            f"Summary: {r.get('body', 'N/A')}\n"
        )
    return "\n---\n".join(formatted)

Your custom tool is built and verified. Next up, you'll create the agent team that uses this tool to research topics and write reports with MCP integration.

Create Your Agent Team with MCP Integration

Your custom web search tool is ready to go. Now it is time to build the agents that will use it.

In this step, you will configure the LLM connection, set up the MCP Filesystem Server for file operations, and define three specialized agents that each handle a different part of the research pipeline.

In this step, get ready to:
  • Configure the LLM and MCP Filesystem Server connection.
  • Define three specialized agents with distinct roles.
  • Create tasks and assemble the sequential pipeline.
Create main.py with LLM configuration and MCP Filesystem Server setup

Before defining agents, you need to wire up the LLM provider and the MCP server that will give your writer agent file access.

  • In VS Code, open main.py (it should be empty) and paste the following code:
import os
from dotenv import load_dotenv
from crewai import Agent, Task, Crew, Process, LLM
from crewai_tools import MCPServerAdapter
from mcp import StdioServerParameters
from tools import web_search

load_dotenv()

# Configure LLM - Groq with Llama 3.3 70B
llm = LLM(
    model="groq/llama-3.3-70b-versatile",
    temperature=0.7
)

# Set up MCP Filesystem Server
output_dir = os.path.abspath("./output")
os.makedirs(output_dir, exist_ok=True)

server_params = StdioServerParameters(
    command="npx",
    args=["-y", "@modelcontextprotocol/server-filesystem", output_dir]
)

What does this code do?

The imports pull in everything you need: CrewAI for agents and orchestration, MCPServerAdapter for protocol-level file access, and the web_search tool you built in Step 2.

  • load_dotenv() reads your .env file so the GROQ_API_KEY is available as an environment variable.
  • The LLM object connects to Groq's Llama 3.3 70B model via LiteLLM. The groq/ prefix tells LiteLLM which provider to route to.
  • StdioServerParameters tells CrewAI how to launch the MCP Filesystem Server. The npx -y flag auto-installs the server package if it is not already cached locally.
  • Save main.py.

Seeing red underlines on the imports?

Make sure your virtual environment is active in VS Code. Check the bottom-left corner of VS Code for the Python interpreter. It should show .venv or the path to your virtual environment.

If the interpreter is wrong, click it and select the one inside your project's .venv folder.

help me fix import errors in main.py

Define three agents: Researcher, Writer, and Editor

Each agent gets a role, a goal, a backstory (its personality prompt), and optionally a set of tools. The Researcher uses your custom search tool. The Writer gets MCP filesystem tools. The Editor works with just the LLM.

  • Add the following code below your server_params block in main.py. This opens the MCP context manager and defines the Researcher agent inside it:
with MCPServerAdapter(server_params) as mcp_tools:

    # Agent 1: The Researcher (with custom web search tool)
    researcher = Agent(
        role="Senior Research Analyst",
        goal=(
            "Produce a comprehensive research brief on the assigned topic "
            "with at least 5 key findings backed by web sources"
        ),
        backstory=(
            "You are a veteran research analyst with 10 years of experience "
            "in competitive intelligence. You always use your web search tool "
            "to find current, accurate information before forming conclusions. "
            "You never rely solely on prior knowledge."
        ),
        llm=llm,
        tools=[web_search],
        allow_delegation=False,
        verbose=True
    )

What does this code do?

The with MCPServerAdapter(server_params) as mcp_tools: line starts the MCP Filesystem Server and provides its tools as a Python list called mcp_tools. Everything inside this block can use those tools. When the block ends, the server shuts down automatically.

The Researcher agent receives your custom web_search tool. Setting allow_delegation=False prevents it from passing work to other agents. Its backstory instructs it to always search the web rather than relying on the LLM's training data.

  • Add the Writer and Editor agents directly below the Researcher, still inside the with block (keep the same indentation level):
    # Agent 2: The Writer (with MCP filesystem tools)
    writer = Agent(
        role="Technical Content Writer",
        goal=(
            "Transform research into a well-structured, engaging markdown "
            "article and save it to the filesystem using your file tools"
        ),
        backstory=(
            "You are an experienced technical writer known for making complex "
            "topics accessible. You write in a clear, engaging style with "
            "short paragraphs. You always save your finished work to a file."
        ),
        llm=llm,
        tools=mcp_tools,
        verbose=True
    )

    # Agent 3: The Editor
    editor = Agent(
        role="Senior Editor",
        goal="Review and polish the article for clarity, accuracy, and flow",
        backstory=(
            "You are a meticulous editor with an eye for detail. "
            "You improve readability, fix inconsistencies, and ensure "
            "the article flows naturally from start to finish."
        ),
        llm=llm,
        verbose=True
    )

What does this code do?

The Writer agent receives mcp_tools (the filesystem tools provided by the MCP server). This gives it the ability to read and write files in your output/ directory without you writing any file I/O code.

The Editor agent has no tools at all. It only uses the LLM to review and polish text. Not every agent needs tools. Giving an agent only what it needs keeps the system focused.

  • Save main.py.

Indentation errors?

All three agent definitions must be indented one level inside the with block (4 spaces). If you see an IndentationError or unexpected red underlines, make sure the researcher, writer, and editor variables all start at the same column.

help me fix indentation in my main.py file

Define tasks for each agent and assemble the sequential crew

Tasks tell each agent exactly what to produce. The sequential process means each task runs in order, and each agent can build on the previous agent's output.

  • Add the research task below the Editor agent definition, still inside the with block:
    # Task 1: Research with web search
    research_task = Task(
        description=(
            "Research the topic: '{topic}'\n\n"
            "Use your Web Search tool to find current information.\n\n"
            "Your deliverables:\n"
            "1. At least 5 key findings with supporting details\n"
            "2. Recent developments and trends (2025-2026)\n"
            "3. Key players and their contributions\n"
            "4. Challenges and limitations in the space\n\n"
            "Present your findings in a structured, numbered format "
            "with source context for each finding."
        ),
        expected_output=(
            "A structured research brief in markdown format with "
            "numbered findings, each containing the fact, its context, "
            "and the source it came from."
        ),
        agent=researcher
    )

What does this code do?

The description is the prompt each agent receives. Notice '{topic}' in curly braces. CrewAI replaces this with the actual topic when you run crew.kickoff(inputs={"topic": "..."}) later.

The expected_output tells the agent what format its response should take. This helps the LLM stay on track and produce structured results.

  • Add the write and edit tasks below the research task:
    # Task 2: Write and save via MCP
    write_task = Task(
        description=(
            "Using the research brief from the previous task, write a "
            "600-900 word article about '{topic}'.\n\n"
            "Requirements:\n"
            "- Use markdown formatting with H1 title and H2 section headers\n"
            "- Write an engaging introduction and conclusion\n"
            "- Keep paragraphs short (3-4 sentences max)\n"
            "- Include a 'Key Takeaways' section at the end\n"
            "- Use the filesystem tools to save the article as 'report.md' "
            "in the allowed directory\n"
        ),
        expected_output=(
            "A well-written markdown article saved to the filesystem "
            "as report.md. Confirm the file was saved successfully."
        ),
        agent=writer
    )

    # Task 3: Edit and finalize
    edit_task = Task(
        description=(
            "Review the article written by the Writer. Check for:\n"
            "- Clarity and readability\n"
            "- Logical flow between sections\n"
            "- Accuracy of claims based on the research\n"
            "- Grammar and style consistency\n\n"
            "Provide the final polished version of the article."
        ),
        expected_output=(
            "The final polished article in markdown format, "
            "ready for publication."
        ),
        agent=editor
    )

What does this code do?

The write task instructs the Writer to use its MCP filesystem tools to save the article as report.md. The agent handles the file write automatically through the MCP protocol.

The edit task gives the Editor a checklist of what to review. Because this crew uses a sequential process, the Editor automatically receives the Writer's output as context.

  • Finally, add the Crew assembly and execution code below your tasks:
    # Assemble the Crew
    crew = Crew(
        agents=[researcher, writer, editor],
        tasks=[research_task, write_task, edit_task],
        process=Process.sequential,
        verbose=True
    )

    # Run the crew
    result = crew.kickoff(inputs={"topic": "The Future of AI Agents in 2026"})

    print("\n" + "=" * 60)
    print("RESEARCH PIPELINE COMPLETE")
    print("=" * 60)
    print(f"\nToken usage: {result.token_usage}")
    print(f"\nCheck ./output/report.md for the saved article.")
    print(f"\nFinal output preview:\n{result.raw[:500]}...")

What does this code do?

The Crew ties agents and tasks together. Process.sequential means tasks run one after another: research first, then write, then edit. Each agent builds on the previous agent's output.

crew.kickoff() starts the pipeline. The inputs dictionary passes "The Future of AI Agents in 2026" as the {topic} variable in every task description that references it.

  • Save main.py.

You won't run the pipeline yet. In the next step, you will execute it and watch the agents collaborate in real time.

Seeing a syntax error on save?

The most common cause is a missing or extra indentation level. Every line from researcher = Agent( through print(f"\nFinal output... must be indented exactly 4 spaces inside the with block. Check that no lines accidentally start at column 0.

help me debug a syntax error in main.py

✔️ Awesome, I've got everything!

Great. Make sure you have saved main.py before moving on.

ⓧ I'd like to double check the full code

Here is the complete main.py file at this point. Compare it with yours:

import os
from dotenv import load_dotenv
from crewai import Agent, Task, Crew, Process, LLM
from crewai_tools import MCPServerAdapter
from mcp import StdioServerParameters
from tools import web_search

load_dotenv()

# Configure LLM - Groq with Llama 3.3 70B
llm = LLM(
    model="groq/llama-3.3-70b-versatile",
    temperature=0.7
)

# Set up MCP Filesystem Server
output_dir = os.path.abspath("./output")
os.makedirs(output_dir, exist_ok=True)

server_params = StdioServerParameters(
    command="npx",
    args=["-y", "@modelcontextprotocol/server-filesystem", output_dir]
)

with MCPServerAdapter(server_params) as mcp_tools:

    # Agent 1: The Researcher (with custom web search tool)
    researcher = Agent(
        role="Senior Research Analyst",
        goal=(
            "Produce a comprehensive research brief on the assigned topic "
            "with at least 5 key findings backed by web sources"
        ),
        backstory=(
            "You are a veteran research analyst with 10 years of experience "
            "in competitive intelligence. You always use your web search tool "
            "to find current, accurate information before forming conclusions. "
            "You never rely solely on prior knowledge."
        ),
        llm=llm,
        tools=[web_search],
        allow_delegation=False,
        verbose=True
    )

    # Agent 2: The Writer (with MCP filesystem tools)
    writer = Agent(
        role="Technical Content Writer",
        goal=(
            "Transform research into a well-structured, engaging markdown "
            "article and save it to the filesystem using your file tools"
        ),
        backstory=(
            "You are an experienced technical writer known for making complex "
            "topics accessible. You write in a clear, engaging style with "
            "short paragraphs. You always save your finished work to a file."
        ),
        llm=llm,
        tools=mcp_tools,
        verbose=True
    )

    # Agent 3: The Editor
    editor = Agent(
        role="Senior Editor",
        goal="Review and polish the article for clarity, accuracy, and flow",
        backstory=(
            "You are a meticulous editor with an eye for detail. "
            "You improve readability, fix inconsistencies, and ensure "
            "the article flows naturally from start to finish."
        ),
        llm=llm,
        verbose=True
    )

    # Task 1: Research with web search
    research_task = Task(
        description=(
            "Research the topic: '{topic}'\n\n"
            "Use your Web Search tool to find current information.\n\n"
            "Your deliverables:\n"
            "1. At least 5 key findings with supporting details\n"
            "2. Recent developments and trends (2025-2026)\n"
            "3. Key players and their contributions\n"
            "4. Challenges and limitations in the space\n\n"
            "Present your findings in a structured, numbered format "
            "with source context for each finding."
        ),
        expected_output=(
            "A structured research brief in markdown format with "
            "numbered findings, each containing the fact, its context, "
            "and the source it came from."
        ),
        agent=researcher
    )

    # Task 2: Write and save via MCP
    write_task = Task(
        description=(
            "Using the research brief from the previous task, write a "
            "600-900 word article about '{topic}'.\n\n"
            "Requirements:\n"
            "- Use markdown formatting with H1 title and H2 section headers\n"
            "- Write an engaging introduction and conclusion\n"
            "- Keep paragraphs short (3-4 sentences max)\n"
            "- Include a 'Key Takeaways' section at the end\n"
            "- Use the filesystem tools to save the article as 'report.md' "
            "in the allowed directory\n"
        ),
        expected_output=(
            "A well-written markdown article saved to the filesystem "
            "as report.md. Confirm the file was saved successfully."
        ),
        agent=writer
    )

    # Task 3: Edit and finalize
    edit_task = Task(
        description=(
            "Review the article written by the Writer. Check for:\n"
            "- Clarity and readability\n"
            "- Logical flow between sections\n"
            "- Accuracy of claims based on the research\n"
            "- Grammar and style consistency\n\n"
            "Provide the final polished version of the article."
        ),
        expected_output=(
            "The final polished article in markdown format, "
            "ready for publication."
        ),
        agent=editor
    )

    # Assemble the Crew
    crew = Crew(
        agents=[researcher, writer, editor],
        tasks=[research_task, write_task, edit_task],
        process=Process.sequential,
        verbose=True
    )

    # Run the crew
    result = crew.kickoff(inputs={"topic": "The Future of AI Agents in 2026"})

    print("\n" + "=" * 60)
    print("RESEARCH PIPELINE COMPLETE")
    print("=" * 60)
    print(f"\nToken usage: {result.token_usage}")
    print(f"\nCheck ./output/report.md for the saved article.")
    print(f"\nFinal output preview:\n{result.raw[:500]}...")

Your entire pipeline is written. Next up, you will create the output directory, run the crew, and watch your three agents collaborate in the terminal.

Run the Pipeline and Review Results

Your multi-agent system is fully assembled. Three agents, three tasks, a custom web search tool, and an MCP Filesystem Server are all wired together and ready to execute.

In this step, you will launch the pipeline and watch your agents collaborate in real time. The Researcher searches the web, the Writer saves a report via MCP, and the Editor polishes it. You will see every decision and tool call logged to your terminal.

In this step, get ready to:
  • Run the full crew pipeline with a research topic.
  • Observe agent collaboration, tool usage, and MCP file operations in your terminal.
  • Review the generated report and verify MCP wrote the file.
Run the full crew pipeline

Your main.py script already handles creating the output/ directory with os.makedirs(output_dir, exist_ok=True). All you need to do is run the script.

  • Launch the research pipeline by running this command in your terminal:
python main.py

What happens when you run this?

The script starts the MCP Filesystem Server via npx, then kicks off the crew with the topic "The Future of AI Agents in 2026". Each agent runs its task sequentially.

The first run may take 1-3 minutes. The MCP server needs a moment to start, and each agent makes multiple calls to Groq's API.

Seeing a rate limit error?

Groq's free tier allows 30 requests per minute for llama-3.3-70b-versatile. A multi-agent crew makes many LLM calls, so you may hit this limit.

  • Wait 60 seconds and run the script again. The rate limit resets each minute.
  • If it keeps failing, open main.py and change the model to groq/llama-3.1-8b-instant which has higher rate limits.
  • Make sure your .env file has a valid GROQ_API_KEY value (not the placeholder text).

Why is my crew hitting rate limits or failing with an API error?

Observe the agent collaboration in your terminal

Because you set verbose=True on each agent, your terminal shows a detailed log of every action. This is where multi-agent orchestration becomes tangible.

  • Watch your terminal output as the pipeline runs. Look for these three phases:

What should I see in the terminal?

  • Phase 1 (Researcher): The Senior Research Analyst activates and calls your custom web_search tool multiple times. You will see the search queries it chooses and the formatted results coming back.
  • Phase 2 (Writer): The Technical Content Writer receives the research brief and begins drafting the article. It uses the MCP filesystem tools to save report.md to your output/ directory.
  • Phase 3 (Editor): The Senior Editor reviews the article for clarity, flow, and accuracy. It produces the final polished version.

At the very end, your script prints a RESEARCH PIPELINE COMPLETE banner with token usage stats and a preview of the final output.

Why is this sequential and not parallel?

You set process=Process.sequential on your crew. This means each task must finish before the next one starts. The Writer needs the Researcher's findings, and the Editor needs the Writer's draft.

In production systems, you choose sequential when tasks have dependencies on each other's outputs. Parallel execution is better for independent tasks that don't share data.

Review the generated report and verify MCP wrote the file

The Writer agent used MCP filesystem tools to save the article directly to disk. This is different from simply returning text in the terminal. The file was written through the Model Context Protocol server, not by your Python script.

  • Verify the report file exists by running:
ls output/
  • You should see report.md listed in the output.
  • Open the report to read its contents by running:
cat output/report.md
  • You should see a markdown article about "The Future of AI Agents in 2026" with an H1 title, H2 section headers, short paragraphs, and a Key Takeaways section at the end.

Why does MCP matter here?

Your Writer agent did not use Python's built-in file writing. It used the MCP Filesystem Server, which exposes read/write operations as protocol-level tools. This is the same pattern production AI systems use to connect agents to databases, APIs, and cloud storage.

The fact that report.md exists on disk proves your MCP integration is working end-to-end.

Don't see report.md?

  • Make sure Node.js is installed. The MCP Filesystem Server runs via npx, which requires Node.js.
  • Check that the output/ directory exists in your project folder. Run ls to confirm.
  • Look at the Writer agent's verbose output for any errors related to the filesystem tools. If the MCP server failed to start, the Writer cannot save files.

Why isn't my MCP Filesystem Server writing the report file?

You just ran a complete multi-agent AI system. Your custom tool pulled live data from the web, MCP handled file operations through a standardized protocol, and three specialized agents collaborated to produce a real intelligence report. Next up, the Secret Mission takes this further with structured Pydantic outputs.

Secret mission

Add Structured Pydantic Outputs

Your research agent produces great reports, but the output is unstructured text. Production AI systems need machine-readable data they can feed into APIs, databases, and downstream workflows. Can you add Pydantic models to make your research task output structured JSON?

Clean Up Your Resources

Clean Up Your Resources

This project runs entirely locally and uses Groq's free tier for API calls. There are no ongoing costs or running services to worry about.

Decide whether to keep your resources for future experimentation, or delete them entirely.

Resources you used:

  • Project folder on your Desktop (contains main.py, tools.py, .env, .venv, and output/).
  • Groq API key (managed at console.groq.com/keys).

Keep everything running

No action needed. Choose this if you plan to keep experimenting with your agents or modifying the pipeline.

  • Your project folder stays on your Desktop, ready to use anytime.
  • Your Groq API key remains active on the free tier with no charges.
  • To run the pipeline again, activate your virtual environment and run python main.py.

Pause - I'll come back to this later

There are no running services to stop. Your crew runs once and exits. The only action is to deactivate your virtual environment if it is still active.

  • Deactivate your virtual environment by running:
deactivate

When you come back, reactivate it with:

macOS

source .venv/bin/activate

Windows

.venv\Scripts\activate

Delete - I don't want to use this again

Remove all project files and revoke your API key so nothing lingers on your machine or your Groq account.

  • Deactivate your virtual environment if it is still active:
deactivate
  • Navigate to your Desktop and delete the project folder:

macOS

cd ~/Desktop
rm -rf [[PROJECT_FOLDER="your project folder name"]]

Windows

cd ~/Desktop
Remove-Item -Recurse -Force [[PROJECT_FOLDER="your project folder name"]]
  • Confirm the folder is gone by running:
ls

You should no longer see your project folder in the output.

  • Revoke your Groq API key by visiting console.groq.com/keys.
  • Find the API key you created for this project in the table.
  • Click the delete or revoke option next to the key to remove it from your account.

Nice Work!

Nice Work!

You just built a production-grade multi-agent research system that takes any topic and produces a professional intelligence report using live web data.

You've learned how to:

  • Built a custom web search tool from scratch using the @tool decorator and the ddgs library.
  • Integrated the MCP Filesystem Server for protocol-level file access, connecting your agents to external tools through the Model Context Protocol.
  • Orchestrated 3 specialized agents (Researcher, Writer, Editor) in a sequential pipeline powered by Groq's Llama 3.3 70B via LiteLLM.
  • Added structured Pydantic outputs to produce machine-readable JSON data alongside human-readable reports.

Ready to quiz yourself?