MCP in JavaScript
MCP in JavaScript: Costruire Server e Client per l’Integrazione AI
Il Model Context Protocol (MCP) rappresenta una svolta nell’integrazione tra modelli di linguaggio e applicazioni esterne. Rilasciato da Anthropic nel novembre 2024, MCP è uno standard open-source che consente ai modelli AI di interagire con fonti di dati esterne attraverso un’interfaccia unificata. Il protocollo fornisce un modo standardizzato per connettere applicazioni LLM a fonti di dati esterne e strumenti, fungendo da ponte universale per l’ecosistema dell’AI.
Cosa è MCP e Perché è Importante – MCP in JavaScript
MCP è uno standard aperto che connette LLM come Claude alle tue fonti di dati, consentendo agli LLM di analizzare file locali (come log, PDF, CSV nel tuo file system). Il protocollo risolve un problema fondamentale: permettere ai modelli AI di accedere a dati specifici del dominio in modo sicuro e controllato, senza compromettere la sicurezza o la privacy.
Immaginate MCP come un “USB-C per l’AI” – un connettore universale che standardizza il modo in cui le applicazioni AI accedono a risorse esterne. Questo elimina la necessità di sviluppare integrazioni custom per ogni combinazione di LLM e fonte di dati.
MCP e i Linguaggi di Programmazione
MCP è progettato per essere language-agnostic, supportando implementazioni in diversi linguaggi:
Python: Il linguaggio più maturo per MCP, con SDK completi e una vasta community TypeScript/JavaScript: L’SDK TypeScript ufficiale implementa la specifica MCP completa, rendendo facile costruire client MCP che possono connettersi a qualsiasi server MCP e creare server MCP che espongono risorse, prompt e strumenti
Go: Implementazioni emergenti per applicazioni ad alte prestazioni
Rust: Per applicazioni system-level che richiedono massima efficienza
JavaScript e TypeScript: L’Ecosistema MCP
L’SDK TypeScript MCP fornisce tutti i building block necessari per creare un server MCP, gestendo i dettagli del protocollo, la gestione delle connessioni e i pattern di comunicazione. L’ecosistema JavaScript/TypeScript offre vantaggi unici:
- Familiarità: La maggior parte degli sviluppatori web conosce JavaScript
- Ecosistema ricco: NPM offre milioni di pacchetti riutilizzabili
- Deployment versatile: Da Node.js a Edge Functions, le opzioni sono infinite
- Tipo safety: TypeScript aggiunge robustezza senza sacrificare la produttività
Esempio 1: Server MCP con TypeScript
Iniziamo con un server MCP che espone un tool per l’analisi di dati meteorologici:
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
McpError,
ErrorCode,
} from "@modelcontextprotocol/sdk/types.js";
interface WeatherData {
temperature: number;
humidity: number;
pressure: number;
location: string;
timestamp: Date;
}
class WeatherMCPServer {
private server: Server;
private weatherData: Map<string, WeatherData> = new Map();
constructor() {
this.server = new Server(
{
name: "weather-analysis-server",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
);
this.setupToolHandlers();
this.initializeMockData();
}
private initializeMockData() {
// Simula dati meteorologici per diverse città
const cities = ["Milano", "Roma", "Napoli", "Torino"];
cities.forEach(city => {
this.weatherData.set(city, {
temperature: Math.round(Math.random() * 30 + 5),
humidity: Math.round(Math.random() * 80 + 20),
pressure: Math.round(Math.random() * 50 + 980),
location: city,
timestamp: new Date()
});
});
}
private setupToolHandlers() {
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: "get_weather",
description: "Recupera dati meteorologici per una città specifica",
inputSchema: {
type: "object",
properties: {
city: {
type: "string",
description: "Nome della città"
},
format: {
type: "string",
enum: ["json", "text"],
description: "Formato di output"
}
},
required: ["city"]
}
},
{
name: "analyze_weather_trend",
description: "Analizza tendenze meteorologiche tra più città",
inputSchema: {
type: "object",
properties: {
cities: {
type: "array",
items: { type: "string" },
description: "Lista di città da confrontare"
}
},
required: ["cities"]
}
}
]
}));
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
switch (name) {
case "get_weather":
return this.getWeatherData(args as { city: string; format?: string });
case "analyze_weather_trend":
return this.analyzeWeatherTrend(args as { cities: string[] });
default:
throw new McpError(
ErrorCode.MethodNotFound,
`Tool ${name} non trovato`
);
}
} catch (error) {
throw new McpError(
ErrorCode.InternalError,
`Errore nell'esecuzione del tool: ${error}`
);
}
});
}
private async getWeatherData(args: { city: string; format?: string }) {
const { city, format = "json" } = args;
const data = this.weatherData.get(city);
if (!data) {
throw new McpError(
ErrorCode.InvalidParams,
`Dati non disponibili per ${city}`
);
}
if (format === "text") {
return {
content: [{
type: "text",
text: `Meteo per ${data.location}:\n` +
`Temperatura: ${data.temperature}°C\n` +
`Umidità: ${data.humidity}%\n` +
`Pressione: ${data.pressure} hPa\n` +
`Ultimo aggiornamento: ${data.timestamp.toLocaleString()}`
}]
};
}
return {
content: [{
type: "text",
text: JSON.stringify(data, null, 2)
}]
};
}
private async analyzeWeatherTrend(args: { cities: string[] }) {
const { cities } = args;
const validData: WeatherData[] = [];
cities.forEach(city => {
const data = this.weatherData.get(city);
if (data) validData.push(data);
});
if (validData.length === 0) {
throw new McpError(
ErrorCode.InvalidParams,
"Nessuna città valida fornita"
);
}
const avgTemp = validData.reduce((sum, d) => sum + d.temperature, 0) / validData.length;
const avgHumidity = validData.reduce((sum, d) => sum + d.humidity, 0) / validData.length;
const maxTemp = Math.max(...validData.map(d => d.temperature));
const minTemp = Math.min(...validData.map(d => d.temperature));
const analysis = {
cities_analyzed: validData.map(d => d.location),
statistics: {
average_temperature: Math.round(avgTemp * 10) / 10,
average_humidity: Math.round(avgHumidity * 10) / 10,
temperature_range: { min: minTemp, max: maxTemp },
temperature_variance: maxTemp - minTemp
},
recommendations: this.generateRecommendations(avgTemp, avgHumidity)
};
return {
content: [{
type: "text",
text: JSON.stringify(analysis, null, 2)
}]
};
}
private generateRecommendations(avgTemp: number, avgHumidity: number): string[] {
const recommendations: string[] = [];
if (avgTemp > 25) {
recommendations.push("Temperature elevate: considerare sistemi di raffreddamento");
}
if (avgTemp < 10) {
recommendations.push("Temperature basse: attenzione al riscaldamento");
}
if (avgHumidity > 70) {
recommendations.push("Umidità alta: rischio di muffe, considerare deumidificazione");
}
if (avgHumidity < 30) {
recommendations.push("Umidità bassa: considerare umidificazione");
}
return recommendations;
}
async start() {
const transport = new StdioServerTransport();
await this.server.connect(transport);
}
}
// Avvio del server
const server = new WeatherMCPServer();
server.start().catch(console.error);
Esempio 2: Client MCP in JavaScript
Ora creiamo un client che interagisce con il nostro server meteorologico:
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
import { spawn } from "child_process";
class WeatherMCPClient {
constructor() {
this.client = new Client(
{
name: "weather-client",
version: "1.0.0",
},
{
capabilities: {},
}
);
}
async connect() {
// Spawna il processo del server MCP
const serverProcess = spawn("node", ["weather-server.js"], {
stdio: ["pipe", "pipe", "inherit"],
});
const transport = new StdioClientTransport({
readable: serverProcess.stdout,
writable: serverProcess.stdin,
});
await this.client.connect(transport);
console.log("Connesso al server MCP meteorologico");
}
async listAvailableTools() {
try {
const tools = await this.client.listTools();
console.log("Tool disponibili:");
tools.tools.forEach(tool => {
console.log(`- ${tool.name}: ${tool.description}`);
});
return tools.tools;
} catch (error) {
console.error("Errore nel recupero dei tool:", error);
throw error;
}
}
async getWeatherForCity(city, format = "json") {
try {
const result = await this.client.callTool({
name: "get_weather",
arguments: { city, format }
});
console.log(`\n=== Meteo per ${city} ===`);
result.content.forEach(content => {
if (content.type === "text") {
console.log(content.text);
}
});
return result;
} catch (error) {
console.error(`Errore nel recupero meteo per ${city}:`, error);
throw error;
}
}
async analyzeMultipleCities(cities) {
try {
const result = await this.client.callTool({
name: "analyze_weather_trend",
arguments: { cities }
});
console.log(`\n=== Analisi trend per ${cities.join(", ")} ===`);
result.content.forEach(content => {
if (content.type === "text") {
const analysis = JSON.parse(content.text);
console.log("Statistiche:");
console.log(`- Temperatura media: ${analysis.statistics.average_temperature}°C`);
console.log(`- Umidità media: ${analysis.statistics.average_humidity}%`);
console.log(`- Range temperature: ${analysis.statistics.temperature_range.min}°C - ${analysis.statistics.temperature_range.max}°C`);
if (analysis.recommendations.length > 0) {
console.log("\nRaccomandazioni:");
analysis.recommendations.forEach(rec => console.log(`- ${rec}`));
}
}
});
return result;
} catch (error) {
console.error("Errore nell'analisi delle città:", error);
throw error;
}
}
async runDemo() {
try {
await this.connect();
await this.listAvailableTools();
// Test singola città
await this.getWeatherForCity("Milano", "text");
await this.getWeatherForCity("Roma");
// Test analisi multiple città
await this.analyzeMultipleCities(["Milano", "Roma", "Napoli"]);
} catch (error) {
console.error("Errore nella demo:", error);
}
}
}
// Esecuzione della demo
const client = new WeatherMCPClient();
client.runDemo();
Esempio 3: Integrazione Avanzata con Express.js
Per scenario più complessi, possiamo integrare MCP in un’applicazione web:
import express from 'express';
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
import cors from 'cors';
interface WeatherApiResponse {
success: boolean;
data?: any;
error?: string;
timestamp: string;
}
class WeatherAPIGateway {
private app: express.Application;
private mcpClient: Client;
private isConnected: boolean = false;
constructor() {
this.app = express();
this.mcpClient = new Client(
{ name: "weather-api-gateway", version: "1.0.0" },
{ capabilities: {} }
);
this.setupMiddleware();
this.setupRoutes();
}
private setupMiddleware() {
this.app.use(cors());
this.app.use(express.json());
this.app.use(express.urlencoded({ extended: true }));
// Middleware per logging
this.app.use((req, res, next) => {
console.log(`${new Date().toISOString()} - ${req.method} ${req.path}`);
next();
});
}
private setupRoutes() {
// Health check
this.app.get('/health', (req, res) => {
res.json({
status: 'healthy',
mcp_connected: this.isConnected,
timestamp: new Date().toISOString()
});
});
// Endpoint per recuperare meteo singola città
this.app.get('/weather/:city', async (req, res) => {
try {
if (!this.isConnected) {
throw new Error('MCP client non connesso');
}
const { city } = req.params;
const { format = 'json' } = req.query;
const result = await this.mcpClient.callTool({
name: 'get_weather',
arguments: { city, format }
});
const response: WeatherApiResponse = {
success: true,
data: this.parseToolResult(result),
timestamp: new Date().toISOString()
};
res.json(response);
} catch (error) {
const response: WeatherApiResponse = {
success: false,
error: error instanceof Error ? error.message : 'Errore sconosciuto',
timestamp: new Date().toISOString()
};
res.status(500).json(response);
}
});
// Endpoint per analisi multiple città
this.app.post('/weather/analyze', async (req, res) => {
try {
if (!this.isConnected) {
throw new Error('MCP client non connesso');
}
const { cities } = req.body;
if (!Array.isArray(cities) || cities.length === 0) {
return res.status(400).json({
success: false,
error: 'Fornire un array di città non vuoto',
timestamp: new Date().toISOString()
});
}
const result = await this.mcpClient.callTool({
name: 'analyze_weather_trend',
arguments: { cities }
});
const response: WeatherApiResponse = {
success: true,
data: this.parseToolResult(result),
timestamp: new Date().toISOString()
};
res.json(response);
} catch (error) {
const response: WeatherApiResponse = {
success: false,
error: error instanceof Error ? error.message : 'Errore sconosciuto',
timestamp: new Date().toISOString()
};
res.status(500).json(response);
}
});
// Endpoint per ottenere tool disponibili
this.app.get('/tools', async (req, res) => {
try {
if (!this.isConnected) {
throw new Error('MCP client non connesso');
}
const tools = await this.mcpClient.listTools();
res.json({
success: true,
data: tools.tools,
timestamp: new Date().toISOString()
});
} catch (error) {
res.status(500).json({
success: false,
error: error instanceof Error ? error.message : 'Errore sconosciuto',
timestamp: new Date().toISOString()
});
}
});
// Endpoint per batch processing
this.app.post('/weather/batch', async (req, res) => {
try {
if (!this.isConnected) {
throw new Error('MCP client non connesso');
}
const { requests } = req.body;
if (!Array.isArray(requests)) {
return res.status(400).json({
success: false,
error: 'Fornire un array di richieste',
timestamp: new Date().toISOString()
});
}
const results = await Promise.allSettled(
requests.map(async (request: any) => {
if (request.type === 'single' && request.city) {
return await this.mcpClient.callTool({
name: 'get_weather',
arguments: { city: request.city, format: request.format || 'json' }
});
} else if (request.type === 'analyze' && request.cities) {
return await this.mcpClient.callTool({
name: 'analyze_weather_trend',
arguments: { cities: request.cities }
});
}
throw new Error('Tipo di richiesta non valido');
})
);
const processedResults = results.map((result, index) => ({
index,
success: result.status === 'fulfilled',
data: result.status === 'fulfilled' ? this.parseToolResult(result.value) : null,
error: result.status === 'rejected' ? result.reason.message : null
}));
res.json({
success: true,
data: processedResults,
timestamp: new Date().toISOString()
});
} catch (error) {
res.status(500).json({
success: false,
error: error instanceof Error ? error.message : 'Errore sconosciuto',
timestamp: new Date().toISOString()
});
}
});
}
private parseToolResult(result: any): any {
if (result.content && result.content[0] && result.content[0].type === 'text') {
try {
return JSON.parse(result.content[0].text);
} catch {
return result.content[0].text;
}
}
return result;
}
async connectToMCP(serverUrl: string) {
try {
// Per questo esempio, assumiamo un server SSE
// In produzione, potresti usare altri transport
const transport = new SSEClientTransport(new URL(serverUrl));
await this.mcpClient.connect(transport);
this.isConnected = true;
console.log('Connesso al server MCP via SSE');
} catch (error) {
console.error('Errore nella connessione MCP:', error);
throw error;
}
}
start(port: number = 3000) {
this.app.listen(port, () => {
console.log(`Weather API Gateway avviato sulla porta ${port}`);
console.log(`Health check: http://localhost:${port}/health`);
});
}
}
// Configurazione e avvio
const gateway = new WeatherAPIGateway();
// In un ambiente reale, configureresti l'URL del server MCP
// gateway.connectToMCP('http://localhost:3001/mcp').catch(console.error);
gateway.start(3000);
export default WeatherAPIGateway;
Passaggi per Sviluppare con MCP – MCP in JavaScript
Setup del Progetto
- Inizializzazione:
npm init -y
npm install @modelcontextprotocol/sdk
npm install -D typescript @types/node ts-node
- Configurazione TypeScript:
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true
}
}
Sviluppo Server MCP
- Definire le capabilities: Stabilire se il server esporrà tools, resources, o prompts
- Implementare i handler: Creare handler per
ListTools,CallTool, etc. - Gestione errori: Implementare error handling robusto con
McpError - Testing: Testare con client di esempio
Sviluppo Client MCP
- Scegliere il transport: stdio, SSE, o HTTP
- Gestire la connessione: Implementare riconnessione automatica
- Cache intelligente: Implementare caching per migliorare le performance
- Monitoraggio: Aggiungere logging e metriche
API Principali e Best Practices – MCP in JavaScript
Server API
server.setRequestHandler(): Per gestire richieste specificheListToolsRequestSchema: Schema per listing dei toolCallToolRequestSchema: Schema per chiamate ai toolMcpError: Gestione errori standardizzata
Client API
client.connect(): Connessione al serverclient.listTools(): Elenco tool disponibiliclient.callTool(): Invocazione tool- Transport layers: stdio, SSE, HTTP
Best Practices – MCP in JavaScript
- Validazione input: Sempre validare parametri in input
- Error handling: Usare
McpErrorper errori specifici del protocollo - Performance: Implementare caching e connection pooling
- Security: Validare permessi e sanitizzare input
- Documentation: Documentare schema degli input accuratamente
Conclusione – MCP in JavaScript
MCP rappresenta un protocollo che consente l’integrazione tra modelli di linguaggio e strumenti, spesso come parte di un agente. L’ecosistema JavaScript/TypeScript offre un’implementazione matura e ben documentata, ideale per sviluppatori che vogliono integrare rapidamente le proprie applicazioni con l’AI.
Gli esempi mostrati dimostrano la versatilità di MCP: da semplici server di dati a gateway API complessi. La standardizzazione offerta da MCP elimina la complessità dell’integrazione diretta con diversi LLM, permettendo agli sviluppatori di concentrarsi sulla logica business.
Con il continuo sviluppo dell’ecosistema AI, MCP si posiziona come protocollo fondamentale per il futuro dell’integrazione AI-application, e l’implementazione JavaScript/TypeScript ne facilita l’adozione in contesti web e enterprise.
Innovaformazione, scuola informatica specialistica segue costantemente il mercato IT ed affianca le aziende nella formazione dei team di sviluppatori. Nell’offerta formativa trovate il Corso Agenti AI ed MCP, mentre l’elenco corsi completo è presente sul nostro sito al seguente LINK.
INFO: info@innovaformazione.net – Tel. 3471012275 (Dario Carrassi)
Articoli correlati
Claude Code per i droni
Claude Code e Migrazioni SAP
Claude Code controllo remoto
Opportunità Carriera Contabilità SAP
Guida SIA AI
