Menu

AI & Machine Learning Topic

Connect an AI Agent to MCP Tools in Python

Lesson 6 of 6 · AI agents, tools and MCP.

Use these lessons in order. The code examples share the same small study project.

  1. AI Agents for Beginners: How a Study Helper Works
  2. Python Tool Calling: Build Your First AI Agent
  3. What Is MCP? Tools, Resources and AI Apps Explained
  4. Build an MCP Server and Client in Python
  5. Test AI Agents and MCP Tools in Python
  6. Connect an AI Agent to MCP Tools in Python

Documentation checked: 25 October 2026. Code examples use made-up lesson data. Technical words are explained as they appear.

You now have a local tool, a model loop and an MCP server. This lesson joins them together.

You ask a question. The model requests a lesson. Your app sends that request through MCP, then returns the result to the model.

Keep the two connections clear

  • Model connection: Your app uses the OpenAI Responses API.
  • Tool connection: Your app uses an MCP client to reach the local study server.

The MCP server does not need the model API key. It only reads the local demo catalog. The agent process holds the model connection.

Files you need

Keep catalog.py, tool_agent.py and study_server.py in one folder. The earlier lessons include each full file.

Use the same SDK versions: MCP 2.3.0 and OpenAI 3.37.0. The code imports the tool description and limits from tool_agent.py, so they stay the same.

1. Add the bridge

Save this as mcp_agent.py:

"""Use a model to select the known lesson tool, then call it through MCP."""
import asyncio
import json
import os

from mcp import Client

from tool_agent import INSTRUCTIONS, MAX_ROUNDS, MAX_TOOL_CALLS, TOOLS

URL = "http://127.0.0.1:8012/mcp"


async def ask_with_mcp(ai, model, question, mcp_url=URL):
    history = [{"role": "user", "content": question}]
    calls_used = 0
    async with Client(mcp_url) as tools_client:
        listing = await tools_client.list_tools()
        if "get_lesson" not in {tool.name for tool in listing.tools}:
            raise RuntimeError("This server does not offer the expected lesson tool")
        # Expose only the known demo tool, not every discovered server tool.
        for _ in range(MAX_ROUNDS):
            response = await ai.responses.create(
                model=model, instructions=INSTRUCTIONS,
                input=history, tools=TOOLS, max_output_tokens=800,
            )
            history.extend(response.output)
            calls = [item for item in response.output if item.type == "function_call"]
            if not calls:
                return response.output_text or "No answer was returned."
            if calls_used + len(calls) > MAX_TOOL_CALLS:
                return "Stopped: too many tool calls. Please ask a smaller question."
            for call in calls:
                calls_used += 1
                try:
                    if call.name != "get_lesson":
                        raise ValueError("Tool is not allowed")
                    args = json.loads(call.arguments)
                    if not isinstance(args, dict) or set(args) != {"course", "lesson"}:
                        raise ValueError("Expected only course and lesson")
                    result = await tools_client.call_tool(call.name, args)
                    output = json.dumps({
                        "is_error": result.is_error,
                        "data": result.structured_content,
                        "text": [b.text for b in result.content if b.type == "text"],
                    })
                except (ValueError, TypeError) as exc:
                    output = json.dumps({"error": str(exc)})
                history.append({"type": "function_call_output",
                                "call_id": call.call_id, "output": output})
    return "Stopped: the response limit was reached."


async def main():
    from openai import AsyncOpenAI
    model = os.environ["OPENAI_MODEL"]
    async with AsyncOpenAI(timeout=30.0, max_retries=1) as ai:
        answer = await asyncio.wait_for(
            ask_with_mcp(ai, model, "Show MCP lesson 2 and its task."),
            timeout=90,
        )
        print(answer)


if __name__ == "__main__":
    asyncio.run(main())

2. Start the local tool server

In terminal one, run:

python study_server.py

It stays on 127.0.0.1:8012. First check that python study_client.py works from another terminal. This separates a connection problem from a model problem.

3. Run the agent

In terminal two, set your own OPENAI_API_KEY and OPENAI_MODEL. Use a model your account can access that supports Responses API function tools. Running this step makes paid API requests.

python mcp_agent.py

The sample asks for MCP lesson two. In the demo catalog, that is Resources. Its task asks you to read study://welcome. A possible final answer explains those facts in a short sentence.

Follow the data

  1. The app discovers the server’s tools.
  2. It checks that the known get_lesson tool exists.
  3. It sends only that tool description to the model.
  4. The model returns a function-call request.
  5. The app checks the name and input fields.
  6. The MCP server checks the actual lesson request.
  7. The app returns the MCP result under the model’s call ID.
  8. The model can answer or request another allowed step.

This is a small, deliberate bridge for one known tool. It is not a general converter for every MCP schema or server feature.

Why not expose every tool?

A server may offer more actions than this task needs. Our study helper only needs to read a lesson. The app does not automatically give the model every discovered action.

For example, a server might later offer a tool to delete lessons. Its presence should not change what this helper can do.

Stopping and errors

The example limits both model responses and tool calls. Its main function also stops the whole task after 90 seconds.

If the lesson tool returns an error, the app sends that error back as data. A connection error may stop the script. That is different from a missing lesson and should not be shown as a made-up answer.

For a public app, add a clear error message, access checks and careful retry rules around these calls. A retry should not repeat a real-world change by mistake.

Test without paying for a model call

The testing lesson includes a scripted model that calls the real in-process MCP server. It checks the bridge without needing an API key.

The live model path has been syntax-checked and tested with those scripted replies. A live paid model request was not part of the tutorial checks.

Try a small change

Ask for Python lesson one instead. Check that the returned title is Lists. Then try a missing lesson and confirm that the answer reports the problem rather than inventing data.

Once this small project is clear, you can replace the demo catalog with a real read-only data source. Keep the same habit: small tools, checked inputs and a clear finish.

Official sources

Connect this lesson to the full AI course

For Python and data basics, start with the first Python project. For document-based answers, read the RAG guide. Use prompt and evaluation checks to assess the answer as well as the tool loop. The local tests check program behaviour; they do not establish the quality of a live model.

All AI & Machine Learning tutorials

Comments (0)

No comments yet. Be the first to comment!

LecturreAI Assistant

Hello! I'm LecturreAI

What would you like to learn today?