MCP Tutorials 7 min read Jun 08, 2026

Building a Lightweight MCP Client Using Python and HTTP Libraries

Learn to build a minimal MCP client in Python using popular HTTP libraries, complete with working code and integration tips for larger applications. This tutorial walks through setup, request handling, and response parsing to facilitate seamless communication with MCP servers.

Building a Lightweight MCP Client Using Python and HTTP Libraries
```html

Building a Lightweight MCP Client Using Python and HTTP Libraries

Welcome to this hands-on tutorial where we will be building a lightweight MCP client using Python and popular HTTP libraries. The aim is to give you the tools and understanding necessary to integrate MCP capabilities into your applications seamlessly. Whether you're a beginner or an experienced developer, this tutorial will offer practical insights and code to enhance your project.

Prerequisites

  • Intermediate knowledge of Python programming
  • Basic understanding of HTTP requests and responses
  • Python environment setup (Python 3.7 or later)

Step 1: Setting Up Your Development Environment

We’ll start by setting up our environment and installing the necessary libraries. We will use the requests library for HTTP requests and json for handling JSON data.

# Create a new directory for the project
mkdir mcp-client
cd mcp-client

# Create a new virtual environment
python3 -m venv venv
source venv/bin/activate  # On Windows use `venv\Scripts\activate`

# Install required libraries
pip install requests

Step 2: Building the MCP Client

Let’s define the skeleton of our MCP client. We will create a class named MCPClient which will handle making requests to and parsing responses from the MCP server.

import requests

class MCPClient:
    def __init__(self, base_url):
        self.base_url = base_url

    def send_request(self, endpoint, data):
        url = f"{self.base_url}/{endpoint}"
        try:
            response = requests.post(url, json=data)
            response.raise_for_status()
            return response.json()
        except requests.exceptions.HTTPError as http_err:
            print(f"HTTP error occurred: {http_err}")
        except Exception as err:
            print(f"Other error occurred: {err}")
        return None

This basic client initializes with a base URL and provides a send_request method for interacting with the MCP server endpoints.

Step 3: Sending and Handling Requests

Now, let's write a function that uses our MCPClient to send a request to an MCP server. We'll handle potential errors and ensure that the response data is correctly parsed.

def interact_with_mcp():
    client = MCPClient(base_url="https://example-mcp-server.com/api")
    endpoint = "process"
    payload = {"command": "run-analysis", "parameters": {"param1": "value1"}}
    
    result = client.send_request(endpoint, payload)

    if result:
        print("Successfully received response from MCP server:")
        print(result)
    else:
        print("Failed to retrieve valid response.")

interact_with_mcp()

This function creates a new MCPClient instance, sets up the necessary endpoint and data payload, sends the request, and prints the result.

Step 4: Enhancing the Client with Custom Headers

To handle scenarios where authentication or custom headers are needed, let's enhance our client to accept additional headers.

class EnhancedMCPClient(MCPClient):
    def send_request(self, endpoint, data, headers=None):
        url = f"{self.base_url}/{endpoint}"
        try:
            response = requests.post(url, json=data, headers=headers)
            response.raise_for_status()
            return response.json()
        except requests.exceptions.HTTPError as http_err:
            print(f"HTTP error occurred: {http_err}")
        except Exception as err:
            print(f"Other error occurred: {err}")
        return None

Step 5: Adding JWT Authentication

In a real-world application, you'll likely need to authenticate your requests using a JSON Web Token (JWT). Here's how you can modify the EnhancedMCPClient to include JWT authentication:

import jwt

class AuthenticatedMCPClient(EnhancedMCPClient):
    def __init__(self, base_url, secret_key):
        super().__init__(base_url)
        self.secret_key = secret_key

    def generate_jwt(self, payload):
        return jwt.encode(payload, self.secret_key, algorithm="HS256")

    def send_request(self, endpoint, data, headers=None):
        if headers is None:
            headers = {}
        jwt_token = self.generate_jwt({"iss": "your-issuer", "exp": 1609459200})
        headers["Authorization"] = f"Bearer {jwt_token}"
        return super().send_request(endpoint, data, headers)

Step 6: Handling Rate Limiting

If your requests are being throttled, you're likely hitting a rate limit imposed by the MCP server. You can handle this by checking response headers for rate limit info and dynamically adjusting your request rate.

class RateLimitedMCPClient(EnhancedMCPClient):
    def send_request(self, endpoint, data, headers=None):
        response = super().send_request(endpoint, data, headers)
        if response is not None:
            rate_limit_remaining = response.headers.get("X-RateLimit-Remaining")
            if rate_limit_remaining is not None and int(rate_limit_remaining) < 10:
                print("Rate limit is low, slowing down requests")
                time.sleep(1)  # slow down requests
        return response

Common Pitfall: Rate Limiting

If your requests are being throttled, you're likely hitting a rate limit imposed by the MCP server. You can handle this by checking response headers for rate limit info and dynamically adjusting your request rate.

Performance Tip: Asynchronous Requests

If you need to perform multiple requests efficiently, consider using asyncio along with an asynchronous HTTP client like aiohttp. This can significantly improve performance by handling I/O-bound tasks concurrently.

import asyncio
import aiohttp

class AsyncMCPClient:
    def __init__(self, base_url):
        self.base_url = base_url

    async def send_request(self, endpoint, data):
        async with aiohttp.ClientSession() as session:
            async with session.post(f"{self.base_url}/{endpoint}", json=data) as response:
                return await response.json()

async def main():
    client = AsyncMCPClient("https://example-mcp-server.com/api")
    tasks = []
    for i in range(10):
        task = asyncio.create_task(client.send_request("process", {"command": "run-analysis", "parameters": {"param1": "value1"}}))
        tasks.append(task)
    results = await asyncio.gather(*tasks)
    for result in results:
        print(result)

asyncio.run(main())
MCP Client MCP Server Response

Advanced Error Handling

To improve the robustness of our client, let's implement advanced error handling. We'll catch specific exceptions and provide informative error messages.

class RobustMCPClient(EnhancedMCPClient):
    def send_request(self, endpoint, data, headers=None):
        try:
            response = super().send_request(endpoint, data, headers)
            if response is None:
                raise Exception("Failed to retrieve a valid response")
            return response
        except requests.exceptions.ConnectionError as conn_err:
            print(f"Connection error occurred: {conn_err}")
        except requests.exceptions.Timeout as timeout_err:
            print(f"Timeout error occurred: {timeout_err}")
        except Exception as err:
            print(f"An error occurred: {err}")
        return None

With this advanced error handling, our client is more robust and provides better error messages.

Using a Retry Mechanism

To handle transient errors, we can implement a retry mechanism. We'll use the tenacity library to retry failed requests.

import tenacity

class RetryingMCPClient(EnhancedMCPClient):
    @tenacity.retry(wait=tenacity.wait_exponential(multiplier=1, min=4, max=10))
    def send_request(self, endpoint, data, headers=None):
        return super().send_request(endpoint, data, headers)

With this retry mechanism, our client will automatically retry failed requests, improving its overall reliability.

Monitoring Request Performance

To monitor the performance of our client, we can use a library like prometheus-client to track metrics such as request latency and success rate.

from prometheus_client import Counter, Gauge

class MonitoringMCPClient(EnhancedMCPClient):
    def __init__(self, base_url):
        super().__init__(base_url)
        self.request_counter = Counter("mcp_requests", "Number of MCP requests")
        self.request_latency_gauge = Gauge("mcp_request_latency", "MCP request latency")

    def send_request(self, endpoint, data, headers=None):
        start_time = time.time()
        response = super().send_request(endpoint, data, headers)
        end_time = time.time()
        self.request_counter.inc()
        self.request_latency_gauge.set(end_time - start_time)
        return response

With this monitoring capability, we can track the performance of our client and identify areas for improvement.

Tags

MCP Python HTTP Client Development Integration