0Pricing
AI Engineering Academy · Lekcja

Budowanie pierwszego serwera MCP

Użyj Python MCP SDK, aby utworzyć serwer udostępniający zasoby, narzędzia i prompty, a następnie połącz go z Claude Desktop i sprawdź jego działanie od początku do końca.

Budowanie pierwszego serwera MCP to bezpłatna lekcja AI Engineering Academy na CoddyKit. To lekcja 2 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej AI Engineering Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs AI Engineering Academy zawiera 4 lekcji w sumie.

Konfiguracja projektu serwera MCP

Tworzenie serwera MCP w języku Python wymaga SDK mcp i środowiska Python. Utworzą Państwo serwer, z którym Claude Desktop lub dowolny klient MCP może łączyć się za pośrednictwem stdio. Na początek należy zainstalować pakiet i utworzyć plik serwera.

# Create a project directory
# mkdir my_mcp_server && cd my_mcp_server

# Create a virtual environment
# python -m venv venv && source venv/bin/activate

# Install the MCP SDK
# pip install mcp httpx

# Project structure:
# my_mcp_server/
#   server.py          <- Your MCP server
#   requirements.txt
#   README.md

Tworzenie obiektu serwera

Należy zaimportować elementy z pakietu mcp i utworzyć instancję Server z nazwą serwera. Nazwa jest wyświetlana klientom na ich liście serwerów MCP — należy wybrać nazwę opisową. Obiekt serwera jest punktem wejścia do rejestrowania wszystkich narzędzi, zasobów i promptów.

# server.py
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp import types
import asyncio
import httpx

# Create the server — name is shown in Claude Desktop
app = Server('weather-server')

# --- Tool registrations go here ---

# Entry point
if __name__ == '__main__':
    asyncio.run(stdio_server(app))

Rejestrowanie narzędzi za pomocą @app.list_tools()

Dekorator @app.list_tools() rejestruje procedurę obsługi, która zwraca listę dostępnych narzędzi, gdy klient o nią poprosi. Każde narzędzie definiuje się za pomocą nazwy, opisu i elementu inputSchema — schematu JSON opisującego jego parametry. Klient wysyła tę listę do LLM, aby model wiedział, które narzędzia może wywoływać.

@app.list_tools()
async def list_tools() -> list[types.Tool]:
    return [
        types.Tool(
            name='get_weather',
            description='Get current weather for a city. Use when the user asks about weather in a specific location.',
            inputSchema={
                'type': 'object',
                'properties': {
                    'city': {
                        'type': 'string',
                        'description': 'City name, e.g. London or New York'
                    },
                    'units': {
                        'type': 'string',
                        'enum': ['metric', 'imperial'],
                        'description': 'Temperature units. Default is metric.'
                    }
                },
                'required': ['city']
            }
        ),
        types.Tool(
            name='list_cities',
            description='Return a list of major cities the user can query weather for.',
            inputSchema={'type': 'object', 'properties': {}, 'required': []}
        )
    ]

Implementowanie wykonywania narzędzi

Dekorator @app.call_tool() obsługuje wykonywanie narzędzi. Gdy klient wywołuje narzędzie, ten moduł obsługi otrzymuje jego nazwę i argumenty. Wykonaj odpowiednią logikę i zwróć listę obiektów TextContent zawierających ciąg znaków z wynikiem.

@app.call_tool()
async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
    if name == 'list_cities':
        cities = ['London', 'New York', 'Tokyo', 'Paris', 'Sydney']
        return [types.TextContent(type='text', text=', '.join(cities))]

    if name == 'get_weather':
        city = arguments['city']
        units = arguments.get('units', 'metric')
        unit_symbol = 'C' if units == 'metric' else 'F'

        # Call real weather API (stub here)
        async with httpx.AsyncClient() as client:
            # Replace with actual API call
            result = f'{city}: 18{chr(176)}{unit_symbol}, partly cloudy, humidity 65%'

        return [types.TextContent(type='text', text=result)]

    raise ValueError(f'Unknown tool: {name}')

Udostępnianie zasobów

Zasoby to statyczne lub dynamiczne dane, które model może odczytywać — można myśleć o nich jak o plikach. Zdefiniuj identyfikatory URI zasobów i zaimplementuj czytnik zasobów. Zasoby pojawiają się u klienta jako elementy, do których AI może się odwoływać; są przydatne w przypadku konfiguracji, dokumentacji lub często używanych migawek danych.

@app.list_resources()
async def list_resources() -> list[types.Resource]:
    return [
        types.Resource(
            uri='weather://supported-cities',
            name='Supported Cities List',
            description='Complete list of cities available in this weather server.',
            mimeType='text/plain'
        )
    ]

@app.read_resource()
async def read_resource(uri: str) -> str:
    if uri == 'weather://supported-cities':
        cities = ['London', 'New York', 'Tokyo', 'Paris', 'Sydney', 'Dubai', 'Singapore']
        return '\n'.join(cities)
    raise ValueError(f'Unknown resource URI: {uri}')

Rejestrowanie szablonów promptów

Prompty to wielokrotnego użytku szablony wiadomości, które klienci udostępniają użytkownikom jako polecenia slash lub szybkie akcje. Przyjmują parametry i zwracają listę wiadomości stających się początkowym kontekstem rozmowy. Prompty świetnie nadają się do kodowania złożonych instrukcji, które użytkownik uruchamia jednym poleceniem.

@app.list_prompts()
async def list_prompts() -> list[types.Prompt]:
    return [
        types.Prompt(
            name='weather-report',
            description='Generate a formatted weather report for a city.',
            arguments=[
                types.PromptArgument(name='city', description='City name', required=True)
            ]
        )
    ]

@app.get_prompt()
async def get_prompt(name: str, arguments: dict) -> types.GetPromptResult:
    if name == 'weather-report':
        city = arguments.get('city', 'London')
        return types.GetPromptResult(
            description=f'Weather report for {city}',
            messages=[
                types.PromptMessage(
                    role='user',
                    content=types.TextContent(
                        type='text',
                        text=f'Use the get_weather tool to look up {city} and give me a detailed weather report including what clothing I should wear.'
                    )
                )
            ]
        )
    raise ValueError(f'Unknown prompt: {name}')

Łączenie z Claude Desktop

Aby używać serwera MCP z Claude Desktop, dodaj go do pliku konfiguracyjnego Claude Desktop. W systemie macOS znajduje się on pod adresem ~/Library/Application Support/Claude/claude_desktop_config.json. Określ polecenie uruchamiające serwer oraz wszystkie wymagane przez niego zmienne środowiskowe.

# ~/Library/Application Support/Claude/claude_desktop_config.json
# Add this JSON configuration:

# {
#   "mcpServers": {
#     "weather-server": {
#       "command": "/path/to/venv/bin/python",
#       "args": ["/path/to/my_mcp_server/server.py"],
#       "env": {
#         "WEATHER_API_KEY": "your_api_key_here"
#       }
#     }
#   }
# }

# After saving, restart Claude Desktop.
# Your server's tools will appear in Claude's tool list.

Testowanie serwera za pomocą MCP CLI

Przed połączeniem z Claude Desktop przetestuj serwer za pomocą inspektora MCP lub narzędzi CLI. Polecenie mcp dev uruchamia serwer i otwiera inspektora w przeglądarce, w którym można ręcznie wywoływać narzędzia oraz wyświetlać surowe komunikaty protokołu, co ułatwia debugowanie problemów.

# Install the MCP development tools
# pip install 'mcp[cli]'

# Start the inspector with your server
# mcp dev server.py

# The inspector opens at http://localhost:5173
# You can:
# - See all registered tools and their schemas
# - Call tools with custom arguments
# - Browse available resources
# - Test prompt templates
# - View the full JSON-RPC message exchange

Obsługa błędów w serwerach MCP

Serwery MCP nigdy nie powinny ulegać awarii z powodu nieprawidłowych danych wejściowych. Umieść wykonywanie wszystkich narzędzi w bloku try/except i zwracaj komunikaty o błędach jako TextContent, zamiast zgłaszać wyjątki. W przypadku błędów krytycznych (problemów z konfiguracją, braku kluczy API) zapisz je w dzienniku podczas uruchamiania i zgłoś wyjątek, zanim serwer wejdzie do głównej pętli.

@app.call_tool()
async def call_tool_safe(name: str, arguments: dict) -> list[types.TextContent]:
    try:
        if name == 'get_weather':
            city = arguments.get('city')
            if not city:
                return [types.TextContent(type='text', text='Error: city argument is required.')]
            result = await fetch_weather(city, arguments.get('units', 'metric'))
            return [types.TextContent(type='text', text=result)]
        raise ValueError(f'Unknown tool: {name}')
    except httpx.TimeoutException:
        return [types.TextContent(type='text', text='Error: Weather API timed out. Try again.')]
    except Exception as e:
        return [types.TextContent(type='text', text=f'Error: {str(e)}')]

Logowanie w serwerach MCP

Ponieważ serwery MCP komunikują się z klientem za pośrednictwem stdio, instrukcje print zakłócą działanie protokołu. Do logowania zawsze używaj stderr — dane trafiają wtedy do osobnego strumienia, który nie zakłóca wymiany komunikatów MCP. Skonfiguruj moduł logowania Pythona tak, aby zapisywał dane do stderr.

import logging
import sys

# Configure logging to stderr (NOT stdout — that's the MCP channel)
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s [%(levelname)s] %(message)s',
    stream=sys.stderr
)
logger = logging.getLogger('weather-server')

# In your tool handler:
# logger.info(f'Getting weather for {city}')
# logger.error(f'API call failed: {e}')

# Claude Desktop captures stderr to a log file:
# ~/Library/Logs/Claude/mcp-server-weather-server.log

Pakowanie serwera MCP

Udostępnij serwer MCP, pakując go z plikiem pyproject.toml i publikując w PyPI, albo rozprowadzając go wśród zespołów jako kontener Docker. Wszystkie dane uwierzytelniające — klucze API, adresy URL baz danych — przechowuj w zmiennych środowiskowych, aby konfiguracja serwera była oddzielona od kodu. W przejrzystym pliku README opisz wymagane zmienne środowiskowe i przykładową konfigurację.

Szybki test

Sprawdź, czy rozumiesz, jak budować serwer MCP w Pythonie.

Podsumowanie lekcji

W tej lekcji nauczyłeś się, że: serwery MCP udostępniają narzędzia za pomocą dekoratorów @app.list_tools() i @app.call_tool(), zasoby i prompty rozszerzają serwer o dane możliwe do odczytu oraz wielokrotnego użytku szablony, a także że logowanie musi używać stderr, aby nie uszkodzić kanału protokołu MCP korzystającego ze stdio. Następnie połączymy serwer MCP z bazą danych i udostępnimy dynamiczne zasoby z paginacją.

Często zadawane pytania

Czy lekcja „Budowanie pierwszego serwera MCP” jest bezpłatna?

Tak — pełny tekst „Budowanie pierwszego serwera MCP” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu AI Engineering Academy, przejdź na CoddyKit PRO. Kurs AI Engineering Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Budowanie pierwszego serwera MCP”?

Użyj Python MCP SDK, aby utworzyć serwer udostępniający zasoby, narzędzia i prompty, a następnie połącz go z Claude Desktop i sprawdź jego działanie od początku do końca. Ćwiczysz AI Engineering Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć AI Engineering Academy?

Nie wymagamy żadnego doświadczenia. AI Engineering Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 4.

Ile czasu zajmuje lekcja „Budowanie pierwszego serwera MCP”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji AI Engineering Academy?

Tak. Każda lekcja AI Engineering Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Czym jest MCP i dlaczego ma znaczenie
  2. Budowanie pierwszego serwera MCP
  3. Udostępnianie zasobów bazy danych przez MCP
  4. Bezpieczeństwo i uwierzytelnianie MCP
← Powrót do AI Engineering Academy