MCP Learning Server

MCP Learning Server

A learning-focused MCP server that provides tools for current time, arithmetic, random quotes, mock weather, and UUID generation, demonstrating the Model Context Protocol end-to-end.

Category
Visit Server

README

šŸ”Œ MCP Learning Project

A complete educational project demonstrating the Model Context Protocol (MCP) end-to-end.

This project is designed for developers who are completely new to MCP and want to understand:

  • What an MCP Server is
  • What an MCP Client is
  • How they communicate
  • How tools are registered and discovered
  • How requests flow and responses return

šŸ“‹ Table of Contents


šŸŽÆ Project Overview

This project implements:

Component Description
MCP Server A FastMCP server exposing 5 tools via stdio transport
Interactive Client A Rich-powered CLI that discovers and calls tools
Chat Interface A natural language chatbot that maps queries to tools
Pydantic Validation Type-safe input validation with friendly errors
Logging Structured logs with INFO/WARNING/ERROR levels
Architecture Diagrams Mermaid diagrams explaining every component

Tech Stack

  • Python 3.11+
  • uv package manager
  • Official MCP Python SDK (mcp[cli] with FastMCP)
  • asyncio
  • Pydantic v2
  • Rich library

šŸ—ļø Architecture

High-Level Architecture

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”          ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│     MCP Client      │   stdio  │     MCP Server      │
│                     │◄────────►│                     │
│  • Rich CLI/Chat    │  JSON-   │  • FastMCP Router   │
│  • ClientSession    │  RPC 2.0 │  • 5 Registered     │
│  • Tool Discovery   │          │    Tools            │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜          ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

Request Flow

User
 ↓
Client UI (Rich terminal)
 ↓
ClientSession.call_tool(name, arguments)
 ↓
JSON-RPC 2.0 Request (serialized)
 ↓
stdio Transport (stdin pipe to server subprocess)
 ↓
FastMCP Server (deserializes, routes by tool name)
 ↓
@mcp.tool() Function → Pydantic validation → Business logic
 ↓
JSON Response (consistent format)
 ↓
stdio Transport (stdout pipe back to client)
 ↓
ClientSession (deserializes response)
 ↓
Rich UI (pretty-prints with syntax highlighting)
 ↓
User sees the result āœ…

Tool Discovery Flow

Client                          Server
  |                               |
  |──── initialize() ───────────►|
  |◄──── capabilities ──────────|
  |                               |
  |──── list_tools() ───────────►|
  |◄──── tool schemas ──────────|
  |       (name, description,     |
  |        inputSchema)           |
  |                               |
  |  Client now knows all tools!  |

šŸ“Œ Key MCP advantage: The client doesn't need hardcoded endpoints. It discovers tools dynamically!


šŸ“ Folder Structure

mcp-learning-project/
│
ā”œā”€ā”€ server/                    # MCP Server (the "backend")
│   ā”œā”€ā”€ __init__.py           # Package marker
│   ā”œā”€ā”€ server.py             # FastMCP setup + tool registration
│   ā”œā”€ā”€ tools.py              # Pure business logic (no MCP dependency)
│   ā”œā”€ā”€ models.py             # Pydantic validation models
│   └── quotes.py             # Hardcoded quote data
│
ā”œā”€ā”€ client/                    # MCP Client (the "frontend")
│   ā”œā”€ā”€ __init__.py           # Package marker
│   ā”œā”€ā”€ client.py             # Interactive CLI client
│   ā”œā”€ā”€ chat.py               # Natural language chat interface
│   └── ui.py                 # Rich terminal UI helpers
│
ā”œā”€ā”€ diagrams/                  # Documentation
│   └── architecture.md       # Mermaid architecture diagrams
│
ā”œā”€ā”€ logs/                      # Generated at runtime
│   └── app.log               # Structured application logs
│
ā”œā”€ā”€ screenshots/               # Screenshots placeholder
│
ā”œā”€ā”€ README.md                  # This file
ā”œā”€ā”€ pyproject.toml            # Project configuration
ā”œā”€ā”€ uv.lock                   # Dependency lock file (auto-generated)
└── .gitignore                # Git ignore rules

Why Each File Exists

File Purpose
server/server.py MCP registration layer — connects business logic to the MCP protocol using @mcp.tool() decorators
server/tools.py Business logic — pure Python functions that can be tested without MCP
server/models.py Validation — Pydantic models that enforce type safety at the input boundary
server/quotes.py Data — separates data from logic for clean architecture
client/client.py Interactive client — demonstrates the full MCP client workflow
client/chat.py Chat interface — shows how natural language maps to tool calls
client/ui.py Presentation — all Rich formatting in one place

šŸš€ Installation

Prerequisites

  • Python 3.11 or higher
  • uv package manager

Setup

# 1. Clone the repository
git clone <repository-url>
cd mcp-learning-project

# 2. Create a virtual environment and install dependencies
uv sync

# This will:
# - Create a .venv/ directory
# - Install mcp[cli], pydantic, and rich
# - Generate uv.lock for reproducibility

ā–¶ļø Running the Project

Option 1: Interactive Client (Recommended)

The client automatically starts the server as a subprocess — you only need one terminal:

uv run python -m client.client

Option 2: Chat Interface

uv run python -m client.chat

Option 3: Server Only (for debugging)

If you want to run the server independently (e.g., to test with MCP Inspector):

uv run python -m server.server

āš ļø The server uses stdio transport, so it reads from stdin and writes to stdout. Running it directly will wait for JSON-RPC input — this is expected behavior.

Testing with MCP Inspector

The MCP Inspector is a visual debugging tool:

npx -y @modelcontextprotocol/inspector uv run python -m server.server

🧰 Available Tools

1. current_time

Returns the current UTC time in ISO 8601 format.

Input None
Output {"success": true, "data": {"current_time": "2026-07-01T12:00:00Z"}}

2. calculator

Performs arithmetic operations on two numbers.

Input {"a": 10, "b": 20, "operation": "add"}
Operations add, subtract, multiply, divide
Output {"success": true, "data": {"result": 30.0}}
Division by zero {"success": false, "error": "Division by zero is not allowed"}

3. random_quote

Returns a random inspirational quote from an internal list of 25 quotes.

Input None
Output {"success": true, "data": {"quote": "Stay hungry, stay foolish. — Steve Jobs"}}

4. weather

Returns mock weather data for Indian cities.

Input {"city": "Jaipur"}
Supported Cities Jaipur, Delhi, Mumbai, Bangalore, Chennai, Kolkata, Hyderabad, Pune
Output {"success": true, "data": {"city": "Jaipur", "temperature": "35°C", "condition": "Sunny"}}
Unknown city {"success": false, "error": "City 'London' not found. Available cities: ..."}

5. uuid_generator

Generates a random UUID (version 4).

Input None
Output {"success": true, "data": {"uuid": "550e8400-e29b-41d4-a716-446655440000"}}

šŸ’» Example Execution

Interactive Client

╭─────────────────────────────────────────────╮
│ šŸ”Œ MCP Learning Client                     │
│    Connected to MCP Server via stdio        │
╰─────────────────────────────────────────────╯

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│            šŸ“‹ Available Tools                │
ā”œā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│ #  │ Tool Name      │ Description            │
ā”œā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│ 1  │ current_time   │ Get the current UTC    │
│    │                │ time in ISO 8601       │
ā”œā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│ 2  │ calculator     │ Perform arithmetic     │
│    │                │ operations             │
ā”œā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│ 3  │ random_quote   │ Get a random           │
│    │                │ inspirational quote    │
ā”œā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│ 4  │ weather        │ Get mock weather data  │
ā”œā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│ 5  │ uuid_generator │ Generate a random UUID │
ā””ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

>>> 2
Enter calculator inputs:
  First number (a): 45
  Second number (b): 87
  Operation (add/subtract/multiply/divide): multiply
ā³ Calling calculator...
╭─── āœ… Success ───╮
│ {                │
│   "result": 3915 │
│ }                │
╰──────────────────╯
ā±  Completed in 0.023s

>>> info weather
šŸ“Œ weather

Get mock weather data for an Indian city.

šŸ“„ Input Schema:
{
  "type": "object",
  "properties": {
    "city": { "type": "string" }
  },
  "required": ["city"]
}

>>> exit
šŸ‘‹ Goodbye!

Chat Interface

šŸ¤– MCP Chat Interface
Ask me about weather, calculations, time, quotes, or UUIDs.
Type 'exit' to quit.

You: What is the weather in Jaipur?
ā³ Calling weather tool...
╭─── šŸ¤– Assistant ───╮
│ šŸŒ¤ļø  Weather in      │
│ Jaipur: 35°C, Sunny │
╰─────────────────────╯

You: Multiply 45 and 87
ā³ Calling calculator tool...
╭─── šŸ¤– Assistant ───╮
│ šŸ”¢ The answer is:   │
│ 3915.0               │
╰─────────────────────╯

You: Give me an inspirational quote
ā³ Calling random_quote tool...
╭───── šŸ¤– Assistant ─────╮
│ šŸ’¬ "Stay hungry, stay   │
│ foolish. — Steve Jobs"  │
╰─────────────────────────╯

Example Log Output (logs/app.log)

2026-07-08 10:30:15 | INFO    | mcp.server | FastMCP server instance created: 'MCP Learning Server'
2026-07-08 10:30:15 | INFO    | mcp.server | Starting MCP Learning Server (stdio transport)...
2026-07-08 10:30:16 | INFO    | mcp.client | Client starting — connecting to MCP server...
2026-07-08 10:30:16 | INFO    | mcp.client | stdio transport established
2026-07-08 10:30:16 | INFO    | mcp.client | MCP session initialized
2026-07-08 10:30:16 | INFO    | mcp.client | Discovered 5 tools: ['current_time', 'calculator', ...]
2026-07-08 10:30:20 | INFO    | mcp.client | Calling tool: calculator with args: {'a': 45, 'b': 87, 'operation': 'multiply'}
2026-07-08 10:30:20 | INFO    | mcp.server | Tool called: calculator(a=45.0, b=87.0, op=multiply)
2026-07-08 10:30:20 | INFO    | mcp.server | Tool result: calculator → {'success': True, 'data': {'result': 3915.0}}
2026-07-08 10:30:20 | INFO    | mcp.client | Tool calculator completed in 0.023s

šŸ“š MCP Learning Notes

What is MCP?

The Model Context Protocol (MCP) is a standardized protocol for connecting AI models to external tools and data sources. Think of it as a "USB-C for AI" — a universal interface that lets any AI assistant use any tool without custom integrations.

How MCP Registers Tools

from mcp.server.fastmcp import FastMCP

mcp = FastMCP("My Server")

@mcp.tool(
    name="calculator",
    description="Perform arithmetic operations"
)
def calculator(a: float, b: float, operation: str) -> str:
    # FastMCP auto-generates a JSON Schema from the function signature:
    # {
    #   "type": "object",
    #   "properties": {
    #     "a": {"type": "number"},
    #     "b": {"type": "number"},
    #     "operation": {"type": "string"}
    #   },
    #   "required": ["a", "b", "operation"]
    # }
    ...

When @mcp.tool() is called:

  1. FastMCP inspects the function's type hints
  2. Generates a JSON Schema for the inputs
  3. Stores the tool in an internal registry
  4. When list_tools() is called, returns all registered schemas

How the Client Discovers Tools

from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

# 1. Define how to launch the server
server_params = StdioServerParameters(
    command="uv",
    args=["run", "python", "-m", "server.server"]
)

# 2. Connect via stdio
async with stdio_client(server_params) as (read, write):
    async with ClientSession(read, write) as session:
        # 3. Initialize the session (handshake)
        await session.initialize()

        # 4. Discover all tools
        tools = await session.list_tools()
        # tools.tools → list of Tool objects with name, description, inputSchema

        # 5. Call a specific tool
        result = await session.call_tool("calculator", {"a": 10, "b": 20, "operation": "add"})

How Requests are Serialized

MCP uses JSON-RPC 2.0 over the stdio transport:

// Client → Server (request)
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "calculator",
    "arguments": {"a": 10, "b": 20, "operation": "add"}
  }
}

// Server → Client (response)
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\"success\": true, \"data\": {\"result\": 30.0}}"
      }
    ]
  }
}

How Transport Works

This project uses stdio transport:

  • The client spawns the server as a subprocess
  • JSON-RPC messages flow through the subprocess's stdin (requests) and stdout (responses)
  • This is why we never print() to stdout in the server — it would corrupt the protocol!

Other MCP transports include:

  • SSE (Server-Sent Events): HTTP-based, good for remote servers
  • Streamable HTTP: Newer HTTP transport for production use

How MCP Differs from REST

Aspect REST API MCP
Discovery You need documentation/OpenAPI spec list_tools() returns schemas dynamically
Protocol HTTP request/response JSON-RPC 2.0 (bidirectional)
Schema OpenAPI (optional) JSON Schema (built-in)
Transport Always HTTP stdio, SSE, Streamable HTTP
Standardization Varies per API One protocol for all tools
AI Integration Custom per model Universal — any model, any tool

How to Add a New Tool

Adding a tool takes 3 steps:

  1. Business logic — Add a function to server/tools.py:
def my_tool(input_param: str) -> dict[str, Any]:
    """Do something useful."""
    return ToolResponse.ok(result="Hello!")
  1. Validation (optional) — Add a Pydantic model to server/models.py:
class MyToolInput(BaseModel):
    input_param: str = Field(..., min_length=1)
  1. Registration — Add a @mcp.tool() decorator in server/server.py:
@mcp.tool(name="my_tool", description="Does something useful")
def tool_my_tool(input_param: str) -> str:
    return json.dumps(my_tool(input_param))

That's it! The client will auto-discover the new tool on next connection.


āš ļø Common Errors

Error Cause Solution
Server script not found Running from wrong directory cd to project root
ModuleNotFoundError: server Missing uv sync or wrong Python Run uv sync first
ConnectionError Server failed to start Check logs/app.log for details
Invalid operation Typo in operation name Use: add, subtract, multiply, divide
Division by zero b=0 with divide operation Expected — handled gracefully
City not found City not in mock data Use: Jaipur, Delhi, Mumbai, Bangalore, Chennai, Kolkata
Broken pipe / garbled output Printing to stdout in server Always use logging or stderr

šŸ”® Future Improvements

  • [ ] Add LLM integration for intelligent intent parsing
  • [ ] Implement SSE transport for remote server access
  • [ ] Add tool caching and rate limiting
  • [ ] Build a web-based UI alongside the CLI
  • [ ] Add unit tests for each tool
  • [ ] Implement MCP Resources and Prompts (beyond Tools)
  • [ ] Add authentication and authorization
  • [ ] Create a tool that chains multiple other tools

šŸ“„ License

MIT License — Feel free to use this for learning and education.


Built as an educational project to understand the Model Context Protocol (MCP).

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured