Creo applicazioni web moderne e strumenti digitali personalizzati per aiutare le attività a crescere attraverso l'innovazione tecnologica. La mia passione è unire informatica ed economia per generare valore reale.
La mia passione per l'informatica è nata tra i banchi dell'Istituto Tecnico Commerciale di Maglie, dove ho scoperto il potere della programmazione e il fascino di creare soluzioni digitali. Fin da subito, ho capito che l'informatica non era solo codice, ma uno strumento straordinario per trasformare idee in realtà.
Durante gli studi superiori in Sistemi Informativi Aziendali, ho iniziato a intrecciare informatica ed economia, comprendendo come la tecnologia possa essere il motore della crescita per qualsiasi attività. Questa visione mi ha accompagnato all'Università degli Studi di Bari, dove ho conseguito la Laurea in Informatica, approfondendo le mie competenze tecniche e la mia passione per lo sviluppo software.
Oggi metto questa esperienza al servizio di imprese, professionisti e startup, creando soluzioni digitali su misura che automatizzano processi, ottimizzano risorse e aprono nuove opportunità di business. Perché la vera innovazione inizia quando la tecnologia incontra le esigenze reali delle persone.
Le Mie Competenze
Analisi Dati & Modelli Previsionali
Trasformo i dati in insights strategici con analisi approfondite e modelli predittivi per decisioni informate
Automazione Processi
Creo strumenti personalizzati che automatizzano operazioni ripetitive e liberano tempo per attività a valore aggiunto
Sistemi Custom
Sviluppo sistemi software su misura, dalle integrazioni tra piattaforme alle dashboard personalizzate
Credo fermamente che l'informatica sia lo strumento più potente per trasformare le idee in realtà e migliorare la vita delle persone.
Democratizzare la Tecnologia
La mia missione è rendere l'informatica accessibile a tutti: dalle piccole imprese locali alle startup innovative, fino ai professionisti che vogliono digitalizzare la propria attività. Ogni realtà merita di sfruttare le potenzialità del digitale.
Unire Informatica ed Economia
Non è solo questione di scrivere codice: è capire come la tecnologia possa generare valore reale. Intrecciando competenze informatiche e visione economica, aiuto le attività a crescere, ottimizzare processi e raggiungere nuovi traguardi di efficienza e redditività.
Creare Soluzioni su Misura
Ogni attività è unica, e così devono esserlo le soluzioni. Sviluppo strumenti personalizzati che rispondono alle esigenze specifiche di ciascun cliente, automatizzando processi ripetitivi e liberando tempo per ciò che conta davvero: far crescere il business.
Trasforma la Tua Attività con la Tecnologia
Che tu gestisca un negozio, uno studio professionale o un'azienda, posso aiutarti a sfruttare le potenzialità dell'informatica per lavorare meglio, più velocemente e in modo più intelligente.
Il mio percorso accademico e le tecnologie che padroneggio
Certificazioni Professionali
8 certificazioni conseguite
Nuovo
Visualizza
Reinvention With Agentic AI Learning Program
Anthropic
Dicembre 2024
Nuovo
Visualizza
Agentic AI Fluency
Anthropic
Dicembre 2024
Nuovo
Visualizza
AI Fluency for Students
Anthropic
Dicembre 2024
Nuovo
Visualizza
AI Fluency: Framework and Foundations
Anthropic
Dicembre 2024
Nuovo
Visualizza
Claude with the Anthropic API
Anthropic
Dicembre 2024
Visualizza
Master SQL
RoadMap.sh
Novembre 2024
Visualizza
Oracle Certified Foundations Associate
Oracle
Ottobre 2024
Visualizza
People Leadership Credential
Connect
Settembre 2024
Linguaggi & Tecnologie
Java
Python
JavaScript
Angular
React
TypeScript
SQL
PHP
CSS/SCSS
Node.js
Docker
Git
💼
12/2024 - Presente
Custom Software Engineering Analyst
Accenture
Bari, Puglia, Italia · Ibrida
Analisi e sviluppo di sistemi informatici attraverso l'utilizzo di Java e Quarkus in Health and Public Sector. Formazione continua su tecnologie moderne per la creazione di soluzioni software personalizzate ed efficienti e sugli agenti.
💼
06/2022 - 12/2024
Analista software e Back End Developer Associate Consultant
Links Management and Technology SpA
Esperienza nell'analisi di sistemi software as-is e flussi ETL utilizzando PowerCenter. Formazione completata su Spring Boot per lo sviluppo di applicazioni backend moderne e scalabili. Sviluppatore Backend specializzato in Spring Boot, con esperienza in progettazione di database, analisi, sviluppo e testing dei task assegnati.
💼
02/2021 - 10/2021
Programmatore software
Adesso.it (prima era WebScience srl)
Esperienza nell'analisi AS-IS e TO-BE, evoluzioni SEO ed evoluzioni website per migliorare le performance e l'engagement degli utenti.
🎓
2018 - 2025
Laurea in Informatica
Università degli Studi di Bari Aldo Moro
Bachelor's degree in Computer Science, focusing on software engineering, algorithms, and modern development practices.
📚
2013 - 2018
Diploma - Sistemi Informativi Aziendali
Istituto Tecnico Commerciale di Maglie
Technical diploma specializing in Business Information Systems, combining IT knowledge with business management.
Contattami
Hai un progetto in mente? Parliamone! Compila il form qui sotto e ti risponderò al più presto.
* Campi obbligatori. I tuoi dati saranno utilizzati solo per rispondere alla tua richiesta.
Introduzione: Dal Protocollo al Codice
Negli articoli precedenti abbiamo esplorato i fondamenti del Model Context Protocol, le tre
primitive (Tools, Resources, Prompts) e l'architettura monorepo. Ora e il momento di scrivere codice:
in questo tutorial costruiremo un server MCP completo in TypeScript, partendo da un progetto
vuoto fino ad arrivare a un server funzionante testabile con Claude Desktop.
Il server che creeremo gestira un sistema di note in memoria, esponendo tool per aggiungere,
leggere, cercare e cancellare note. Implementeremo validazione avanzata con Zod,
gestione errori strutturata e una risorsa per accedere alle note via URI. Tutto il codice e disponibile
nel repository Tech-MCP su GitHub.
Cosa Imparerai in Questo Articolo
Come configurare un progetto TypeScript per un server MCP (package.json, tsconfig.json)
Come creare un server con la classe McpServer dell'SDK ufficiale
Come registrare tool con server.tool() e validare input con schemi Zod
Come esporre risorse con server.resource() e URI template
Come gestire errori con il pattern isError: true
Come configurare il transport StdioServerTransport
Come testare il server con MCP Inspector e Claude Desktop
Regole di debugging: perchè non usare mai console.log()
Prerequisiti
Prima di iniziare, assicurati di avere installato i seguenti strumenti sul tuo sistema:
Node.js 18+: runtime JavaScript/TypeScript (node --version per verificare)
npm o pnpm: package manager per gestire le dipendenze
TypeScript 5.x: il linguaggio tipizzato che useremo per tutto il server
Claude Desktop (opzionale): per testare il server con un client MCP reale
Step 1: Inizializzazione del Progetto
Creiamo la struttura del progetto da zero. Il nostro server si chiamara mcp-notes-server
e sarà un progetto Node.js con TypeScript.
Creazione della Directory e Inizializzazione
# Crea la directory del progetto
mkdir mcp-notes-server && cd mcp-notes-server
# Inizializza il progetto Node.js
npm init -y
# Installa le dipendenze di produzione
npm install @modelcontextprotocol/sdk zod
# Installa le dipendenze di sviluppo
npm install -D typescript @types/node
# Crea la directory per il codice sorgente
mkdir -p src
Le dipendenze installate hanno ruoli specifici:
@modelcontextprotocol/sdk: l'SDK ufficiale MCP che fornisce McpServer, i transport e le utility del protocollo
zod: libreria di validazione schema che MCP usa per definire e validare i parametri dei tool
typescript: compilatore TypeScript per la tipizzazione statica
@types/node: definizioni di tipo per le API di Node.js
Configurazione package.json
Aggiorna il file package.json generato con la configurazione necessaria per un server MCP.
Il campo "type": "module" e fondamentale perchè l'SDK MCP utilizza ESM (ECMAScript Modules):
Il campo "type": "module" e obbligatorio. Senza di esso, Node.js trattera i file .js
come CommonJS e l'import dell'SDK MCP fallirà con un errore di sintassi. Se dimentichi questo campo,
vedrai un errore come SyntaxError: Cannot use import statement in a module.
Configurazione TypeScript (tsconfig.json)
Crea il file tsconfig.json nella root del progetto. La configurazione deve essere compatibile
con ESM e Node.js 16+:
target: "ES2022": compila verso una versione moderna di JavaScript con supporto per top-level await
module: "Node16": genera moduli compatibili con il sistema ESM di Node.js 16+
moduleResolution: "Node16": risolve i moduli secondo le regole di Node.js 16 (richiede estensioni .js negli import)
strict: true: abilita tutti i controlli di tipo rigorosi per maggiore sicurezza
declaration: true: genera file .d.ts per il type checking esterno
Struttura Finale del Progetto
Alla fine del setup, la struttura del progetto sarà la seguente:
mcp-notes-server/
src/
index.ts # Entry point e logica del server
package.json # Configurazione npm con type: module
tsconfig.json # Configurazione TypeScript
node_modules/ # Dipendenze installate
Step 2: Creare il Server MCP Minimo
Iniziamo con un server minimo che registra un singolo tool. Crea il file src/index.ts:
#!/usr/bin/env node
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
// ============================================
// Stato in memoria
// ============================================
const notes: Map<string, string> = new Map();
// ============================================
// Creazione del server MCP
// ============================================
const server = new McpServer({
name: "notes-server",
version: "1.0.0",
});
// ============================================
// Tool: aggiungere una nota
// ============================================
server.tool(
"add-note",
"Aggiunge una nuova nota con titolo e contenuto",
{
title: z.string().describe("Titolo della nota"),
content: z.string().describe("Contenuto della nota"),
},
async ({ title, content }) => {
notes.set(title, content);
return {
content: [
{
type: "text",
text: `Nota "
#123;title}" salvata con successo.`,
},
],
};
},
);
// ============================================
// Avvio del server con transport STDIO
// ============================================
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Notes MCP Server avviato su stdio");
}
main().catch((error) => {
console.error("Errore fatale:", error);
process.exit(1);
});
Anatomia del Codice
Analizziamo ogni sezione del codice per comprendere il ruolo di ciascun componente.
1. Lo Shebang e gli Import
#!/usr/bin/env node
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
Lo shebang (#!/usr/bin/env node) permette di eseguire il file direttamente come
script dalla riga di comando. Gli import caricano tre componenti essenziali:
McpServer: la classe principale che gestisce il lifecycle del protocollo, la registrazione dei tool e la negoziazione delle capability
StdioServerTransport: il transport che comunica tramite stdin/stdout del processo
z (Zod): la libreria di validazione per definire gli schemi dei parametri
2. Creazione dell'Istanza Server
const server = new McpServer({
name: "notes-server", // Identificativo univoco del server
version: "1.0.0", // Versione seguendo il formato semver
});
L'istanza McpServer accetta un oggetto di configurazione con name (identificativo
univoco che il client usa per riconoscere il server) e version (la versione in formato semver).
Questi dati vengono inviati al client durante la fase di inizializzazione del protocollo.
3. La Firma di server.tool()
Il metodo server.tool() e il cuore della registrazione dei tool. Accetta quattro argomenti:
Argomenti di server.tool()
Argomento
Tipo
Descrizione
name
string
Identificativo univoco del tool (es. "add-note")
description
string
Descrizione letta dal modello AI per decidere quando invocare il tool
inputSchema
object
Oggetto con chiavi Zod che definisce i parametri accettati
handler
async function
Funzione asincrona che riceve i parametri tipizzati e ritorna il risultato
4. Il Formato del Risultato
Ogni tool handler deve ritornare un oggetto con un campo content, che e un array di elementi
tipizzati. MCP supporta tre tipi di contenuto nel risultato:
const transport = new StdioServerTransport();
await server.connect(transport);
StdioServerTransport configura il server per comunicare tramite stdin (ricezione messaggi dal client)
e stdout (invio risposte al client). Il metodo connect() avvia il loop di
lettura/scrittura e da quel momento il server e in ascolto di messaggi JSON-RPC.
Regola Fondamentale: Mai Scrivere su stdout
Con il transport STDIO, il canale stdout e riservato esclusivamente ai messaggi JSON-RPC
del protocollo. Qualsiasi altro output su stdout (come un console.log()) corromperebbe
il protocollo e causerebbe errori di parsing nel client. Per il logging e il debugging, usa
sempreconsole.error(), che scrive su stderr.
Step 3: Aggiungere Tool Completi (CRUD)
Espandiamo il server con un set completo di operazioni CRUD sulle note. Aggiungi i seguenti tool
dopo il tool add-note nel file src/index.ts:
Tool: Leggere una Nota
server.tool(
"get-note",
"Recupera il contenuto di una nota dal titolo",
{
title: z.string().describe("Titolo della nota da leggere"),
},
async ({ title }) => {
const content = notes.get(title);
if (!content) {
return {
content: [{ type: "text", text: `Nota "#123;title}" non trovata.` }],
isError: true,
};
}
return {
content: [{ type: "text", text: content }],
};
},
);
Tool: Elencare Tutte le Note
server.tool(
"list-notes",
"Elenca tutte le note salvate con i rispettivi titoli",
{}, // Nessun parametro richiesto: schema vuoto
async () => {
if (notes.size === 0) {
return {
content: [{ type: "text", text: "Nessuna nota salvata." }],
};
}
const list = Array.from(notes.entries())
.map(([title, content], i) =>
`#123;i + 1}. #123;title} (#123;content.length} caratteri)`
)
.join("\n");
return {
content: [{ type: "text", text: `Note salvate:\n#123;list}` }],
};
},
);
Tool: Cancellare una Nota
server.tool(
"delete-note",
"Cancella una nota esistente dal titolo",
{
title: z.string().describe("Titolo della nota da cancellare"),
},
async ({ title }) => {
const deleted = notes.delete(title);
if (!deleted) {
return {
content: [{ type: "text", text: `Nota "#123;title}" non trovata.` }],
isError: true,
};
}
return {
content: [{ type: "text", text: `Nota "#123;title}" cancellata con successo.` }],
};
},
);
Step 4: Validazione Avanzata con Zod
Zod non e solo una libreria di validazione: e il linguaggio con cui descrivi i parametri dei tuoi tool
al modello AI. Ogni schema Zod viene convertito in JSON Schema e inviato al client durante la fase
di discovery (tools/list). Il metodo .describe() e particolarmente importante
perchè il testo viene incluso nello schema e aiuta l'AI a capire cosa fornire come argomento.
Aggiungiamo un tool di ricerca con validazione avanzata per dimostrare le capacità di Zod:
server.tool(
"search-notes",
"Cerca note per parola chiave con opzioni di filtro avanzate",
{
query: z.string()
.min(2)
.describe("Testo da cercare (minimo 2 caratteri)"),
caseSensitive: z.boolean()
.optional()
.default(false)
.describe("Se true, la ricerca e case-sensitive"),
limit: z.number()
.int()
.min(1)
.max(100)
.optional()
.default(10)
.describe("Numero massimo di risultati (1-100)"),
},
async ({ query, caseSensitive, limit }) => {
const results: string[] = [];
for (const [title, content] of notes) {
const haystack = caseSensitive ? content : content.toLowerCase();
const needle = caseSensitive ? query : query.toLowerCase();
if (haystack.includes(needle)) {
results.push(title);
}
if (results.length >= limit) break;
}
return {
content: [{
type: "text",
text: results.length > 0
? `Trovate #123;results.length} note:\n#123;results.join("\n")}`
: `Nessun risultato per "#123;query}".`,
}],
};
},
);
Pattern Zod Comuni per MCP
Ecco un riferimento rapido dei pattern Zod più utilizzati nella definizione degli schemi dei tool MCP:
Riferimento Rapido Zod per MCP
Pattern
Codice
Uso
Stringa obbligatoria
z.string()
Parametro testuale richiesto
Stringa non vuota
z.string().min(1)
Parametro che non può essere vuoto
Email valida
z.string().email()
Validazione formato email
URL valido
z.string().url()
Validazione formato URL
Intero positivo
z.number().int().positive()
ID, contatori
Range numerico
z.number().min(0).max(100)
Percentuali, punteggi
Booleano
z.boolean()
Flag on/off
Enum di stringhe
z.enum(["low", "medium", "high"])
Valori predefiniti
Opzionale con default
z.string().optional().default("valore")
Parametri con valore predefinito
Array di stringhe
z.array(z.string())
Liste, tag
Descrizione per l'AI
z.string().describe("spiegazione")
Guida il modello AI nella compilazione
Il metodo .describe() e cruciale per l'esperienza con l'AI: il testo viene incluso nello
schema JSON inviato al modello durante il discovery, permettendogli di capire esattamente cosa fornire
come valore per ogni parametro. Scrivi descrizioni chiare e concise.
Step 5: Gestione Errori con isError
MCP definisce un pattern chiaro per la gestione degli errori a livello di tool. Esistono due categorie
di errori da gestire:
Errori di Esecuzione (Gestiti dal Tool)
Quando un tool incontra un errore prevedibile (risorsa non trovata, validazione fallita, servizio non
disponibile), deve ritornare un risultato con isError: true. Questo segnala al modello AI
che l'operazione e fallita, permettendogli di reagire in modo appropriato:
server.tool(
"get-note",
"Recupera una nota dal titolo",
{
title: z.string().describe("Titolo della nota"),
},
async ({ title }) => {
try {
const content = notes.get(title);
if (!content) {
// Errore prevedibile: nota non trovata
return {
content: [{
type: "text",
text: `Errore: la nota "#123;title}" non esiste.`,
}],
isError: true,
};
}
return {
content: [{ type: "text", text: content }],
};
} catch (error) {
// Errore imprevisto: catturato e gestito
return {
content: [{
type: "text",
text: `Errore: #123;error instanceof Error ? error.message : String(error)}`,
}],
isError: true,
};
}
},
);
Errori di Protocollo (Eccezioni Non Gestite)
Se un tool handler lancia un'eccezione non gestita, l'SDK la trasforma automaticamente in un errore
JSON-RPC con codice -32603 (Internal Error). E buona pratica gestire sempre gli errori
esplicitamente con try/catch per fornire messaggi d'errore più informativi al modello.
Best Practice: Gestisci Sempre gli Errori
Il pattern consigliato e avvolgere tutta la logica del tool in un blocco try/catch,
ritornando isError: true con un messaggio descrittivo in caso di errore. Questo
permette al modello AI di ricevere l'errore come contesto e decidere come procedere (riprovare,
chiedere all'utente, o provare un approccio diverso).
Step 6: Esporre Risorse con server.resource()
Oltre ai tool, un server MCP può esporre risorse: dati contestuali accessibili dal
client tramite URI. Le risorse sono utili per fornire informazioni di sola lettura senza richiedere
un'azione esplicita del modello.
Aggiungiamo una risorsa che espone l'elenco completo delle note:
import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
// Risorsa: elenco di tutte le note
server.resource(
"notes-list",
"note://list",
"Elenco completo di tutte le note salvate",
async (uri) => {
const allNotes = Array.from(notes.entries())
.map(([title, content]) => `## #123;title}\n#123;content}`)
.join("\n\n---\n\n");
return {
contents: [
{
uri: uri.href,
mimeType: "text/markdown",
text: allNotes || "Nessuna nota disponibile.",
},
],
};
},
);
// Risorsa con template URI: singola nota
server.resource(
"note-by-title",
new ResourceTemplate("note://{title}", { list: undefined }),
"Accede a una singola nota tramite il titolo",
async (uri, { title }) => {
const content = notes.get(title as string);
return {
contents: [
{
uri: uri.href,
mimeType: "text/plain",
text: content ?? `Nota "#123;title}" non trovata.`,
},
],
};
},
);
La differenza tra tool e risorse e fondamentale nel protocollo MCP:
Tools: azioni invocate dal modello AI, possono avere effetti collaterali (creare, modificare, cancellare dati)
Resources: dati di sola lettura accessibili dall'applicazione client, non hanno effetti collaterali
Step 7: Il Server Completo
Ecco il codice completo del server con tutti i tool, le risorse e la gestione degli errori.
Questo e il file src/index.ts finale:
#!/usr/bin/env node
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
// ============================================
// Stato in memoria
// ============================================
const notes: Map<string, string> = new Map();
// ============================================
// Creazione del server MCP
// ============================================
const server = new McpServer({
name: "notes-server",
version: "1.0.0",
});
// ============================================
// TOOL: Aggiungere una nota
// ============================================
server.tool(
"add-note",
"Aggiunge una nuova nota con titolo e contenuto",
{
title: z.string().min(1).describe("Titolo della nota"),
content: z.string().min(1).describe("Contenuto della nota"),
},
async ({ title, content }) => {
if (notes.has(title)) {
return {
content: [{
type: "text",
text: `Nota "#123;title}" già esistente. Usa un titolo diverso.`,
}],
isError: true,
};
}
notes.set(title, content);
return {
content: [{
type: "text",
text: `Nota "#123;title}" salvata con successo (#123;content.length} caratteri).`,
}],
};
},
);
// ============================================
// TOOL: Leggere una nota
// ============================================
server.tool(
"get-note",
"Recupera il contenuto di una nota dal titolo",
{
title: z.string().describe("Titolo della nota da leggere"),
},
async ({ title }) => {
const content = notes.get(title);
if (!content) {
return {
content: [{ type: "text", text: `Nota "#123;title}" non trovata.` }],
isError: true,
};
}
return {
content: [{ type: "text", text: content }],
};
},
);
// ============================================
// TOOL: Elencare tutte le note
// ============================================
server.tool(
"list-notes",
"Elenca tutte le note salvate con i rispettivi titoli",
{},
async () => {
if (notes.size === 0) {
return {
content: [{ type: "text", text: "Nessuna nota salvata." }],
};
}
const list = Array.from(notes.entries())
.map(([title, content], i) =>
`#123;i + 1}. #123;title} (#123;content.length} caratteri)`
)
.join("\n");
return {
content: [{ type: "text", text: `Note salvate:\n#123;list}` }],
};
},
);
// ============================================
// TOOL: Cancellare una nota
// ============================================
server.tool(
"delete-note",
"Cancella una nota esistente dal titolo",
{
title: z.string().describe("Titolo della nota da cancellare"),
},
async ({ title }) => {
const deleted = notes.delete(title);
if (!deleted) {
return {
content: [{ type: "text", text: `Nota "#123;title}" non trovata.` }],
isError: true,
};
}
return {
content: [{ type: "text", text: `Nota "#123;title}" cancellata.` }],
};
},
);
// ============================================
// TOOL: Cercare note con filtri avanzati
// ============================================
server.tool(
"search-notes",
"Cerca note per parola chiave con opzioni di filtro",
{
query: z.string().min(2).describe("Testo da cercare (minimo 2 caratteri)"),
caseSensitive: z.boolean().optional().default(false)
.describe("Se true, la ricerca e case-sensitive"),
limit: z.number().int().min(1).max(100).optional().default(10)
.describe("Numero massimo di risultati (1-100)"),
},
async ({ query, caseSensitive, limit }) => {
const results: string[] = [];
for (const [title, content] of notes) {
const haystack = caseSensitive ? content : content.toLowerCase();
const needle = caseSensitive ? query : query.toLowerCase();
if (haystack.includes(needle)) {
results.push(title);
}
if (results.length >= limit) break;
}
return {
content: [{
type: "text",
text: results.length > 0
? `Trovate #123;results.length} note:\n#123;results.join("\n")}`
: `Nessun risultato per "#123;query}".`,
}],
};
},
);
// ============================================
// RISORSA: Elenco completo note
// ============================================
server.resource(
"notes-list",
"note://list",
"Elenco completo di tutte le note salvate",
async (uri) => {
const allNotes = Array.from(notes.entries())
.map(([title, content]) => `## #123;title}\n#123;content}`)
.join("\n\n---\n\n");
return {
contents: [{
uri: uri.href,
mimeType: "text/markdown",
text: allNotes || "Nessuna nota disponibile.",
}],
};
},
);
// ============================================
// Avvio del server con transport STDIO
// ============================================
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Notes MCP Server avviato su stdio");
}
main().catch((error) => {
console.error("Errore fatale:", error);
process.exit(1);
});
Step 8: Build e Test con MCP Inspector
Compila il progetto e testa il server con l'MCP Inspector, lo strumento ufficiale di debug per server MCP:
# Compila il progetto TypeScript
npm run build
# Testa con MCP Inspector (apre un'interfaccia web)
npx @modelcontextprotocol/inspector dist/index.js
L'MCP Inspector apre un'interfaccia web nel browser dove puoi:
Visualizzare tutti i tool registrati con i rispettivi schemi
Invocare i tool manualmente fornendo parametri di test
Verificare il formato delle risposte
Testare le risorse tramite URI
Monitorare i messaggi JSON-RPC scambiati
Step 9: Configurazione Claude Desktop
Per utilizzare il server con Claude Desktop (il client MCP più diffuso), aggiungi la configurazione
nel file appropriato per il tuo sistema operativo:
Dopo aver salvato il file e riavviato Claude Desktop, il modello vedra i tool del server notes
e potra invocarli automaticamente durante le conversazioni. Puoi verificare che il server sia
connesso correttamente cercando l'icona dei tool nella barra inferiore di Claude Desktop.
Aggiungere Variabili d'Ambiente
Se il tuo server ha bisogno di variabili d'ambiente (ad esempio per API key o configurazione),
puoi aggiungerle nella configurazione con il campo env:
Il debugging di un server MCP presenta sfide uniche rispetto alle applicazioni tradizionali.
Ecco le regole fondamentali e i suggerimenti per risolvere i problemi più comuni:
Regola 1: Mai Usare console.log()
Con il transport STDIO, console.log() scrive su stdout, che e il canale
riservato ai messaggi JSON-RPC. Un singolo console.log() può corrompere l'intero
protocollo. Usa sempreconsole.error() per il debugging:
// SBAGLIATO - corrompe il protocollo STDIO
console.log("Debug: nota aggiunta");
// CORRETTO - scrive su stderr, non interferisce
console.error("Debug: nota aggiunta");
console.error("[DEBUG]", JSON.stringify({ title, content }));
Regola 2: Verifica il Build
Un errore comune e dimenticare di ricompilare dopo le modifiche. Usa npm run dev
per la compilazione automatica in modalità watch:
# Compilazione automatica ad ogni modifica
npm run dev
# In un altro terminale, testa con l'inspector
npx @modelcontextprotocol/inspector dist/index.js
Regola 3: Controlla i Log del Server
I messaggi scritti su stderr sono visibili nel terminale di Claude Desktop
(Sviluppatore > Apri Console) e nell'MCP Inspector. Usa log strutturati per facilitare il debug:
Error handling: pattern isError: true per errori gestiti e try/catch per eccezioni impreviste
server.resource(): esposizione di dati contestuali tramite URI e template URI
StdioServerTransport: configurazione del transport per comunicazione via stdin/stdout
Testing: verifica con MCP Inspector e configurazione di Claude Desktop
Debugging: regola fondamentale di non scrivere mai su stdout, uso di console.error() per i log
Prossimo Articolo
Nel prossimo articolo della serie affronteremo l'altro lato del protocollo: creare un Client MCP
in TypeScript. Vedremo come connettersi a un server, negoziare le capability, elencare e invocare
tool programmaticamente e gestire il transport HTTP per connessioni remote. Impareremo a costruire
un client che può comunicare con qualsiasi server MCP compatibile con il protocollo standard.
Il codice completo di tutti gli esempi e disponibile nel repository
Tech-MCP su GitHub.