EN PL
Poradnik Azure AI

Budujemy serwer MCP ze statystykami piłkarskimi — Azure Functions + Agent Foundry

Wdróż własny serwer MCP na Azure Functions, połącz go z Agentem Foundry wykorzystującym gpt-5-mini i odpytuj dane piłkarskie w języku naturalnym

Maciej Rubczyński 16 marca 2026 12 min czytania
Spis treści

Wprowadzenie

W tym poradniku zbudujemy kompletny system agenta AI, który rozumie statystyki piłkarskie. Stworzymy własny serwer MCP wdrożony na Azure Functions, a następnie połączymy go z Agentem Foundry zasilanym modelem gpt-5-mini od OpenAI. Agent będzie mógł wyszukiwać zawodników, pobierać szczegółowe statystyki i porównywać graczy bezpośrednio w języku naturalnym — bez zapytań SQL, bez skomplikowanych API do nauki.

Po ukończeniu tego poradnika będziesz wiedzieć, jak:

Ten wzorzec działa dla dowolnej domeny: bazy HR, pipeline sprzedażowe, systemy magazynowe czy wewnętrzne bazy wiedzy. Zbudujmy coś prawdziwego.

Czym jest MCP?

Model Context Protocol (MCP) to otwarty standard definiujący sposób interakcji modeli AI z zewnętrznymi narzędziami i źródłami danych. Można go sobie wyobrazić jako uniwersalny adapter między dużymi modelami językowymi a Twoimi aplikacjami.

Zamiast budować osobne integracje dla każdego modelu (OpenAI, Anthropic, Google itp.), definiujesz narzędzia raz zgodnie ze specyfikacją MCP, a każdy model kompatybilny z MCP może z nich korzystać. Azure Functions ma teraz natywne wsparcie dla MCP poprzez MCP tool trigger binding, który pozwala udostępniać funkcje Pythona jako narzędzia MCP za pomocą jednego dekoratora.

Dlaczego MCP na Azure Functions?

Azure Functions obsługuje skalowanie, uwierzytelnianie i monitoring. Ty piszesz logikę biznesową w Pythonie; Functions i rozszerzenie MCP zajmują się protokołem, udostępniając Twoje funkcje natychmiast każdemu klientowi MCP, w tym Agentom Foundry.

Przegląd architektury

Nasz system składa się z trzech głównych komponentów:

Komponent 1: Serwer MCP (Azure Functions)
Hostuje trzy funkcje Pythona oznaczone jako narzędzia MCP:

Komponent 2: Usługa Foundry Agent
Uruchamia gpt-5-mini i orkiestruje wywołania narzędzi:

Komponent 3: Klient (Python SDK)
Używa azure-ai-projects SDK v2 do tworzenia agentów, zarządzania konwersacjami i wywoływania agenta z zapytaniami użytkownika.

Przegląd architektury: Klient → Microsoft Foundry → Azure Functions (serwer MCP)

A oto przepływ żądania pokazujący dokładnie co się dzieje, gdy użytkownik pyta „Porównaj Lewandowskiego i Mbappé":

Przepływ żądania: 7-etapowy diagram sekwencji pokazujący, jak zapytanie o porównanie przechodzi przez system

Wymagania wstępne

Krok 1: Tworzenie zasobów Azure

Stworzymy grupę zasobów, workspace Foundry i Function App za pomocą Azure CLI.

Tworzenie grupy zasobów

az group create \
  --name rg-foundry-mcp-tutorial \
  --location eastus

Tworzenie workspace Foundry

az cognitiveservices account create \
  --resource-group rg-foundry-mcp-tutorial \
  --name foundry-mcp-tutorial \
  --kind AIServices \
  --sku s0 \
  --location eastus \
  --yes

Tworzenie projektu Foundry

Wejdź na portal Azure AI Foundry, zaloguj się i utwórz nowy projekt o nazwie mcp-football-agent. Następnie wdróż model gpt-5-mini do swojego projektu.

Tworzenie Function App

az storage account create \
  --resource-group rg-foundry-mcp-tutorial \
  --name stmcpfootball01 \
  --location eastus \
  --sku Standard_LRS

az functionapp create \
  --resource-group rg-foundry-mcp-tutorial \
  --consumption-plan-location eastus \
  --runtime python \
  --runtime-version 3.11 \
  --functions-version 4 \
  --name func-mcp-football \
  --storage-account stmcpfootball01
Uwaga: Plan Consumption

Plan Consumption skaluje się automatycznie i płacisz tylko za wykonania. Idealny do developmentu i scenariuszy z niewielkim ruchem.

Krok 2: Budowa serwera MCP

Teraz napiszemy kod Pythona dla naszego serwera MCP. Utwórz lokalny katalog i skonfiguruj function app.

Inicjalizacja Function App lokalnie

func init football-mcp-server --python
cd football-mcp-server
func new --name football_stats --template "HTTP trigger"

Instalacja zależności

Utwórz plik requirements.txt:

azure-functions==1.25.0b3

Zainstaluj zależności:

pip install -r requirements.txt

Tworzenie function_app.py

Zastąp wygenerowane pliki poniższą implementacją:

Uwaga: Poniższe statystyki piłkarzy to dane przykładowe do celów demonstracyjnych. W scenariuszu produkcyjnym połączyłbyś się z prawdziwym API danych piłkarskich.
"""
MCP Server for Football (Soccer) Statistics
Deployed as Azure Functions with MCP extension bindings.
"""
import logging
import azure.functions as func
import json

app = func.FunctionApp(http_auth_level=func.AuthLevel.ANONYMOUS)

PLAYERS = {
    "lewandowski": {
        "id": "lew9", "name": "Robert Lewandowski",
        "nationality": "Poland", "position": "Striker",
        "club": "FC Barcelona", "age": 37,
        "stats": {
            "season": "2025/26", "appearances": 28, "goals": 19,
            "assists": 5, "minutes_played": 2340, "goals_per_90": 0.73,
            "shots_on_target_pct": 52.1, "pass_accuracy": 81.3,
            "career_goals": 672, "career_assists": 171,
        },
    },
    "mbappe": {
        "id": "mba7", "name": "Kylian Mbappé",
        "nationality": "France", "position": "Forward",
        "club": "Real Madrid", "age": 26,
        "stats": {
            "season": "2025/26", "appearances": 25, "goals": 22,
            "assists": 8, "minutes_played": 2100, "goals_per_90": 0.94,
            "shots_on_target_pct": 58.3, "pass_accuracy": 79.2,
            "career_goals": 286, "career_assists": 108,
        },
    },
    "haaland": {
        "id": "haa9", "name": "Erling Haaland",
        "nationality": "Norway", "position": "Striker",
        "club": "Manchester City", "age": 25,
        "stats": {
            "season": "2025/26", "appearances": 24, "goals": 21,
            "assists": 4, "minutes_played": 2016, "goals_per_90": 0.94,
            "shots_on_target_pct": 61.2, "pass_accuracy": 76.8,
            "career_goals": 198, "career_assists": 52,
        },
    },
    "vinicius": {
        "id": "vin11", "name": "Vinícius Júnior",
        "nationality": "Brazil", "position": "Winger",
        "club": "Real Madrid", "age": 24,
        "stats": {
            "season": "2025/26", "appearances": 26, "goals": 16,
            "assists": 9, "minutes_played": 2210, "goals_per_90": 0.65,
            "shots_on_target_pct": 54.7, "pass_accuracy": 82.1,
            "career_goals": 112, "career_assists": 84,
        },
    },
    "salah": {
        "id": "sal11", "name": "Mohamed Salah",
        "nationality": "Egypt", "position": "Forward",
        "club": "Liverpool FC", "age": 33,
        "stats": {
            "season": "2025/26", "appearances": 27, "goals": 18,
            "assists": 7, "minutes_played": 2250, "goals_per_90": 0.72,
            "shots_on_target_pct": 55.8, "pass_accuracy": 80.4,
            "career_goals": 267, "career_assists": 123,
        },
    },
    "bellingham": {
        "id": "bel5", "name": "Jude Bellingham",
        "nationality": "England", "position": "Midfielder",
        "club": "Real Madrid", "age": 22,
        "stats": {
            "season": "2025/26", "appearances": 22, "goals": 8,
            "assists": 6, "minutes_played": 1890, "goals_per_90": 0.38,
            "shots_on_target_pct": 45.2, "pass_accuracy": 88.7,
            "career_goals": 34, "career_assists": 31,
        },
    },
    "yamal": {
        "id": "yam19", "name": "Lamine Yamal",
        "nationality": "Spain", "position": "Forward",
        "club": "FC Barcelona", "age": 18,
        "stats": {
            "season": "2025/26", "appearances": 20, "goals": 7,
            "assists": 5, "minutes_played": 1440, "goals_per_90": 0.44,
            "shots_on_target_pct": 48.3, "pass_accuracy": 84.2,
            "career_goals": 12, "career_assists": 8,
        },
    },
    "szczesny": {
        "id": "szc1", "name": "Wojciech Szczęsny",
        "nationality": "Poland", "position": "Goalkeeper",
        "club": "FC Barcelona", "age": 36,
        "stats": {
            "season": "2025/26", "appearances": 24, "goals": 0,
            "assists": 0, "minutes_played": 2160, "clean_sheets": 8,
            "saves": 72, "save_percentage": 78.3, "pass_accuracy": 71.5,
            "career_goals": 0, "career_assists": 0,
        },
    },
}

@app.mcp_tool()
@app.mcp_tool_property(
    arg_name="query",
    description="Search term: player name, country, position, or club name.",
)
def search_players(query: str) -> str:
    """Search for football players by name, nationality, position, or club."""
    query_lower = query.lower()
    matches = []

    for key, player in PLAYERS.items():
        if (query_lower in player.get("name", "").lower() or
            query_lower in player.get("nationality", "").lower() or
            query_lower in player.get("position", "").lower() or
            query_lower in player.get("club", "").lower()):
            matches.append(player)

    if not matches:
        return f"No players found matching '{query}'"

    result = f"Found {len(matches)} player(s) matching '{query}':\n\n"
    for p in matches:
        result += f"- {p['name']} ({p['nationality']}) - {p['position']} at {p['club']}\n"

    return result


@app.mcp_tool()
@app.mcp_tool_property(
    arg_name="player_name",
    description="Full or partial player name.",
)
def get_player_stats(player_name: str) -> str:
    """Get detailed season and career statistics for a specific football player."""
    player_key = None
    for key, player in PLAYERS.items():
        if player_name.lower() in player.get("name", "").lower():
            player_key = key
            break

    if not player_key:
        return f"Player '{player_name}' not found."

    p = PLAYERS[player_key]
    stats = p.get("stats", {})

    result = f"{p['name']} - {p['position']}\n"
    result += f"Club: {p['club']} | Age: {p['age']} | Nationality: {p['nationality']}\n\n"
    result += f"Season {stats.get('season', 'N/A')} Stats:\n"
    result += f"  Appearances: {stats.get('appearances', 'N/A')}\n"
    result += f"  Goals: {stats.get('goals', 'N/A')}\n"
    result += f"  Assists: {stats.get('assists', 'N/A')}\n"
    result += f"  Minutes Played: {stats.get('minutes_played', 'N/A')}\n"
    result += f"  Goals per 90 min: {stats.get('goals_per_90', 'N/A')}\n"
    result += f"  Shot Accuracy: {stats.get('shots_on_target_pct', 'N/A')}%\n"
    result += f"  Pass Accuracy: {stats.get('pass_accuracy', 'N/A')}%\n\n"
    result += f"Career Stats:\n"
    result += f"  Total Goals: {stats.get('career_goals', 'N/A')}\n"
    result += f"  Total Assists: {stats.get('career_assists', 'N/A')}\n"

    return result


@app.mcp_tool()
@app.mcp_tool_property(arg_name="player1_name", description="First player name.")
@app.mcp_tool_property(arg_name="player2_name", description="Second player name.")
def compare_players(player1_name: str, player2_name: str) -> str:
    """Compare statistics of two football players side-by-side."""
    p1_key = None
    p2_key = None

    for key, player in PLAYERS.items():
        if player1_name.lower() in player.get("name", "").lower():
            p1_key = key
        if player2_name.lower() in player.get("name", "").lower():
            p2_key = key

    if not p1_key or not p2_key:
        return f"Could not find both players: '{player1_name}' and '{player2_name}'"

    p1 = PLAYERS[p1_key]
    p2 = PLAYERS[p2_key]
    s1 = p1.get("stats", {})
    s2 = p2.get("stats", {})

    result = f"Comparison: {p1['name']} vs {p2['name']}\n\n"
    result += f"{'Metric':<25} {'':<20} {p1['name']:<20} {p2['name']:<20}\n"
    result += "-" * 70 + "\n"
    result += f"{'Position':<25} {p1['position']:<20} {p2['position']:<20}\n"
    result += f"{'Club':<25} {p1['club']:<20} {p2['club']:<20}\n"
    result += f"{'Goals (Season)':<25} {s1.get('goals', 'N/A'):<20} {s2.get('goals', 'N/A'):<20}\n"
    result += f"{'Assists (Season)':<25} {s1.get('assists', 'N/A'):<20} {s2.get('assists', 'N/A'):<20}\n"
    result += f"{'Goals per 90':<25} {s1.get('goals_per_90', 'N/A'):<20} {s2.get('goals_per_90', 'N/A'):<20}\n"
    result += f"{'Pass Accuracy':<25} {s1.get('pass_accuracy', 'N/A')}%{'':<17} {s2.get('pass_accuracy', 'N/A')}%\n"
    result += f"{'Career Goals':<25} {s1.get('career_goals', 'N/A'):<20} {s2.get('career_goals', 'N/A'):<20}\n"

    return result

Tworzenie host.json

Upewnij się, że Twój host.json zawiera Preview extension bundle zapewniający wsparcie MCP:

{
  "version": "2.0",
  "logging": {
    "applicationInsights": {
      "samplingSettings": {
        "isEnabled": true,
        "maxTelemetryItemsPerSecond": 20
      }
    }
  },
  "extensionBundle": {
    "id": "Microsoft.Azure.Functions.ExtensionBundle.Preview",
    "version": "[4.*, 5.0.0)"
  }
}
Ważne: Wersja Extension Bundle

MCP tool trigger wymaga Preview extension bundle. Użycie standardowego bundle nie zapewni wsparcia MCP.

Krok 3: Wdrożenie na Azure Functions

Teraz spakujemy i wdrożymy function app na Azure.

Wdrożenie przez ZIP

func azure functionapp publish func-mcp-football --build remote

To polecenie przesyła Twój kod i buduje go w Azure. Proces trwa 2-3 minuty.

Weryfikacja wdrożenia

Sprawdź, czy Twoje funkcje zostały wdrożone:

az functionapp function list \
  --resource-group rg-foundry-mcp-tutorial \
  --name func-mcp-football

Powinieneś zobaczyć trzy funkcje: search_players, get_player_stats i compare_players, wszystkie z bindingiem mcpToolTrigger.

Pobieranie URL i klucza funkcji

az functionapp keys list \
  --resource-group rg-foundry-mcp-tutorial \
  --name func-mcp-football \
  --query "functionKeys"

Endpoint MCP jest dostępny pod adresem:

https://func-mcp-football.azurewebsites.net/runtime/webhooks/mcp

Zapisz klucz funkcji — będzie potrzebny do uwierzytelnienia klienta MCP w następnym kroku.

Testowanie endpointu MCP

curl -X POST https://func-mcp-football.azurewebsites.net/runtime/webhooks/mcp \
  -H "x-functions-key: YOUR_KEY_HERE" \
  -H "Content-Type: application/json" \
  -d '{"action": "list_tools"}'

Powinno to zwrócić listę dostępnych narzędzi MCP (search_players, get_player_stats, compare_players).

Przegląd Azure Functions app z func-mcp-football w statusie Running, lokalizacja East US, system Linux
Przegląd Azure Functions — func-mcp-football działający na planie Consumption (Linux)
Lista funkcji pokazująca 3 narzędzia MCP: compare_players, get_player_stats, search_players z bindingiem mcpToolTrigger
Wszystkie trzy narzędzia MCP zarejestrowane z bindingami mcpToolTrigger

Krok 4: Tworzenie Agenta Foundry

Teraz napiszemy skrypt Pythona, który tworzy Agenta Foundry i łączy go z Twoim serwerem MCP.

Instalacja SDK

pip install azure-ai-projects==2.0.1

Tworzenie create_agent.py

Ten skrypt tworzy agenta, konfiguruje narzędzie MCP i demonstruje API konwersacji:

import os
from azure.identity import DefaultAzureCredential
from azure.ai.projects import AIProjectClient
from azure.ai.projects.models import MCPTool, PromptAgentDefinition

# Configuration
FOUNDRY_ENDPOINT = "https://foundry-mcp-tutorial.services.ai.azure.com/api/projects/mcp-football-agent"
MCP_SERVER_URL = "https://func-mcp-football.azurewebsites.net/runtime/webhooks/mcp"
MCP_SERVER_KEY = os.getenv("MCP_SERVER_KEY")  # Set as environment variable
MODEL = "gpt-5-mini"

if not MCP_SERVER_KEY:
    raise ValueError("MCP_SERVER_KEY environment variable not set")

# Initialize the project client
credential = DefaultAzureCredential()
project_client = AIProjectClient(
    endpoint=FOUNDRY_ENDPOINT,
    credential=credential
)

# Define the MCP tool
mcp_tool = MCPTool(
    server_label="football-stats",
    server_url=MCP_SERVER_URL,
    headers={"x-functions-key": MCP_SERVER_KEY},
    require_approval="never",
)

# Create the agent
agent = project_client.agents.create_version(
    agent_name="football-scout-agent",
    definition=PromptAgentDefinition(
        model=MODEL,
        instructions=(
            "You are a football statistics expert. You have access to tools "
            "that query a database of world-class football players. "
            "Use these tools to answer questions about player statistics, "
            "compare players, and provide insights about their performance. "
            "Always provide context and explain comparisons."
        ),
        tools=[mcp_tool],
    ),
)

print(f"Agent created: {agent.name}")
print(f"Agent ID: {agent.id}")

# Get the OpenAI client for conversations
openai_client = project_client.get_openai_client()

# Example conversation: Query 1
print("\n--- Query 1: Who are the Polish players? ---")
conversation = openai_client.conversations.create(
    items=[{
        "type": "message",
        "role": "user",
        "content": "Who are the Polish players?"
    }]
)

response = openai_client.responses.create(
    conversation=conversation.id,
    extra_body={
        "agent_reference": {
            "name": agent.name,
            "type": "agent_reference"
        }
    }
)

print(response.output_text)

Ustawienie zmiennej środowiskowej

export MCP_SERVER_KEY="your-function-key-here"

Uruchomienie skryptu tworzenia agenta

python create_agent.py

Skrypt utworzy agenta i zademonstruje jego pierwsze zapytanie.

Portal Azure AI Foundry pokazujący projekt mcp-football-agent z wdrożonym gpt-5-mini
Dashboard projektu Foundry — mcp-football-agent z wdrożeniem gpt-5-mini

Krok 5: Testowanie Agenta

Przetestujmy agenta trzema różnymi zapytaniami, aby zaprezentować jego możliwości.

Zapytanie testowe 1: Wyszukiwanie polskich zawodników

Wejście: „Who are the Polish players?"

Akcja agenta: Wywołuje search_players("Poland")

Wynik:

Found 2 player(s) matching 'Poland':

- Robert Lewandowski (Poland) - Striker at FC Barcelona
- Wojciech Szczęsny (Poland) - Goalkeeper at FC Barcelona

Both players represent Polish football excellence at the highest level.
Lewandowski is one of the world's most prolific strikers with 672 career
goals, while Szczęsny provides defensive stability as a goalkeeper.

Zapytanie testowe 2: Szczegółowe statystyki zawodnika

Wejście: „Get me detailed stats for Haaland"

Akcja agenta: Wywołuje get_player_stats("Haaland")

Wynik:

Erling Haaland - Striker
Club: Manchester City | Age: 25 | Nationality: Norway

Season 2025/26 Stats:
  Appearances: 24
  Goals: 21
  Assists: 4
  Minutes Played: 2016
  Goals per 90 min: 0.94
  Shot Accuracy: 61.2%
  Pass Accuracy: 76.8%

Career Stats:
  Total Goals: 198
  Total Assists: 52

Haaland's 0.94 goals per 90 minutes demonstrates elite finishing ability,
ranking among the best strikers globally. His shot accuracy of 61.2% shows
clinical efficiency.

Zapytanie testowe 3: Porównanie dwóch zawodników

Wejście: „Compare Lewandowski and Mbappé"

Akcja agenta: Wywołuje compare_players("Lewandowski", "Mbappé")

Wynik:

Comparison: Robert Lewandowski vs Kylian Mbappé

Metric                    Robert Lewandowski       Kylian Mbappé
----------------------------------------------------------------------
Position                  Striker                  Forward
Club                      FC Barcelona             Real Madrid
Goals (Season)            19                       22
Assists (Season)          5                        8
Goals per 90              0.73                     0.94
Pass Accuracy             81.3%                    79.2%
Career Goals              672                      286

Despite being three years older, Lewandowski maintains exceptional
consistency with 19 goals this season. Mbappé's 0.94 goals per 90 shows
slightly higher efficiency, while Lewandowski's career record demonstrates
sustained excellence over a decade.
Wyjście terminala pokazujące test agenta: wyszukiwanie polskich graczy, statystyki Haalanda i porównanie Lewandowski vs Mbappé
Wyjście testowe agenta — trzy zapytania demonstrujące wyszukiwanie, statystyki i porównanie

Najważniejsze wnioski

Pro Tip: Szybkie iteracje

Podczas developmentu użyj func start do uruchomienia Functions lokalnie, a następnie skieruj agenta na http://localhost:7071/runtime/webhooks/mcp. Żadne wdrożenie nie jest potrzebne do szybkiego iterowania.

Dalsze kroki

Zbudowałeś fundament. Oto sposoby na rozbudowę systemu:

1. Integracja z prawdziwym API piłkarskim

Zastąp bazę PLAYERS w pamięci danymi na żywo z football-data.org. Twoje funkcje będą automatycznie odpytywać aktualne statystyki bez zmian w agencie.

2. Dodanie kolejnych narzędzi

Rozszerz serwer MCP o dodatkowe funkcje:

3. Wdrożenie jako bot Teams

Opakuj agenta w bota Teams za pomocą Teams SDK. Wprowadź analitykę piłkarską bezpośrednio do czatu Twojej organizacji.

4. Dodanie workflow zatwierdzania

Zmień require_approval="never" na require_approval="always" dla wrażliwych operacji. Agent będzie prosił o zatwierdzenie przed wykonaniem wybranych narzędzi.

5. Implementacja cache'owania

Dodaj cache Redis do swoich Azure Functions, aby unikać zbędnych wywołań API i przyspieszyć powtarzające się zapytania.

6. Budowa interfejsu webowego

Stwórz frontend w React lub Next.js komunikujący się z Twoim Agentem Foundry. Użytkownicy dostaną dopracowany UI, podczas gdy AI obsługuje złożoność pod maską.

Gotowość produkcyjna

Ten poradnik obejmuje podstawowy wzorzec. Na produkcji dodaj monitoring z Application Insights, zaimplementuj uwierzytelnianie przez Azure AD, użyj managed identities zamiast kluczy funkcji i włącz Backup & Disaster Recovery.

Referencje i dalsze materiały

  1. MCP tool trigger for Azure Functions Microsoft Learn
    Oficjalna dokumentacja tworzenia MCP tool triggers w Azure Functions.
  2. Model context protocol bindings for Azure Functions Microsoft Learn
    Kompletna dokumentacja bindingów MCP, konfiguracji i najlepszych praktyk.
  3. Azure Functions MCP Extension GitHub GitHub
    Kod źródłowy i przykłady rozszerzenia MCP dla Azure Functions.
  4. Remote MCP Functions Python Sample GitHub Sample
    Kompletny przykład end-to-end w Pythonie z wdrożeniem serwerów MCP na Azure Functions.
  5. Azure AI Projects SDK v2 Microsoft Learn
    Dokumentacja Python SDK do tworzenia i zarządzania agentami Foundry.
  6. Model Context Protocol Oficjalna strona
    Oficjalna specyfikacja i dokumentacja projektowa MCP.
  7. Building MCP Apps with Azure Functions Microsoft Tech Community
    Szczegółowe omówienie architektury rozszerzenia MCP, wzorców i przykładów z praktyki.