🎄 How to Create an MCP Server for Santa: Integrating Reindeer with Copilot Studio

📑 Table of Contents

  1. Introduction
  2. What Is an MCP Server?
  3. Advantages of Using MCP Server
  4. Step 1: Create Azure Function App
  5. Step 2: Deploy on Azure
  6. Step 3: Configure API Management
  7. Step 4: Configure MCP Server
  8. Step 5: Use the API in Copilot Studio
  9. Monitoring and Debugging
  10. Real Use Cases
  11. Conclusion
  12. Next Steps

Introduction

In this article, I’ll show you how to create a Christmas-themed MCP Server (Model Context Protocol) and deploy it on Azure to integrate it with Copilot Studio. We’ll build an API for managing reindeer from the North Pole, connect it to API Management, and leverage the MCP Server preview feature so Copilot Studio can query available reindeer for gift delivery.

Christmas context: This MCP Server is part of Santa’s Headquarters: Multi-agents in Copilot Studio where the Reindeer Preparing Elf accesses data about available reindeer.

What Is an MCP Server?

The Model Context Protocol (MCP) is an open standard that allows AI models to securely and structurally access tools and data. An MCP Server exposes your services (APIs, databases, etc.) so Copilot Studio can discover and use them automatically.

In our case, we expose the Santa’s Reindeer API so the Reindeer Preparing Elf can:

  • Query the status of reindeer
  • Verify availability by date
  • Assign the optimal team for each delivery

Advantages of Using MCP Server with Copilot Studio

  • 🚀 Quick integration without complex code
  • 🔐 Security through API Management
  • 📊 Scalability with serverless on Azure
  • 🤖 Process automation with AI
  • 💰 Cost-effective with pay-as-you-go architecture
  • 🦌 Real-time reindeer management

Step 1: Create an Azure Function App in Python

We’ll create a Function App with three HTTP endpoints that manage Santa’s reindeer for Headquarters.

Project Structure

ReindeerAPI/
├── function_app.py
├── requirements.txt
├── host.json
├── local.settings.json
├── .gitignore
└── README.md

Understanding Azure Functions Structure

Before creating functions, it’s important to understand how they’re structured in Azure Functions v4 with Python.

Main Components

  1. Required Imports

    • azure.functions: Main library for Azure Functions
    • json: For serializing/deserializing JSON data
  2. FunctionApp Instance

    app = func.FunctionApp()
    

    This is the base application that contains all endpoints.

  3. Decorators

    • @app.route(): Defines an HTTP route
    • @app.function_name(): Unique function name (optional)
    • Defines HTTP method (GET, POST, PUT, DELETE, etc.)
  4. Function Signature

    def my_function(req: func.HttpRequest) -> func.HttpResponse:
    
    • Receives an HttpRequest
    • Returns an HttpResponse

Parts of an HTTP Function

@app.function_name("get_reindeer")                 # Unique name (optional)
@app.route(route="reindeer")                       # Endpoint route
def get_reindeer(req: func.HttpRequest) -> func.HttpResponse:
    """Returns a list of available reindeer"""  # Docstring
    
    # LOGIC: Process the request
    reindeer = [
        {"id": 1, "name": "Rudolph", "energy": 95, "available": True},
        {"id": 2, "name": "Donner", "energy": 88, "available": True}
    ]
    
    # RESPONSE: Return data with status code
    return func.HttpResponse(
        json.dumps(reindeer),                      # Body (converted to JSON)
        status_code=200,                           # HTTP code
        mimetype="application/json"                # Content type
    )

Create the Function App

import azure.functions as func
import json
from datetime import datetime, timedelta

app = func.FunctionApp()

# Endpoint 1: Get all Santa's reindeer
@app.function_name("reindeer_list")
@app.route(route="reindeer")
def get_reindeer(req: func.HttpRequest) -> func.HttpResponse:
    """
    Gets the complete list of Santa's reindeer with their availability
    for the North Pole Headquarters. Used by the Reindeer Preparing Elf.
    """
    reindeer = [
        {
            "id": 1,
            "name": "Rudolph",
            "emoji": "🔴",
            "description": "The guide reindeer with the bright red nose",
            "available": True,
            "last_flight": "2025-11-27",
            "energy_level": 95,
            "speed_kmh": 250,
            "capacity_kg": 500,
            "next_available": "2025-12-01T08:00:00Z",
            "flight_history": [
                {"date": "2025-11-27", "duration_hours": 4, "distance_km": 850},
                {"date": "2025-11-26", "duration_hours": 3.5, "distance_km": 750}
            ]
        },
        {
            "id": 2,
            "name": "Donner",
            "emoji": "🦌",
            "description": "Fast and reliable, excellent for long routes",
            "available": True,
            "last_flight": "2025-11-28",
            "energy_level": 88,
            "speed_kmh": 240,
            "capacity_kg": 480,
            "next_available": "2025-12-01T09:00:00Z",
            "flight_history": []
        },
        {
            "id": 3,
            "name": "Blitzen",
            "emoji": "⚡",
            "description": "The lightning striker, fastest of all",
            "available": True,
            "last_flight": "2025-11-25",
            "energy_level": 92,
            "speed_kmh": 280,
            "capacity_kg": 490,
            "next_available": "2025-12-01T07:00:00Z",
            "flight_history": []
        },
        {
            "id": 4,
            "name": "Cupid",
            "emoji": "💘",
            "description": "Strong and dependable, handles difficult terrain",
            "available": True,
            "last_flight": "2025-11-27",
            "energy_level": 85,
            "speed_kmh": 235,
            "capacity_kg": 510,
            "next_available": "2025-12-01T10:00:00Z",
            "flight_history": []
        },
        {
            "id": 5,
            "name": "Comet",
            "emoji": "☄️",
            "description": "Stable flyer, good for weather conditions",
            "available": False,
            "last_flight": "2025-11-28",
            "energy_level": 60,
            "speed_kmh": 230,
            "capacity_kg": 475,
            "next_available": "2025-12-02T14:00:00Z",
            "flight_history": []
        },
        {
            "id": 6,
            "name": "Dasher",
            "emoji": "💨",
            "description": "Quick starter, excellent acceleration",
            "available": True,
            "last_flight": "2025-11-26",
            "energy_level": 90,
            "speed_kmh": 260,
            "capacity_kg": 485,
            "next_available": "2025-12-01T08:30:00Z",
            "flight_history": []
        },
        {
            "id": 7,
            "name": "Prancer",
            "emoji": "🎩",
            "description": "Elegant and precise, for premium routes",
            "available": True,
            "last_flight": "2025-11-28",
            "energy_level": 93,
            "speed_kmh": 245,
            "capacity_kg": 495,
            "next_available": "2025-12-01T07:30:00Z",
            "flight_history": []
        }
    ]
    
    return func.HttpResponse(
        json.dumps(reindeer),
        status_code=200,
        mimetype="application/json"
    )

# Endpoint 2: Get reindeer by ID
@app.function_name("get_reindeer_by_id")
@app.route(route="reindeer/{reindeer_id}")
def get_reindeer_by_id(req: func.HttpRequest) -> func.HttpResponse:
    """
    Gets a specific reindeer by ID with detailed information.
    Path parameter: reindeer_id (integer)
    """
    reindeer_id = req.route_params.get("reindeer_id")
    
    if not reindeer_id:
        return func.HttpResponse(
            json.dumps({"error": "reindeer_id is required"}),
            status_code=400,
            mimetype="application/json"
        )
    
    # Simplified: In production, query a database
    reindeer_data = {
        "1": {
            "id": 1,
            "name": "Rudolph",
            "description": "The guide reindeer with the bright red nose",
            "available": True,
            "energy_level": 95
        },
        "2": {
            "id": 2,
            "name": "Donner",
            "description": "Fast and reliable",
            "available": True,
            "energy_level": 88
        }
    }
    
    reindeer = reindeer_data.get(str(reindeer_id))
    
    if not reindeer:
        return func.HttpResponse(
            json.dumps({"error": f"Reindeer {reindeer_id} not found"}),
            status_code=404,
            mimetype="application/json"
        )
    
    return func.HttpResponse(
        json.dumps(reindeer),
        status_code=200,
        mimetype="application/json"
    )

# Endpoint 3: Assign reindeer for delivery
@app.function_name("assign_reindeer")
@app.route(route="assign", methods=["POST"])
def assign_reindeer(req: func.HttpRequest) -> func.HttpResponse:
    """
    Assigns a reindeer for a delivery based on optimal criteria.
    Body: {"delivery_date": "2025-12-01", "distance_km": 500, "priority": "high"}
    """
    try:
        req_body = req.get_json()
    except ValueError:
        return func.HttpResponse(
            json.dumps({"error": "Invalid JSON"}),
            status_code=400,
            mimetype="application/json"
        )
    
    delivery_date = req_body.get("delivery_date")
    distance_km = req_body.get("distance_km", 500)
    priority = req_body.get("priority", "normal")
    
    if not delivery_date:
        return func.HttpResponse(
            json.dumps({"error": "delivery_date is required"}),
            status_code=400,
            mimetype="application/json"
        )
    
    # Assign the best reindeer based on distance and priority
    assignment = {
        "delivery_date": delivery_date,
        "distance_km": distance_km,
        "priority": priority,
        "assigned_reindeer": "Rudolph",
        "reindeer_id": 1,
        "estimated_time_hours": distance_km / 250,
        "status": "ASSIGNED",
        "confirmation_code": "SANTA-2025-001-RUD"
    }
    
    return func.HttpResponse(
        json.dumps(assignment),
        status_code=200,
        mimetype="application/json"
    )

Summary of Endpoint Types Used

EndpointMethodPurposePath ParameterBody
/reindeerGETGet all reindeerNoNo
/reindeer/{reindeer_id}GETGet specific reindeerYes (ID)No
/assignPOSTAssign reindeer for deliveryNoYes (JSON)

Step 2: Deploy the Function App on Azure

To deploy to Azure:

# 1. Create a resource group
az group create --name SantasHQ --location eastus

# 2. Create a storage account
az storage account create \
  --name santastorageacct \
  --resource-group SantasHQ \
  --location eastus

# 3. Create the Function App
az functionapp create \
  --resource-group SantasHQ \
  --consumption-plan-location eastus \
  --runtime python \
  --runtime-version 3.11 \
  --functions-version 4 \
  --name ReindeerAPI \
  --storage-account santastorageacct

# 4. Deploy the code
func azure functionapp publish ReindeerAPI

Step 3: Configure API Management

To secure and manage the API:

# 1. Create API Management instance
az apim create \
  --name SantasAPIM \
  --resource-group SantasHQ \
  --publisher-name "Santa's Headquarters" \
  --publisher-email "santa@northpole.com" \
  --sku-name Developer

# 2. Create API
az apim api create \
  --resource-group SantasHQ \
  --apim-name SantasAPIM \
  --display-name "Reindeer API" \
  --path reindeer \
  --protocols https

# 3. Add operations
az apim api operation create \
  --resource-group SantasHQ \
  --apim-name SantasAPIM \
  --api-id reindeer \
  --operation-id list-reindeer \
  --display-name "List All Reindeer" \
  --method GET \
  --url-template "/reindeer"

Step 4: Configure the MCP Server in Azure API Management (Preview)

Azure API Management includes a built-in MCP Server feature (currently in preview) that allows you to expose your managed APIs as MCP servers. This is the recommended approach for production MCP configurations.

Create MCP Server in API Management Portal

  1. Navigate to MCP Servers:

    • Open Azure Portal
    • Go to your API Management instance (SantasAPIM)
    • In the left menu, under APIs, select MCP Servers
  2. Create New MCP Server:

    • Click + Create MCP server
    • Select Expose an API as an MCP server
  3. Configure Backend MCP Server:

    • Select the API: Choose your Reindeer API from the dropdown
    • Select operations to expose as tools:
      • ✓ List All Reindeer (GET /reindeer)
      • ✓ Get Reindeer Details (GET /reindeer/{reindeer_id})
      • ✓ Assign Reindeer for Delivery (POST /assign)
  4. Configure New MCP Server Details:

    • Name: Reindeer MCP
    • Description: Manages reindeer availability and assignments for Santa's delivery operations
  5. Click Create

    • Azure API Management generates the MCP server endpoint
    • Operations become available as “tools” for AI agents
    • Endpoint format: https://{apim-name}.azure-api-ms.net/mcp

Add policies to your MCP server for enhanced security:

<!-- Authentication: Require API Key -->
<validate-azure-ad-token failed-validation-httpcode="401" failed-validation-error-message="Unauthorized">
    <client-certificate-thumbprint>thumbprint</client-certificate-thumbprint>
</validate-azure-ad-token>

<!-- Rate Limiting: Prevent abuse -->
<rate-limit-by-key calls="100" renewal-period="60" counter-key="@(context.Request.Headers.GetValueOrDefault("Subscription-Key"))"/>

<!-- IP Filtering: Only allow Copilot Studio IPs -->
<ip-filter action="allow">
    <address>40.70.0.0/16</address>
    <address>20.42.0.0/16</address>
</ip-filter>

Get Your MCP Server Endpoint

After creation, copy the MCP server endpoint:

  • Go back to MCP Servers list
  • Click your Reindeer MCP server
  • Copy the Endpoint URL: https://santasapim.azure-api-ms.net/mcp
  • Note the Access Key for authentication

Step 5: Connect MCP Server to Copilot Studio (or GitHub Copilot)

Option A: Connect with GitHub Copilot (Agent Mode)

  1. Open Visual Studio Code
  2. Select Agent Mode in GitHub Copilot (if available)
  3. Add MCP Server:
    • Navigate to Copilot Agent Settings
    • Add MCP server endpoint: https://santasapim.azure-api-ms.net/mcp
    • Provide access key for authentication
  4. Select Tools:
    • Check the boxes for reindeer operations you want available
    • The agent now has access to these tools

Option B: Connect with Copilot Studio

  1. Go to Copilot StudioSettingsConnected services
  2. Add Custom MCP Server (if available)
  3. Configure:
    • Server URL: https://santasapim.azure-api-ms.net/mcp
    • API Key: Your API Management subscription key
    • Name: Reindeer MCP
  4. Save and test the connection

Use the MCP Server in Your Agent

In your Reindeer Preparing Elf agent:

  1. Go to Actions or Knowledge sources
  2. The MCP tools are now available as actions:
    • List All Reindeer - Get available reindeer
    • Get Reindeer Details - Check specific reindeer status
    • Assign Reindeer for Delivery - Assign for specific delivery
  3. Create agent instructions to use these tools when needed

Monitoring and Debugging

Monitor the Function App

# View logs
az functionapp logs tail --name ReindeerAPI --resource-group SantasHQ

# Monitor performance
az monitor metrics list \
  --resource /subscriptions/{subscription-id}/resourceGroups/SantasHQ/providers/Microsoft.Web/sites/ReindeerAPI \
  --metric "FunctionExecutionCount"

Monitor MCP Server in API Management

Monitor your MCP server through Azure Portal:

  1. Go to your API Management instanceMCP Servers

  2. Click your Reindeer MCP server

  3. View Metrics:

    • Requests: Total API calls through MCP
    • Successful Requests: Requests completed successfully
    • Failed Requests: API errors and failures
    • Average Response Time: Performance monitoring
    • Gateway CPU: API Management resource usage
    • Gateway Memory: Memory consumption
  4. Set up Alerts:

    • Go to MonitoringAlerts
    • Create alert rule for high error rate or slow responses
    • Configure notifications (email, Slack, etc.)
  5. Enable Application Insights (optional):

    • Configure in API Management settings
    • Get detailed diagnostic logs and tracing
    • Track agent calls to MCP tools

Real Use Cases

Example 1: Check Reindeer Availability

User: "Check which reindeer are available for tomorrow"
→ Reindeer Preparing Elf calls /reindeer
→ Gets list of available reindeer
→ Returns recommendations to Coordinator

Example 2: Assign for Delivery

User: "Assign a reindeer for a 500km delivery"
→ Route Optimization Elf calls /assign with POST
→ MCP Server processes and returns assignment
→ Coordinator confirms the assignment

Conclusion

This MCP Server demonstrates how to:

  • Build a Python-based Azure Function
  • Deploy serverless infrastructure
  • Integrate with Copilot Studio
  • Create real business automation

The Reindeer MCP is just the beginning of Santa’s AI-powered North Pole! 🎅🦌✨