PluginWorld
Mc

mcp-playground

MCP

Provides a weather lookup tool and greeting resource via MCP, enabling AI agents to fetch current conditions and personalized messages through natural language.

@mhamzanadeem · no license · updated today

SECURITY

B

SCORE

54

STARS

0

PLUG IN

git clone https://github.com/mhamzanadeem/mcp-playground.git

See the README to configure this MCP server

README

MCP Demo — Python Agent Tooling from the Ground Up

Python Version MCP SDK Transport License

A production-oriented, runnable example of the Model Context Protocol (MCP) using Python, the official MCP Python SDK, stdio transport, Pydantic, and OpenAI function calling.


What is MCP?

MCP (Model Context Protocol) is a standardized protocol that lets AI applications discover and use external tools, resources, and prompts through one consistent interface.

Instead of every AI framework inventing a different integration for every database, API, filesystem, or internal service, an MCP host can connect to an MCP server and use the same protocol surface.

Problems MCP Solves

Problem MCP Solution
Vendor lock-in Integrations expose capabilities through MCP rather than tying to one model provider or agent framework
Inconsistent tool-calling Tools have machine-readable schemas and standardized discovery/call semantics
No context persistence MCP separates context/tool providers from the model, enabling long-lived connections
Dynamic data sources Databases, APIs, files, and internal systems wrapped as MCP resources/tools without embedding implementation into model runtime

On the wire, MCP uses JSON-RPC 2.0 messages over transports such as stdio and HTTP-based transports (SSE/Streamable HTTP). This repository uses stdio: the client launches the server as a subprocess, sends protocol messages through stdin, and receives responses through stdout.


Architecture

flowchart TD
    A[User: "What's the weather in London?"] --> B[AI Agent<br/>OpenAI Responses API]
    B --> C[1. Discovers MCP tools]
    B --> D[2. Decides whether to call]
    B --> E[3. Emits function call]
    E --> F[MCP Client<br/>ClientSession + stdio]
    F --> G[initialize]
    F --> H[tools/list]
    F --> I[tools/call]
    I --> J[JSON-RPC 2.0<br/>stdin/stdout]
    J --> K[MCP Server subprocess]
    K --> L[get_current_weather tool]
    K --> M[greeting://{name} resource]

Why the Official SDK?

This repository uses the official Python MCP SDK instead of reimplementing the protocol. The SDK supplies:

  • Protocol lifecycle & validation
  • Transport abstraction (stdio, HTTP/SSE)
  • Typed client/server APIs

The application code still makes the important MCP concepts explicit: server registration, tool schemas, initialize, tools/list, tools/call, resource reads, and stdio process management.

The current SDK's stable v2 API uses MCPServer for server construction and ClientSession/stdio_client for stdio clients.


Project Layout

mcp-demo/
├── README.md
├── requirements.txt
├── .env.example
├── pyproject.toml
├── src/
│   ├── mcp_server/
│   │   ├── __init__.py
│   │   ├── server.py      # MCP server entry point
│   │   ├── tools.py       # Tool implementations
│   │   ├── handlers.py    # Request handlers
│   │   └── utils.py       # Shared utilities
│   ├── mcp_client/
│   │   ├── __init__.py
│   │   ├── client.py      # MCP client wrapper
│   │   ├── agent.py       # OpenAI agent integration
│   │   └── runner.py      # Demo runner
│   └── shared/
│       ├── __init__.py
│       └── types.py       # Shared Pydantic models
├── tests/
│   ├── test_server.py
│   └── test_client.py
├── examples/
│   └── demo.ipynb
└── scripts/
    └── run_demo.sh

Requirements

  • Python 3.10+
  • OpenAI API key (for the AI-agent demo)
  • No weather API key required — the weather tool uses deterministic sample data so the MCP path works offline

Quick Start

1. Create Virtual Environment

python -m venv .venv
source .venv/bin/activate        # Linux/macOS
.venv\Scripts\Activate.ps1       # Windows PowerShell

2. Install Dependencies

python -m pip install --upgrade pip
pip install -r requirements.txt

3. Configure OpenAI

cp .env.example .env

Edit .env with your credentials:

OPENAI_API_KEY=your_api_key_here
OPENAI_MODEL=gpt-4.1-mini

The server itself does not need the OpenAI key.


Run the Demo

From Repository Root

python src/mcp_client/runner.py

What the runner does:

Step Description
1️⃣ Launches src/mcp_server/server.py as a child process
2️⃣ Performs MCP initialization handshake
3️⃣ Calls tools/list
4️⃣ Converts discovered MCP schemas → OpenAI function tools
5️⃣ Asks model to answer a natural-language question
6️⃣ When model chooses get_current_weather, sends tools/call through MCP
7️⃣ Sends MCP result back to model
8️⃣ Prints final answer
9️⃣ Shuts down server cleanly

Alternative: Shell Wrapper

bash scripts/run_demo.sh

Expected Output

Exact wording varies by model, but the log flow looks like:

INFO mcp_client.client: -> MCP initialize
INFO mcp_client.client: <- MCP initialize: server=mcp-demo-server
INFO mcp_client.client: -> MCP tools/list
INFO mcp_client.client: <- MCP tools/list: ["get_current_weather"]
INFO mcp_client.agent: User: What's the weather in London?
INFO mcp_client.agent: OpenAI requested tool: get_current_weather {"city":"London","units":"metric"}
INFO mcp_client.client: -> MCP tools/call name=get_current_weather arguments={"city":"London","units":"metric"}
INFO mcp_server.tools: weather lookup city=London units=metric
INFO mcp_client.client: <- MCP tools/call result={"city":"London","temperature":18.0,...}
INFO mcp_client.agent: Final: London is 18°C and partly cloudy.

The logs deliberately show MCP semantic messages at the application boundary. The SDK handles JSON-RPC framing internally.


Run MCP Server Standalone

python src/mcp_server/server.py

A stdio MCP server appears to "hang" — this is expected. It waits for protocol messages on stdin. A host/client should launch it and own the stdio pipes.

Interactive Protocol Inspection

pip install "mcp[cli]"
mcp dev src/mcp_server/server.py

MCP Methods Demonstrated

The official SDK handles the JSON-RPC lifecycle:

Method Direction Purpose
initialize Client → Server Handshake & capability negotiation
tools/list Client → Server Discover available tools
tools/call Client → Server Invoke a tool
resources/list Client → Server Discover available resources
resources/read Client → Server Read a resource

The client explicitly calls initialize() before listing or invoking capabilities. The server's decorators generate tool/resource schemas from Python type annotations.


Tool: get_current_weather

get_current_weather(
    city: str,
    units: Literal["metric", "imperial"] = "metric"
) -> WeatherResponse

Returns structured Pydantic-backed payload:

{
  "city": "London",
  "temperature": 18.0,
  "units": "metric",
  "condition": "partly cloudy",
  "humidity_percent": 72
}

Unknown cities fail with a controlled MCP tool error rather than crashing the server.


Agent Integration Flow

The agent uses plain OpenAI function calling (no extra framework) to keep the demo focused:

flowchart LR
    A[MCP Tool Schema] --> B[OpenAI Function Tool]
    B --> C[Model Chooses Function]
    C --> D[MCP ClientSession.call_tool]
    D --> E[MCP Server Executes Tool]
    E --> F[Function Call Output]
    F --> G[Final Model Answer]

This is the same pattern agent frameworks wrap: discover MCP tools → expose schemas to model → route selected calls back through MCP → feed results into next model turn.


Testing

pytest -q

Test suite covers:

  • ✅ Tool execution (metric weather)
  • ✅ Tool execution (imperial weather)
  • ✅ Validation/error behavior (unknown city)
  • ✅ In-process MCP client discovery & tool invocation

Tests use the SDK's in-memory client where possible — avoids subprocess flakiness while exercising the real MCP protocol layer.


Formatting & Linting

This project uses Ruff:

# Check
ruff check .
ruff format --check .

# Format
ruff format .

Production Notes

This demo is deliberately small, but represents several production concerns:

Concern Implementation
stdout discipline Server never prints app logs to stdout (belongs to MCP); logs go to stderr via logging
Typed I/O Pydantic models validate tool inputs/outputs at application boundary
Controlled failures Tool exceptions → MCP error results (SDK), not process crashes
Subprocess lifecycle SDK's stdio context manager owns process startup/shutdown
Least-privilege env MCP stdio client explicitly passes env vars needed by child process
Dynamic discovery Agent doesn't hard-code weather tool schema; discovers via tools/list

For real external data sources: replace deterministic weather with authenticated API/database calls, add timeouts, retries, rate limiting, observability, and secrets management.


Protocol Mental Model

Simplified JSON-RPC sequence:

// Client -> Server
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{...}}

// Server -> Client
{"jsonrpc":"2.0","id":1,"result":{...}}

// Client -> Server
{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}

// Server -> Client
{"jsonrpc":"2.0","id":2,"result":{"tools":[...]}}

// Client -> Server
{"jsonrpc":"2.0","id":3,"method":"tools/call",
 "params":{"name":"get_current_weather","arguments":{"city":"London"}}}

// Server -> Client
{"jsonrpc":"2.0","id":3,"result":{"content":[...],"structuredContent":{...}}}

The exact protocol schema is maintained by the MCP specification and the SDK. The above is intentionally simplified for teaching.


References


Made with ❤️ for the MCP community

SIMILAR PLUGINS