# The complete guide to Model Context Protocol (MCP)

TLDR; Think of Model Context Protocol (MCP) as a “universal language” that lets different AI tools understand and work with each other. If done correctly, MCP helps AI agents to be more effective at using tools.

# Introduction

You’re probably here because you’ve heard the buzz about MCP and all of a sudden, every tool you use seems to have its own MCP server. But what is MCP? How does MCP work? And like every developer would ask: *“How do I build my own MCP server?”* You’re in the right place. I’ve gone down the MCP (Model Context Protocol) rabbit hole to satisfy my curiosity. I’ve consolidated everything I’ve learned here so you don’t have to.

## **Some context on how MCP started and its current state**

Here’s how everything started, Model Context Protocol emerged in November 2024 as Anthropic's open standard for connecting AI systems to external data and tools, solving what developers call the "N×M integration problem." [**Within s**](https://github.com/modelcontextprotocol/inspector)**ix months, MCP achieved adoption by OpenAI, Google DeepMind, and Microsoft**, amassing over 16,000 servers across the ecosystem. Yet beneath this explosive growth lies a more complex reality: 43% of implementations contain critical security vulnerabilities, enterprises face a steep learning curve, and the protocol itself remains in active evolution.

In this guide, I hope to cuts through the hype to deliver actionable intelligence for developers building real-world MCP implementations.

### Before MCP

If you understand the problem MCP solves, its existence makes sense. Developers are used to working with APIs, where they're responsible for preparing the expected input. Take a weather API endpoint—the docs list required parameters like `lat`, `lon`, and others. Your job as a developer is to prepare your software to pass exactly what the endpoint expects. Building a weather alert app? You'd load `lat` and `lon` from user data, call the API, and return something useful.

!!! Insert diagram

Now imagine you want AI to handle this. You'd need to spell out exactly where to get the data, how to prepare it, and the conditions for each field. You'd repeat this for every single endpoint the AI needs to access. Working with 10 different API providers? Good luck—especially when one endpoint changes and everything breaks.

## Why and how does MCP works?

MCP (Model Context Protocol) is a standardized way for APIs to tell AI how to use them—similar to how API documentation tells developers how to use them. The key innovation? It shifts responsibility to the API provider. Instead of developers spelling out every detail for the AI, the weather API itself now tells the AI how to use its endpoints. That's MCP.

### **A real-life example**

Let’s take a look at **Figma MCP**, to really understand how MCP works.

Let's look at this open-source Figma MCP server. Here's the API endpoint developers had to work with:

```typescript
  // Taken from Figma-Context-MCP ./src/services/Figma.ts

  /**
   * Get raw Figma API response for specific nodes (for use with flexible extractors)
   */
  async getRawNode(
    fileKey: string,
    nodeId: string,
    depth?: number | null,
  ): Promise<GetFileNodesResponse> {
    const endpoint = `/files/${fileKey}/nodes?ids=${nodeId}${depth ? `&depth=${depth}` : ""}`;
    Logger.log(
      `Retrieving raw Figma node: ${nodeId} from ${fileKey} (depth: ${depth ?? "default"})`,
    );

    const response = await this.request<GetFileNodesResponse>(endpoint);
    writeLogs("figma-raw.json", response);

    return response;
  }
```

To make this useful, developers needed to build additional modules and components around it—just to get information about a single node.

**With MCP: Instant Integration**  
Connect your AI agent to the Figma MCP server. It instantly knows what to provide to get node information. No extra setup required.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1763004194706/eaef265a-a630-471b-92d7-dbbec4e684d7.png align="center")

## A list of MCP servers

You can get a list of MCP server [here](https://www.pulsemcp.com/servers) or refer to cursor mcp [list](https://cursor.com/docs/context/mcp/directory). But here are a few that I personally find it useful as a developer

| **Name** | **URL** | **Why Useful (For Developers)** |
| --- | --- | --- |
| Figma | [https://figma.com](https://figma.com) | Figma offers responsive design, advanced component creation, interactive prototyping, and a Dev Mode for precise handoff—making it easier for developers to collaborate with designers, inspect specs, and t**ranslate design into production code efficiently .** |
| Task-master | [https://mcpmarket.com/task-master](https://mcpmarket.com/task-master) | **Task-master automates task planning**, parsing PRDs into actionable tasks, and integrates directly with MCP servers and **AI-driven editors**, allowing developers to streamline workflows, automate CI/CD setup, and focus on coding rather than on meta-work . |
| Context7 | [https://upstash.com/context7](https://upstash.com/context7) | Context7 plugs **up-to-date, version-specific documentation** and code examples into your workflow, reducing time spent on debugging and validation, thus accelerating development and lowering technical debt for both new and experienced developers . |
| Firecrawl | [https://firecrawl.dev](https://firecrawl.dev) | Firecrawl enables fast, scalable web scraping and data extraction, supports **turning URLs into structured LLM-ready data**, automates lead enrichment, supports dynamic content and anti-bot mechanisms, and accelerates agentic AI development for research or production use . |

## How to use MCP?

**Using MCP with Cursor and Other AI Tools**

You can use MCP with chat models (ChatGPT, Claude), AI-powered IDEs (GitHub Copilot, Cursor, Claude Code), or directly in your terminal. While setup varies by provider, the process typically follows three steps: configure the MCP, get your token, and enable it.

**Setting Up Cursor MCP**

Cursor maintains an official Cursor MCP list with pre-configured servers. For any MCP in this list, integration is straightforward:

1. Open Cursor settings using `Ctrl+Shift+P` (or `Cmd+Shift+P` on Mac)
    
2. Navigate to the **Tools & MCP** tab
    
3. Click the **Add** button next to your desired MCP server
    

Cursor handles the configuration automatically—no manual setup required.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1762937324777/347eb452-a14c-421c-8fa7-9af413c9801f.png align="center")

Click `install` and you’ll see this screen next:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1762937513185/d0691349-8037-4024-a3e4-b1cab8aaea14.png align="center")

If you expand on the tools, you’ll see exactly what your AI agent (cursor in this case) have access to:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1762937556196/a6122797-db8a-453c-b4c1-e9cc5eb773ad.png align="center")

Now let’s get to the fun part!

# Building your own MCP with FastMCP

Up to this point, we’ve talked a lot about what is mcp, why mcp and how to use it. Now let’s get our hands dirty. To start off, building an MCP server involves exposing capabilities through **tools** (functions that perform actions), **resources** (data providers), and **prompts** (reusable templates).

## What is FastMCP?

FastMCP is a Python framework that makes it easy to build MCP servers. It has become the preferred Python frame for this task due to its significant developer experience improvements.

### Why Use FastMCP?

**Before MCP existed**, integrating AI with APIs was painful:

* Developers had to write detailed instructions for every single API endpoint
    
* Each API required custom prompt engineering
    
* When an API changed, everything broke
    
* Managing 10+ different APIs? A maintenance nightmare.
    

**With FastMCP and MCP**, your server tells AI agents exactly how to use it—automatically.

---

## Understanding MCP Server Capabilities

FastMCP lets you expose three types of capabilities:

| Capability | Decorator | Purpose | Example |
| --- | --- | --- | --- |
| **Tools** | `@mcp.tool()` | Functions that DO things | Generate fortune, send email, fetch data |
| **Resources** | `@mcp.resource()` | Data that can be READ | View archives, access files, read stats |
| **Prompts** | `@mcp.prompt()` | Templates that GUIDE AI behavior | Personality modes, task instructions |

**Most MCP servers primarily use Tools.** Resources and Prompts are optional extras for specific use cases.

---

## Building Your First FastMCP Server

### Step 0: Prerequisite

1. Install [uv](https://docs.astral.sh/uv/getting-started/installation/) (Python package and project manager)
    
2. Create a folder with `pyproject.toml` file
    

```python
[project]
name = "a-sample-project"
version = "1.0.0"
```

### Step 1: Install FastMCP

```bash
uv add fastmcp
```

After running this you’ll see fastmcp added into your dependencies array in the `pyproject.toml` file.

### Step 2: Create Your MCP Server

Create a file called `fortune_teller.py`:

```python
from fastmcp import FastMCP

mcp = FastMCP("Fortune Teller 🔮")

# TOOL: Performs an action
@mcp.tool()
def predict_fortune(name: str, category: str = "random") -> str:
    """
    Generate a new fortune prediction.
    
    Args:
        name: The person's name to personalize the fortune
        category: Type of fortune (career, love, money, or random)
    
    Returns:
        A personalized fortune message
    """
    import random
    
    fortunes = {
        "career": [
            f"{name}, your next bug fix will be legendary! 🐛→✨",
            f"{name}, you'll finally understand that legacy codebase! 📚",
        ],
        "love": [
            f"{name}, you'll meet someone special in a GitHub discussion! 💕",
            f"{name}, your code review will spark romance! ❤️",
        ],
        "money": [
            f"{name}, you'll find a forgotten $20 in your jacket! 💰",
            f"{name}, your side project will earn DOZENS of dollars! 💸",
        ],
        "random": [
            f"{name}, your code will compile on the first try! 🎯",
            f"{name}, you'll finally understand async/await! ✨",
        ]
    }
    
    fortune_list = fortunes.get(category, fortunes["random"])
    return random.choice(fortune_list)

# RESOURCE: Provides readable data
@mcp.resource("fortune://history")
def fortune_history() -> str:
    """View all past fortune predictions."""
    return """
    📜 Fortune History Archive
    
    Recent fortunes:
    - Alice: Your code will compile on the first try! 🎯
    - Bob: You'll find a forgotten $20 in your jacket! 💰
    - Carol: Your next bug fix will be legendary! 🐛→✨
    
    Total fortunes predicted: 1,337
    Accuracy rate: 0% (but 100% entertaining!)
    """

# PROMPT: Guides the AI's behavior
@mcp.prompt()
def fortune_teller_mode() -> str:
    """Activate mystical fortune teller personality."""
    return """You are a mystical fortune teller with a quirky sense of humor.

Your role:
- Greet users warmly and ask for their name
- Offer different types of fortunes (career, love, money, random)
- Deliver fortunes with emojis and dramatic flair
- Always end with "The stars have spoken! ✨"

Be entertaining, not accurate!"""

if __name__ == "__main__":
    mcp.run(transport="http", port=8000)
```

### Step 3: Understanding Your Code

**Why docstrings and type hints matter:**

FastMCP uses [Python's introspection](https://deepwiki.com/modelcontextprotocol/python-sdk/2.4-context-and-lifespan-management) to automatically generate a structured manifest that tells AI agents how to use your tools. Here's what happens:

```python
@mcp.tool()
def predict_fortune(name: str, category: str = "random") -> str:
    #   ^^^^^^^^^^^^^^  ^^^^       ^^^^^^^^              ^^^
    #   |               |          |                     |
    #   Tool name       Parameter  Default value         Return type
    
    """
    Generate a new fortune prediction.
    # ↑ Becomes the tool description
    
    Args:
        name: The person's name to personalize the fortune
        # ↑ Becomes parameter description for 'name'
        
        category: Type of fortune (career, love, money, or random)
        # ↑ Becomes parameter description for 'category'
    """
```

**FastMCP converts this into JSON Schema:**

```json
{
  "name": "predict_fortune",
  "description": "Generate a new fortune prediction.",
  "inputSchema": {
    "type": "object",
    "properties": {
      "name": {
        "type": "string",
        "description": "The person's name to personalize the fortune"
      },
      "category": {
        "type": "string",
        "description": "Type of fortune (career, love, money, or random)",
        "default": "random"
      }
    },
    "required": ["name"]
  }
}
```

This manifest is what AI agents read to understand your tools!

### Step 4: Run Your FastMCP Server

```bash
 uv run fortune_teller.py
```

You should see this:

```bash


                             ╭──────────────────────────────────────────────────────────────────────────────╮                              
                             │                                                                              │                              
                             │                         ▄▀▀ ▄▀█ █▀▀ ▀█▀ █▀▄▀█ █▀▀ █▀█                        │                              
                             │                         █▀  █▀█ ▄▄█  █  █ ▀ █ █▄▄ █▀▀                        │                              
                             │                                                                              │                              
                             │                               FastMCP 2.13.0.2                               │                              
                             │                                                                              │                              
                             │                                                                              │                              
                             │                  🖥  Server name: Fortune Teller 🔮                           │                              
                             │                                                                              │                              
                             │                  📦 Transport:   HTTP                                        │                              
                             │                  🔗 Server URL:  http://127.0.0.1:8000/mcp                   │                              
                             │                                                                              │                              
                             │                  📚 Docs:        https://gofastmcp.com                       │                              
                             │                  🚀 Hosting:     https://fastmcp.cloud                       │                              
                             │                                                                              │                              
                             ╰──────────────────────────────────────────────────────────────────────────────╯                              


[11/13/25 10:50:35] INFO     Starting MCP server 'Fortune Teller 🔮' with transport 'http' on http://127.0.0.1:8000/mcp      server.py:2050
INFO:     Started server process [788]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```

Your server is now running at `http://localhost:8000/mcp` 🔮

---

## Connecting FastMCP to Cursor (Cursor MCP Integration)

Now let's connect your FastMCP server to Cursor so the AI can use your tools.

### Setting Up Cursor MCP

**Method 1: Using Cursor's MCP Settings**

1. Open Cursor and press `Ctrl+Shift+P` (or `Cmd+Shift+P` on Mac)
    
2. Navigate to **Settings** → **Tools & MCP**
    
3. Click **New MCP Server**
    
4. Update the `mcp.json`
    

```json
{
  "mcpServers": {
    "fortune-teller": {
      "url": "http://localhost:8000/mcp",
      "name": "Fortune Teller 🔮"
    }
  }
}
```

5. Head back to the cursor settings and you’ll see this
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1763002470221/7961e219-0f44-477c-bb5e-7df6c967e94c.png align="center")

---

## How Cursor MCP Uses Your FastMCP Tools

Let's see what happens when you chat with Cursor after connecting your FastMCP server.

### Example Conversation

**You:** "Can you predict my fortune for my career?"

**Behind the scenes:**

1. **Cursor checks available tools**
    
    ```python
    Cursor: "User wants a fortune prediction. Let me check my tools..."
    
    Found: predict_fortune from Fortune Teller 🔮
    Needs: name (required), category (optional)
    ```
    
2. **Cursor plans the call**
    
    ```python
    Cursor: "I'll call predict_fortune with:
    - name: 'User' (from context)
    - category: 'career' (user specified)"
    ```
    
3. **Cursor calls your FastMCP tool**
    
    ```json
    {
      "tool": "predict_fortune",
      "arguments": {
        "name": "User",
        "category": "career"
      }
    }
    ```
    
4. **Your FastMCP server responds**
    
    ```json
    {
      "result": "User, your next bug fix will be legendary! 🐛→✨"
    }
    ```
    
5. **Cursor formats the response**
    
    ```python
    Cursor: "🔮 User, your next bug fix will be legendary! 🐛→✨"
    ```
    

### The Magic: Self-Describing Tools

Your FastMCP server **self-describes** its capabilities. Cursor doesn't need manual instructions—it automatically knows:

* What tools are available
    
* What parameters each tool needs
    
* What types those parameters should be
    
* Which parameters are required vs optional
    
* What each parameter means (from your docstrings)
    

---

## Testing you FastMCP Server inside Cursor

Start a new cursor window and open up a new cursor chat. Try asking “Can you predict my fortune for my career”?

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1763002674946/542d22a8-4f54-433e-8c00-4ea3d24ec19f.png align="center")

As you can see, the cursor mcp knows you is aware of your MCP can calls the right tool. Depending on your setting, cursor may ask you for permission before running it. If you accept it, here’s what you’ll see.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1763002867638/b80151a7-2e4c-4e19-948b-03f0a71ddfb5.png align="center")

## Testing Your FastMCP Server

You can test your FastMCP server without Cursor using the FastMCP client:

```python
import asyncio
from fastmcp import Client

client = Client("http://localhost:8000/mcp")

# Test the tool
async def test_tool():
    async with client:
        result = await client.call_tool(
            "predict_fortune", 
            {"name": "Alice", "category": "career"}
        )
        print(f"🔮 {result}")

# Test the resource
async def test_resource():
    async with client:
        history = await client.read_resource("fortune://history")
        print(f"📜 {history}")

# Test the prompt
async def test_prompt():
    async with client:
        prompt = await client.get_prompt("fortune_teller_mode")
        print(f"🎭 {prompt}")

# Run tests
asyncio.run(test_tool())
asyncio.run(test_resource())
asyncio.run(test_prompt())
```

Run the file with `uv run test.py`

**Output:**

```python
🔮 Alice, your next bug fix will be legendary! 🐛→✨
📜 Fortune History Archive...
🎭 You are a mystical fortune teller...
```

---

## FastMCP Best Practices for AI-Friendly Tools

**Quick Rule:** If a human developer would struggle to understand your function without comments, AI will too. Make everything explicit through docstrings and type hints!

| ✅ DO | ❌ DON'T |
| --- | --- |
| **Write Clear Docstrings**. Include purpose, parameters (Args), and return value descriptions. AI reads these to understand your tool. | **Skip Docstrings**. Without docstrings, AI has no context about what your tool does or how to use it properly. |
| **Use Type Hints**. Always specify parameter and return types (`name: str`, `age: int`). This tells AI exactly what data types to provide. | **Use Vague Function Names**. Avoid generic names like `process()` or `calc()`. Use descriptive names like `convert_markdown_to_html()`. |
| **Use Literal for Enums**. For parameters with specific valid values, use `Literal["option1", "option2"]` to tell AI the exact allowed choices. | **Leave Parameters Untyped**. Untyped parameters default to `Any`, making it unclear what the AI should provide. |
| **Use Descriptive Parameter Names**. Name parameters clearly: `birth_year` instead of `x`, `email_address` instead of `data`. | **Use Single-Letter Variables**. Avoid unclear names like `x`, `y`, `data`, or `input` that don't explain what they represent. |
| **Document Edge Cases**. Explain constraints in docstrings (e.g., "Cannot divide by zero", "Date must be in ISO format"). | **Assume AI Knows Context**. Don't assume AI understands implicit rules or constraints without documentation. |

---

# Conclusion

So there you have it! Model Context Protocol (MCP) in a nutshell. Instead of wrestling with endless API integrations, MCP lets your tools explain themselves to AI agents. If you've made it this far, you've got everything you need to start building. Spin up a FastMCP server, hook it into Cursor MCP, and watch the magic happen. The MCP protocol is still evolving, but with major players like OpenAI and Google backing it, this is clearly where things are headed. Don't overthink it! Start with something simple, see what MCP can do, and iterate from there. The best way to understand what MCP is all about? Build something with it. Your fortune-telling server is waiting!
