Learning Server
БесплатноНе проверенA learning-focused MCP server that provides tools for current time, arithmetic, random quotes, mock weather, and UUID generation, demonstrating the Model Contex
Описание
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.
README
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
- Architecture
- Folder Structure
- Installation
- Running the Project
- Available Tools
- Example Execution
- MCP Learning Notes
- Common Errors
- Future Improvements
🎯 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+
uvpackage 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:
- FastMCP inspects the function's type hints
- Generates a JSON Schema for the inputs
- Stores the tool in an internal registry
- 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:
- 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!")
- Validation (optional) — Add a Pydantic model to
server/models.py:
class MyToolInput(BaseModel):
input_param: str = Field(..., min_length=1)
- Registration — Add a
@mcp.tool()decorator inserver/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).
Установка Learning Server
У этого сервера нет опубликованного пакета — он собирается из исходников. Открой репозиторий и следуй инструкции в README.
▸ github.com/somyabhadada/mcp_serverFAQ
Learning Server MCP бесплатный?
Да, Learning Server MCP бесплатный — установка в пару кликов через Unyly без оплаты.
Нужен ли API-ключ для Learning Server?
Нет, Learning Server работает без API-ключей и переменных окружения.
Learning Server — hosted или self-hosted?
Self-hosted: сервер запускается локально на твоей машине командой из раздела установки.
Как установить Learning Server в Claude Desktop, Claude Code или Cursor?
Открой Learning Server на unyly.org, выбери вкладку своего клиента (Claude Desktop, Claude Code, Cursor) и нажми Install — конфиг сгенерируется автоматически, без правки JSON.
Похожие MCP
GitHub
PRs, issues, code search, CI status
автор: GitHubFilesystem
Secure file operations with configurable access controls.
Memory
Knowledge graph-based persistent memory system.
Template MCP Server
A CLI tool to create a new Model Context Protocol server project with TypeScript support, dual transport options, and an extensible structure
автор: mcpdotdirectCompare Learning Server with
Не уверен что выбрать?
Найди свой стек за 60 секунд
Автор?
Embed-бейдж для README
Похожее
Все в категории development
