MCP agent workflow
Now let's build a complete MCP agent: connect to the server, discover tools, ask the AI to pick the right one, execute it via MCP, and return the final answer. This is the full pattern.
MCP protocol flow
How agents communicate with external data through MCP
The MCP server only provides tools. It does not decide which tool to call or how to interpret the result. That decision-making is the AI model's job. The agent workflow connects the two: the AI picks the tool, MCP executes it, and the AI explains the result.
Context grounding: When the user asks "What's the weather today?", the AI needs to know what "today" means. We inject the current date/time into the prompt. This is called context grounding, providing real-world context that the AI doesn't have.
from datetime import datetime
async def run_agent():
"""Full MCP agent workflow."""
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
# Step 1: Discover available tools
tools_result = await session.list_tools()
tools = tools_result.tools
tools_desc = "\n".join(
[f"- {t.name}: {t.description}" for t in tools]
)First, connect to the MCP server and discover what tools it offers. We build a text description of the tools so the AI can read them.
Next, we build the prompt that tells the AI what tools are available and injects real-world context like the current date. This is the context grounding step.
# Step 2: Context grounding, inject current date
current_time = datetime.now().strftime("%Y-%m-%d %H:%M")
# Step 3: Build prompt for the AI to pick a tool
question = "What is the weather in Berlin today?"
prompt = f"""Current date: {current_time}
Available tools:
{tools_desc}
Question: {question}
Respond with JSON: {{"tool": "name", "parameters": {{...}}}}"""We inject the current date (context grounding) and list the available tools. The AI will read this prompt and decide which tool to call with what parameters.
Now we send the prompt to the AI and parse its tool choice. The AI returns JSON with the tool name and parameters, which we execute through the MCP session.
# Step 4: AI picks the tool, we execute via MCP
response = completion(
model=DEFAULT_MODEL,
messages=[{"role": "user", "content": prompt}]
)
action = json.loads(response.choices[0].message.content)
result = await session.call_tool(
action["tool"], action["parameters"]
)
tool_output = result.content[0].textThe AI returns JSON with the tool name and parameters. We parse it and call the tool through the MCP session. The result comes back as text.
Finally, we take the raw tool output and ask the AI to write a human-friendly answer. This two-pass pattern, where the AI picks the tool then explains the result, is the standard MCP agent workflow.
# Step 5: Generate a natural-language answer from the data
final = completion(
model=DEFAULT_MODEL,
messages=[{"role": "user",
"content": f"Question: {question}\nData: {tool_output}\nProvide a natural answer."}]
)
print(final.choices[0].message.content)Finally, we send the raw tool output back to the AI and ask it to write a human-friendly answer. This two-pass pattern (AI picks tool, then AI explains result) is the standard MCP agent workflow.
Notice something powerful: this works with any LLM provider. Change DEFAULT_MODEL from Gemini to GPT-4o to Claude and the MCP connection and tool calling stays the same. That's the power of MCP + LiteLLM together: provider-agnostic tool calling.
Fill in the blanks: Complete the MCP agent
Loading practiceโฆ
Checkpoint: MCP concepts
Loading practiceโฆ
You have built a complete MCP agent workflow from connection to tool execution. Next, we will wrap up MCP with best practices and learn when to choose MCP over plain function calling.