AI & Machine Learning Topic

Build an MCP Server and Client in Python

Lesson 4 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.

In this lesson, you will make a small MCP server. It offers one tool and one resource. A separate client calls them.

This is a real MCP connection, but it does not call an AI model. You do not need an OpenAI key to run it.

What you need

Use Python 3.11 or newer and the separate environment from the Python tool-calling lesson. That lesson contains the full catalog.py file used here.

This tutorial uses MCP Python SDK 2.3.0. Install the matching release:

python -m pip install "mcp==2.3.0"

Use the MCPServer and Client imports below. Older v1 tutorials may use different classes and names. Do not mix those examples with this one.

1. Add the server

Put study_server.py next to catalog.py:

"""MCP Python SDK v2 server, using only local, read-only demo data."""
from mcp.server import MCPServer
from mcp.server.mcpserver.exceptions import ToolError

from catalog import get_lesson as lookup_lesson

mcp = MCPServer("Lecturre Study Tools")


@mcp.tool()
def get_lesson(course: str, lesson: int) -> dict:
    """Read lesson 1, 2, or 3 from the python, agents, or mcp course."""
    try:
        return lookup_lesson(course, lesson)
    except ValueError as exc:
        raise ToolError(str(exc)) from exc


@mcp.resource("study://welcome")
def welcome() -> str:
    """Short notes about the demo study catalog."""
    return "Demo courses: python, agents, mcp. Each has three lessons."


if __name__ == "__main__":
    # Separate from the Lecturre development server on port 8081.
    mcp.run(transport="streamable-http", host="127.0.0.1", port=8012)

@mcp.tool() marks a function as an MCP tool. Its name, description and type hints help describe the inputs.

@mcp.resource gives the welcome text an address. It is not a normal website page. The MCP client reads it through the server.

The catalog checks the course and lesson again. A model or client cannot create a new lesson simply by asking for one.

2. Start it in terminal one

python study_server.py

Leave this terminal open. The server uses port 8012 and listens only on your computer. A waiting server has not become stuck; it is ready for a client.

3. Add a client

Save this as study_client.py:

"""Call a local MCP tool directly. This file does not call an AI model."""
import asyncio
import json

from mcp import Client

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


async def main():
    async with Client(URL) as client:
        listing = await client.list_tools()
        print("Tools:", sorted(tool.name for tool in listing.tools))
        result = await client.call_tool("get_lesson", {"course": "python", "lesson": 2})
        if result.is_error:
            raise RuntimeError("The MCP tool reported an error")
        if result.structured_content is not None:
            print(json.dumps(result.structured_content, indent=2))
        else:
            for block in result.content:
                if block.type == "text":
                    print(block.text)
        resource = await client.read_resource("study://welcome")
        for block in resource.contents:
            if hasattr(block, "text"):
                print("Resource:", block.text)


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

4. Run the client in terminal two

Open another terminal in the same folder. Activate the same environment, then run:

python study_client.py

The output should list get_lesson. The tool result includes the title Dictionaries and a practice task. The final line reads the welcome resource.

Some tool results have structured data as well as text blocks. The example reads structured data when available. It can also display text blocks.

Read the connection step by step

  • async with Client(URL) opens and closes the connection.
  • list_tools() asks what the server offers.
  • call_tool() calls one named tool.
  • is_error tells the client that a tool failed.
  • read_resource() reads the welcome note.

The client does not decide which lesson a person wants. It calls lesson two because we wrote that in the file. The later agent lesson lets the model choose the inputs.

Try two changes

First, change the course to mcp and the lesson to 1. You should get a different lesson.

Next, try lesson 99. The server should report an error. This is useful: the app must not invent a lesson when its data has no match.

Common problems

  • Connection refused: Start the server first and check the port.
  • Port already in use: Choose a free port and change both files.
  • Import error: Check the active environment and SDK version.
  • No output from the server: Run the client from a second terminal.

Stop the local server with Ctrl+C when you finish. Keep this first project local. A public server needs a separate plan for access, deployment and monitoring.

Next step

Learn how to check the project in Test AI Agents and MCP Tools in Python.

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?