J’ai envoyé “prépare-moi pour mon rendez-vous de 14h” sur WhatsApp. Trente secondes plus tard, mon téléphone vibrait avec un briefing structuré : qui j’allais rencontrer, ce qu’on s’était dit par email, ce que mon équipe en disait sur Slack, et trois points à aborder. Aucun onglet à ouvrir. Pas d’ordinateur. Juste un message pendant mon trajet.
C’est la promesse d’un assistant IA toujours disponible. Et jusqu’à peu, le construire vraiment fonctionnel était presque impossible.
Des frameworks open source comme OpenClaw ont popularisé les agents de messagerie bidirectionnels sans interface. Claude Code Channels a confirmé que l’approche tenait la route. Channels est en préversion de recherche, mais la direction est claire. Anthropic utilise déjà ce schéma pour les transitions entre leur app desktop, leur app mobile et Claude Code. Une mise en disponibilité générale est à prévoir.
Mais passer d’une démo de week-end à un assistant fiable révèle des lacunes qu’aucune optimisation de prompt ne résout. Autorisation. Fiabilité des outils. Gestion des sessions. L’agent doit accéder à votre calendrier, vos emails et Slack, et il faut s’assurer que ce n’est pas une faille de sécurité.
J’ai construit une version fonctionnelle. Ce guide couvre l’ensemble : un serveur relais WhatsApp, un serveur MCP, Claude Code comme cerveau, et Arcade.dev pour un accès sécurisé aux outils. Du code fonctionnel à chaque étape.
On commence par les pièges à connaître, puis on construit.
TL;DR
- Les frameworks headless style OpenClaw donnent à l’agent un accès total à chaque service connecté, s’appuient sur des wrappers d’outils fragiles, gonflent la fenêtre de contexte avec des réponses API brutes, et ne produisent aucune trace d’audit. Acheter un Mac Mini dédié pour les faire tourner ne change rien. La machine n’est pas le vrai risque : ce sont les identifiants.
- Ce guide crée un assistant IA WhatsApp avec un serveur relais qui gère les webhooks Meta, un serveur MCP qui fait le pont avec Claude Code, Arcade pour l’accès sécurisé aux outils et la journalisation d’audit, ainsi qu’une compétence de préparation de réunion qui puise dans Google Calendar, Gmail et Slack pour envoyer des briefings structurés directement sur WhatsApp.
- Chaque couche inclut du code fonctionnel exécutable en local : ingestion de webhooks avec validation de signature HMAC, une file de messages à curseur, des définitions d’outils MCP, la configuration de Claude Code, et un fichier de compétences complet qui encode un workflow de préparation de réunion en trois phases.
De la démo à la production : les quatre pièges des agents IA toujours actifs
Le dispositif headless popularisé par OpenClaw est le point de départ. Dès qu’on cherche à passer d’un proof of concept de week-end à quelque chose qu’on confierait vraiment à son calendrier et ses emails, quatre problèmes architecturaux apparaissent.
Piège 1 : les identifiants en mode dieu et le risque de sécurité des agents
Les frameworks d’agents headless héritent du profil d’accès complet de la machine hôte. L’agent obtient les mêmes permissions que le développeur qui l’a lancé. Chaque token OAuth, chaque clé API, chaque service connecté : tout est grand ouvert.
Une seule injection de prompt ou une dépendance compromise se propage à tout. Votre Google Drive, votre CRM, vos dépôts de code source. Une mauvaise entrée suffit pour transformer l’agent en menace interne.
Ce n’est pas théorique. CVE-2026-25253 a exposé une RCE en un clic dans OpenClaw. La passerelle n’avait aucune validation d’origine. Un attaquant pouvait exfiltrer le token d’authentification via un lien malveillant et compromettre totalement le système.
Nous avons analysé ce schéma en détail dans OpenClaw doesn’t need your tokens.
Piège 2 : les wrappers API fragiles et le problème de fiabilité des outils
La plupart des outils d’agents sont de simples wrappers autour de REST APIs. Ils obligent le modèle à deviner des paramètres de payload complexes et à réessayer quand le langage naturel ne correspond pas à des schémas rigides.
Ensuite apparaissent les registres fantômes. Différentes équipes construisent des wrappers en double, non versionnés, pour les mêmes APIs. Un changement d’API non annoncé casse plusieurs agents de façon imprévisible. Les registres d’outils publics sont déjà devenus un vecteur d’attaque sur la chaîne d’approvisionnement, avec des outils malveillants qui exfiltrent l’état local ou créent des backdoors.
Pour les patterns qui rendent les outils MCP plus résilients, voir 54 Patterns for Building Better MCP Tools.
Piège 3 : la surcharge de la fenêtre de contexte par les réponses API brutes
Les outils non optimisés déversent l’intégralité de la réponse API dans la fenêtre de contexte. Un historique de ticket Jira ? Des dizaines de milliers de tokens de métadonnées inutiles. Le raisonnement de l’agent dévie. Les coûts s’envolent à chaque tour de conversation.
Piège 4 : aucune trace d’audit, aucune fiabilité, aucune conformité
Maintenir un agent auto-hébergé en vie avec tmux ou systemd crée un trou noir d’audit. Quand le processus plante ou se comporte mal, il n’y a aucun log structuré pour retracer ce qui s’est passé. Quelle action a été effectuée ? Avec quels paramètres ? Quel utilisateur a déclenché la requête ?
Impossible de répondre à « qu’a fait l’agent ? » si vous n’avez jamais rien enregistré.
C’est un échec immédiat pour SOC2, ISO27001 et toute revue de conformité sérieuse.
Pourquoi acheter un Mac Mini ne règle rien
La tendance monte : des développeurs achètent des Mac Minis dédiés ou lancent des VMs pour faire tourner des agents de type OpenClaw en 24/7. L’idée : en donnant sa propre machine à l’agent, on l’isole.
Pas vraiment. La machine n’est pas le vrai risque. Ce sont les credentials.
Ce Mac Mini a quand même besoin de tokens OAuth pour Google Calendar, de clés API pour votre CRM, d’un accès à votre workspace Slack. Une dépendance compromise se moque que ça tourne sur votre laptop ou un serveur dans un placard. Le rayon d’impact est identique. Pour une comparaison plus poussée des stratégies d’isolation qui réduisent vraiment ce rayon, voir IA Agent Sandboxing Guide.
L’isolation matérielle règle la disponibilité. Elle ne touche pas à l’autorisation, la fiabilité des outils, la gestion du contexte ou les logs d’audit.
Vous avez construit une machine coûteuse, toujours allumée, avec un accès illimité à vos systèmes métier. Tous les pièges évoqués ci-dessus s’appliquent toujours.
Comment Arcade, Claude Code et les Skills résolvent ces problèmes
Il me fallait trois choses : un moyen sécurisé de connecter les outils métier, un runtime d’agent éprouvé, et une façon d’encoder des workflows sans écrire de code d’intégration.
Arcade s’occupe de la couche outils et authentification. Il s’intercale entre l’agent et vos outils métier. Quand l’agent veut lire votre calendrier, Arcade évalue les permissions, génère un token juste-à-temps limité à cette action précise, et exécute l’appel. Le LLM ne voit jamais de credentials longue durée. Votre token Google Calendar ne traîne pas dans un fichier .env sur un Mac Mini. Il est géré par le runtime d’Arcade avec une autorisation par action.
Arcade règle aussi le problème des outils fragiles. Au lieu d’écrire des wrappers REST bricolés, vous utilisez des intégrations préconstruites et optimisées pour les agents qui renvoient des données synthétisées, pas des dumps JSON bruts. Quand Google modifie son API Calendar, Arcade s’en charge. Votre code agent reste intact. Et chaque appel d’outil génère des logs d’audit structurés, liés à l’utilisateur et à l’action.
Claude Code est le runtime agent. Il est plus éprouvé qu’OpenClaw, dispose d’un support MCP natif, et orchestre les outils sans la gestion de processus fragile des tmux et systemd scripts.
Les Skills encodent les vrais workflows. C’est la pièce que la plupart des gens oublient. Arcade donne à l’agent un accès à vos outils avec la bonne authentification. Les Skills lui indiquent comment bien les utiliser. Pour aller plus loin sur cette distinction, voir Skills vs Tools for IA Agents.
Un skill est un fichier markdown qui encode une expertise métier : quels outils appeler, dans quel ordre, quoi chercher dans les résultats, comment formater la sortie. Sans skill, vous avez un agent avec accès au calendrier mais aucune idée de comment préparer un compte-rendu de réunion. Avec un skill, vous avez un assistant qui récupère les événements, croise les fils d’e-mails, vérifie Slack pour le contexte interne, et produit un briefing structuré, le tout depuis un simple message WhatsApp.
Arcade donne l’accès. Les Skills donnent l’expertise. Ensemble, ils transforment un LLM en assistant vraiment utile.
Et comme les skills ne sont que des fichiers markdown, n’importe qui dans l’équipe peut les écrire et les faire évoluer. Pas de déploiement de code. Pas de tickets engineering.
Voici ce que nous construisons : un relais WhatsApp pour les messages, Claude Code comme cerveau, Arcade pour l’accès aux outils avec gestion des authentifications, et des skills qui encodent les workflows de votre équipe.
Pas à pas : construire l’assistant IA WhatsApp avec MCP et Arcade
Assez d’architecture. Voici ce que nous faisons : les messages WhatsApp passent par un serveur relais vers un serveur MCP, qui les transmet à Claude Code. Claude Code traite les messages via des skills, appelle les outils métier via Arcade, et répond en remontant la même chaîne.
Un détail à ne pas négliger : l’API Cloud de WhatsApp ne supporte que les webhooks. Pas de WebSocket ni de long-polling. Il faut donc exposer une URL publique pour recevoir les callbacks de Meta. Comme tout tourne en local, le serveur relais joue ce rôle, et ngrok achemine le trafic depuis les serveurs de Meta jusqu’à votre machine.

Prérequis : WhatsApp Business API, Claude Code et Arcade
Avant de commencer, assurez-vous d’avoir un compte développeur Meta avec une application WhatsApp Business configurée (guide de démarrage Meta), Node.js 20+ et npm, ngrok pour exposer vos webhooks en local, Claude Code installé et configuré, et un compte Arcade avec accès API, et un numéro de téléphone enregistré avec l’API WhatsApp Business.
Étape 1 : Structure du projet et configuration de l’environnement
Voici l’arborescence des dossiers :
whatsapp-assistant/
├── whatsapp.ts # MCP server (bridge between relay and Claude Code)
├── package.json # MCP server dependencies
├── .mcp.json # Claude Code MCP server registration
├── whatsapp-relay/
│ ├── relay.ts # Relay server (faces the internet via ngrok)
│ ├── package.json # Relay server dependencies
│ └── .env # WhatsApp API credentials (from .env.example)
└── skills/
└── meeting-prep/
└── SKILL.md # Meeting preparation skill for Claude Code
Commencez par initialiser les deux projets :
# Create the project
mkdir whatsapp-assistant && cd whatsapp-assistant
# Initialize the MCP server
npm init -y
npm install @modelcontextprotocol/sdk
npm install -D typescript @types/node tsx
# Initialize the relay server
mkdir whatsapp-relay && cd whatsapp-relay
npm init -y
npm install hono @hono/node-server
npm install -D typescript @types/node tsx
cd ..
Créez votre fichier .env dans whatsapp-relay/ avec les variables suivantes :
# Meta WhatsApp Cloud API
WHATSAPP_ACCESS_TOKEN= # Bearer token from Meta App Dashboard
WHATSAPP_PHONE_NUMBER_ID= # Bot's phone number ID
WHATSAPP_VERIFY_TOKEN= # Any string, used for webhook verification handshake
WHATSAPP_APP_SECRET= # App secret for validating webhook signatures
# Relay auth
RELAY_SECRET= # Shared secret, local MCP server sends this in X-Relay-Secret header
La clé RELAY_SECRET est partagée entre le relais et le serveur MCP. Générez une valeur aléatoire (openssl rand -hex 32). Elle empêche tout élément de votre réseau d’usurper l’identité du serveur MCP.
Étape 2 : Construire le serveur relais webhook WhatsApp
Le relais est le seul composant exposé sur internet. Il a trois rôles : valider les webhooks WhatsApp entrants, mettre les messages en file d’attente pour le serveur MCP, et proxifier les messages sortants vers l’API Meta.
Validation de la signature webhook
Chaque payload webhook envoyé par Meta inclut une signature HMAC-SHA256. Le relais la vérifie avant tout traitement :
import { createHmac, timingSafeEqual } from 'node:crypto';
const APP_SECRET = process.env.WHATSAPP_APP_SECRET!;
function verifySignature(rawBody: string, header: string | undefined): boolean {
if (!header) return false;
const sig = header.replace('sha256=', '');
if (!sig) return false;
const expected = createHmac('sha256', APP_SECRET).update(rawBody).digest('hex');
if (sig.length !== expected.length) return false;
return timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
}
On utilise timingSafeEqual pour prévenir les attaques temporelles, un détail qui compte quand vous validez des signatures provenant d’un tiers.
Handler webhook : toujours renvoyer 200
Meta utilise une livraison « au moins une fois ». Si votre endpoint renvoie autre chose que 200, Meta réessaie, ce qui peut générer une avalanche d’événements dupliqués. Le relais accuse réception immédiatement et traite de façon asynchrone :
app.post('/webhook', async c => {
const rawBody = await c.req.text();
if (!verifySignature(rawBody, c.req.header('x-hub-signature-256'))) {
// Still return 200. Returning 4xx causes Meta to retry with the same bad signature.
console.error('webhook: invalid signature');
return c.text('ok', 200);
}
try {
const payload = JSON.parse(rawBody) as WaWebhookPayload;
parseMessages(payload);
} catch (err) {
console.error('webhook: parse error:', err);
}
return c.text('ok', 200);
});
Notez le schéma : même en cas de signature invalide, on renvoie 200. Journaliser le rejet suffit. Renvoyer 4xx ne fait qu’inciter Meta à renvoyer le même payload invalide.
File de messages en mémoire avec polling
Le relais met en file d’attente les messages validés et expose un endpoint de polling pour le serveur MCP. Ce dernier passe un curseur (l’ID du dernier message reçu) pour ne récupérer que les nouveaux messages :
let queue: InboundMessage[] = [];
let nextId = 1;
const MAX_QUEUE = 1000;
function enqueue(msg: Omit<InboundMessage, 'id' | 'timestamp'>): void {
queue.push({ ...msg, id: nextId++, timestamp: Date.now() });
if (queue.length > MAX_QUEUE) {
queue = queue.slice(queue.length - MAX_QUEUE);
}
}
// Polling endpoint, protected by relay secret
app.get('/poll', c => {
const since = Number(c.req.query('since') ?? '0') || 0;
const messages = queue.filter(m => m.id > since);
const cursor = messages.length > 0 ? messages[messages.length - 1].id : since;
return c.json({ messages, cursor });
});
Le relais authentifie toutes les routes locales avec le secret partagé via l’en-tête x-relay-secret. Les routes webhook côté WhatsApp n’utilisent pas ce mécanisme : elles sont validées par la signature HMAC de Meta.
Proxy de messages sortants
Quand Claude Code veut répondre, il passe par le serveur MCP, qui appelle le relais, qui appelle l’API Meta :
const WA_API = `https://graph.facebook.com/v21.0/${PHONE_NUMBER_ID}`;
async function waApi(
path: string,
body: Record<string, unknown>,
): Promise<{ ok: boolean; status: number; data: unknown }> {
const res = await fetch(`${WA_API}${path}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${ACCESS_TOKEN}`,
},
body: JSON.stringify(body),
});
const data = await res.json();
return { ok: res.ok, status: res.status, data };
}
Le relais est construit avec Hono, un framework léger qui maintient le code minimal. Le relais complet fait environ 200 lignes et gère les messages texte, images, documents, audio, vidéo, stickers, réactions et partages de position.
Étape 3 : Construire le serveur MCP pour Claude Code
Le serveur MCP est le pont entre le relais et Claude Code. Il interroge le relais en polling pour récupérer les messages WhatsApp entrants et expose des outils que Claude Code peut appeler pour répondre.
Définitions des outils
Le serveur enregistre quatre outils auprès de Claude Code via le Model Context Protocol :
const mcp = new Server(
{ name: 'whatsapp', version: '1.0.0' },
{
capabilities: { tools: {}, experimental: { 'claude/channel': {} } },
instructions: [
'The sender reads WhatsApp, not this session.',
'Anything you want them to see must go through the reply tool.',
'Messages arrive as <channel source="whatsapp" chat_id="..." wamid="..." user="..." ts="...">.',
'Reply with the reply tool. Pass chat_id (phone number) back.',
'WhatsApp has a 24-hour session window: you can only send free-form messages',
"within 24 hours of the user's last message.",
].join('\n'),
},
);
Le champ instructions indique à Claude Code comment interpréter les messages entrants et qu’il doit utiliser l’outil reply pour envoyer quoi que ce soit. Sans cela, le modèle pourrait tenter de répondre dans sa propre transcription, que l’utilisateur WhatsApp ne verrait jamais.
Les quatre outils sont reply (envoyer du texte), react (réactions emoji), mark_read (accusés de lecture), et send_media (images, documents, audio, vidéo). Voici la définition de l’outil reply :
{
name: 'reply',
description: 'Reply on WhatsApp. Pass chat_id (phone number) from the inbound message.',
inputSchema: {
type: 'object',
properties: {
chat_id: { type: 'string', description: 'Phone number to send to' },
text: { type: 'string', description: 'Message text' },
reply_to: { type: 'string', description: 'wamid to quote-reply to (optional)' },
},
required: ['chat_id', 'text'],
},
}
Boucle de polling avec persistance du curseur
Le serveur MCP interroge le relais toutes les 2 secondes et transmet les nouveaux messages à Claude Code sous forme de notifications de canal :
const CURSOR_FILE = join(process.env.HOME || '/tmp', '.whatsapp-relay-cursor');
let cursor = loadCursor();
async function poll(): Promise<void> {
try {
const result = await relay(`/poll?since=${cursor}`);
if (!result.ok) return;
const { messages, cursor: newCursor } = result.data as {
messages: InboundMessage[];
cursor: number;
};
for (const msg of messages) {
const meta = {
chat_id: msg.from,
wamid: msg.wamid,
user: msg.pushName || msg.from,
ts: new Date(msg.timestamp).toISOString(),
type: msg.type,
};
mcp.notification({
method: 'notifications/claude/channel',
params: {
content: msg.text ?? `(${msg.type})`,
meta,
},
});
}
if (newCursor > cursor) {
cursor = newCursor;
saveCursor(cursor);
}
} catch (err) {
process.stderr.write(`whatsapp channel: poll error: ${err}\n`);
}
}
Le curseur est persisté sur le disque (~/.whatsapp-relay-cursor, ce qui évite de retraiter les anciens messages en cas de redémarrage du serveur MCP. Chaque message devient une notification de canal que Claude Code reçoit comme une nouvelle entrée, avec le numéro de téléphone de l’expéditeur, son nom d’affichage, l’horodatage et le type de message en métadonnées.
Étape 4 : Enregistrer le serveur MCP auprès de Claude Code
Créez un fichier .mcp.json à la racine de votre projet :
{
"mcpServers": {
"whatsapp": {
"command": "node",
"args": ["--import", "tsx", "whatsapp.ts"]
}
}
}
C’est tout. Quand Claude Code démarre dans ce répertoire, il détecte le serveur MCP, le lance comme processus enfant via stdio, et le canal WhatsApp devient disponible. Claude Code reçoit alors les messages WhatsApp comme notifications de canal et peut appeler les outils reply, react, mark_read et send_media.
Étape 5 : Configurer la gateway Arcade et la connecter à Claude Code
Avant que l’assistant puisse accéder aux outils métier, vous devez créer une gateway Arcade qui définit quels outils l’agent peut utiliser et avec quelles permissions.
Connectez-vous au tableau de bord Arcade, créez une nouvelle gateway et ajoutez les serveurs MCP pour les services dont votre assistant a besoin : Google Calendar, Gmail, Slack, et tout autre service utile à vos workflows. Pour chaque serveur, sélectionnez uniquement les outils spécifiques que vous souhaitez exposer à l’agent. C’est ici que vous limitez les permissions. Si la compétence de préparation de réunion n’a besoin que de lister des événements et de chercher des e-mails, inutile d’exposer des outils qui suppriment des événements ou envoient des e-mails en votre nom.
Une fois la gateway créée, enregistrez-la auprès de Claude Code depuis la ligne de commande :
claude mcp add 'arcade-gateway' \
--transport http 'https://api.arcade.dev/mcp/<your-gateway-slug>' \
--header "Authorization: Bearer <your-arcade-api-key>" \
--header 'Arcade-User-ID: <your-email>'
Cela écrit la configuration de la gateway dans ~/.claude.json. Claude Code dispose maintenant de deux serveurs MCP : le serveur de canal WhatsApp local (depuis .mcp.json dans le projet) et la gateway Arcade distante (depuis ~/.claude.json). Le serveur WhatsApp gère la messagerie. La gateway Arcade gère l’accès aux outils métier avec une autorisation par action.
L’en-tête Arcade-User-ID indique à Arcade les identifiants de quel utilisateur employer lors de l’exécution des appels d’outils. Dans la configuration mono-utilisateur, il s’agit de votre e-mail. Dans l’architecture multi-utilisateurs décrite plus loin, l’orchestrateur transmet un identifiant utilisateur différent par session.
Étape 6 : Créer une compétence de préparation de réunion avec les outils Arcade
Le canal étant configuré, l’assistant a besoin de capacités. C’est là qu’outils et compétences fonctionnent ensemble. Arcade offre un accès sécurisé aux outils métier (Google Calendar, Gmail, Slack), et les compétences indiquent à l’agent comment les utiliser pour réaliser un workflow spécifique.
Les compétences dans Claude Code sont des fichiers markdown. Pas de code, pas de déploiement, juste un prompt structuré qui encode l’expertise métier. Voici la structure :
skills/
└── meeting-prep/
└── SKILL.md
Le fichier de compétence comporte deux parties : un frontmatter qui indique à Claude Code quand l’activer, et un corps qui définit le workflow. Voici la compétence de préparation de réunion :
---
name: meeting-prep
description: "Prepare briefings for upcoming customer meetings by reading
your Google Calendar, identifying external/customer meetings (based on
attendee email domains), then pulling relevant context from Gmail threads
and Slack conversations."
---
# Meeting Prep
You are a meeting preparation assistant. Your job is to create concise,
actionable briefings for upcoming external meetings.
## Customer Directory
Read the centralized client registry at `$AGENT_DATA_DIR/clients.md`.
Use it to match calendar attendee domains to known customers, find the
correct Slack channel, and locate customer-specific data files.
## Phase 1: Discover (Find the Meeting)
- Search Google Calendar using `list_events` for the relevant time window
- Identify external meetings by checking attendee email domains
- Any attendee whose domain is NOT your organization signals an external meeting
## Phase 2: Gather (Pull Context from Email and Slack)
### Email Context (Gmail)
1. Search for recent threads involving external attendees (last 30 days)
2. Read the 3-5 most relevant threads, looking for decisions, action items, tone
3. Check the calendar event itself for agenda or documents
### Slack Context
1. If there's a dedicated customer channel, read recent messages there
2. Otherwise search by company name or contact names (last 2 weeks)
3. Look for internal context not in email: concerns, feature requests, deal status
## Phase 3: Brief (Deliver the Prep)
### Meeting Briefing: [Title]
**When:** [Date & Time]
**With:** [Attendees + roles/company]
**Meeting type:** [Quarterly review, Demo, Follow-up, Intro call]
**Quick Context:** 2-3 sentences on where things stand
**Recent History:** Chronological recap of last interactions
**Key Things to Know:** Open items, concerns, opportunities
**Suggested Talking Points:** 3-5 practical conversation starters
**People Notes:** Brief note on new stakeholders or unfamiliar attendees
La compétence indique à l’agent précisément quels outils propulsés par Arcade utiliser (list_events, search_messages, read_thread), dans quel ordre, quels signaux chercher dans les résultats, et comment formater la sortie. La recherche dans l’annuaire clients évite à l’agent de gaspiller des tokens à faire du fuzzy-matching sur les noms d’entreprises. Il va directement au bon domaine e-mail et au bon canal Slack.
Quand un utilisateur envoie « prépare-moi pour mon 14h » sur WhatsApp, Claude Code reçoit le message via le canal, active cette compétence, exécute le workflow en trois phases via les outils Arcade, et renvoie le briefing par l’outil reply WhatsApp. Tout le flux, du message WhatsApp au briefing structuré, se déroule sans que l’utilisateur quitte la conversation.
Étape 7 : Lancer et tester l’assistant WhatsApp en local
Démarrez tout dans l’ordre :
# Terminal 1: Start the relay server
cd whatsapp-relay
node --import tsx relay.ts
# → "whatsapp relay listening on :3000"
# Terminal 2: Expose the relay via ngrok
ngrok http 3000
# → Copy the https:// forwarding URL
# Terminal 3: Start Claude Code from the project root
cd whatsapp-assistant
claude --dangerously-load-development-channels server:whatsapp
# Claude Code discovers .mcp.json and launches the MCP server
# → "whatsapp channel: connected, polling http://localhost:3000 every 2000ms"
Enregistrez votre webhook auprès de Meta en accédant à votre application dans le Meta Developer Dashboard, puis en naviguant vers WhatsApp, Configuration, Webhook. Définissez l’URL de callback sur votre URL ngrok suivie de /webhook (par ex., https://abc123.ngrok.io/webhook), définissez le Verify Token sur la valeur figurant dans votre fichier .env et abonnez-vous aux messages webhook field.
Envoyez maintenant un message depuis votre téléphone vers le numéro WhatsApp Business. Vous devriez le voir transiter par le relais, entrer dans le serveur MCP, puis apparaître dans Claude Code. Claude Code le traite et renvoie une réponse via la même chaîne.
Essayez d’envoyer « prépare-moi pour ma prochaine réunion ». La première fois que Claude Code appelle un outil Arcade (comme la lecture de votre calendrier), Arcade affiche une URL d’autorisation dans le terminal. Ouvrez-la dans votre navigateur et authentifiez-vous avec le compte concerné (Google, Slack, etc.). Cette étape n’est à faire qu’une seule fois par service. Ensuite, Arcade gère automatiquement le renouvellement des tokens.
Si vous avez configuré la skill de préparation de réunion et connecté Google Calendar / Gmail via Arcade, vous recevrez un briefing structuré directement dans WhatsApp.
Passer d’un utilisateur à plusieurs : ce qui change dans l’architecture
Tout ce qui précède fonctionne pour un seul utilisateur : une instance Claude Code, un jeu de credentials Arcade, un contexte d’identité. Voici ce qui se casse dès qu’un deuxième utilisateur écrit au bot, et ce qu’il faut modifier.
Pourquoi une seule instance Claude Code ne fonctionne pas pour plusieurs utilisateurs
La configuration mono-utilisateur repose sur une hypothèse implicite : chaque message WhatsApp vous appartient. Quand Claude Code appelle un outil Arcade comme list_events, Arcade utilise les credentials que vous avez fournis lors de la configuration. Aucun identifiant utilisateur n’est transmis dans l’appel.
Si l’utilisateur 2 écrit au même bot, Claude Code appelle quand même Arcade avec vos credentials. L’utilisateur 2 accède à votre calendrier. Pire : Claude Code tourne dans un contexte de conversation unique. Le briefing de l’utilisateur 1 (conditions commerciales, messages Slack internes, chiffres de revenus) se trouve dans la fenêtre de contexte quand le message de l’utilisateur 2 arrive. Une injection de prompt de l’utilisateur 2 pourrait exposer les données de l’utilisateur 1. Arcade a sécurisé les credentials correctement, mais la fenêtre de contexte partagée brise l’isolation entre tenants.
Il vous faut deux choses : des instances d’agents séparées pour que le contexte ne se croise jamais entre utilisateurs, et un routage des credentials par utilisateur pour qu’Arcade sache quel calendrier lire.
L’architecture multi-utilisateur
Le serveur relais, les schémas d’outils MCP (reply, react, send_media) et les skills restent identiques. Ce qui change, c’est la couche d’orchestration.
La version mono-utilisateur utilise Claude Code CLI avec sa fonctionnalité de canaux intégrée. Pour le multi-utilisateur, vous construisez un orchestrateur personnalisé via le Claude Agent SDK. Le SDK ne gère pas nativement les canaux, mais il vous fournit sessions, hooks, permissions d’outils et connexions MCP (les briques pour reproduire ce que font les canaux pour un seul utilisateur, mais à grande échelle).
Le serveur relais devient un routeur. Quand un message arrive du +1111, l’orchestrateur identifie quelle session d’agent est associée à ce numéro et lui route le message. Quand le +2222 écrit, il route vers une session différente. Chaque session dispose de sa propre fenêtre de contexte, de sa propre instance de serveur MCP et de son propre contexte utilisateur Arcade. Aucune donnée ne passe d’une session à l’autre.
Le routage des credentials passe par le paramètre user_id d’Arcade sur les appels d’outils. Chaque utilisateur effectue une fois le flux d’authentification navigateur Arcade (la même étape d’URL d’autorisation que dans la configuration mono-utilisateur). Ensuite, quand l’orchestrateur appelle un outil Arcade au nom de l’utilisateur 2, il transmet l’identité de l’utilisateur 2. Arcade résout les autorisations OAuth correspondantes, génère un token limité pour cette action précise et exécute l’appel. La requête calendrier de l’utilisateur 2 retourne le calendrier de l’utilisateur 2. Pour un guide complet sur ce modèle d’autorisation dans différents frameworks, consultez SSO for IA Agents : guide d’authentification et d’autorisation.
L’association des identités est simple. Mappez chaque identifiant d’expéditeur WhatsApp à une identité d’entreprise via un flux de vérification unique : envoyez un code par un WhatsApp Authentication Template, demandez à l’utilisateur de le confirmer dans un portail web, et stockez le mapping.
Arcade gère le reste de la complexité multi-utilisateur : échange de tokens OAuth par utilisateur et grants just-in-time pour la délégation de credentials, exécution d’outils cloisonnée pour éviter l’accès aux données d’autres tenants, registre d’outils versionné qui résiste aux changements d’API en amont, et journaux d’audit structurés liés à l’utilisateur et à l’action. Ce sont les quatre mêmes écueils évoqués plus tôt. Ils se compliquent tous à l’échelle multi-utilisateur, et Arcade les gère nativement.
Checklist de mise en production pour les agents IA
Avant de passer à un usage au-delà du local, vérifiez ces cinq points :
- Isolation des credentials. Le LLM peut-il voir vos tokens d’authentification ? Si oui, arrêtez-vous. L’architecture doit prévoir une autorisation just-in-time par action, où le modèle ne touche jamais des credentials longue durée. Les privilèges de compte de service permanents sont à proscrire.
- Fiabilité des outils. Vos outils sont-ils optimisés pour les agents ou de simples wrappers REST naïfs ? Si le modèle doit deviner des paramètres complexes et enchaîner les tentatives, vous rencontrerez des échecs invisibles jusqu’en production.
- Versioning et rollbacks. Pouvez-vous mettre à jour un outil sans casser l’assistant en cours d’exécution ? Si un changement d’API en amont fait tomber votre agent, il vous faut un registre versionné avec des périodes de dépréciation sécurisées.
- Auditabilité. Pouvez-vous retracer chaque action jusqu’à la personne qui l’a demandée ? Si non, vous échouez aux certifications SOC2 et ISO27001. Il vous faut des journaux immuables avec identifiants utilisateurs, noms d’outils et paramètres assainis.
- Répartition du temps développeur. Vos ingénieurs construisent-ils des plomberies OAuth et des logiques de retry de webhooks, ou des skills et des workflows ? Si c’est la première option, l’architecture est trop bas niveau.
Prochaines étapes
Vous avez maintenant un assistant WhatsApp fonctionnel. Un relais qui gère les webhooks Meta. Un serveur MCP qui fait le pont avec Claude Code. Une skill de préparation de réunion qui transforme « prépare-moi pour mon 14h » en briefing structuré tiré de votre calendrier, de vos emails et de Slack.
La partie intéressante, c’est la suite. Le relais et le serveur MCP sont des infrastructures que vous écrivez une seule fois. Les skills, c’est là que réside la valeur continue, et n’importe qui dans l’équipe peut en créer. La préparation de réunion a été la première que j’ai construite. Résumés de notes de frais, standups quotidiens, rappels de suivi clients : même schéma, fichier markdown différent.
Pour les déploiements multi-utilisateurs, le Claude Agent SDK vous donne les briques pour orchestrer des sessions d’agent par utilisateur, le relay acheminant les messages et Arcade gérant la délégation de credentials par utilisateur, l’isolation des tenants et la journalisation des audits. Vous vous concentrez sur les skills, pas sur l’infrastructure.
Le code de ce guide est sur GitHub. Forkez-le et construisez quelque chose d’utile.
FAQ
Qu’est-ce qu’un assistant exécutif IA disponible en permanence ?
Un assistant disponible en permanence tourne en continu et interagit via des canaux de messagerie comme WhatsApp ou Slack. Il maintient l’état entre les conversations et effectue des actions dans les outils métier connectés de façon asynchrone, sans avoir besoin d’un onglet de navigateur ouvert.
Quels sont les risques d’utiliser OpenClaw pour un agent IA ?
Ces solutions reposent généralement sur des credentials machine partagés, des scripts fragiles et des wrappers d’outils non gouvernés. Cela crée un risque élevé de fuite de tokens, d’appels d’outils peu fiables, de surcharge de contexte et d’absence de pistes d’audit nécessaires à la conformité.
Comment empêcher un agent d’avoir un accès dieu aux systèmes de l’entreprise ?
Utilisez une autorisation par action au runtime avec des grants de courte durée délivrés juste-à-temps (ex. : échange de token OAuth). L’agent ne détient jamais de credentials larges ou persistants, et chaque action est évaluée selon les permissions de l’utilisateur demandeur.
Qu’est-ce qu’Arcade et comment sécurise-t-il l’accès aux outils des agents IA ?
Arcade est un runtime qui s’intercale entre un agent IA et vos outils métier. Plutôt que de donner à l’agent des credentials stockés, Arcade évalue chaque appel d’outil par rapport aux permissions de l’utilisateur demandeur, génère un token juste-à-temps limité à cette action, exécute l’appel et journalise le résultat. Il propose aussi des intégrations optimisées pour les agents, qui renvoient des données résumées plutôt que des réponses API brutes. Pour une vue d’ensemble complète, consultez Comment fonctionne Arcade.
Est-il sûr de donner à un agent IA l’accès à mon Google Calendar et à mes e-mails ?
Pas si l’agent détient directement des tokens OAuth ou des clés API à longue durée de vie. Une injection de prompt ou une dépendance compromise peut exfiltrer ces credentials et accéder à tout ce que l’agent peut atteindre. L’approche sûre est l’autorisation par action : un runtime comme Arcade génère un token à courte durée de vie et limité en portée pour chaque action spécifique, puis le révoque immédiatement après, réduisant le rayon d’impact à un seul appel.
Comment le serveur relay gère-t-il les webhooks WhatsApp en double ?
WhatsApp délivre les événements avec une sémantique « au moins une fois ». Le relay renvoie 200 OK immédiatement (même en cas de signature invalide) pour éviter les tempêtes de relances, et traite les messages de façon asynchrone. En production, ajoutez un store de déduplication comme Redis indexé par ID de message.
Qu’est-ce que la fenêtre de messagerie de 24 heures de WhatsApp ?
Les réponses libres sont autorisées dans les 24 heures suivant le dernier message de l’utilisateur. Les messages proactifs en dehors de cette fenêtre doivent utiliser des templates de messages WhatsApp pré-approuvés (templates HSM). Pour un brief matinal à 8h, vous auriez besoin d’un template approuvé.
Puis-je utiliser cette architecture avec d’autres modèles que Claude ?
Oui. Le serveur relay et le protocole MCP sont indépendants du modèle. Le relay gère les entrées/sorties WhatsApp, et le serveur MCP définit les outils via un protocole standard. Vous pouvez remplacer Claude Code par n’importe quel runtime compatible MCP.
Comment ajouter de nouvelles skills ou de nouveaux workflows à un agent Claude Code ?
Créez un nouveau répertoire sous skills/ avec un fichier SKILL.md. La description dans le frontmatter de la skill indique à Claude Code quand l’activer. Les skills sont de simples prompts structurés, aucun déploiement de code n’est nécessaire.
