Raccourcis clavier

Appuyez sur ← ou → pour passer d'un chapitre à l'autre

Appuyez sur S ou / pour rechercher dans la documentation

Appuyez sur ? pour afficher cette aide

Appuyez sur Échap pour masquer cette aide

Conception des connecteurs MCP

Ce chapitre décrit comment un service externe devient un ensemble d'outils MCP dans Chatbotaurus. Le moteur est le crate forge-mcp. À la fin, vous saurez écrire le manifeste d'un connecteur et ce que le contrôle de conformité UE en refuse.

Deux types de connecteurs

1. Connecteur déclaratif YAML (cas général)

Un fichier connectors/<nom>.yaml décrit le service et ses outils. Au démarrage, forge-api lit le dossier désigné par la variable CONNECTOR_CATALOGUE_DIR (par défaut ./connectors). Il enregistre chaque connecteur dans le registre. Le fichier connectors/TEMPLATE.yaml est un modèle à copier. Pas à pas : Créer votre premier connecteur MCP. Le dossier n'est lu qu'au démarrage : redémarrez forge-api après chaque modification (voir Fichiers de configuration).

Exemple tiré de connectors/matomo.yaml, abrégé (l'URL par défaut y est remplacée par une adresse d'exemple) :

id: matomo
display_name: "Matomo Analytics"
base_url: "${MATOMO_URL:-https://matomo.exemple.eu}"
base_url_env: MATOMO_URL
auth:
  type: query_token
  param: token_auth
  token_env: MATOMO_API_KEY
compliance:
  data_residency: self-hosted
  self_hosted: true
  gdpr_compliant: true
  license: "GPL-3.0"
  gaia_x: true
timeout_ms: 30000
tools:
  - name: get_visits_summary
    description: "Matomo Reporting API VisitsSummary.get"
    method: GET
    path: "/index.php"
    param_location: query
    input_schema:
      type: object
      properties:
        module: { type: string, default: "API" }
        method: { type: string, default: "VisitsSummary.get" }
        format: { type: string, default: "JSON" }

Champs principaux (structure HttpConnectorConfig, crates/forge-mcp/src/connector/http.rs) :

ChampRôle
id, display_nameIdentifiant unique et nom lisible
base_urlURL de base ; ${VAR:-défaut} est résolu depuis l'environnement au démarrage
base_url_envVariable portant l'URL propre à un locataire (instance auto-hébergée par client)
authMéthode d'authentification, discriminée par type
complianceMétadonnées de conformité UE, contrôlées à l'enregistrement
timeout_msDélai de la requête (30 000 par défaut)
cache_ttl_sCache de lecture des GET, désactivé à 0 (défaut) ; jamais actif dans un contexte locataire
rate_limit_per_minPlafond publié par l'amont, vérifié avant l'appel
tools[]name, description, method (GET, POST, PUT, PATCH, DELETE), path ({param} est remplacé par un argument), param_location (query, body ou path), input_schema (JSON Schema)

Types d'authentification : none, bearer, api_key, basic, query_token, jwt_exchange, oauth2, login_token, checksum, json_rpc_session. Les fichiers YAML ne portent jamais de secret : ils nomment la variable d'environnement qui le contient (token_env, key_env, entre autres).

Les outils sont exposés avec le préfixe de leur connecteur : l'outil get_visits_summary du connecteur matomo s'appelle matomo.get_visits_summary.

2. Connecteur natif Rust

Pour un protocole qui n'est pas du REST (par exemple l'XML-RPC d'Odoo, crates/forge-mcp/src/connector/odoo_xmlrpc_client.rs) ou une transformation lourde, on implémente le trait McpConnector (crates/forge-mcp/src/connector/mod.rs) :

#[async_trait::async_trait]
pub trait McpConnector: Send + Sync + 'static {
    fn id(&self) -> &str;
    fn display_name(&self) -> &str;
    fn compliance(&self) -> &EuCompliance;
    async fn list_tools(&self) -> ForgeResult<Vec<ToolDefinition>>;
    async fn execute(
        &self,
        tool_name: &str,
        arguments: serde_json::Value,
    ) -> ForgeResult<Vec<ContentBlock>>;
    async fn health_check(&self) -> ForgeResult<ConnectorHealth>;
    // ... plus des méthodes avec valeur par défaut (voir le fichier)
}

Un connecteur natif est enregistré dans le ConnectorRegistry au même titre qu'un connecteur YAML.

Contrôle de conformité UE

ConnectorRegistry::register appelle EuCompliance::validate. Un connecteur dont gdpr_compliant vaut false, ou dont la résidence des données n'est pas européenne (ou self-hosted), est rejeté au démarrage et journalisé. La classification de la résidence est centralisée dans crates/forge-mcp/src/connector/residence.rs. Côté exécution, chaque appel de serveur repasse par la fonction unique forge_core::compliance::is_server_allowed.

Cycle de vie

  1. Enregistrement : au démarrage, le CatalogueLoader charge les YAML et le registre reçoit aussi les connecteurs natifs.
  2. Découverte : le client MCP appelle tools/list sur POST /api/v1/mcp. Le catalogue se consulte aussi par GET /api/v1/mcp/catalogue.
  3. Exécution : le client appelle tools/call sur la même route.
  4. Résultat : le connecteur renvoie des blocs de contenu MCP (ContentBlock).

Chaîne verticale : un manifeste YAML ou un connecteur natif Rust sont enregistrés dans le registre avec contrôle de conformité UE, puis découverts par tools/list, exécutés par tools/call et rendus en blocs de contenu.

Figure 3 : du connecteur à l'outil MCP, avec le contrôle de conformité UE à l'enregistrement et à chaque appel.

Identifiants et secrets

  • Les identifiants tiers d'un locataire sont chiffrés en AES-256-GCM avant stockage. Au démarrage, crates/forge-api/src/connector_credential_env.rs les déchiffre et les publie dans les variables d'environnement que lisent les connecteurs. Les locataires réglementés peuvent utiliser une couche OpenBao.
  • À chaque requête, les secrets du seul locataire appelant sont installés pour la durée de l'appel. Avec FORGE_MULTI_TENANT_STRICT=1, le chargement global au démarrage est sauté et aucun connecteur ne retombe sur les identifiants globaux du déploiement.

Sécurité

  • SSRF : une URL de base fournie par un locataire (base_url_env) est contrôlée en deux temps avant tout appel réseau. Premier temps : analyse de l'URL (schéma, adresse, hôtes internes). Second temps : résolution DNS avec refus des adresses privées. Une violation renvoie Forbidden avec le motif SSRF_BLOCKED. Ce contrôle ne s'applique qu'aux URL fournies par un locataire : l'URL du YAML ou de l'environnement de l'opérateur n'y est pas soumise. Aucune option d'exploitation ne le désactive ; seule la compilation des tests le lève.
  • Actions irréversibles : un outil irréversible ne s'exécute qu'après une approbation humaine explicite, limitée au nom exact de l'outil (crates/forge-mcp/src/connector/human_authorization.rs). Un outil destructif n'est jamais autorisable par cette voie.
  • Un connecteur peut être limité à la lecture : le connecteur Matomo, par exemple, ne publie que des outils de rapport.

Bonnes pratiques

  • Connexions : un seul client reqwest réutilisé par connecteur.
  • Limites amont : déclarez rate_limit_per_min quand l'API publie un plafond ; une réponse 429 est renvoyée en RATE_LIMITED avec son Retry-After.
  • Cache : cache_ttl_s convient aux données publiques en lecture seule ; laissez-le à 0 pour tout le reste.
  • Données publiques : le champ grounded fait accompagner chaque réponse de sa source (source_url, retrieved_at).

Voir aussi