SharePoint
SharePoint Copilot Apps : ce que la démo de la communauté passe sous silence
Le pitch dit tester SharePoint Copilot Apps en quelques minutes, sans code, sans licence. Je l'ai pris au mot sur un tenant de dev et j'ai documenté le vrai parcours maker. Le sans licence est vrai et sous-vendu. Mais une chaîne de murs non documentés sépare le clone d'un agent qui tourne, et le dernier ne peut être scripté par aucun token, pas même l'Azure CLI de Microsoft.
Le pitch communautaire pour SharePoint Copilot Apps est net : télécharger un sample, copier un fichier solution dans le tenant, ajouter l'agent, et tester l'UX dans Microsoft 365 Copilot. Pas de licence Copilot. Pas de code. Quelques minutes. Les hashtags en dessous racontent la vraie stack : SPFx, MCP, agents.
Je l'ai pris au mot. Tenant de dev neuf, sample officiel, aucun raccourci. Je voulais savoir si un consultant pouvait vraiment reproduire la démo, ou si le "en minutes" était une slide qui sautait le setup.
Voici la version honnête. Une partie de la promesse est vraie et même sous-vendue. Le reste cache une chaîne de murs que personne ne mentionne sur scène, et le dernier réserve une vraie surprise : une seule action de tout le parcours qu'aucune automatisation ne peut exécuter, prouvé contre l'outillage de Microsoft lui-même.
La seule promesse qui tient : pas de licence Copilot
Directement depuis les prérequis du tutorial Microsoft Learn :
"During the public preview, no Microsoft 365 Copilot license is required to build, deploy, or run SharePoint Copilot Apps."
C'est réel, et c'est le fait le plus intéressant de toute la fonctionnalité. Vous pouvez construire une app custom qui s'affiche dans le canvas Microsoft 365 Copilot, la déployer dans votre tenant, et la faire tourner, sans payer un seul siège Copilot pendant la preview. Pour quiconque évalue du développement autour de Copilot, ça supprime le mur de licence habituel avant la première expérimentation. La communauté a eu raison sur ce point, puis l'a enterré sous le "sans code".
Les deux parcours, et pourquoi ils ne sont pas identiques
Le "copier un fichier" écrase deux parcours très différents en une seule phrase.
Le parcours démo. On vous remet un package .sppkg déjà construit. Si votre tenant a déjà un App Catalog, vous l'uploadez, vous l'activez, vous cliquez un bouton. Là, ça prend vraiment quelques minutes. C'est le parcours de la démo sur scène.
Le parcours maker. Vous clonez le sample et vous construisez le .sppkg from source. C'est ce qu'un consultant fait réellement quand il veut modifier l'app, pas juste faire tourner celle d'un autre. La première fois, comptez une à deux heures. Ensuite, le build lui-même est de 45 secondes et 172 tests passent au vert.
L'écart entre ces deux parcours, c'est tout l'article. Le parcours démo suppose un App Catalog qui peut ne pas exister et un package construit par quelqu'un d'autre. Le parcours maker, c'est là que vivent les murs non documentés.
Ce que sont vraiment les SharePoint Copilot Apps
La fonctionnalité arrive avec SPFx 1.24 (en beta en juillet 2026) sous un modèle que Microsoft appelle "MCP Apps". Les composants dérivent d'une classe de base, BaseCopilotComponent. Chaque app expose des tools avec des schémas d'entrée typés, et les tools affichent une UX custom dans deux display modes, inline ou fullscreen, dans le canvas Microsoft 365 Copilot.
Le build produit deux artefacts, pas un. Il y a le .sppkg qui atterrit dans SharePoint, et un package d'agent déclaratif séparé qui se synchronise vers le tenant agent catalog. Ce deuxième artefact est celui qui apparaît dans Microsoft 365 Copilot après déploiement. Comprendre qu'il y a deux sorties compte, parce que l'étape "ajouter l'agent" du pitch est une vraie action de déploiement séparée, pas un renommage de l'upload.
Mur 1 : Node 22 LTS ou rien
La toolchain SPFx 1.24 tourne sur Heft, et Heft veut Node 22 LTS spécifiquement. Le sample déclare engines: ">=22.14.0 < 23.0.0".
Ma machine tournait sous Node 24, la ligne current non-LTS. SPFx ne supporte que les LTS, donc le générateur refuse tout au-dessus de 22. Il n'y avait ni nvm ni install side-by-side, juste un seul Node sur le PATH.
Le fix est un Node 22 portable préfixé au PATH du shell, laissant le Node global intact :
node --version
# v24.14.0 -> rejete par SPFx 1.24
# Node 22 portable, prefixe au PATH pour ce shell uniquement
export PATH="/c/Users/you/tools/node22:$PATH"
node --version
# v22.23.1 -> satisfait engines ">=22.14.0 <23.0.0"
Rien dans le pitch communautaire ne mentionne une contrainte de version Node. C'est la première chose qui casse.
Mur 2 : le lockfile pointe vers un feed Microsoft privé
C'est le piège qui coûterait à n'importe qui reproduisant le sample, et c'est le moins évident.
Clonez le sample, lancez npm install, et regardez-le échouer en environ 19 secondes :
npm error Unable to authenticate, your authentication token seems to be invalid.
La partie déroutante : npm config get registry renvoie le registre public, et il n'y a aucun .npmrc local. Alors d'où vient ce mur d'auth ?
Le package-lock.json commité. Il épingle 1288 URLs de tarball vers un feed Azure DevOps interne, onedrive.pkgs.visualstudio.com, le feed privé de l'équipe OneDrive/ODSP de Microsoft. Zéro URL ne pointe vers le registre public. Le lockfile a été généré à l'intérieur du réseau Microsoft et commité tel quel. Quiconque hors de ce réseau se prend le mur d'auth immédiatement.
Le fix est contre-intuitif, parce qu'il implique de se méfier d'un lockfile commité :
mv package-lock.json package-lock.PRIVATE-FEED.json.bak
rm -rf node_modules
npm install --registry=https://registry.npmjs.org/
npm re-résout depuis le registre public et écrit un lockfile propre. Le package.json lui-même est sain, des plages de versions normales, donc la résolution publique réussit sans conflit. Ce second install a pris 2 minutes 49 secondes pour 1290 packages, zéro erreur de peer-dependency. L'audit remonte des vulnérabilités transitives, dont aucune ne bloque le build.
Mur 3 : l'App Catalog doit exister d'abord
Les prérequis Learn le disent clairement : "A Microsoft 365 tenant with the SharePoint app catalog provisioned." Le pitch communautaire non.
Sur un tenant neuf, l'App Catalog n'est pas là. Les tenant settings renvoient un CorporateCatalogUrl vide, et les chemins du site catalog renvoient 404. Sans lui, vous ne pouvez ni déployer le .sppkg ni publier l'agent. Point final.
Le cadrage communautaire laisse entendre qu'on dépose juste un fichier quelque part. En réalité, vous provisionnez d'abord de l'infrastructure au niveau tenant. La bonne nouvelle, et ça contredit l'hypothèse "admin manuel uniquement", c'est que c'est scriptable. Un seul appel PnP PowerShell contre l'URL admin le provisionne :
# app-only, auth par certificat, PnP PowerShell 3.x sur pwsh 7
Register-PnPAppCatalogSite -Url "https://your-tenant.sharepoint.com/sites/appcatalog" `
-Owner "admin@your-tenant.onmicrosoft.com" -TimeZoneId 3
Le provisioning tourne en environ deux minutes de commandes, puis se propage. Ensuite, Get-PnPTenantAppCatalogUrl renvoie l'URL du catalog et le site est actif.
Mur 4 : le déploiement headless, et la seule vraie étape humaine
Deux surprises plus petites clôturent le déploiement.
D'abord, Add-PnPApp est inutilisable en headless. Même avec -Overwrite -SkipFeatureDeployment, en add seul ou en add-and-publish, il force un prompt interactif et meurt sur "PowerShell is in NonInteractive mode." Le contournement est du SharePoint REST brut avec un token certificat app-only, en sautant la cmdlet entièrement :
POST /_api/web/tenantappcatalog/Add(overwrite=true, url='time-off.sppkg')
body = <octets du package>
POST .../AvailableApps/GetById('<app-id>')/Deploy
body = {"skipFeatureDeployment": true}
Ce chemin a déployé le package construit from source. La vérification a renvoyé Deployed=True, Enabled=True, Version=1.0.0.0, No errors. Le .sppkg construit sur mesure était live dans le tenant, upload et deploy en moins de 30 secondes une fois le token forgé.
Ensuite, et c'est là que tout se joue : synchroniser l'agent vers le catalog Microsoft 365 Copilot. L'endpoint SyncSolutionToTeams rejette les tokens app-only avec un HTTP 400, code 10005, "Authentication method is not allowed." La première hypothèse, c'est qu'il est simplement delegated-only, donc qu'un token d'utilisateur connecté passerait. Cette hypothèse est fausse, et le prouver est le résultat le plus intéressant de toute la manip. Plus de détails plus bas, parce que ça s'avère être la seule étape humaine irréductible de tout le parcours.
La mécanique du clic lui-même mérite d'être connue, parce que l'UI la cache. Le tenant sert l'App Catalog en interface classique. Le bouton n'est pas dans la barre de commandes de la liste (le "Sync" qui s'y trouve, c'est le sync OneDrive). Vous sélectionnez la ligne du package, vous ouvrez l'onglet FILES du ruban classique, et le bouton "Sync to Teams" est à droite. Environ 30 secondes une fois trouvé, mais facilement 15 minutes perdues à le chercher la première fois. Les walkthroughs communautaires ne montrent jamais cet écran.
Mur 5 : le "build vert" livre un package incomplet
Un build qui passe 172 tests et sort en exit zero a quand même livré une app cassée. Après déploiement du .sppkg from source, l'agent apparaissait dans Microsoft 365 Copilot et affichait un canvas blanc.
Le diagnostic est venu de la comparaison des deux zips. Le package from source contenait 23 entrées et 11 assets. Le package pré-buildé Microsoft en contenait 26 et 14. Des bundles de composants entiers manquaient au build local, dont le request component. L'agent déclaratif référençait des composants absents du package, donc le canvas remontait vide. Un build Heft vert ne garantit pas un package complet. Le fix : redéployer le package officiel pré-buildé par-dessus, même nom, overwrite true. Déployé, aucune erreur.
Mur 6 : l'App Catalog bloque sa propre configuration
Le provisioning des listes de données du sample et d'un pointeur de propriété tenant s'est heurté à un Access denied 0x80070005 sur le write de property-bag. Le site App Catalog est livré en NoScript site par défaut, et le NoScript bloque net les writes de property-bag.
Le fix app-only, c'est deux appels : lever le NoScript, puis poser la storage entity.
Set-PnPSite -Identity "https://yourtenant.sharepoint.com/sites/appcatalog" -NoScriptSite:$false
Set-PnPStorageEntity -Key "TimeOffSite" -Value "/sites/timeoff"
Mur 7 : le snapshot d'agent se fige sur l'ancienne version
Canvas blanc encore, cette fois avec un package complet, des données réelles, et le pointeur posé. La console du navigateur a donné la vraie explication : le conteneur McpWidgetHost bootait (iframe, sandbox, CSP tous OK), mais la ressource du composant renvoyait $value 404.
La cause est documentée dans les release notes SPFx 1.24 : si la version ne change pas, Copilot peut continuer à utiliser l'agent précédemment synchronisé. Deux redéploiements avaient tous gardé Version=1.0.0.0, donc le snapshot d'agent pointait toujours les ressources du premier package incomplet. Le fix, c'est de la chirurgie de package : bumper AppManifest.xml vers 1.1.0.0 et le manifest.json Teams dans le zip de l'agent vers 1.1.0, repacker, redéployer, puis re-sync et ouvrir une nouvelle conversation (l'ancienne garde le binding périmé).
La leçon se généralise : tout changement de contenu d'un Copilot App exige un bump de version, sinon vous avez un cache d'agent silencieux.
Le dernier mur : même le client first-party de Microsoft ne peut pas scripter la sync
Retour à SyncSolutionToTeams et son 10005. Le réflexe après "l'app-only est rejeté", c'est un token d'utilisateur délégué. Il échoue de la même façon. Puis le test le plus fort possible : l'access token de l'Azure CLI lui-même, décodé pour confirmer que c'est bien le vrai client first-party Microsoft (app_displayname: Microsoft Azure CLI), porteur d'un contexte utilisateur réel et des scopes SharePoint corrects.
Même POST SyncSolutionToTeams(id=2), même résultat :
{"error":{"code":"10005","message":{"lang":"en-US","value":"Authentication method is not allowed."}}}
[HTTP 400]
L'endpoint a résolu l'item (pas de 404), donc l'échec est au gate d'authentification, pas à la résolution d'item. SyncSolutionToTeams n'est pas seulement delegated-only. C'est un mur applicatif réservé à la poignée de clients SharePoint first-party derrière le bouton UI "Sync to Teams" et le cmdlet Sync-SPOSolutionToTeams. L'Azure CLI de Microsoft lui-même, délégué, correctement scopé, est refoulé par le même 10005. Aucun token forgeable sans navigateur interactif ou prompt device-code ne franchit ce gate.
Ce qui est vraiment sous-vendu
Une fois le setup écarté, la fonctionnalité est plus intéressante que ce que le pitch laisse croire.
Le hosting est automatique et in-tenant. Le build bundle les assets client-side dans le package. Pas de CDN externe à configurer, pas de hosting séparé à monter. L'app est servie depuis votre tenant.
L'agent déclaratif se synchronise tout seul. Une fois que vous cliquez "Add to Teams", l'agent atterrit dans le tenant agent catalog et apparaît dans Microsoft 365 Copilot. Pas de jonglage de manifest dans un portail séparé.
Le modèle de programmation est réel. MCP Apps plus BaseCopilotComponent, des inputs de tools typés, et deux display modes vous donnent un vrai modèle de composant pour l'UX du canvas Copilot, pas un vernis low-code. Pour un développeur, c'est la partie qui vaut l'heure de setup.
Le verdict
Le "sans licence" est vrai et c'est la meilleure raison de regarder ça maintenant, tant que la preview garde la porte ouverte. Le "sans code, copier un fichier, minutes" ne décrit que le parcours démo, et seulement si votre App Catalog existe déjà.
Pour le parcours maker, comptez plus que la première heure que vous imaginez. Vous croiserez Node 22, un lockfile câblé vers un feed privé, un App Catalog manquant, et un accroc de déploiement headless, puis un build vert qui livre un package incomplet, un site NoScript qui bloque sa propre configuration, et un snapshot d'agent qui se fige sur l'ancienne version tant que vous ne le bumpez pas. Ensuite, la boucle est rapide : build de 45 secondes, 172 tests au vert, scriptez presque tout.
Presque. La seule chose que vous ne pouvez pas scripter, c'est le clic "Sync to Teams". Ni en app-only, ni avec un token délégué tierce, ni même avec l'Azure CLI first-party de Microsoft, que l'endpoint rejette tous avec le même 10005. C'est la forme honnête de la fonctionnalité : un parcours qui s'automatise de bout en bout sauf une action humaine irréductible tout à la fin.
Aucun de ces murs n'est rédhibitoire. Tous sont invisibles dans le cadrage communautaire. Si vous allez essayer SharePoint Copilot Apps, et la fenêtre de preview sans licence est une bonne raison, entrez en connaissant le vrai parcours, y compris l'unique clic qu'aucun script ne prendra à votre place.