MCP Meets Groq: Build Your Agent's Brain
Learn how to integrate MCP with Groq to give your AI agent superpowers. Discover the potential of this powerful combination and how it can revolutionize your AI projects.
Day 4 — MCP Meets Groq: Build Your Agent's Brain
MCP & AI Agents — Agent Bootcamp Part 2 — RohithBuilds
This is the day it all clicks together. You'll build an MCP client — a Python program that connects to the server you've built over the last three days, asks Groq what to do, and runs whatever tool Groq picks. No if/else router. No manual prompt explaining each tool. Groq sees your real tools and decides for itself.
Step 1 — Today's Big Picture
Here's the flow you're building:
- You send a message (e.g. "How many days until GATE 2027?")
- Your client tells Groq what tools are available — straight from your MCP server
- Groq decides: does this need a tool? If so, which one, with what arguments?
- Your client calls that tool on your MCP server via
session.call_tool() - The result goes back to Groq, which writes the final, natural-language answer
💡 About async/await
Every line that talks to your MCP server uses await, and the code lives inside async def functions run with asyncio.run(). Don't worry about mastering async theory today — just follow the pattern. This is how almost all real AI agent code is written.
Create a new file client.py in the same folder as server.py — we'll build it up piece by piece.
Step 2 — Connect to Your Server & List Tools
Add this to client.py:
import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
server_params = StdioServerParameters(
command="python",
args=["server.py"],
)
async def main():
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
tools_result = await session.list_tools()
print("Tools available on this server:\n")
for tool in tools_result.tools:
print(f"- {tool.name}: {tool.description}")
asyncio.run(main())
Run it:
python client.py
Expected Output
stdio_client just launched server.py as a background process and talked to it over MCP — the same server you've been testing in Inspector all week.
Step 3 — Convert MCP Tools to Groq's Format
Groq needs tool definitions in a specific JSON format. The good news: MCP's tool.inputSchema is already a JSON schema, so converting is mostly relabeling. Add this near the top of client.py, and add import json to your imports:
def mcp_tools_to_groq_format(mcp_tools):
groq_tools = []
for tool in mcp_tools:
groq_tools.append({
"type": "function",
"function": {
"name": tool.name,
"description": tool.description,
"parameters": tool.inputSchema,
}
})
return groq_tools
Now update the loop inside main() to use it:
tools_result = await session.list_tools()
groq_tools = mcp_tools_to_groq_format(tools_result.tools)
print(json.dumps(groq_tools, indent=2))
Run python client.py again.
Expected Output
That's the exact JSON Groq will use to understand what your tools do and what arguments they need — generated automatically from your @mcp.tool() functions.
Step 4 — Ask Groq: Does This Need a Tool?
Add Groq to your imports and set up the client — same pattern as Part 1, Day 2:
import os
from dotenv import load_dotenv
from groq import Groq
load_dotenv()
groq_client = Groq(api_key=os.getenv("GROQ_API_KEY"))
Now replace the print(json.dumps(...)) line with a real question for Groq:
messages = [
{"role": "user", "content": "How many days until GATE 2027? The exam is on 2027-02-08."}
]
response = groq_client.chat.completions.create(
model="llama-3.3-70b-versatile",
messages=messages,
tools=groq_tools,
)
reply = response.choices[0].message
if reply.tool_calls:
for call in reply.tool_calls:
print(f"Tool: {call.function.name}")
print(f"Arguments: {call.function.arguments}")
else:
print("No tool needed. Direct answer:", reply.content)
Expected Output
Groq read your question, looked at the tools you gave it, and decided exam_countdown was the right one — extracting exam_name and exam_date from your plain-English sentence. It hasn't run anything yet. That's your job next.
Step 5 — Execute the Tool Call via MCP
Update the if reply.tool_calls: block to actually call the tool on your MCP server:
if reply.tool_calls:
for call in reply.tool_calls:
args = json.loads(call.function.arguments)
print(f"Calling {call.function.name}({args})")
result = await session.call_tool(call.function.name, arguments=args)
print("Result:", result.content[0].text)
else:
print("No tool needed. Direct answer:", reply.content)
Expected Output
Read that output carefully: Groq chose the tool and the arguments, but session.call_tool() — talking to your server.py from Day 1 — actually ran the Python code and did the date math. Two systems, one job.
Step 6 — Send the Result Back to Groq
Right now you get a raw tool result, not a natural answer. Fix that by sending the result back to Groq for a final response. Replace the if reply.tool_calls: block once more:
if reply.tool_calls:
messages.append({
"role": "assistant",
"content": reply.content,
"tool_calls": [
{
"id": call.id,
"type": "function",
"function": {
"name": call.function.name,
"arguments": call.function.arguments,
},
}
for call in reply.tool_calls
],
})
for call in reply.tool_calls:
args = json.loads(call.function.arguments)
print(f"Calling {call.function.name}({args})")
result = await session.call_tool(call.function.name, arguments=args)
messages.append({
"role": "tool",
"tool_call_id": call.id,
"content": result.content[0].text,
})
final = groq_client.chat.completions.create(
model="llama-3.3-70b-versatile",
messages=messages,
)
print("Rohi:", final.choices[0].message.content)
else:
print("Rohi:", reply.content)
Expected Output
That's a complete tool-use round trip — but it only handles one message. Let's make it a real conversation.
Step 7 — The Complete Agent (Chat Loop)
Replace the entire contents of client.py with this final version. It wraps everything you just built in a persistent chat loop — one connection to your server, many messages:
import asyncio
import json
import os
from dotenv import load_dotenv
from groq import Groq
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
load_dotenv()
groq_client = Groq(api_key=os.getenv("GROQ_API_KEY"))
server_params = StdioServerParameters(
command="python",
args=["server.py"],
)
def mcp_tools_to_groq_format(mcp_tools):
groq_tools = []
for tool in mcp_tools:
groq_tools.append({
"type": "function",
"function": {
"name": tool.name,
"description": tool.description,
"parameters": tool.inputSchema,
}
})
return groq_tools
async def main():
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
tools_result = await session.list_tools()
groq_tools = mcp_tools_to_groq_format(tools_result.tools)
messages = [
{
"role": "system",
"content": (
"You are Rohi, a helpful assistant for a CS student. "
"Use the available tools whenever they help answer the question."
),
}
]
print("Rohi (MCP-powered) is ready. Type 'quit' to exit.\n")
while True:
user_input = input("You: ")
if user_input.lower() == "quit":
break
messages.append({"role": "user", "content": user_input})
response = groq_client.chat.completions.create(
model="llama-3.3-70b-versatile",
messages=messages,
tools=groq_tools,
)
reply = response.choices[0].message
if reply.tool_calls:
messages.append({
"role": "assistant",
"content": reply.content,
"tool_calls": [
{
"id": call.id,
"type": "function",
"function": {
"name": call.function.name,
"arguments": call.function.arguments,
},
}
for call in reply.tool_calls
],
})
for call in reply.tool_calls:
args = json.loads(call.function.arguments)
print(f" -> calling {call.function.name}({args})")
result = await session.call_tool(call.function.name, arguments=args)
messages.append({
"role": "tool",
"tool_call_id": call.id,
"content": result.content[0].text,
})
final = groq_client.chat.completions.create(
model="llama-3.3-70b-versatile",
messages=messages,
)
final_text = final.choices[0].message.content
messages.append({"role": "assistant", "content": final_text})
print(f"Rohi: {final_text}\n")
else:
messages.append({"role": "assistant", "content": reply.content})
print(f"Rohi: {reply.content}\n")
if __name__ == "__main__":
asyncio.run(main())
Step 8 — Test Your MCP-Powered Agent
Run it:
python client.py
Try a conversation that exercises multiple tools, for example:
How many days until GATE 2027 on 2027-02-08?Add a note: revise OS notes tonightWhat notes do I have?
Expected Output
Look at what just happened: the same server you tested manually in Inspector all week is now being driven entirely by an AI model, choosing tools on its own, message by message.
Step 9 — Troubleshooting
⚠️ Common Issues
- Groq never returns tool_calls: double-check the model is exactly
llama-3.3-70b-versatile— not every Groq model supports tool calling. - "Connection closed" or the agent hangs: make sure
server.pyruns without errors on its own first (python test_local.pyfrom Day 1-3 still works). - JSON errors on tool arguments: print
call.function.argumentsbeforejson.loads()— it should be a JSON string like{"exam_name": "GATE 2027", "exam_date": "2027-02-08"}. - Wrong note ID in delete_note: remember IDs keep incrementing even after deletions — run
list_notesfirst to check current IDs.
✅ Day 4 Complete
Here is what you accomplished today:
Key Takeaways
- Built a real MCP client that connects to your server ✅
- Listed tools programmatically and converted them to Groq's format ✅
- Watched Groq choose tools and arguments from plain English ✅
- Executed those tool calls live via MCP ✅
- Fed results back to Groq for natural-language answers ✅
- Built a full chat loop — your MCP-powered agent is alive ✅
What Is Coming Tomorrow
On Day 5 you will:
- Wrap today's agent in a Flask web app
- Build a browser-based chat UI — no terminal required
- Handle your async MCP agent inside Flask's request/response cycle
- Chat with your MCP-powered Rohi from a real web page
See you there! 🚀
Continue Learning with Rohi
You've used your 3 free Rohi questions. Create a free account to continue learning.