Simple-MCP-Server-with-Python

gmossy/Simple-MCP-Server-with-Python

3.1

If you are the rightful owner of Simple-MCP-Server-with-Python and would like to certify it and/or have it hosted online, please leave a comment on the right or send an email to henry@mcphub.com.

The Model Context Protocol (MCP) server is a framework that standardizes the interface between applications and large language models (LLMs), allowing for secure and modular interaction.

Tools
1
Resources
0
Prompts
0

Building a Simple MCP Server in Python Using the MCP Python SDK

ℹ️ About This Project

Note: This project is part of my learning journey with the Model Context Protocol (MCP) and Python server development. Feel free to contribute or provide feedback!

ℹ️ Note for macOS Users

This guide is optimized for macOS. While the core MCP functionality works across platforms, some installation and setup steps may vary for other operating systems.

MCP Documentation Python Version

📚 MCP Resources

Before we begin, here are some valuable MCP resources:

🚀 Tutorial Overview

This tutorial will guide you through creating a simple Model Context Protocol (MCP) server using Python 3.12. MCP is a standardized way to supply context to large language models (LLMs), allowing you to build servers that expose data (resources), functionality (tools), and interaction templates (prompts) to LLM applications in a secure and modular fashion.

What You'll Learn

  • How to set up an MCP server with Python 3.12
  • How to define and expose tools for LLMs to use
  • How to create and manage prompts and resources
  • How to test and interact with your MCP server

Use Cases

  • Building custom LLM-powered applications
  • Creating modular AI assistants
  • Developing secure interfaces between LLMs and your data/systems

Introduction to MCP

The Model Context Protocol (MCP) standardizes the interface between applications and LLMs, allowing you to separate the concerns of providing context, executing code, and managing user interactions. The MCP Python SDK implements the full MCP specification, enabling you to:

  • Expose Resources: Deliver data to LLMs (similar to GET endpoints)
  • Define Tools: Provide functionality that performs actions or computations (like POST endpoints)
  • Create Prompts: Offer reusable, templated interactions

MCP Primitives

Every MCP server can implement three core primitives. These define who controls the invocation and what role each primitive plays:

PrimitiveControlDescriptionExample Use
PromptsUser‑controlledInteractive templates invoked by user choiceSlash commands, menu options
ResourcesApplication‑controlledContextual data managed by the client applicationFile contents, API responses
ToolsModel‑controlledFunctions exposed to the LLM to take actionsAPI calls, data updates
  • Prompts let you define structured conversation starters.
  • Resources are like read‑only data endpoints for the LLM’s context.
  • Tools enable the LLM to do things—calculate, fetch, update.

Server Capabilities

During initialization, an MCP server advertises which features it supports. Clients (and front‑ends) can adapt dynamically based on these flags:

CapabilityFeature FlagDescription
promptslistChangedPrompt template management
resourcessubscribe
listChanged
Resource exposure and live updates
toolslistChangedTool discovery and execution
loggingServer logging configuration
completionArgument completion suggestions
  • listChanged signals that the set of available prompts/resources/tools can change at runtime.
  • subscribe lets clients register for updates when resource data changes.
  • logging and completion are simple toggles for debug output and autocomplete help.

This tutorial will guide you through creating a simple MCP server using the MCP Python SDK.

Prerequisites

Before you begin, ensure you have the following installed:

  • Python 3.12+ (preferably 3.12 or higher)
  • pip – the Python package installer
  • Node.js 18.x

You will also need to install the MCP Python SDK. You have two options:

  • Using pip directly:

    pip install "mcp[cli]"
    
  • Using uv:
    If you are managing your project with uv, initialize your project and add MCP as a dependency.

    uv init mcp-server
    cd mcp-server
    uv add "mcp[cli]"
    

For more detailed installation instructions, please check the MCP Python SDK documentation.

Quickstart: Using uv with Python 3.12

If you are using uv, here is a concise setup that pins Python 3.12 for this project and installs dependencies:

# From the repository root
cd mcp-server

# Ensure Python 3.12 is available to uv
uv python install 3.12


- **Python 3.12+** (required)

  ```bash
  # Install Python 3.12 using Homebrew
  brew install python@3.12
  • uv (Python package manager and resolver)

    curl -sSf https://astral.sh/uv/install.sh | sh
    
  • Node.js 18.x (for the MCP Inspector UI)

    # Install Node.js 18 using Homebrew
    brew install node@18
    

Installation

  1. Set up the project environment:

    # Navigate to the project directory
    cd Simple-MCP-Server-with-Python/mcp-server
    
    # Ensure Python 3.12 is available to uv
    uv python install 3.12
    
    # Pin the project to Python 3.12
    uv python pin 3.12
    
    # Install dependencies from pyproject/uv.lock
    uv sync
    
  2. Verify the installation:

    mcp --help  # Check if MCP is installed and view available commands
    

Development Tools

For development, you'll want to install these essential Python tools:

pip install ipykernel black flake8
  • ipykernel: For Jupyter notebook support
  • black: Code formatter for consistent Python code style
  • flake8: Linter to ensure code quality

Verify Your Installation on macOS

After installation, verify everything is working correctly on your macOS system:

python --version  # Should show Python 3.12.x
mcp --help       # Should show MCP command help if installed

Running the MCP Server

Development Mode (Recommended)

For development with authentication disabled (easier testing):

# From the mcp-server directory
DANGEROUSLY_OMIT_AUTH=true mcp dev server.py

Or use the included script:

# Make the script executable if you haven't already
chmod +x start_dev_server.sh

# Run the development server
./start_dev_server.sh
Production Mode

For production use with authentication enabled:

# From the mcp-server directory
mcp dev server.py

In both cases, this will:

  1. Start the MCP server
  2. Open the MCP Inspector in your default web browser
  3. Allow you to interact with your MCP tools and resources

The MCP Inspector will be available at http://localhost:6274 where you can test your tools and resources.

Using the Demo Server in Claude Desktop App

To use your MCP server directly in the Claude desktop app:

  1. Install the server in Claude:

    mcp install mcp-server/server.py
    
  2. Open the Claude desktop app

  3. Your server (named "Demo Server" by default) will be available in Claude's interface

  4. You can now use your MCP tools directly within your conversations with Claude

Important Notes:

  • Make sure to activate the virtual environment (source .venv/bin/activate on macOS/Linux or .venv\Scripts\activate on Windows) every time you work on your project in a new terminal session.
  • The pyproject.toml and uv.lock files are used to manage dependencies with uv. If you add new dependencies, update these files accordingly.
  • For development, the MCP Inspector provides a web interface to test your server's functionality at http://localhost:6274.

With your environment set up, you're ready to create your MCP server!


🛠️ Setting Up Your Project on macOS

Create a new directory for your project and navigate into it. Then, create a file called server.py in the root of your project.

Your project structure should look like this:

mcp-server/
├── server.py
└── (other files such as .env, README.md, etc. as needed)

Creating Your MCP Server

In this section, we will create a simple MCP server that exposes a calculator tool and a dynamic greeting resource. You can later extend this to add more functionality using prompts or additional tools.

Defining Tools

Tools are functions that perform computations or side effects. In this example, we’ll define a simple addition tool.

Open server.py and add the following code:

# server.py
from mcp.server.fastmcp import FastMCP

# Create an MCP server instance with a custom name.
mcp = FastMCP("Demo Server")

# Add a calculator tool: a simple function to add two numbers.
@mcp.tool()
def add(a: int, b: int) -> int:
    """
    Add two numbers together.
    
    :param a: First number.
    :param b: Second number.
    :return: Sum of the numbers.
    """
    return a + b

Exposing Resources

Resources provide data that can be loaded into an LLM’s context. Here, we define a resource that returns a personalized greeting.

Add the following code to server.py:

# Expose a greeting resource that dynamically constructs a personalized greeting.
@mcp.resource("greeting://{name}")
def get_greeting(name: str) -> str:
    """
    Return a greeting for the given name.
    
    :param name: The name to greet.
    :return: A personalized greeting.
    """
    return f"Hello, {name}!"

Adding Prompts (Optional)

Prompts allow you to supply reusable templates for interactions. For example, you can add a prompt to review code.

Add the following code if desired:

from mcp.server.fastmcp.prompts import base

@mcp.prompt()
def review_code(code: str) -> str:
    """
    Provide a template for reviewing code.
    
    :param code: The code to review.
    :return: A prompt that asks the LLM to review the code.
    """
    return f"Please review this code:\n\n{code}"

Running Your MCP Server

First, make sure your Python virtual environment is activated by running:

source .venv/bin/activate

This should be done in the root directory of your project. Then, change into your MCP server folder:

cd mcp-server

With your server defined in server.py, you can now run it. There are several ways to run an MCP server depending on your goals — development, debugging, integration, or deployment.

The Best Way to Run and Test Your Server: Use mcp dev

The easiest way to interact with your MCP server is using the built-in MCP Inspector, which gives you a visual UI in the browser.

For development and testing, the MCP Development Inspector provides an intuitive web interface to interact with your server.

1. Start Your Server in Development Mode

In your terminal, run:

mcp dev server.py

This command launches your MCP server and typically opens the Inspector in your web browser. You'll see your "Demo Server" and the exposed tools (add), resources (greeting), and prompts (review_code).

  1. Start your server with the Inspector:

    mcp dev server.py
    
  2. It performs several important tasks:

  • Starts your MCP server using the default STDIO transport.
  • Enables live reloading, so your code updates are applied immediately without needing to restart the server.
  • Launches the MCP Inspector interface, a web-based UI available at http://localhost:6274/, where you can explore and test all the functionalities of your server.

After running the command, your terminal output should resemble:

This output confirms that the Inspector is active and ready for interaction.

  1. In the UI, you can:
    • Test the add(a, b) tool
    • Use greeting://John to test the resource
    • Review code with the review_code prompt

💡 If any packages are missing, mcp dev will help you install them automatically.

Navigating the MCP Inspector Interface

2. Interact Through the Inspector

When you open the Inspector in your browser, you will notice several key sections designed to facilitate server testing.

Go to the top of the MCP Inspector interface where it shows:

Transport Type: STDIO  
Command: python  
Arguments: run --with mcp mcp run server.py

Due to in our server.py is a standalone script using a normal pip-based virtual environment, we need to correct configuration in the MCP Inspector to:

Transport Type: STDIO  
Command: python  
Arguments: server.py

Then click Connect and our server will be launched properly using the Python interpreter.

Example Usage: Testing the "Add" Tool

  • Call a Tool (add): Navigate to the add tool in the Inspector.

Consider the following scenario while working with our example (which registers an addition tool, a greeting resource, and a code review prompt):

  1. Accessing the Tools Tab:
    With the MCP Inspector loaded, navigate to the Tools tab. Click List Tools. Here, you will see a list of all registered tools from your server. In our case, one of the entries is the add tool.

  2. Testing the "Add" Functionality:
    Click on the add tool from the list. The Inspector displays the input schema for the tool, prompting you for two numbers (for example, parameters a and b).

    • Input Parameters: Enter a = 10 and b = 15.
    • Execution: Click the execute button in the Inspector’s execution panel.
  3. Reviewing the Output:
    Once executed, the Inspector immediately displays the result of the operation. You should see that the sum is returned as 25.
    This immediate feedback loop shows how you can quickly verify that your tool’s logic is working as expected without leaving the development interface.

Using the review_code Prompt via the MCP Inspector

  • In the Inspector’s sidebar, expand PromptsList prompts.
  • You should see review_code listed:

    review_code

In the prompt pane you’ll find a form or JSON editor ready to accept arguments.

  1. Supply the code parameter. For example:
print(1+1)

Hit Run Tool .

Viewing the Generated Prompt

The Inspector will display the output:

{
  "messages": [
    {
      "role": "user",
      "content": {
        "type": "text",
        "text": "Please review this code:\n\nprint(1+1)"
      }
    }
  ]
}

Accessing a Resource (greeting://Alice)

Once your server is up and running in the Inspector (using the setup from above), you can invoke any registered resource by its URI:

1. Open the Resource Interaction Pane

  • In the MCP Inspector sidebar, click ResourcesResource Templates. click List templates and select get_greeting

2. Enter the Resource URI

  • In the input field name, type:
Alice

3. Invoke the Resource

  • Click Read Resource.
  • The Inspector will send that URI to your @mcp.resource("greeting://{name}") handler.

4. View the Response

  • You should see:
{
  "contents": [
    {
      "uri": "greeting://Alice",
      "mimeType": "text/plain",
      "text": "Hello, Alice!"
    }
  ]
}
  • This confirms your dynamic greeting resource is wired up correctly and returns personalized output on demand.

The MCP Inspector acts as a user-friendly client, handling the underlying protocol communication for you.

What’s Happening with python server.py

Your code is correct, but when you run:

python server.py

…it looks like nothing happens. That’s because your server is using stdio (standard input/output) as the default transport, and it just sits silently waiting for a client to connect and send it a request.

This is normal! But you need the right interface to interact with it.

🐍 Using a Python MCP Client (client.py)

For programmatic interaction, you can use the mcp Python SDK to create a client. Your provided client.py is a correct example of this:

# client.py
import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

async def main():
    server_params = StdioServerParameters(
        command="python",
        args=["server.py"],
    )

    async with stdio_client(server_params) as (reader, writer):
        async with ClientSession(reader, writer) as session:
            await session.initialize()

            result = await session.call_tool("add", arguments={"a": 3, "b": 4})
            print(f"Result of add tool: {result}")

if __name__ == "__main__":
    asyncio.run(main())

To run this client:

  1. Start your server:
    python server.py
    
  2. Run the client in a separate terminal:
    python client.py

The output will be:

[your log output indicating the tool call]
Result of add tool: meta=None content=[TextContent(type='text', text='7', annotations=None)] isError=False

This demonstrates how a Python client can connect to your server and invoke tools programmatically.

Running the MCP Inspector with Authentication (Recommended)

When you run the Inspector via mcp dev, it launches a local web UI and a proxy. Your Python server continues to run over STDIO; the HTTP ports are only for the Inspector UI and proxy.

  • Inspector UI: http://localhost:6274
  • Proxy: http://localhost:6277 (enforces session token)
  • Server transport: STDIO (not HTTP)

One‑liner

From the mcp-server/ directory:

./start_server.sh

What happens:

  • Frees busy Inspector ports (6274/6277)
  • Starts mcp dev server.py with auth enabled
  • Auto‑detects the printed “Session token:” and opens http://localhost:6274?token=<TOKEN> in your browser

If your browser does not open automatically, copy the URL printed in your terminal and open it manually.

Important: Which token to use?

  • Use the long token printed by mcp dev (the Inspector token)
  • Do not use MCP_SESSION_TOKEN in the browser; that is for programmatic clients.

Disable auth (development only)

For quick, unauthenticated testing:

./start_dev_server.sh

This sets DANGEROUSLY_OMIT_AUTH=true and starts mcp dev server.py.

Running Server and Client with the Same Token

Sometimes you want a programmatic client and server to share a token (without Inspector).

  • Run the client (spawns server via STDIO with the same token):
./run_client.sh --token my-shared-token
  • Or run the server directly with a token (no Inspector):
./run_server_env.sh --token my-shared-token

Both scripts will:

  • Unset DANGEROUSLY_OMIT_AUTH
  • Export MCP_SESSION_TOKEN to the given (or generated) value
  • Start the appropriate process

Troubleshooting Inspector Authentication

  • Ports in use (6274/6277):
    lsof -ti:6274,6277 | xargs kill -9
    
  • Wrong token: Use the token printed by mcp dev (Inspector token), not MCP_SESSION_TOKEN.
  • Browser caching: Open a private/incognito window or hard refresh (Cmd+Shift+R).
  • Still blocked: Append the token to the URL, for example:
    http://localhost:6274?token=<PASTE_TOKEN_HERE>
    

Conclusion

In this tutorial, we built a simple MCP server in Python using the MCP Python SDK. We:

  • Set up our project and installed the MCP SDK.
  • Created an MCP server that exposes a calculator tool and a dynamic greeting resource.

The MCP Python SDK empowers you to build robust and modular servers that provide data, functionality, and reusable interaction templates to LLM applications. As you expand your server, consider adding more complex tools, resources, and prompts to fully leverage the power of the Model Context Protocol.