Aller au contenu

Connecter un serveur MCP

Les serveurs MCP sont la source de donnees de vos applications webmcp-auto-ui. Ce tutoriel vous montre comment connecter un serveur MCP externe, creer les couches d’outils, et les integrer dans la boucle agent pour que le LLM puisse interroger des donnees distantes.

Connecter un serveur MCP distant, recuperer ses outils, et les utiliser dans la boucle agent pour que le LLM puisse interroger des donnees en temps reel.

Une application qui se connecte a un ou plusieurs serveurs MCP, avec gestion des erreurs, lazy loading des outils, et integration complete dans la boucle agent.

graph LR
App[Application] -->|HTTP| Proxy[Proxy MCP]
Proxy -->|stdio| Server[Serveur MCP]
Server -->|donnees| Proxy
Proxy -->|JSON-RPC| App
App -->|tools| Agent[Agent Loop]
Agent -->|queries| App

MCP (Model Context Protocol) est un protocole client-serveur standardise qui permet a un LLM d’acceder a des outils distants. Chaque serveur MCP expose :

  • Des outils (tools) : actions atomiques que le LLM peut appeler (ex: query_sql, search, fetch_document)
  • Des recettes (recipes) : guides de composition qui expliquent comment combiner les outils (optionnel)

Le transport utilise HTTP Streamable avec des messages JSON-RPC 2.0.


Le package @webmcp-auto-ui/core fournit McpClient pour se connecter a un serveur MCP :

import { McpClient } from '@webmcp-auto-ui/core';
const mcpClient = new McpClient('http://localhost:3000/mcp', {
clientName: 'WebMCP Auto-UI',
clientVersion: '1.0.0',
timeout: 30000,
});
// Initialiser la connexion (handshake MCP)
await mcpClient.initialize();
// Recuperer la liste des outils disponibles
const tools = await mcpClient.listTools();
console.log('Outils disponibles :', tools);

L’initialisation effectue :

  1. Une requete initialize avec les capabilities du client
  2. Le serveur repond avec ses propres capabilities et ses informations
  3. Une requete tools/list pour recuperer les outils disponibles
sequenceDiagram
participant Client as McpClient
participant Server as Serveur MCP
Client->>Server: POST /mcp {method: "initialize"}
Server-->>Client: {serverInfo, capabilities}
Client->>Server: POST /mcp {method: "tools/list"}
Server-->>Client: {tools: [{name, description, inputSchema}]}
Note over Client: Pret !

Verification : la variable tools contient un tableau d’objets avec name, description et inputSchema.


Une ToolLayer encapsule un serveur MCP avec ses outils dans le format attendu par l’agent :

import type { ToolLayer } from '@webmcp-auto-ui/agent';
import { McpClient } from '@webmcp-auto-ui/core';
async function createMcpLayer(url: string, name: string): Promise<ToolLayer> {
const client = new McpClient(url);
await client.initialize();
const tools = await client.listTools();
return {
protocol: 'mcp',
serverName: name,
description: `Outils du serveur ${name}`,
serverUrl: url,
tools: tools.map(t => ({
name: t.name,
description: t.description ?? '',
inputSchema: t.inputSchema,
})),
};
}
// Creer la couche
const wikiLayer = await createMcpLayer(
'https://demos.hyperskills.net/mcp-wikipedia/mcp',
'wikipedia'
);

Les champs de la ToolLayer :

ChampRole
protocolToujours 'mcp' pour un serveur MCP distant
serverNameNom unique, utilise comme prefixe dans les outils
descriptionDescription pour le system prompt
serverUrlURL du serveur (pour le routage des appels)
toolsListe des outils avec nom, description et schema

Passez le client MCP et les couches d’outils a runAgentLoop() :

import { runAgentLoop, RemoteLLMProvider } from '@webmcp-auto-ui/agent';
const provider = new RemoteLLMProvider({
proxyUrl: '/api/chat',
model: 'sonnet',
});
const result = await runAgentLoop('Cherche des informations sur Svelte', {
client: mcpClient,
provider,
layers: [wikiLayer],
maxIterations: 5,
callbacks: {
onToolCall: (call) => {
console.log(`Outil appele : ${call.name}`);
if (call.result) console.log('Resultat :', call.result);
if (call.error) console.log('Erreur :', call.error);
},
onText: (text) => {
console.log('Agent dit :', text);
},
},
});

Le client est passe pour que la boucle agent puisse executer les appels d’outils sur le serveur MCP. Les layers servent a construire le system prompt et le jeu d’outils initial.

Verification : la console affiche les appels d’outils et les resultats retournes par le serveur MCP.


McpMultiClient gere automatiquement plusieurs connexions et le routage des appels :

import { McpMultiClient } from '@webmcp-auto-ui/core';
import { fromMcpTools } from '@webmcp-auto-ui/agent';
import type { McpLayer, ToolLayer } from '@webmcp-auto-ui/agent';
const multi = new McpMultiClient();
// Connecter plusieurs serveurs
await multi.addServer('https://demos.hyperskills.net/mcp-wikipedia/mcp');
await multi.addServer('https://demos.hyperskills.net/mcp-metmuseum/mcp');
await multi.addServer('https://demos.hyperskills.net/mcp-openmeteo/mcp');
// Construire les layers automatiquement
const layers: ToolLayer[] = multi.listServers().map(server => ({
protocol: 'mcp' as const,
serverName: server.name,
tools: fromMcpTools(server.tools),
}));
// Lancer l'agent avec tous les serveurs
const result = await runAgentLoop('Quel temps fait-il a Paris ?', {
client: multi,
provider,
layers,
maxIterations: 5,
});

Pour un controle plus fin, creez chaque client separement :

const dataClient = new McpClient('https://api.data.example.com/mcp');
await dataClient.initialize();
const dataLayer: ToolLayer = {
protocol: 'mcp',
serverName: 'data-api',
tools: (await dataClient.listTools()).map(t => ({
name: t.name,
description: t.description ?? '',
inputSchema: t.inputSchema,
})),
};
const searchClient = new McpClient('https://search.example.com/mcp');
await searchClient.initialize();
const searchLayer: ToolLayer = {
protocol: 'mcp',
serverName: 'search-engine',
tools: (await searchClient.listTools()).map(t => ({
name: t.name,
description: t.description ?? '',
inputSchema: t.inputSchema,
})),
};
// Combiner les layers
const result = await runAgentLoop('Analyse les tendances', {
client: dataClient,
provider,
layers: [dataLayer, searchLayer],
maxIterations: 10,
});
graph TD
Agent[Agent Loop] -->|prefixe data-api_mcp_*| Data[Serveur Data API]
Agent -->|prefixe search-engine_mcp_*| Search[Serveur Search]
Agent -->|prefixe autoui_webmcp_*| WebMCP[WebMCP local]

Enveloppez la connexion dans un try/catch pour gerer les cas d’erreur :

async function safeConnectMcp(url: string) {
try {
const client = new McpClient(url, {
timeout: 10000,
});
await client.initialize();
console.log('Connexion etablie');
return client;
} catch (error) {
if (error instanceof Error) {
if (error.message.includes('timeout')) {
console.error('Serveur MCP inaccessible (timeout)');
} else if (error.message.includes('404')) {
console.error('Endpoint MCP non trouve');
} else {
console.error('Erreur de connexion :', error.message);
}
}
return null;
}
}
const client = await safeConnectMcp('https://api.example.com/mcp');
if (client) {
// Continuer avec le client
}

Pour les serveurs avec de nombreux outils, le lazy loading evite de surcharger le prompt du LLM. La boucle agent gere cela automatiquement :

import { buildDiscoveryTools, activateServerTools } from '@webmcp-auto-ui/agent';
// Au demarrage, seuls les outils de decouverte sont exposes
// (search_recipes, get_recipe)
// Quand le LLM appelle un outil d'un serveur, tous les
// outils de ce serveur sont actives automatiquement
const result = await runAgentLoop('...', {
client: mcpClient,
provider,
layers: [mcpLayer],
// Le lazy loading est actif par defaut
});

Le benefice : avec 4 serveurs et 50 outils au total, le mode discovery expose environ 20 outils au lieu de 50, soit une economie de 3000 a 5000 tokens dans le prompt initial.


Pour encapsuler la boucle agent dans une route API :

src/routes/api/agent/+server.ts
import { json } from '@sveltejs/kit';
import { runAgentLoop, RemoteLLMProvider } from '@webmcp-auto-ui/agent';
import { McpClient } from '@webmcp-auto-ui/core';
export async function POST({ request }) {
const { message, mcpUrl } = await request.json();
const client = new McpClient(mcpUrl);
await client.initialize();
const tools = await client.listTools();
const layer = {
protocol: 'mcp' as const,
serverName: new URL(mcpUrl).hostname,
description: 'MCP Server',
tools: tools.map(t => ({
name: t.name,
description: t.description ?? '',
inputSchema: t.inputSchema,
})),
};
const result = await runAgentLoop(message, {
client,
provider: new RemoteLLMProvider({
proxyUrl: '/api/chat',
model: 'sonnet',
}),
layers: [layer],
maxIterations: 5,
});
return json({
text: result.text,
toolCalls: result.toolCalls,
metrics: result.metrics,
});
}

Voici un composant complet qui combine connexion MCP, agent et affichage :

<script lang="ts">
import { canvas } from '@webmcp-auto-ui/sdk/canvas';
import { BlockRenderer, AgentProgress } from '@webmcp-auto-ui/ui';
let mcpUrl = $state('https://demos.hyperskills.net/mcp-wikipedia/mcp');
let userMessage = $state('');
let loading = $state(false);
async function runAgent() {
if (!userMessage.trim()) return;
loading = true;
canvas.setGenerating(true);
try {
const response = await fetch('/api/agent', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message: userMessage, mcpUrl }),
});
const result = await response.json();
canvas.addMsg('user', userMessage);
canvas.addMsg('assistant', result.text);
userMessage = '';
} catch (error) {
console.error('Erreur agent :', error);
} finally {
loading = false;
canvas.setGenerating(false);
}
}
</script>
<div class="flex flex-col gap-4 p-6">
<input bind:value={mcpUrl} placeholder="URL du serveur MCP" disabled={loading} />
<textarea bind:value={userMessage} placeholder="Votre question..." disabled={loading} />
<button onclick={runAgent} disabled={loading}>
{loading ? 'En cours...' : 'Envoyer'}
</button>
{#if loading}
<AgentProgress />
{/if}
<div class="grid gap-4">
{#each canvas.blocks as block (block.id)}
<BlockRenderer id={block.id} type={block.type} data={block.data} />
{/each}
</div>
</div>

Pour inspecter les appels MCP en detail :

const result = await runAgentLoop(message, {
client,
provider,
layers,
callbacks: {
onToolCall: (call) => {
console.log('=== APPEL OUTIL ===');
console.log('Nom :', call.name);
console.log('Args :', JSON.stringify(call.args, null, 2));
console.log('Resultat :', call.result);
console.log('Duree :', call.elapsed, 'ms');
},
},
});

Voici les serveurs MCP publics deja deployes et utilisables :

ServeurURL de productionOutils principaux
Wikipedia/mcp-wikipedia/mcpsearch, readArticle
Met Museum/mcp-metmuseum/mcpsearch-museum-objects, get-museum-object
Open Meteo/mcp-openmeteo/mcpweather_forecast, geocoding
HackerNews/mcp-hackernews/mcpget-front-page, search-posts
iNaturalist/mcp-inaturalist/mcpsearch_observations
NASA/mcp-nasa/mcpnasa_apod, nasa_mars_rover

Les URLs sont prefixees par https://demos.hyperskills.net.


ProblemeCause probableSolution
”Failed to initialize”Serveur MCP eteint ou URL incorrecteTestez avec curl -X POST <url>
”Timeout”Serveur lent au demarrageAugmentez le timeout a 30s
Outils videsLe serveur n’expose pas tools/listVerifiez la version du serveur MCP
”CORS error”Appel cross-origin bloqueUtilisez un proxy nginx (voir Deployer les proxies MCP)

  • Creer votre propre serveur MCP : n’importe quel serveur MCP compatible (Node, Python) fonctionne
  • Configurer les proxies : pour deployer des serveurs MCP en production, voir Deployer les proxies MCP
  • Combiner MCP et WebMCP : pour comprendre la symetrie entre donnees distantes et affichage local, voir Architecture MCP / WebMCP