0Pricing
AI Engineering Academy · Lezione

Creare il primo server MCP

Utilizzi l'SDK MCP per Python per creare un server che esponga risorse, strumenti e prompt, quindi lo colleghi a Claude Desktop per verificarne il funzionamento end-to-end.

Creare il primo server MCP è una lezione AI Engineering Academy gratuita su CoddyKit. Questa è la lezione 2 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento AI Engineering Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso AI Engineering Academy include 4 lezioni in totale.

Configurazione del progetto per un server MCP

Per creare un server MCP in Python sono necessari l'SDK mcp e un ambiente Python. Creerà un server a cui Claude Desktop o qualsiasi client MCP potrà connettersi tramite stdio. Inizi installando il pacchetto e creando il file del server.

# 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

Creazione dell'oggetto server

Importi il pacchetto mcp e crei un'istanza di Server con il nome del server. Il nome viene mostrato ai client nell'elenco dei server MCP: ne scelga uno descrittivo. L'oggetto server è il punto di accesso per registrare tutti gli strumenti, le risorse e i prompt.

# 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))

Registrazione degli strumenti con @app.list_tools()

Il decorator @app.list_tools() registra un gestore che restituisce l'elenco degli strumenti disponibili quando il client lo richiede. Ogni strumento viene definito con un nome, una descrizione e un inputSchema, ovvero lo JSON Schema che descrive i relativi parametri. Il client invia questo elenco all'LLM, così il modello sa quali strumenti può chiamare.

@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': []}
        )
    ]

Implementazione dell'esecuzione degli strumenti

Il decoratore @app.call_tool() gestisce l'esecuzione degli strumenti. Quando il client chiama uno strumento, questo gestore riceve il nome dello strumento e gli argomenti. Esegua la logica appropriata e restituisca un elenco di oggetti TextContent contenenti la stringa del risultato.

@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}')

Esposizione delle risorse

Le risorse sono dati statici o dinamici che il modello può leggere; le si può considerare come file. Definisca gli URI delle risorse e implementi un lettore di risorse. Le risorse vengono visualizzate nel client come elementi a cui l'IA può fare riferimento, utili per la configurazione, la documentazione o snapshot di dati consultati frequentemente.

@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}')

Registrazione dei template di prompt

I prompt sono template di messaggi riutilizzabili che i client presentano agli utenti come comandi slash o azioni rapide. Accettano parametri e restituiscono un elenco di messaggi che diventano il contesto iniziale di una conversazione. I prompt sono ideali per codificare istruzioni complesse che gli utenti possono attivare con un singolo comando.

@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}')

Connessione a Claude Desktop

Per usare il server MCP con Claude Desktop, lo aggiunga al file di configurazione di Claude Desktop. Su macOS, il file si trova in ~/Library/Application Support/Claude/claude_desktop_config.json. Specifichi il comando per avviare il server e tutte le variabili d'ambiente necessarie.

# ~/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.

Test del server con MCP CLI

Prima di effettuare la connessione a Claude Desktop, testi il server usando l'inspector MCP o gli strumenti CLI. Il comando mcp dev avvia il server e apre un inspector nel browser, dal quale può chiamare manualmente gli strumenti e visualizzare i messaggi grezzi del protocollo, facilitando il debug dei problemi.

# 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

Gestione degli errori nei server MCP

I server MCP non devono mai arrestarsi a causa di input non validi. Racchiuda tutta l'esecuzione degli strumenti in un blocco try/except e restituisca i messaggi di errore come TextContent, invece di sollevare eccezioni. Per gli errori irreversibili, come problemi di configurazione o chiavi API mancanti, li registri all'avvio e sollevi un'eccezione prima che il server entri nel ciclo principale.

@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)}')]

Logging per i server MCP

Poiché i server MCP comunicano con il client tramite stdio, le istruzioni di stampa interromperebbero il protocollo. Usi sempre stderr per il logging: viene inviato a un flusso separato che non interferisce con lo scambio di messaggi MCP. Configuri il modulo di logging di Python per scrivere su 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

Distribuzione del server MCP

Condivida il server MCP impacchettandolo con un pyproject.toml e pubblicandolo su PyPI, oppure lo distribuisca come container Docker per i team. Utilizzi variabili d'ambiente per tutti i segreti, come chiavi API e URL dei database, in modo che la configurazione del server rimanga separata dal codice. Documenti in un README chiaro le variabili d'ambiente richieste e un esempio di configurazione.

Verifica rapida

Verifichi la Sua comprensione della creazione di un server MCP in Python.

Riepilogo della lezione

In questa lezione ha imparato che: i server MCP espongono strumenti tramite i decoratori @app.list_tools() e @app.call_tool(), le risorse e i prompt estendono il server con dati leggibili e template riutilizzabili e il logging deve usare stderr per evitare di corrompere il canale stdio del protocollo MCP. Nella prossima lezione collegheremo un server MCP a un database ed esporremo risorse dinamiche con impaginazione.

Domande Frequenti

La lezione «Creare il primo server MCP» è gratuita?

Sì — il testo completo di «Creare il primo server MCP» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso AI Engineering Academy, passa a CoddyKit PRO. Il corso AI Engineering Academy include 4 lezioni in totale.

Cosa imparerò in «Creare il primo server MCP»?

Utilizzi l'SDK MCP per Python per creare un server che esponga risorse, strumenti e prompt, quindi lo colleghi a Claude Desktop per verificarne il funzionamento end-to-end. Eserciti AI Engineering Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare AI Engineering Academy?

Non è richiesta alcuna esperienza precedente. AI Engineering Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 2 di 4.

Quanto tempo richiede la lezione «Creare il primo server MCP»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione AI Engineering Academy?

Sì. Ogni lezione AI Engineering Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Che cos'è MCP e perché è importante
  2. Creare il primo server MCP
  3. Esporre risorse del database tramite MCP
  4. Sicurezza e autenticazione di MCP
← Torna a AI Engineering Academy