Agents IA (MCP)
graal Data Platform expose un serveur MCP (Model Context Protocol) : un agent IA s’y branche et pilote la plateforme avec les droits du compte qu’il porte, pas avec les siens. Le serveur ne parle pas à la base de données : il traduit MCP vers l’API REST de la Data Platform, en portant l’identité de l’appelant. C’est le même serveur pour tous les clients MCP compatibles, y compris les agents de graal Agentic Platform (voir Les deux produits).
Une précision qui compte avant tout le reste : la plateforme et les données restent chez vous, mais ce que les outils renvoient à l’agent passe par le modèle de langage que vous avez choisi. Si ce modèle est hébergé par un tiers, la sortie de données est votre décision, pas un effet de bord. Voir Souveraineté et architecture.
Les 113 outils
Section intitulée « Les 113 outils »82 en lecture, 31 en écriture, dont 7 destructifs qui ne sont exposés que si l’exploitant le
décide. Un serveur installé sans rien changer en expose 106, et aucun ne supprime quoi que ce soit
ni n’arrête une exécution (stop_workspace met un espace en pause, fichiers gardés : il est
réversible). Chaque outil s’adosse à une opération que l’API sert réellement : un test refuse qu’un
outil tombe sur une fonction coupée.
Lecture (82)
Section intitulée « Lecture (82) »| Domaine | Outils |
|---|---|
| Projets | list_projects, get_project (et ses compteurs) |
| Jobs et exécutions | list_jobs, get_job, list_runs, get_run, get_run_logs (par curseur), list_run_files, read_run_file |
| Workflows | list_workflows, get_workflow, get_workflow_run, get_workflow_run_steps (chaque tentative de chaque étape) |
| Orchestration | get_backfill (avancement d’un rejeu), list_triggers (les déclencheurs d’un projet, sans leur secret), list_trigger_events (ce qu’un déclencheur a lancé) |
| Buckets et fichiers | list_bucket_files, read_bucket_file (texte en clair, binaire en base64, borné) |
| Catalogue | list_catalog (couches → bases → tables → champs), get_table, search_catalog (les tables qu’un agent a le droit de lire) |
| Connexions de données | list_connections (bases, S3, SFTP, API HTTP, HDFS d’un projet — le secret est désigné, jamais rendu), test_connection, inspect_connection (tables ou fichiers, échantillon borné) |
| Lignage et qualité | list_lineage_datasets, get_lineage (le graphe autour d’un jeu, colonnes comprises), get_run_lineage, get_run_quality (le rapport des contrôles d’une exécution) |
| Entrepôts de données | list_datawarehouses, get_datawarehouse — jamais leurs identifiants de connexion ; query_sql (une requête SQL de lecture, lignes bornées) |
| Low-code | verify_lowcode (un DAG est-il valide ?), generate_lowcode_code (le code Pandas, PySpark… qu’il produit), list_templates (la galerie de gabarits), render_template (un gabarit rendu en DAG), verify_dag (un DAG typé est-il valide ?), get_preview (l’aperçu d’une étape) |
| Recherche | search (projets, jobs, workflows, tables… filtrés par les droits de l’agent) |
| Espaces de travail | list_workspaces, get_workspace (et ses derniers logs) |
| Expériences et modèles ML | list_experiments, get_experiment (et les métriques de ses runs), list_experiment_runs, get_experiment_run, list_models |
| Serving de modèles | list_endpoints, get_endpoint, predict (interroger un modèle servi ; rien n’est écrit) |
| Sessions interactives | list_sessions (les noyaux Python ouverts et leur état) |
| Passerelle LLM et RAG | llm_chat (une complétion au compte d’un projet, données personnelles masquées), list_llm_models, list_rag_indexes, search_documents, ask_documents (une réponse qui cite ses sources) |
| Assistants | generate_pipeline (un pipeline low-code décrit en langage naturel, vérifié par le moteur), describe_table (une description de table proposée), get_drift (la dérive d’un modèle servi) |
| Agents gouvernés | list_agents, list_agent_activity, get_approval_status (où en est une action soumise à validation), run_agent (un agent intégré : exploitation ou data engineer) |
| Bibliothèques et secrets | list_libraries, list_secrets — le nom des secrets, jamais leur valeur |
| Infrastructures et runtimes | list_infrastructures, list_runtimes, list_instance_types |
| Quotas, usages, coûts | list_quotas, list_usages, get_costs, get_costs_by_project, get_usage_metrics (CPU et mémoire réellement consommés) |
| Activité | list_activity (connexions, notifications, fil d’activité d’un projet), list_audit_actions (le journal des actions, humaines ou d’agent), export_audit_actions (toute une période) |
| Identité | list_identity_providers (configuration seulement, jamais un secret), get_mfa_policy |
| Facturation | list_invoices, get_invoice (et son PDF, borné), list_transactions, get_transaction, list_payment_methods (IBAN masqué), get_payment_provider — lecture seule |
verify_lowcode, generate_lowcode_code, render_template et verify_dag envoient un DAG ou un
gabarit au moteur low-code, qui valide, traduit ou rend sans rien enregistrer : ce sont des
lectures. test_connection et inspect_connection aussi : la plateforme ouvre la connexion, liste ou
lit un échantillon, et n’écrit rien. list_audit_actions exige le droit de lecture du
journal d’audit : un agent qui ne l’a pas reçoit un refus, pas une liste vide.
Écriture (24, plus 7 destructifs)
Section intitulée « Écriture (24, plus 7 destructifs) »| Outil | Ce qu’il fait |
|---|---|
upload_code |
Téléverse un fichier de code dans les librairies du tenant |
create_job |
Crée un job dans un projet (Python, Spark, low-code ou bash) |
run_job |
Lance une exécution, sans attendre son résultat |
schedule_job |
Pose ou remplace la planification cron d’un job |
rerun_run |
Relance une exécution passée, mêmes paramètres, même infrastructure |
run_workflow |
Lance un workflow |
upload_bucket_file |
Dépose un fichier dans un bucket |
set_table_description |
Documente une table du catalogue |
create_workspace |
Démarre un espace de travail Jupyter ou VS Code |
start_workspace |
Redémarre un espace arrêté, fichiers retrouvés tels quels |
stop_workspace |
Arrête un espace pour libérer ses ressources, fichiers gardés |
comment_activity |
Commente une activité du fil d’un projet, signé de l’identité de l’agent |
follow_resource |
Abonne l’identité de l’agent à une ressource (projet, job, workflow, table…) |
create_session |
Ouvre une session Python interactive (un noyau, dans son espace de travail) |
run_session_cell |
Exécute une cellule de code dans le noyau d’une session et rend ses sorties |
register_model_version |
Enregistre une version de modèle depuis une exécution du projet |
set_model_version_stage |
Passe une version de modèle d’étape (Staging, Production…) ou d’alias |
schedule_workflow |
Pose ou remplace la planification cron d’un workflow (fuseau, rattrapage des échéances manquées) |
create_backfill |
Rejoue la planification d’un job ou d’un workflow sur une période passée (1 à 5 exécutions à la fois) |
create_trigger |
Crée un déclencheur : fin d’une exécution, fichier déposé dans un bucket, ou webhook signé |
create_endpoint |
Déploie une version de modèle du registre derrière un endpoint |
scale_endpoint |
Change le nombre de répliques d’un endpoint (0 l’arrête, réversible) |
create_connection |
Déclare une connexion de données d’un projet ; son secret est désigné, jamais transmis |
preview_lowcode_step |
Lance l’aperçu d’une étape d’un pipeline low-code (une exécution éphémère) |
Destructifs, fermés par défaut (GRAAL_MCP_ALLOW_DESTRUCTIVE=1 pour les ouvrir) :
| Outil | Ce qu’il fait |
|---|---|
cancel_run |
Arrête une exécution de job en cours |
cancel_workflow_run |
Arrête une exécution de workflow en cours |
delete_workspace |
Supprime un espace de travail (fichiers internes perdus, buckets et tables intacts) |
delete_bucket_file |
Supprime jusqu’à 20 fichiers d’un bucket (jamais un répertoire) |
delete_session |
Supprime une session interactive et son noyau |
cancel_backfill |
Arrête un rejeu en cours |
delete_trigger |
Supprime un déclencheur |
Ce qu’un agent peut détruire, et qui le décide
Section intitulée « Ce qu’un agent peut détruire, et qui le décide »Par défaut, rien. C’est une décision d’architecture, tenue dans le code plutôt que par une convention :
- les sept outils qui annulent ou suppriment ne sont enregistrés que si l’exploitant du
serveur pose
GRAAL_MCP_ALLOW_DESTRUCTIVE=1; sans cela, l’agent ne les voit même pas ; - un test refuse qu’un autre outil appelle une opération
DELETE; - ouverts, ils sont annoncés au protocole avec
destructiveHint: true, pour que le client MCP demande confirmation, et ils passent par la même garde que toute écriture : droits du projet, notification au propriétaire ; - supprimer un projet, un job, un workflow, un secret ou une table, et retirer une planification, restent des gestes humains, dans la console. Aucun outil ne le fait, option ou non.
Un agent qui se trompe encombre ; il ne détruit que ce que vous lui avez explicitement permis de détruire.
Aucune valeur de secret ne sort
Section intitulée « Aucune valeur de secret ne sort »list_secrets rend des noms, pour qu’un job puisse les référencer ; aucun outil ne rend la valeur
d’un secret, un jeton, un mot de passe d’entrepôt, un kubeconfig ni une clé S3. Dans l’autre sens,
aucun outil n’en reçoit : une connexion de données désigne son secret par son identifiant, et un
mot de passe passé en argument est refusé avant tout appel. Chaque sortie est
réduite aux champs utiles, puis repassée par un filtre qui masque toute valeur rangée sous un nom de
secret.
Une seule exception, voulue et testée : create_trigger rend une fois le secret de signature
d’un webhook qu’il vient de créer, avec l’adresse du webhook et la règle de signature (HMAC-SHA256
de l’horodatage et du corps). La plateforme ne le rend qu’à la création ; aucun autre outil ne le
laisse sortir, et un secret perdu se renouvelle dans la console.
Les deux modes d’authentification
Section intitulée « Les deux modes d’authentification »| Mode | Qui détient le secret | D’où vient le tenant | Pour quoi |
|---|---|---|---|
| Jeton présenté | personne : l’agent apporte son jeton, le serveur le relaie | du realm porté par le jeton | l’usage normal, seul mode admis hors machine locale |
| Application graal | le serveur, avec les identifiants d’une « Application graal » par tenant | d’un en-tête ou de sa configuration | poste local : démonstration, mise au point |
Quatre garde-fous, dans cet ordre :
- Un jeton ne sort jamais de son tenant. L’émetteur (
iss) du jeton doit valoir exactement<url Keycloak>/realms/<realm du tenant>, vérifié avant le premier appel à l’API. - Le tenant n’est jamais un paramètre d’outil. Il vient de la session et part en en-tête ; un agent ne peut pas changer de tenant en changeant un argument.
- Le mode « Application graal » refuse de démarrer ailleurs que sur l’interface locale
(
127.0.0.1,::1,localhost) : sur un port exposé, quiconque l’atteint obtiendrait les droits de l’agent.GRAAL_MCP_REQUIRE_BEARER=1coupe entièrement ce mode. - La frontière de sécurité est le RBAC par projet de l’API, pas le serveur MCP. Restreindre un
agent à un projet par sa configuration (
GRAAL_MCP_PROJECT_ID) est un confort ; les droits réels sont ceux du compte derrière le jeton.
Ce que l’agent laisse comme trace
Section intitulée « Ce que l’agent laisse comme trace »Chaque écriture réussie est attribuée et notifiée :
- le job créé porte les étiquettes
graal.systems/created-by: agentetgraal.systems/agent: agent-mcp; - l’exécution lancée porte l’initiateur
agent-mcp; - une notification part vers le propriétaire du projet touché, plus les destinataires configurés. Une notification en échec n’annule pas l’action : le résultat rendu à l’agent dit explicitement si elle est passée.
Autrement dit, « qui a lancé ce run ? » a une réponse, portée par le run lui-même.
Le journal d’audit de la plateforme relie en plus chaque action à son acteur, humain ou agent (voir sécurité et gouvernance).
Brancher un agent
Section intitulée « Brancher un agent »Le serveur écoute en Streamable HTTP sur 127.0.0.1:7332 par défaut : point d’entrée
POST /mcp, vivacité GET /healthz (qui ne touche pas à l’API). Un transport stdio existe
aussi, pour un agent lancé en sous-processus.
Côté client MCP, la configuration tient en quelques lignes :
{ "mcpServers": { "graal": { "type": "http", "url": "http://127.0.0.1:7332/mcp", "headers": { "X-Graal-Tenant": "mon-tenant" } } }}En mode « jeton présenté », ajouter un en-tête Authorization: Bearer <jeton> : le tenant est alors
déduit du realm du jeton et l’en-tête X-Graal-Tenant devient inutile.
Pour lister les outils et l’opération REST derrière chacun sans rien démarrer :
graal-mcp --tools.
Configuration
Section intitulée « Configuration »| Variable | Rôle |
|---|---|
GRAAL_API_URL |
Racine de l’API, préfixe /api/v1 compris |
GRAAL_KEYCLOAK_URL |
Origine publique de Keycloak, qui sert à calculer l’émetteur attendu |
GRAAL_MCP_TENANT, GRAAL_MCP_REALM |
Le tenant servi et son realm |
GRAAL_MCP_CLIENT_ID, GRAAL_MCP_CLIENT_SECRET |
L’« Application graal » du tenant (mode Application) |
GRAAL_MCP_TENANTS |
La même chose en JSON, pour servir plusieurs tenants |
GRAAL_MCP_PROJECT_ID |
Restreint l’agent à un projet (confort, pas une isolation) |
GRAAL_MCP_NOTIFY_TO |
Destinataires supplémentaires des notifications |
GRAAL_MCP_REQUIRE_BEARER |
À 1, seul le mode « jeton présenté » est accepté |
GRAAL_MCP_ALLOW_DESTRUCTIVE |
À 1, enregistre les 7 outils destructifs (fermés par défaut) |
GRAAL_MCP_HOST, GRAAL_MCP_PORT |
Interface et port d’écoute (défaut 127.0.0.1:7332) |
Aucune de ces variables ne contient un secret d’utilisateur : le serveur n’en stocke aucun, et il ne conserve un jeton en mémoire que le temps de sa validité.
Erreurs, et comment un agent doit les lire
Section intitulée « Erreurs, et comment un agent doit les lire »Toutes les erreurs sont rendues sous la même forme, avec une catégorie stable que l’agent peut
exploiter : feature_disabled, unauthorized, forbidden, not_found, api_error, auth_error,
config_error, unknown_tenant, out_of_scope, precondition_failed, transport_error.
Le cas à connaître est le premier : une fonction désactivée sur l’installation répond 501 feature_disabled, jamais 404, et le message dit à l’agent de ne pas réessayer. Un 404 signifie
donc vraiment « cette ressource n’existe pas », ce qui rend les deux cas distinguables sans
deviner.
Brancher un client
Section intitulée « Brancher un client »Le serveur parle MCP sur HTTP. Un serveur exposé au-delà de la machine locale exige le jeton de l’appelant. Dans un client compatible (Claude Code, Claude Desktop, ou tout autre client MCP), la déclaration tient en quelques lignes :
{ "mcpServers": { "graal": { "type": "http", "url": "https://mcp.example.com/mcp", "headers": { "Authorization": "Bearer <jeton>" } } }}Le jeton décide de qui agit :
- le jeton d’une application, c’est-à-dire d’un compte de service (voir rôles et permissions) : l’agent travaille pour une équipe, avec les rôles que cette application détient sur ses projets ;
- le jeton d’une personne : l’agent agit pour le compte de cette personne, avec exactement ses droits.
Dans les deux cas, le locataire vient du domaine du jeton.
Ce qu’un agent peut faire, concrètement
Section intitulée « Ce qu’un agent peut faire, concrètement »Une fois branché, l’agent dispose de tous les outils. Une demande comme « prépare un job qui agrège la consommation régionale et planifie-le tous les matins à 6 h » se traduit chez lui en :
list_projects → trouver le projetupload_code → déposer le script dans le bucket du projetcreate_job → créer le job (python, spark, bash ou lowcode — rien d'autre)run_job → lancer une première exécutionget_run · get_run_logs → suivre, et lire les logs par curseurschedule_job → poser le cronChaque écriture pousse une notification au propriétaire du projet et allume le badge « Agent » dans la console. Vous n’avez pas à demander à l’agent ce qu’il a fait : c’est écrit.
Les garde-fous de l’agent
Section intitulée « Les garde-fous de l’agent »- Rien de destructif par défaut. Les 7 outils qui annulent ou suppriment ne sont enregistrés
qu’avec
GRAAL_MCP_ALLOW_DESTRUCTIVE=1, et un test refuse qu’un autre outil s’adosse à une opérationDELETE. - Un type de job connu.
create_jobn’accepte quepython,spark,bashetlowcode, et refuse avant tout appel réseau. - Aucune valeur inventée. Si le type d’instance ou l’identité ne peut pas être déduit de ce que la plateforme connaît, l’outil s’arrête et nomme le geste humain attendu plutôt que de forcer l’écriture.
Pour aller plus loin
Section intitulée « Pour aller plus loin »- Installation — ce qu’il faut pour faire tourner la plateforme
- Souveraineté et architecture
- Sécurité et gouvernance
- Les deux produits — les agents de l’Agentic Platform opèrent la Data Platform par ce serveur
- Page publique : Agents IA