Context Protocol (MCP)?

As a result of the emergence of AI agents and RAG-based applications in recent years, there’s an increasing demand for customizing Large Language Models (LLMs) by integrating with external resources (e.g. RAG-based systems) and tools (e.g. Agent-based systems). This enhances LLMs’ existing capabilities by incorporating external knowledge and enabling autonomous task execution.

Model Context Protocol (MCP), first introduced in November 2024 by Anthropic, has grown in popularity as it offers a more coherent and consistent way to connect LLMs with external tools and resources, making it a compelling alternative to building custom API integrations for each use case. MCP is a standardized, open-source protocol that provides a consistent interface that enable LLM to interact with various external tools and resources, hence allow end users to MCP server that has been encapsulated with enhanced functionalities. Compared to current agentic system design patterns, MCP offers several key benefits:

  • Increase scalability and maintainability of the system through standardized integrations.
  • Reduce duplicate development effort since a single MCP server implementation works with multiple MCP clients.
  • Avoid vendor lock-in by providing flexibility to switch between LLM providers, since the LLM is no longer tightly coupled with the agentic system.
  • Speed up the development process significantly by enabling rapid creation of workable products.

This article is aim for guiding you through the fundamentals of Model Context Protocol and the essential components of building an MCP server. We will apply these concepts through a practical example of building a MCP server that allows LLMs to summarize and visualize GitHub codebases by simply providing a URL like the example below.

User Input:

https://github.com/aws-samples/aws-cdk-examples/blob/main/python/codepipeline-docker-build/Base.py

MCP Output:


Understanding MCP Components

MCP Architecture

MCP Architecture

MCP adopts a client-server architecture where the client is a device or application that requests services offered by a centralized server. A helpful analogy for the client-server relationship is that of a customer and a restaurant. The customer acts like the client-side, sending requests by ordering from the menu, while the restaurant resembles the server, providing services like dishes and seatings. The restaurant possesses sufficient resources to serve multiple customers in a short period of time, while customers only need to worry about receiving their orders.

MCP architecture consists of three components: MCP server, MCP client and MCP host. MCP server offers tools and resources, exposing functionalities that AI models can leverage through structured requests. MCP host offers the runtime environment that manages communication between clients and servers, such as Claude Desktop or IDEs with MCP-supported extensions. If we continue with the same customer-restaurant analogy above, MCP host can be considered as a restaurant management system that coordinates communications between customers (clients) and restaurants, handles order taking and payment processing. MCP client is typically built into the host application allowing the users to interact with the server through an interface. However, there is the flexibility of developing custom MCP clients for specialized use cases and requirements, such as building a simple AI web app using Streamlit to support more front-end functionalities.

MCP Server Components

In this article, we will focus on understanding MCP server and apply our knowledge to build a simple, custom MCP server. MCP server wraps around various APIs calls to the external tools and resources, enabling the clients accessing these functionalities without worrying about the extra setup. The MCP server supports incorporating three types of components which aligns with three common LLM customization strategies.

  • Resources are data, files and documents that serve as the external knowledge base to enrich LLM’s existing knowledge. This is particularly useful in a RAG-based system.
  • Tools are executable functions and integrations with other programs to enrich LLM’s action space, for example, perform Google Search, create a Figma prototype etc, which can be leveraged in an Agent-based system.
  • Prompts are pre-defined instruction templates to guide LLM’s output, e.g. response in a professional or casual tone. This is useful in the system that benefits from prompt engineering techniques.

If you are interested to know more about LLM customization strategies, check out my previous article and video on “6 Common LLM Customization Strategies Briefly Explained”.


Build Your MCP Server in 6 Steps

We will use a simple example to demonstrate how to build your first MCP server using Python, which enables calling a custom visualize_code tool to turn raw code files extracted from GitHub repositories into visual diagrams like the following example.

For people with data science background learning to build MCP servers, there are several software development concepts that may be unfamiliar but important to understand: asynchronous programming for handling asynchronous operations, client/server architecture, and Python decorators for modifying function behavior. We will explain these concepts in more detail as we walk through this practical example.

Step 1. Environment Setup

  • Package managers setup: MCP uses uv as the default package manager. For macOS and Linux system, install uv and execute it using sh with the shell command:
  • Initiate a new working directory /visual, activate the virtual environment, create the project structure to store the main script visual.py:
# Create a new directory for our project
uv init visual
cd visual

# Create virtual environment and activate it
uv venv
source .venv/bin/activate

# Install dependencies
uv add "mcp[cli]" httpx

# Create our server file
touch visual.py
  • Install required dependencies: pip install mcp httpx fastapi uvicorn

Further Reading:

The official blog post from Anthropic “For Server Developers – Model Context Protocol” provides easy-to-follow guide for setting up the MCP server development environment.

Step 2: Basic Server Setup

In the visual.py script, import the required libraries and initiate our MCP server instance and define a user agent for making HTTP requests. We will use FastMCP as the official Python MCP SDK.

from typing import Any
import httpx
from mcp.server.fastmcp import FastMCP

# Initialize FastMCP server
mcp = FastMCP("visual_code_server")

Step 3: Create Helper Functions

We’ll create a helper function get_code() to fetch code from the GitHub URL.

async def get_code(url: str) -> str:
    """
    Fetch source code from a GitHub URL.
    
    Args:
        url: GitHub URL of the code file
    Returns:
        str: Source code content or error message
    """
    USER_AGENT = "visual-fastmcp/0.1"

    headers = {
        "User-Agent": USER_AGENT,
        "Accept": "text/html"
    }
    
    async with httpx.AsyncClient() as client:
        try:
            # Convert GitHub URL to raw content URL
            raw_url = url.replace("github.com", "raw.githubusercontent.com")\
                        .replace("/blob/", "/")
            response = await client.get(raw_url, headers=headers, timeout=30.0)
            response.raise_for_status()
            return response.text
        except Exception as e:
            return f"Error fetching code: {str(e)}"

Let’s break down the get_code() function into a few components.

Asynchronous Implementation

Asynchronous programming allows multiple operations to run concurrently, improving efficiency by not blocking execution while waiting for operations to complete. It is typically used to handle I/O operations efficiently, such as network request, user inputs and API calls. In contrast, synchronous operations, typically used for machine learning tasks, are executed sequentially, with each operation blocking until completion before moving to the next task. The following changes are made to define this function asynchronously:

  • The function is declared with async def to allow handling multiple operations concurrently.
  • Use async with context manager and httpx.AsyncClient() for non-blocking HTTP requests.
  • Handle asynchronous HTTP requests by adding await keyword to client.get().

URL Processing

Configure Accept header for HTML content and set appropriate User-Agent to identify the client making the HTTP requests, i.e. visual-fastmcp/0.1 . Convert regular GitHub URLs to raw file format.

Error Handling

Catch HTTP-specific exceptions (httpx.RequestError, httpx.HTTPStatusError) and catch other generic exception handling as fallback, then return descriptive error messages for debugging.

Further Reading:

Step 4: Implement the MCP Server Tool

Using a few extra lines of code, we can now create our main MCP server tool visualize_code().

@mcp.tool()
async def visualize_code(url: str) -> str:
    """
    Visualize the code extracted from a Github repository URL in the format of SVG code.

    Args:
        url: The GitHub repository URL
    
    Returns:
        SVG code that visualizes the code structure or hierarchy.
    """

    code = await get_code(url)
    if "error" in code.lower():
        return code
    else:
        return "\n---\n".join(code)
    return "\n".join(visualization)

Decorator

A Python Decorator is a special function that modifies or enhances the behavior of another function or method without altering its original code. FastMCP provides decorators that wrap around custom functions to integrate them into the MCP server. For example, we use @mcp.tool() to create an MCP server tool by decorating the visualize_code function. Similarly, we can use @mcp.resource() for resources and @mcp.prompt() for prompts.

Type Hint and Docstring

The FastMCP class leverages Python type hints and docstrings to automatically enhancing tool definitions, simplifying the creation and maintenance of MCP tools. For our use case, we create tool functions with type hints visualize_code(url: str) -> str, accepting input parameter url with string format and generating the output as a combined string of all code extracted from the source file. Then, add the docstring below to help the LLM to understand tool usage.

    """
    Visualize the code extracted from a Github repository URL in the format of SVG code.

    Args:
        url: The GitHub repository URL
    
    Returns:
        SVG code that visualizes the code structure or hierarchy.
    """

Let’s compare how the MCP tool functions with and without docstring provided, by calling the MCP server through the Claude Desktop.

Model output without docstring – only text summary is generated

Model output with docstring provided – both text summary and diagram are generated

Further reading:

Step 5: Configure the MCP Server

Add the main execution block as the last step in the visual.py script. Run the server locally with simple I/O transport using “stdio”. When running the code on your local machine, the MCP server is located on your local machine and listening for tool requests from MCP clients. For production deployment, you can configure different transport options like “streamable-http” for web-based deployments.

if __name__ == "__main__":
    mcp.run(transport='stdio')

Step 6. Use the MCP Server from Claude Desktop

We will demonstrate how to use this MCP server through Claude Desktop, however, please note that it allows connecting the server to different hosts (e.g. Cursor) by slightly tweaking the configuration. Check out “For Claude Desktop Users – Model Context Protocol” for Claude’s official guide.

  1. Download the Claude Desktop
  2. Set up config file for server settings in your local folder ~/Library/Application\\ Support/Claude/claude_desktop_config.json (for MacOS) and update to your own working folder path.
{
    "mcpServers": {
        "visual": {
            "command": "uv",
            "args": [
                "--directory",
                "/visual",
                "run",
                "visual.py"
            ]
        }
    }
}
  1. Run it using command line uv --directory /visual run visual.py
  2. Launch (or restart) Claude Desktop and select the “Search and tools” then “visual”. You should be able to toggle on the visualize_code tool we just created.
  1. Try the visualization tool by providing a GitHub URL, for example:

Take-Home Message

This article provides an overview of MCP architecture (MCP client, host and server), with the primary focus on MCP server components and applications. It guides through the process of building a custom MCP server that enables code-to-diagram from GitHub repositories.

Essential steps for building a custom MCP server:

  1. Environment Setup
  2. Basic Server Setup
  3. Create Helper Functions
  4. Implemente the MCP Tool
  5. Configure the MCP Server
  6. Use the MCP Server from Claude Desktop

If you are interested in further exploration, potential directions include exploring remote MCP servers on cloud provider, implementing security features and robust error handling.

More Contents Like This

Share.

Comments are closed.