Dataverse
Le plugin Dataverse Skills : ce que Microsoft vient d'officialiser
Le 21 juillet, Microsoft a publié un plugin qui transforme des années d'habitudes de praticiens Dataverse en huit skills nommés. La règle phare, managed SDK plutôt que raw HTTP, est exactement ce que les makers rigoureux faisaient déjà à la main. Mais un test hands-on a révélé deux defects inédits du SDK v1.0.0 et une surprise de gouvernance dont personne ne parle : l'endpoint MCP impose une allowlist de clients.
Le 21 juillet, Microsoft a publié un plugin Dataverse pour les coding agents. Le cadrage est modeste : une fiche marketplace, une commande d'installation, huit skills. En lisant les skills, quelque chose de plus intéressant apparaît. Microsoft a pris un ensemble d'habitudes que des praticiens Dataverse expérimentés ont construites à la main, sur des années, et les a couchées sur le papier comme des règles nommées et applicables.
La règle phare, beaucoup de makers la suivent déjà sans jamais l'énoncer : préférer un managed SDK plutôt que du raw HTTP. Ce test a soumis le plugin à une vraie charge de travail sur un tenant de développement, pour voir si la version officielle tient la route, ou si elle ne fait que réétiqueter ce que les gens faisaient de toute façon.
Réponse courte : les garde-fous sont la vraie valeur et ils sont vraiment bons. La couche d'exécution fonctionne mais ne fait pas mieux qu'à la main. Et le SDK jeune sur lequel elle s'appuie a deux defects à connaître avant de lui confier des changements de schéma.
Ce qu'est vraiment le plugin
Ce n'est pas un binaire. C'est de la connaissance procédurale plus quelques scripts helper, packagés pour qu'un coding agent puisse les charger. Le plugin livre huit skills écrits en langage naturel (chacun un SKILL.md) qui routent un agent à travers quatre surfaces d'exécution :
- un Dataverse MCP server (un proxy stdio vers
npx @microsoft/dataverse), - une Dataverse CLI (le package npm
@microsoft/dataverse), - un Python SDK (
PowerPlatform-Dataverse-Client), - et la PAC CLI pour le cycle de vie des solutions.
Les huit skills découpent le domaine proprement : dv-overview (routing transversal et hard rules), dv-connect (setup one-time et auth), dv-metadata (tables, colonnes, relations, forms, views), dv-data (create, bulk import, sample data), dv-query (reads, filtres, agrégats), dv-solution (cycle de vie solution via PAC), dv-admin (bulk delete, retention, org settings derrière une allowlist dure) et dv-security (roles, app users, business units).
Le coeur du plugin est un script appelé auth.py. Il résout l'authentification par ordre de priorité : un service principal depuis un fichier .env d'abord, puis le cache MSAL partagé de la Dataverse CLI, puis le device code interactif. Il appose aussi un User-Agent de télémétrie sur chaque appel pour que Microsoft attribue le trafic du plugin. Tout le reste du plugin passe par cette couche d'auth unique.
Le test : trois scénarios, un tenant
Le test a tourné sur un tenant de développement avec un préfixe d'artefacts pour ne rien percuter de l'existant. Trois scénarios : créer du metadata, exécuter des opérations data et query, et exporter une solution. Chaque write a été vérifié contre l'API brute, puis nettoyé et confirmé supprimé.
Atteindre le premier scénario a fait surgir le premier mur.
Mur 1 : l'installation et l'auth supposent un humain
L'installation Claude Code documentée est /plugin install dataverse@claude-plugins-official, une commande de session interactive qui déclenche un restart et un popup d'auth. Un agent autonome ne peut pas exécuter cette commande ni cliquer dans le popup. Le contournement a été de lire les skills directement depuis un clone et de les exécuter fidèlement, ce qui teste la matière du plugin plutôt que son packaging.
L'auth avait sa propre version du même problème. La voie non-interactive par défaut du plugin, le cache partagé de la Dataverse CLI, a échoué avec une erreur silencieuse d'acquisition de token parce que la CLI n'avait jamais été authentifiée, et le fix prescrit (dataverse auth create --deviceCode) est interactif. La voie service principal a marché et a produit un token valide. C'est la route non-interactive du plugin lui-même, donc les scénarios ont tourné dessus.
Mur 2 : le SDK produit une colonne date invalide
Le scénario A a créé un publisher, une solution, et une table poc_projet avec trois colonnes : une colonne texte primaire, une decimal, et une date. Les trois ont été créées et vérifiées avec un HTTP 200 contre les entity definitions. Puis une colonne choice a été ajoutée, et elle a échoué :
HttpError: Failure in generation Filteredpoc_projet for attribute poc_datedebut. Verify metadata for that attribute.
La root cause est un defect du SDK, pas du schéma. Le mapping de type "datetime" du SDK a produit une colonne avec Format=DateOnly mais DateTimeBehavior=None, une combinaison invalide. Cette combinaison casse la génération de la vue SQL Filtered de la table, et dès que cette vue ne peut plus se régénérer, tout ajout de colonne ultérieur sur cette table échoue, même un simple boolean. L'échec a persisté sur trois essais. C'est invisible jusqu'à ce qu'on ajoute une deuxième colonne, et là, cela verrouille le schéma de la table.
Mur 3 : un footgun query sans garde-fou
Les opérations data ont marché proprement : cinq lignes créées, une query filtrée et triée a retourné les bons enregistrements. Mais la surface query a un bord tranchant. Passer orderby="poc_budget desc" en simple string se fait éclater en une liste de caractères individuels, produisant un $orderby invalide et une erreur qui montre littéralement les caractères éclatés. La signature veut Optional[List[str]], donc orderby=["poc_budget desc"] est correct. Rien dans le SDK n'attrape le cas string. C'est un footgun, facile à contourner une fois connu, et facile à déclencher la première fois.
Mur 4 : le paramètre solution qui ne route pas
Le scénario C a exporté la solution et a réussi en douze secondes, mais le zip exporté était vide de la table. La root cause : le composant table était enregistré contre la solution Default, pas la solution cible. Autrement dit, client.tables.create(solution="poc_dvplugin") n'a pas routé la table dans la solution nommée. Elle a atterri dans Default et y est restée.
Le fix est venu de la propre voie PAC du plugin : ajouter la table comme solution component explicitement, puis re-exporter. Le deuxième export a produit un zip correct contenant la table et ses trois colonnes, vérifié dans customizations.xml. Le plugin s'est donc rattrapé, mais uniquement parce que PAC était là pour corriger le SDK. Le paramètre solution= est un no-op silencieux.
Mur 5 : l'endpoint MCP impose une allowlist de clients
Le finding le plus intéressant est venu de l'exercice du MCP server, la surface qui porte le tooling managé le plus riche. Il y a deux façons de l'atteindre.
Via le propre proxy du plugin, en s'authentifiant avec le service principal, le handshake a renvoyé un HTTP 403 verbatim :
The application '...' is not authorized to access MCP. For details on approved clients and instructions to enable additional applications, see: https://aka.ms/configuremcpclientlist
Le service principal s'est authentifié sans problème. C'est l'endpoint /api/mcp lui-même qui impose une allowlist de clients, et un service principal arbitraire n'y figure pas. C'est un contrôle de gouvernance que la plupart des équipes n'attendront pas. Le remède prescrit par le plugin est soit une opération admin one-time, dataverse mcp allow <appId>, soit l'usage d'un client pré-approuvé comme l'app de la Dataverse CLI elle-même.
Via la deuxième voie, le MCP server natif de la Dataverse CLI utilisant son app pré-approuvée, le handshake s'est terminé proprement. Le serveur s'est identifié en version 1.0.59.0, et tools/list a retourné 16 tools : read_query, create_table, update_table, delete_table, create_record, update_record, delete_record, search, upsert_skill, create_skill_resource, delete_skill, describe, search_data, init_file_upload, commit_file_upload, et file_download. Les trois tools destructifs portent un destructiveHint et exigent un flag d'approbation explicite. Un appel de tool en lecture seule a retourné des lignes sans erreur.
Une chose de plus que le deuxième passage a réglée : l'auth non-interactive est possible pour la CLI et le SDK (service principal, managed identity, et options federated existent tous), mais pas pour le MCP server via un service principal arbitraire. La voie MCP headless réaliste est un client pré-approuvé, ou un admin qui allowliste le principal d'abord.
Plugin contre travail à la main
Voici la comparaison honnête face à une approche manuelle légère (un petit helper de token plus PAC directement).
| Critère | Plugin Dataverse skills | Approche manuelle |
|---|---|---|
| Setup | Lourd : Python SDK, pandas, msal-extensions, la DV CLI, PAC, .NET, et auth interactive | Léger : un helper de token déjà en place |
| Vitesse | Bon une fois installé : reads SDK sous la seconde, export PAC autour de douze secondes | Comparable, sans setup |
| Garde-fous | Supérieur : hard rules, un gate bulk-delete, une allowlist dure sur les org settings, attribution télémétrie | Ad hoc, dans la tête de l'opérateur |
| Couverture | Large : metadata, data, query, solution, admin, security | Seulement ce qui est codé au cas par cas |
| Coût token | Élevé : les skills sont longues et se chargent en contexte, coûteuses sans context saver | Minimal |
| Fiabilité SDK | Deux defects et un footgun rencontrés dans ce run ; v1.0.0 est jeune | Connue et stable pour son périmètre |
Le verdict : adopter partiellement
Le plugin vaut d'être adopté pour ce qu'il standardise, pas pour ce qu'il exécute. Trois findings ont porté la recommandation.
D'abord, il formalise une règle que les makers rigoureux suivaient déjà : préférer une surface managée qui porte l'auth, le paging et le retry, et réserver le raw HTTP aux vraies lacunes du SDK. Voir cela écrit comme une hard rule applicable est utile en soi.
Ensuite, le Python SDK en v1.0.0 a de vrais defects sur le metadata authoring. Une colonne date peut verrouiller le schéma d'une table, et le paramètre solution= peut échouer silencieusement à router. Les deux sont invisibles jusqu'à ce qu'un export ou une deuxième colonne les exposent. Une couche managée ne supprime pas le besoin de vérifier le résultat brut.
Enfin, la valeur du plugin est les garde-fous et le routage intent-vers-skill, pas l'exécution. Export et create marchent aussi bien à la main. Ce que le plugin ajoute, ce sont des gates de sécurité déterministes et une table de routage, au prix d'une forte verbosité en contexte et d'une dépendance à un SDK jeune.
Une courte checklist pour une équipe de gouvernance qui évalue ceci :
- Empruntez les garde-fous dans vos propres skills ou standards : les hard rules, le gate bulk-delete, l'allowlist des org settings, et l'habitude de confirmer l'environnement avant le premier write. C'est la vraie valeur.
- Ne dépendez pas du SDK v1.0.0 pour du metadata authoring critique tant que les defects de la colonne date et de
solution=ne sont pas corrigés. Gardez PAC dans la boucle pour l'export et le travail de solution ; il était de toute façon nécessaire pour router la table. - Anticipez l'allowlist MCP avant tout usage headless ou CI. Un service principal arbitraire recevra un 403 de
/api/mcp. Soit allowlistez le principal avec une opération admin, soit utilisez un client pré-approuvé. - Budgetez le coût en contexte. Les skills sont longues et se chargent en contexte. Sans couche de context saving, elles coûtent cher à garder chargées sur une session.
Le plugin est une bonne chose. C'est Microsoft qui reconnaît, par écrit, ce que les praticiens disciplinés croyaient déjà. Entrez simplement en sachant que le SDK dessous est une version un, et que la porte MCP a un verrou que personne n'a mis sur la slide.