|8 min de lecture|Yvann Lièvre

Gouverner les appels d'outils MCP d'un agent IA avec OPA/Rego

OPA Rego permissions agent IA MCP : comment interposer une décision de politique-as-code avant chaque appel d'outil, selon le rôle, la sensibilité et l'heure.

OPARegoMCP
Gouverner les appels d'outils MCP d'un agent IA avec OPA/Rego

Un serveur MCP (Model Context Protocol) donne à un agent l'accès à des outils réels : lecture de fichiers, appels d'API internes, requêtes en base de données. C'est précisément ce qui rend l'agent utile, et c'est précisément ce qui rend la question du contrôle d'accès non négociable. Le protocole MCP décrit comment un outil est exposé et appelé ; il ne décrit pas qui a le droit de l'appeler, dans quel contexte, ni avec quelles données. Cette responsabilité reste entièrement à la charge de celui qui déploie le serveur.

Open Policy Agent (OPA) et son langage Rego répondent à ce manque avec un principe simple : la politique d'autorisation devient du code, versionné, testé, séparé de la logique métier. Appliqué aux appels d'outils MCP, cela permet de poser une question précise avant chaque exécution : ce rôle, sur cette donnée, à cette heure, a-t-il le droit d'appeler cet outil ?

Le problème : des outils réels sans autorisation contextuelle

La plupart des intégrations MCP livrées en configuration par défaut appliquent un contrôle binaire : le serveur est accessible ou non, l'outil est enregistré ou non. Une fois la connexion établie, l'agent peut appeler n'importe quel outil exposé, sur n'importe quelle ressource, à n'importe quel moment. Il n'existe pas de notion native de rôle, de sensibilité de la donnée manipulée, ou de fenêtre horaire autorisée.

Le risque concret : un agent conversationnel qui dispose d'un outil query_database générique peut, en théorie, exécuter une requête sur une table contenant des données RH ou des secrets applicatifs, simplement parce que l'outil existe et que rien n'en limite l'usage selon qui pose la question. Le sandboxing applicatif (isolation du code exécuté, limitation des appels systèmes) protège contre l'exécution de code arbitraire, mais il ne répond pas à la question métier : cet appel-là, dans ce contexte-là, est-il légitime ?

Le point d'application : interposer une décision avant l'exécution

La réponse tient en une phrase : chaque appel d'outil MCP doit passer par un point de décision avant d'atteindre l'outil lui-même. Concrètement, le serveur MCP (ou un proxy placé devant lui) interroge un moteur OPA avec le contexte de l'appel, et n'exécute l'outil que si la réponse est allow: true.

Avant (sans point d'application) :

agent -> serveur MCP -> outil "query_database" -> exécution directe

Après (avec OPA en coupure) :

agent -> serveur MCP -> requête de decision OPA (rôle, outil, sensibilité, heure)
                          -> allow == true  -> exécution de l'outil
                          -> allow == false -> refus + journalisation

L'important est que ce point d'application soit incontournable : il ne s'agit pas d'un contrôle optionnel côté agent (que l'agent pourrait contourner en reformulant sa demande), mais d'une barrière côté serveur, entre la réception de l'appel et son exécution effective.

Écrire la politique Rego

Une politique Rego pour ce cas d'usage prend en entrée un objet input décrivant l'outil demandé, le rôle de l'utilisateur ou du contexte d'exécution de l'agent, la sensibilité de la ressource ciblée, et l'heure de l'appel :

# Exemple d'input évalué par OPA à chaque appel d'outil
input:
  tool: "query_database"
  user:
    role: "analyst"
  resource:
    sensitivity: "internal"
  timestamp: "2026-07-17T14:32:00Z"

La politique elle-même, en Rego :

package mcp.authz
 
import future.keywords.in
 
default allow := false
 
# Un analyste peut lire un fichier, sauf si la ressource est classée critique.
allow if {
    input.tool == "read_file"
    input.user.role in {"analyst", "admin"}
    input.resource.sensitivity != "critical"
}
 
# Un administrateur peut interroger la base, uniquement en heures ouvrées.
allow if {
    input.tool == "query_database"
    input.user.role == "admin"
    business_hours
}
 
business_hours if {
    hour := time.clock(time.now_ns())[0]
    hour >= 7
    hour < 20
}

Chaque règle allow combine explicitement l'outil demandé (input.tool), le rôle, et une condition contextuelle (sensibilité ou fenêtre horaire). Rien n'est autorisé par défaut : c'est la construction volontaire de la règle suivante qui compte.

Deny-by-default, journalisation, tests avec opa test

Le default allow := false n'est pas un détail stylistique : c'est la propriété qui garantit que tout outil nouvellement exposé, toute combinaison de rôle non anticipée, se traduit par un refus plutôt que par un accès silencieux. C'est l'inverse d'une liste noire, qui oublie toujours un cas.

Chaque décision, positive ou négative, doit être journalisée avec son contexte complet (outil, rôle, ressource, résultat, motif) pour permettre l'audit a posteriori. Une politique sans traçabilité de ses propres décisions est aussi risquée qu'une absence de politique : personne ne peut vérifier ce qui a réellement été autorisé.

Enfin, une politique Rego se teste comme du code applicatif, avec opa test :

package mcp.authz_test
 
import data.mcp.authz.allow
 
test_analyst_can_read_internal_file if {
    allow with input as {
        "tool": "read_file",
        "user": {"role": "analyst"},
        "resource": {"sensitivity": "internal"}
    }
}
 
test_analyst_cannot_read_critical_file if {
    not allow with input as {
        "tool": "read_file",
        "user": {"role": "analyst"},
        "resource": {"sensitivity": "critical"}
    }
}
opa test policies/ -v

Ce cycle de test doit tourner en intégration continue, au même titre que n'importe quel test unitaire : une modification de la politique qui casse un cas couvert doit être détectée avant le déploiement, pas après un incident.

Complémentarité avec le sandboxing applicatif

OPA ne remplace pas le sandboxing applicatif, il le complète, et les deux barrières répondent à des questions différentes. Le sandboxing (isolation de l'exécution, restriction des appels systèmes et réseau disponibles au code exécuté) répond à la question : que peut techniquement faire ce code, quoi qu'il arrive ? La politique OPA répond à une question distincte : cette action précise, pour ce rôle précis, sur cette donnée précise, est-elle autorisée maintenant ?

Confondre les deux crée un doublon de barrière inutile ou, pire, un faux sentiment de sécurité : un sandbox bien conçu n'a pas vocation à connaître la sensibilité métier d'une donnée, et une politique de haut niveau n'a pas vocation à réimplémenter l'isolation d'exécution. Chaque couche reste sur son terrain, et c'est cette séparation qui rend l'ensemble auditable plutôt que redondant.

Un pack de politiques prêtes à l'emploi

Écrire ces politiques depuis zéro pour chaque scénario MCP (lecture de fichiers, requêtes en base, appels d'API sortants, actions d'écriture) est un travail répétitif que peu d'équipes ont le temps de refaire correctement, avec les tests et la journalisation qui vont avec. C'est exactement ce que nous livrons dans le pack de politiques OPA/Rego pour agents MCP : des règles de contrôle contextuel (rôle, sensibilité, horaire, outil) déjà rédigées, testées avec opa test, et prêtes à s'interposer devant vos serveurs MCP.

Articles liés