Comprendre akko-mcp-trino en cinq niveaux

Un parcours qui part du problème et finit par la preuve. Chaque niveau tient sur un écran, porte une seule idée et se manipule. Le niveau 1 est une illustration ; les suivants s’appuient sur le sandbox public, ses vraies règles d’accès et de vraies réponses.

Niveau 1 · comprendre en 30 secondes

Un agent branché avec un compte qui voit tout

Basculez entre les deux montages et regardez ce que Trino reçoit.

Carol, analyste, demande la liste des clients

Avec un service account aux droits larges

Ce montage est une illustration, sans akko-mcp-trino.

Carolcarol_analyst
L’agentcompte : mcp-svc
Trino voitmcp-svc

10 clients, e-mails en clair. Trino ne sait pas que c’est Carol qui demande. Ses règles (clients européens seulement, e-mails masqués) ne s’appliquent plus. Il ne reste qu’une consigne dans le prompt.

Avec akko-mcp-trino

Mode jwt, le mode par défaut

Carolcarol_analyst
L’agenttoken de Carol
Trino voitcarol_analyst

5 clients européens, e-mails masqués. Trino reconnaît Carol et applique ses règles, exactement comme dans sa console SQL ou ses tableaux de bord.

À retenir. Les droits sont déjà écrits dans Trino. Le seul vrai travail est de faire arriver la bonne identité jusqu’à lui.

Niveau 2 · un petit jeu

Qui voit quoi ?

Même question, même client MCP, même serveur. Devinez d’abord, puis vérifiez.

Combien de clients Carol voit-elle sur 10 ?

Choisissez une réponse.

execute_querySELECT full_name, email, country FROM demo.crm.customers ORDER BY id

Alice est administratrice : elle voit toutes les lignes et toutes les colonnes en clair.

Ce que Trino renvoie à alice_admin
full_nameemailcountry
Lena Vogellena.vogel@example.comDE
Hugo Martinhugo.martin@example.comFR
Sofia Ruizsofia.ruiz@example.orgES
Noah Bennettnoah.bennett@example.comUS
Giulia Contigiulia.conti@example.orgIT
Kenji Satokenji.sato@example.comJP
Emma de Vriesemma.devries@example.orgNL
Lucas Almeidalucas.almeida@example.comBR
Olivia Clarkeolivia.clarke@example.orgGB
Chloe Tremblaychloe.tremblay@example.comCA

Carol est analyste : un row filter country IN ('DE', 'ES', 'FR', 'IT', 'NL') et un column mask sur email.

Ce que Trino renvoie à carol_analyst
full_nameemailcountry
Lena Vogel***@example.comDE
Hugo Martin***@example.comFR
Sofia Ruiz***@example.orgES
Noah Bennett (ligne retirée par le row filter)***@example.comUS
Giulia Conti***@example.orgIT
Kenji Sato (ligne retirée par le row filter)***@example.comJP
Emma de Vries***@example.orgNL
Lucas Almeida (ligne retirée par le row filter)***@example.comBR
Olivia Clarke (ligne retirée par le row filter)***@example.orgGB
Chloe Tremblay (ligne retirée par le row filter)***@example.comCA

Dave est lecteur : la table customers lui est refusée (et cachée des listes), comme la colonne balance des comptes.

Access Denied: Cannot select from columns [country, full_name, id, email] in table or view demo.crm.customers

À retenir. Ces réponses viennent de ce que Trino laisse voir à chacun, avec la policy OPA du sandbox. Le serveur MCP n’a recopié aucune règle : il a seulement transmis l’identité de chacun.

Niveau 3 · l’agent pas à pas

Comment l’agent trouve les bonnes colonnes

Suivez un vrai essai de Mistral sur la 0.7.0, tool call par tool call. Les appels sont ceux de l’essai, mot pour mot. Les réponses des tools ont été rejouées sur la même version du serveur.

  1. 1 · Chercher

    search_columns

    « Quel segment de clients détient le plus gros solde total ? »

    L’agent ne connaît pas les tables. Il cherche avec quatre mots de la question, en un seul tool call sur tous les catalogs qu’il a le droit de voir.

    search_columns {"text": "customer segment account balance"}
  2. 2 · Mot par mot

    le serveur répond

    Aucune colonne ne porte les quatre mots, alors le serveur cherche chaque mot

    Chaque résultat dit quels mots l’ont trouvé, et "partial": true prévient l’agent. Les colonnes marquées sont celles que sa réponse utilisera.

    • crm.accounts.balanceaccountbalanceretenue
    • crm.customers.segmentcustomersegmentretenue
    • crm.accounts.customer_idcustomerretenue
    • crm.transactions.account_idaccount
    • crm.transactions.amountaccount
    Extrait de la réponse du serveur
    "partial": true,
    "hint": "No table or column holds every word together, so these are partial matches, each found by the words it lists: pick the right one, or search again with a single word from a column or table name, then call describe_table on the table."
  3. 3 · Une jointure fausse

    execute_query

    L’agent joint sur c.customer_id, une colonne que customers n’a pas

    Il écrit tout de suite la requête, sans lire la table customers.

    execute_query {"sql": "SELECT c.segment, SUM(a.balance) AS total_balance FROM demo.crm.customers c JOIN demo.crm.accounts a ON c.customer_id = a.customer_id GROUP BY c.segment ORDER BY total_balance DESC LIMIT 1", "max_rows": 1}
  4. 4 · L’erreur guide

    le serveur répond

    L’erreur dit quelle table lire

    Depuis la 0.7.0, une erreur porte un kind et, pour une colonne introuvable, un hint qui nomme la table à lire.

    {"error": "TrinoUserError(type=USER_ERROR, name=COLUMN_NOT_FOUND, message=\"line 1:105: Column 'c.customer_id' cannot be resolved\", query_id=20261101_211014_00019_85v63)", "kind": "column", "identifier": "c.customer_id", "hint": "Read the columns of demo.crm.customers with describe_table, then write the query again with a column it has."}
  5. 5 · Lire la table

    describe_table

    customers a une colonne id, pas customer_id

    L’agent suit le hint et lit les colonnes de customers.

    describe_table {"catalog": "demo", "schema": "crm", "table": "customers"}
    {"columns": ["Column", "Type", "Extra", "Comment"], "rows": [["id", "integer", "", ""], ["full_name", "varchar", "", ""], ["email", "varchar", "", ""], ["country", "char(2)", "", ""], ["segment", "varchar", "", "Retail or business customer"]], "truncated": false}
  6. 6 · Corriger et répondre

    execute_query

    Le segment business, avec 17 620,75 de solde total

    L’agent relance la requête avec c.id = a.customer_id. Trino calcule sur les seuls clients que Carol a le droit de voir : les 17 620,75 sont en EUR, les deux comptes de Sofia Ruiz, car le row filter de Carol écarte les clients business hors d’Europe. L’agent cite les colonnes utilisées. Le même chemin revient dans 4 des 5 essais de cette question.

    execute_query {"sql": "SELECT c.segment, SUM(a.balance) AS total_balance FROM demo.crm.customers c JOIN demo.crm.accounts a ON c.id = a.customer_id GROUP BY c.segment ORDER BY total_balance DESC LIMIT 1", "max_rows": 1}
    {"columns": ["segment", "total_balance"], "rows": [["business", "17620.75"]], "row_count": 1, "truncated": false}
    The customer segment that holds the largest total account balance is the business segment, with a total balance of 17620.75.
    
    The columns relied on are:
    - demo.crm.customers.segment
    - demo.crm.accounts.balance
    - demo.crm.customers.id
    - demo.crm.accounts.customer_id
1 / 6

Niveau 4 · votre situation

Quel mode d’identité pour vous ?

Répondez aux questions dans l’ordre et le bon mode s’allume. Les quatre schémas suivent la même grille, pour qu’on voie ce qui change.

Votre identity provider sait-il échanger des tokens (RFC 8693, comme Keycloak 26) ?
Trino peut-il autoriser un service account à agir au nom des utilisateurs ?
Le serveur tourne-t-il sur le poste d’une seule personne (Claude Desktop, Cursor), avec la connexion OAuth2 de Trino ?

Répondez à la première question.

Du plus sûr au plus simple : jwt si votre identity provider échange des tokens, impersonation si Trino autorise un service account à agir au nom des utilisateurs, oauth2 sur le poste d’une seule personne si Trino a sa connexion OAuth2, et service sinon.

jwtpar défaut

Trino voit l’utilisateur

MCP hostserveurTrinoIdPtoken de l’utilisateurtoken échangé

Le plus sûr. Le serveur ne transmet jamais le token reçu : il en obtient un autre, émis pour Trino.

impersonation

Trino voit l’utilisateur

MCP hostserveurTrinotoken vérifiécompte du serveur,au nom de l’utilisateur

Sans échange de tokens. Trino autorise ce seul compte à agir au nom des autres. Le nom vient toujours du token vérifié.

oauth2poste de travail

Trino voit la personne connectée

navigateurserveur localTrinoconnexion à Trinoaucun nom envoyéstdio seulement

Sur le poste. La connexion navigateur d’un Trino configuré en OAuth2, sans identity provider à configurer côté serveur.

serviceà nommer exprès

Trino voit un seul compte, pour tous

personne Apersonne BserveurTrinomcp-reader

Les mêmes droits pour tous, en lecture seule. Le serveur l’annonce par un avertissement au démarrage.

Niveau 5 · les preuves

Mesuré avec de vrais agents, et à essayer vous-même

Les mêmes cinq questions métier, chacune avec une colonne piège, posées à Mistral et à Claude.

Bonnes réponsesPlus haut, c’est mieux
0 %50 %100 %62,570Mistral93,393,3Claude
Tool calls par questionPlus bas, c’est mieux
05105,83,0Mistral8,54,9Claude
0.6.10.7.0

Mistral Codestral a répondu 40 fois par version, et Claude Sonnet 5.5, 15 fois. Les tool calls de Mistral viennent de la passe à 5 essais. Les rapports sont publics dans akko-sandbox.

L’écart de Mistral reste dans sa variation d’un run à l’autre. Moins de tool calls, mais environ 41 % de tokens d’entrée en plus par question, parce que les descriptions des tools sont plus longues.

# Essayer en cinq minutes avec Keycloak, Trino, OPA et le serveur, et les trois utilisateurs du niveau 2
$ git clone https://github.com/AKKO-p/akko-sandbox.git
$ cd akko-sandbox
$ docker compose up -d --wait
$ ./demo.sh

Pour poser vos propres questions depuis Claude Desktop ou Cursor, suivez le guide Brancher Claude Desktop ou Cursor.

Pour aller plus loin

Suivez une question de bout en bout dans Comment ça marche, puis lisez les garanties et les preuves dans la présentation. Le cœur open source donne l’installation et la configuration, et la page Versions liste chaque release.