Welcome to an in-depth MCP Server Tutorial designed to empower you with the knowledge to create your own custom Model Context Protocol (MCP) servers in Python. This guide is based on insights from the tutorial video “Coding Your Own Custom MCP Server in Python – Full Tutorial” by NeuralNine (available at: https://www.youtube.com/watch?v=IuZk3j-D_C0).
In the evolving landscape of AI-powered development tools, seamlessly integrating custom functionalities into your workflow can significantly boost productivity. Imagine an AI assistant in your code editor that doesn’t just suggest code, but can also create GitHub issues, send emails, or interact with your internal APIs on command. This is precisely what custom MCP servers enable. By the end of this comprehensive guide, you’ll have built two practical MCP servers and integrated them with popular AI clients like Cursor or Claw Desktop, unlocking a new realm of automated possibilities.
1. The Power of MCP: Understanding Model Context Protocol
Before we dive into the code, let’s solidify our understanding of what MCP is and why it’s a game-changer for AI-assisted workflows.
What Exactly is MCP?
MCP, or Model Context Protocol, is a standardized communication protocol designed to bridge the gap between AI tools (specifically large language models or LLMs) and various external functionalities. Think of it as a universal language that allows your AI assistant to “talk” to your custom scripts and services.
At its core, MCP defines how an MCP client (like Cursor or Claw Desktop, often enhanced by an LLM) requests actions from an MCP server, which in turn provides specific functionalities. The server exposes “tools” – Python functions in our case – that the client can call. When an LLM within your client recognizes a user’s intent that aligns with a provided tool, it can automatically execute that tool.
Why Build Your Own Custom MCP Server?
The ability to craft your own custom MCP servers opens up a world of automation and personalization. Here’s why this MCP Server Tutorial is incredibly valuable:
- Tailored Automation: Off-the-shelf AI tools are powerful, but they can’t do everything. Custom MCP servers let you automate tasks unique to your workflow, team, or project. Whether it’s filing bug reports in your specific project management tool, deploying a snippet of code, or generating a report, you can build a tool for it.
- Enhanced Productivity: Reduce context switching and manual tasks. By integrating frequently performed actions directly into your AI-powered development environment, you can trigger complex operations with simple natural language commands, saving precious time and effort.
- Seamless Integration: MCP ensures that your custom tools integrate smoothly with compatible AI clients. This means your new functionalities feel like native features, not clunky add-ons.
- Problem-Solving Power: Your AI can not only help you identify problems but also take immediate action to resolve them. For instance, if an error pops up, your AI could be prompted to summarize it and automatically create a GitHub issue, notifying your team.
- Extensibility: The possibilities are virtually limitless. You can connect your AI assistant to any API, database, or external service you can access programmatically.
In this MCP Server Tutorial, we’ll demonstrate two compelling examples: sending emails and creating GitHub issues. These examples serve as a springboard for you to imagine and implement far more complex and specialized tools for your daily tasks.
2. Laying the Foundation: Setting Up Your MCP Server Environment
A solid development environment is crucial for any project. This section will guide you through setting up your Python environment and managing dependencies for your custom MCP servers. While uv is used in the source video for its speed, we’ll also mention pip as a widely accessible alternative.
Prerequisites:
- Python: Ensure Python 3.8+ is installed on your system. You can download it from Python.org.
- Basic Python Knowledge: Familiarity with functions, decorators, and basic I/O will be helpful.
- Command Line Fundamentals: You’ll be using your terminal to manage directories and install packages.
- GitHub Account & Access Token (for GitHub server): If you plan to build the GitHub issue server, you’ll need a GitHub account and a Personal Access Token with appropriate repository permissions. More on this later.
Environment Setup Steps:
-
Choose Your Environment Manager:
-
Option A: Using
uv(Recommended for Speed):
If you don’t haveuvinstalled, you can get it withpip install uvor follow the instructions on its official documentation. -
Option B: Using
pipandvenv(Standard Python Practice):
Python’s built-invenvmodule is excellent for creating isolated environments. You’ll activate these environments before installing packages.
-
-
Create Project Directories:
It’s good practice to separate your custom MCP servers into distinct directories for better organization.mkdir mcp_mail_function mkdir mcp_issue_function -
Initialize Environment and Install Dependencies:
-
For the Email Server (
mcp_mail_function):
Navigate into the directory and initialize your chosen environment.Using
uv:cd mcp_mail_function uv init uv add python-dotenv mcp-serverUsing
pipandvenv:cd mcp_mail_function python -m venv .venv # Create a virtual environment source .venv/bin/activate # Activate it (on Windows: .venv\Scripts\activate) pip install python-dotenv mcp-server(Remember to activate the environment every time you work on this server.)
-
For the GitHub Issue Server (
mcp_issue_function):
Repeat the process for the second directory.Using
uv:cd ../mcp_issue_function uv init uv add python-dotenv mcp-server pygithubUsing
pipandvenv:cd ../mcp_issue_function python -m venv .venv source .venv/bin/activate pip install python-dotenv mcp-server pygithub(Remember to activate the environment every time you work on this server.)
-
Why python-dotenv?
This package is crucial for handling sensitive information like API keys and passwords. It allows you to store environment variables in a .env file, which is then loaded into your application’s environment. This prevents you from hardcoding credentials directly into your source code, making your applications more secure and portable. Crucially, never commit your .env files to public version control repositories like GitHub!
With your environment set up, you’re ready to start coding your first custom MCP server.
3. Building Your First Custom MCP Server: The Email Sender
Our first MCP Server Tutorial example will demonstrate how to create a simple yet powerful MCP server that can send emails on command. This functionality can be incredibly useful for sending notifications, summaries, or reports directly from your AI development environment.
Goal: To create an MCP server that exposes a “send email” tool.
Steps:
-
Create
main.py:
Inside yourmcp_mail_functiondirectory, create a new file namedmain.py. This will house the code for your email server. -
Import Necessary Libraries:
At the top ofmain.py, add the following import statements:import os # For interacting with environment variables import smtplib # For sending emails using SMTP protocol from email.message import EmailMessage # For constructing email messages from dotenv import load_dotenv # For loading .env file from mcp_server.fastmcp import FastMCP # The core MCP server frameworksmtplibis Python’s standard library for sending emails using the Simple Mail Transfer Protocol (SMTP).EmailMessagehelps in creating well-formatted email messages with headers (From, To, Subject) and content.FastMCPis a convenient wrapper aroundmcp-serverthat provides aFastAPI-like syntax for defining tools, making it very intuitive for developers familiar with web frameworks.
-
Load Environment Variables:
load_dotenv()This single line, placed after imports, tells
python-dotenvto look for a.envfile in the current directory and load any key-value pairs found there as environment variables. -
Create MCP Instance:
mcp = FastMCP()This initializes our MCP server application. It’s the central object that will manage and expose our tools.
-
Define the
send_emailTool:
Now, let’s define the core functionality – thesend_emailtool. We’ll use the@mcp.tooldecorator to register this Python function as an MCP tool.@mcp.tool def send_email(recipient: str, subject: str, body: str) -> str: """ Sends an email to the specified recipient with the given subject and body. Expects MAIL_ACCOUNT, MAIL_PASSWORD, SMTP_SERVER, and SMTP_PORT in .env. """ try: # Retrieve credentials and server info from environment variables mail_account = os.getenv("MAIL_ACCOUNT") mail_password = os.getenv("MAIL_PASSWORD") smtp_server = os.getenv("SMTP_SERVER") smtp_port = int(os.getenv("SMTP_PORT")) # Port should be an integer # Create the email message object message = EmailMessage() message["From"] = mail_account message["To"] = recipient message["Subject"] = subject message.set_content(body) # Establish a secure connection and send the email with smtplib.SMTP(smtp_server, smtp_port) as server: server.starttls() # Enable TLS encryption for security server.login(mail_account, mail_password) # Authenticate with the mail server server.send_message(message) # Send the prepared message return "Email sent successfully!" except Exception as e: # Return an error message if anything goes wrong return f"Failed to send email due to: {str(e)}"- Decorator (
@mcp.tool): This is what transforms a regular Python function into an MCP-callable tool. - Function Signature: The type hints (
recipient: str,subject: str,body: str,-> str) are crucial. MCP clients use these to understand what parameters your tool expects and what kind of result it returns. The LLM can then infer how to use your tool based on these definitions and its docstring. - Credential Retrieval:
os.getenv()safely fetches sensitive information from your loaded.envfile. smtplib.SMTP: This initializes the SMTP client.server.starttls()is vital for encrypting your communication with the mail server, protecting your credentials and message content.- Error Handling: The
try...exceptblock ensures that if an issue occurs (e.g., incorrect credentials, network problem), the server returns a helpful error message instead of crashing.
- Decorator (
-
Add the Main Execution Block:
This standard Python idiom ensures that your MCP server runs when themain.pyfile is executed directly.if __name__ == "__main__": mcp.run(transport="stdio")mcp.run()starts the MCP server.transport="stdio"specifies that the server will communicate with the client over standard input/output. This is a common and convenient way to run local MCP servers.
-
Create
.envFile:
In themcp_mail_functiondirectory (alongsidemain.py), create a file named.env. Add your email account details. Replace the placeholder values with your actual email service credentials.MAIL_ACCOUNT=your_sender_email@example.com MAIL_PASSWORD=your_email_password SMTP_SERVER=smtp.example.com # e.g., smtp.gmail.com for Gmail, smtp-mail.outlook.com for Outlook SMTP_PORT=587 # Common ports: 587 (TLS), 465 (SSL)Critical Security Reminder: As mentioned, never share this file or commit it to public repositories. For better security, consider using app-specific passwords or OAuth if your email provider supports them, rather than your main email password. You’ll need to look up the specific SMTP server and port for your email provider (e.g., “Gmail SMTP server” or “ProtonMail SMTP settings”).
With these steps complete, your first custom MCP server is ready to run!
4. Expanding Functionality: The GitHub Issue Creator MCP Server
Building on our first MCP Server Tutorial example, let’s create a second server that can automatically create GitHub issues. This is incredibly useful for turning an AI-suggested task directly into an actionable item in your project’s issue tracker.
Goal: To create an MCP server that can create new issues in a specified GitHub repository.
Steps:
-
Create
main.py:
Inside yourmcp_issue_functiondirectory, create a new file namedmain.py. -
Import Necessary Libraries:
import os # For interacting with environment variables from github import Github # The PyGithub library for GitHub API interaction from dotenv import load_dotenv # For loading .env file from mcp_server.fastmcp import FastMCP # The core MCP server frameworkpygithub(imported asGithub) is a powerful Python library that simplifies interaction with the GitHub API.
-
Load Environment Variables:
load_dotenv()This line will load the
ACCESS_TOKENwe’ll define in the.envfile. -
Create MCP Instance:
mcp = FastMCP()Again, we initialize our
FastMCPapplication. -
Define the
create_issueTool:
Now, we define thecreate_issuefunction, decorated as an MCP tool.@mcp.tool def create_issue(issue_title: str, issue_text: str) -> str: """ Creates a GitHub issue in the specified repository. Requires a GitHub Personal Access Token with 'repo' scope in the .env file. """ try: # Initialize GitHub object with the access token access_token = os.getenv("ACCESS_TOKEN") if not access_token: raise ValueError("GitHub ACCESS_TOKEN not found in environment variables.") g = Github(access_token) # Get the repository object. Replace "your_username/your_repo_name" # with the actual path to your target GitHub repository. # Make sure your token has permissions for this specific repo. repo_name = os.getenv("GITHUB_REPO", "neural9/tutorial-mcp-repo") # Default for demo repo = g.get_repo(repo_name) # Create the issue issue = repo.create_issue(title=issue_title, body=issue_text) return f"GitHub issue '{issue.title}' created successfully! URL: {issue.html_url}" except Exception as e: return f"Failed to create GitHub issue due to: {str(e)}"Github(access_token): This authenticates yourpygithubclient with your GitHub account using the provided Personal Access Token.repo.get_repo("your_username/your_repo_name"): This fetches the specific repository where you want to create issues. You must replace"neural9/tutorial-mcp-repo"with your own repository’s path (e.g.,"myorg/my-project-repo").repo.create_issue(): This method creates the new issue with the provided title and body.- Return Value: The tool returns a success message including the URL of the newly created issue for user convenience.
-
Add the Main Execution Block:
Similar to the email server, this ensures the GitHub server runs when executed.if __name__ == "__main__": mcp.run(transport="stdio") -
Create
.envFile:
In yourmcp_issue_functiondirectory, create a.envfile and add your GitHub Personal Access Token.ACCESS_TOKEN=your_github_personal_access_token_here # Optionally, specify the target repository: # GITHUB_REPO=your_username/your_repo_nameGenerating a GitHub Personal Access Token:
If you don’t have one, you’ll need to generate a fine-grained personal access token on GitHub. Go to your GitHub Settings > Developer settings > Personal access tokens > Fine-grained tokens. Create a new token, give it a descriptive name, and select the specific repository you want it to access. For permissions, you’ll generally needContents(read & write for issues) andIssues(read & write). More details can be found in the GitHub documentation on personal access tokens.
Again, do not commit this.envfile to your repository!
You now have two functional custom MCP servers! The next step in this MCP Server Tutorial is to integrate them with your preferred AI client.
5. Connecting Your MCP Servers: Integration with Cursor & Claw Desktop
Having built your custom MCP servers, the final crucial step is to inform your AI clients (like Cursor or Claw Desktop) about their existence and how to run them. This is typically done via a JSON configuration file.
Goal: Configure Cursor or Claw Desktop to recognize and use your custom mail_tool and git_issue_tool.
Steps:
-
Locate the MCP Configuration File:
The location of this file can vary slightly by operating system and client version.-
Cursor:
- Linux/macOS:
~/.config/cursor/mcp.json - Windows:
%APPDATA%\cursor\mcp.json(or similar, check Cursor’s documentation)
- Linux/macOS:
-
Claw Desktop:
- Linux/macOS:
~/.config/clot/desktop_config.jsonor~/.config/clot_desktop/config.json - Windows: Similar path under
%APPDATA%or program files.
- Linux/macOS:
If the file doesn’t exist, you can create it.
-
-
Edit the Configuration File:
Open themcp.json(ordesktop_config.json) file in your preferred text editor. This file defines a JSON object containing an"mcp_servers"key, which itself is a dictionary mapping server names to their execution commands.-
Basic Empty Structure (if file is new):
{ "mcp_servers": {} } -
Adding Your Servers:
Now, add yourmail_toolandgit_issue_tooldefinitions within the"mcp_servers"object. Crucially, replace the paths in the"args"array with the absolute paths to yourmain.pyfiles.{ "mcp_servers": { "mail_tool": { "command": "uv", "args": [ "run", "/home/your_username/documents/programming/neural9/tutorial/mcp_mail_function/main.py" ] }, "git_issue_tool": { "command": "uv", "args": [ "run", "/home/your_username/documents/programming/neural9/tutorial/mcp_issue_function/main.py" ] } } }Important Notes for Configuration:
-
Paths: Ensure you use the absolute path to your
main.pyfile for each server. Relative paths often do not work correctly in this context. Replace/home/your_username/documents/programming/neural9/tutorial/with the actual parent directory containing yourmcp_mail_functionandmcp_issue_functionfolders. -
"command"and"args":- If you’re using
uv,commandshould beuvandargsshould be["run", "path/to/main.py"]. - If you’re using
pipandvenv, and you’ve activated your virtual environment manually before starting Cursor/Claw, yourcommandmight bepythonandargswould just be["path/to/main.py"]. - Alternatively, if you want the client to activate the virtual environment, you might need to specify the full path to the Python executable within the virtual environment (e.g.,
/home/user/.venv/bin/python) and then the path to your script. Theuv runcommand handles this automatically.
- If you’re using
-
No Whitespace in Paths (Especially Linux/macOS): As highlighted in the source material, avoid whitespace characters in any part of the path to your server files, especially on Linux/macOS, as this can lead to configuration errors in clients like Cursor. Use hyphens (
-) or underscores (_) instead. -
JSON Syntax: Be meticulous with JSON syntax – commas after each server definition (except the last one), correct curly braces and square brackets. A single misplaced comma or bracket can cause a “Configuration Error.”
-
-
-
Restart Cursor/Claw Desktop:
After saving themcp.jsonfile, it’s essential to fully close and restart your AI client for the changes to take effect. This allows the client to re-read its configuration files. -
Verify the Integration:
- Once your client restarts, navigate to its settings or “Tools” section.
- You should now see
mail_toolandgit_issue_toollisted. Ensure they show as “enabled” or “1 tool enabled.” If you see “Configuration Error” or “0 tools enabled,” refer to the troubleshooting section below. - Now, in a chat with your AI assistant within Cursor or Claw Desktop, you can prompt it to use your new tools. For the email tool, try something like: “Send an email to
your_recipient@example.comwith the subject ‘Project Update’ and the body ‘The project is progressing well.'” For the GitHub tool: “Create a GitHub issue titled ‘Refactor authentication module’ with the description ‘The current authentication code needs to be updated for better security practices.'”
Your AI client, empowered by the LLM, will interpret your natural language request, identify the appropriate tool, and prompt you to run it, providing the necessary parameters. This completes the core MCP Server Tutorial process!
6. Troubleshooting Common MCP Server Issues
Even with careful following of an MCP Server Tutorial, you might encounter issues. Here are some common problems and their solutions:
-
“Configuration Error” in Cursor/Claw Desktop:
- JSON Syntax: The most frequent culprit. Use an online JSON validator (search for “JSON validator online”) to check your
mcp.jsonfile for syntax errors like missing commas, unclosed brackets, or incorrect quotation marks. - Incorrect Path: Double-check that the absolute paths in your
mcp.jsonfile (within the"args"array) precisely point to yourmain.pyfiles. Typos are common. - Whitespace in Path: As mentioned, ensure no folders in the path to your
main.pyfiles have spaces in their names. Rename them to use hyphens or underscores. - Client Restart: Always ensure you’ve completely restarted the AI client after making changes to
mcp.json.
- JSON Syntax: The most frequent culprit. Use an online JSON validator (search for “JSON validator online”) to check your
-
“Cannot import name ‘Github’ from ‘github'” (or similar import errors):
-
Missing Dependency: This means the required Python package (
pygithubin this case) was not installed in the environment where your MCP server is running.- If using
uv: Ensure you ranuv add pygithubin the correct directory. - If using
pipandvenv: Ensure you activated the virtual environment (source .venv/bin/activate) before runningpip install pygithub.
- If using
-
Case Sensitivity: Python is case-sensitive. Ensure your import statement matches the package name exactly (e.g.,
from github import Githubforpygithub). The errorcannot import name github from githuboften meanspygithubwas imported with a lowercase ‘g’ where an uppercase ‘G’ was expected for the class name.
-
-
“Failed due to…” Errors (after running a tool):
- Check
.env: Verify that all environment variables (MAIL_ACCOUNT,MAIL_PASSWORD,ACCESS_TOKEN, etc.) in your.envfiles are correctly spelled and contain the right values. A missing or incorrect credential is a common issue. -
Permissions:
- For email: Ensure your
MAIL_ACCOUNThas permission to send emails through the specifiedSMTP_SERVER. Some email providers require “less secure app access” or app-specific passwords. - For GitHub: Ensure your
ACCESS_TOKENhas the necessary scopes (e.g.,repoorissuesread/write) for the target repository.
- For email: Ensure your
- Network Issues: Your server needs to be able to reach the external services (SMTP server, GitHub API). Check your internet connection or firewall settings.
- Console Output: When troubleshooting, manually run your
main.pyscript from the terminal (e.g.,uv run main.pyorpython main.py) in its respective directory. Any direct error messages from your Python code will appear there, which can provide more detailed insights than the general “Failed due to…” message from the client.
- Check
-
Tool Not Triggering / LLM Not Calling Tool:
- Clear Prompts: Ensure your prompts to the AI client are clear and unambiguous about the action you want to perform. The LLM needs enough context to infer that a tool should be used.
- Tool Description: While not explicitly required for simple tools, adding a clear docstring to your
@mcp.toolfunction ("""Sends an email...""") helps the LLM understand its purpose better. For complex tools, a detailed docstring explaining inputs and outputs is highly beneficial. - Parameters: If the LLM tries to call your tool but fails, it might be passing incorrect parameters. Review the
issue_title: str,issue_text: str, etc., in your function signatures to ensure they match what the LLM expects.
By systematically checking these points, you can resolve most issues encountered during your MCP Server Tutorial journey.
7. Beyond the Basics: Advanced Customization and Next Steps
This MCP Server Tutorial has provided a strong foundation for building custom MCP servers. The true power of this protocol lies in its extensibility. Here are ideas for advanced customization and how you can take your MCP server development further:
- Multiple Tools per Server:
You are not limited to one tool permain.pyfile. You can define multiple@mcp.toolfunctions within a singleFastMCPinstance in the same file. This is efficient for grouping related functionalities. For example, a “Project Management Tool” server could havecreate_issue,assign_task,update_status, etc. - Richer Tool Descriptions (Docstrings):
While our examples used short docstrings, for more complex tools, a detailed docstring explaining the tool’s purpose, parameters, and expected output will significantly improve the AI’s ability to use it correctly. Themcp-serverlibrary automatically exposes these docstrings to the client. -
Integrating with Other APIs and Services:
The possibilities are boundless. Think about what repetitive tasks you perform daily that involve interacting with web services.- Communication: Integrate with Slack, Microsoft Teams, Discord (e.g., “Send a Slack message to #dev-updates: ‘New build deployed.'”).
- Project Management: Connect to Jira, Trello, Asana (e.g., “Create a Jira ticket for bug in module X”).
- Cloud Services: Interact with AWS, Google Cloud, Azure APIs (e.g., “Start EC2 instance
my-dev-server-01“). - Internal Tools: If your company has internal APIs, you can build MCP tools to interact with them directly.
- Data Retrieval: Create tools to fetch data from databases or external data sources and present it to your AI.
-
Deployment Considerations:
For long-running or shared MCP servers, consider more robust deployment strategies:- Docker: Containerize your MCP server application. This ensures consistent environments across different machines.
- Cloud Functions (AWS Lambda, Azure Functions, Google Cloud Functions): For event-driven or infrequently used tools, serverless functions can be a cost-effective and scalable deployment option. You’d expose them via HTTP endpoints, and your
mcp.jsonwould point to these external URLs, rather than localstdioprocesses. - Dedicated Servers: For highly critical or frequently used tools, running them on a dedicated server might be appropriate.
- Error Handling and Logging:
For production-ready tools, enhance your error handling. Implement detailed logging to track tool usage, successes, and failures. This is crucial for debugging and monitoring your custom functionalities. - Input Validation:
While type hints (str,int) provide basic validation, for more complex tools, add explicit input validation within your tool functions to ensure that the parameters received from the AI client are valid and safe to process.
This MCP Server Tutorial is just the beginning. The more you experiment and integrate your specific workflows, the more indispensable your AI-powered development environment will become. The ability to customize and extend your tools is a significant step towards a truly personalized and hyper-efficient coding experience.
Conclusion
You’ve successfully navigated this comprehensive MCP Server Tutorial, learning how to build and integrate your own custom MCP servers in Python. From setting up the environment to crafting functional email and GitHub issue creation tools, you now possess the core knowledge to extend the capabilities of AI clients like Cursor and Claw Desktop.
The Model Context Protocol offers a powerful paradigm for enhancing developer productivity by allowing AI models to seamlessly interact with custom functionalities. By applying the principles outlined in this guide, you can automate repetitive tasks, streamline complex workflows, and unlock a new dimension of intelligent assistance in your daily coding life.
Remember the key takeaways: secure credential management with .env files, the simplicity of FastMCP for defining tools, and meticulous configuration of your client’s mcp.json. Now, go forth and build amazing things! Experiment, integrate, and transform your development environment into an even more intelligent and responsive partner.
“`
Discover more from teguhteja.id
Subscribe to get the latest posts sent to your email.

