Copilot Studio
Construire son propre serveur MCP et le brancher dans Copilot Studio
Un guide pratique pour construire un serveur Model Context Protocol custom en Python, l'exposer publiquement et le connecter à un agent Copilot Studio : les pièges du wizard, la dynamic tool discovery et l'angle gouvernance DLP dont personne ne parle.
La plupart des tutoriels Copilot Studio s'arrêtent aux connecteurs prebuilt et aux sources de connaissance. C'est suffisant pour une démo, mais cela masque le seul geste d'architecture qui distingue un maker d'un architecte : donner à un agent des tools que vous possédez, adossés à votre propre logique, tournant sur votre propre serveur.
Le Model Context Protocol (MCP) est le standard ouvert qui permet à un agent de découvrir et d'appeler des tools via HTTP. Copilot Studio le parle nativement. Cet article déroule la construction d'un petit serveur MCP en Python, son exposition sur internet, son câblage dans un agent via le maker portal, et prouve deux choses qui comptent en production : les tools répondent en moins d'une seconde, et de nouveaux tools apparaissent sans toucher à la configuration de l'agent. Au passage, il signale les comportements du wizard qui vous coûteront vingt minutes chacun si personne ne vous prévient.
Pourquoi le MCP custom est la brique de l'architecte
Les connecteurs prebuilt répondent à la question "ce que cet agent peut atteindre". Un serveur MCP custom répond à "ce que cet agent peut faire et qui n'existe que dans votre monde". Le statut d'un projet interne, le propriétaire d'un enregistrement dans un système sans connecteur Power Platform, un calcul de capacité qui vit dans un tableur que personne ne veut migrer : tout cela devient un tool dès l'instant où vous l'exposez en MCP.
La deuxième raison, c'est la gouvernance, et c'est la partie que l'on oublie. Quand vous enregistrez un serveur MCP dans Copilot Studio, il ne reste pas un endpoint HTTP volant. Il se matérialise comme un custom connector dans votre environnement. Ce seul fait signifie que chaque garde-fou déjà appliqué par votre tenant aux connecteurs (classification Data Loss Prevention, scoping d'environnement, contrôle de partage) s'applique désormais aux tools custom de votre agent sans aucun travail supplémentaire. On y revient plus bas.
Le serveur en une trentaine de lignes
L'implémentation de référence d'un serveur MCP en Python est FastMCP. Il gère le handshake du protocole, l'enregistrement des tools et le transport Streamable HTTP, ce qui vous laisse écrire de simples fonctions. Voici la forme d'un serveur qui expose trois tools sur des données de projet fictives :
from fastmcp import FastMCP
mcp = FastMCP("project_hub")
PROJECTS = {
"PRJ-002": {"name": "Helios Reporting", "status": "At Risk", "owner": None},
}
@mcp.tool
def get_project_status(project_id: str) -> dict:
"""Return status and owner for a project by id."""
return PROJECTS.get(project_id, {"error": "not found"})
@mcp.tool
def assign_owner(project_id: str, owner: str) -> dict:
"""Assign an owner to a project."""
PROJECTS[project_id]["owner"] = owner
return PROJECTS[project_id]
@mcp.tool
def list_open_risks() -> list:
"""List all open risks across projects."""
return [{"id": "RSK-11", "project": "PRJ-002", "severity": "High"}]
if __name__ == "__main__":
mcp.run(transport="http", host="127.0.0.1", port=8787)
Trois décorateurs, trois tools. La docstring n'est pas décorative : elle devient la description du tool que le planner de l'agent lit pour décider s'il doit appeler votre fonction. Écrivez-la comme un prompt, parce que c'en est un.
Un piège à poser dès maintenant, parce qu'il vous mordra lors de la démo de dynamic discovery plus loin : chaque @mcp.tool doit être défini avant l'appel mcp.run(). Une fonction tool ajoutée en bas du fichier, après run(), n'est jamais enregistrée. Le serveur démarre, a l'air sain, et sert silencieusement l'ancienne liste de tools.
L'exposer sans infrastructure
Le backend de l'agent tourne dans le cloud Microsoft, il lui faut donc une URL publique pour atteindre votre serveur. Pour un proof of concept, pas besoin d'une VM ni d'un reverse proxy. Un quick tunnel suffit :
cloudflared tunnel --url http://127.0.0.1:8787
Cela imprime une URL HTTPS publique de la forme https://your-tunnel.trycloudflare.com. Votre endpoint MCP est cet host plus /mcp. Deux choses à savoir : l'URL se régénère à chaque run, prenez-la donc fraîche dans la console à chaque fois, et le tunnel est éphémère, parfait pour un POC, pas pour ce que vous laissez tourner.
Avant de toucher à Copilot Studio, prouvez que le serveur répond. Un petit client MCP qui lance initialize puis tools/list contre l'URL publique /mcp doit renvoyer le handshake plus vos trois tools. Si ça marche à travers le tunnel, le wizard marchera aussi. Si ça ne marche pas, aucun clic dans le portail ne vous sauvera.
Le wizard, pas à pas, avec les pièges
Dans le maker portal, ouvrez votre agent et allez dans Tools > Add a tool > New tool > Model Context Protocol. Le wizard demande un nom de serveur, une description, l'URL du serveur et un mode d'authentification : None, API key ou OAuth 2.0. Pour un POC sans auth, renseignez l'URL du tunnel avec /mcp, choisissez None et créez.
Ça, c'est le chemin heureux. Voici ce que le chemin heureux ne vous dit pas.
La deuxième surprise : même avec l'authentification sur None, Copilot Studio exige quand même une connexion avant que l'agent puisse appeler le tool. La créer prend deux clics, mais au premier test, l'agent peut répondre par une carte adaptive qui dit quelque chose comme "connectons-nous d'abord". Quand cela arrive, ouvrez le gestionnaire de connexions depuis cette carte, confirmez la connexion et cliquez sur Réessayer. Ce n'est pas une erreur, c'est la plateforme qui câble le connecteur à votre session.
Un troisième, plus discret : le maker portal est une single-page app lourde. Les deep-links directs vers /tools peuvent geler le renderer. Naviguez depuis la page d'accueil de l'agent au lieu de coller une URL de tools, et laissez à l'agent un warm-up de vingt à vingt-cinq secondes à son premier appel pendant que le backend démarre à froid le connecteur.
La démo : données réelles, puis dynamic discovery
Le tool câblé, posez à l'agent une question que seul votre serveur peut résoudre : "What is the status of project PRJ-002?" L'agent construit un plan d'exécution, appelle get_project_status et renvoie la réponse ("Helios Reporting, At Risk, unassigned") directement depuis le process FastMCP qui tourne sur votre laptop, à travers le tunnel. En test, cet appel s'est résolu en environ 0,80 seconde, et le log serveur a montré les requêtes POST arrivant depuis la plage d'IP du backend Microsoft. Cet aller-retour est tout l'intérêt : l'agent a raisonné, choisi votre tool et l'a exécuté.
Maintenant la partie qui rend le MCP digne de l'effort. Ajoutez un quatrième tool au serveur (get_team_capacity, par exemple), redémarrez le serveur et ne changez rien dans l'agent. C'est ici que la promesse de la documentation ("Copilot Studio reflète dynamiquement les changements de votre serveur MCP") rencontre une nuance qui compte.
Cette distinction est facile à rater et gênante à se planter devant un public. Le jeu de tools est rafraîchi par session, pas par tour. Préparez votre démo autour d'un nouveau chat, pas d'un message de suivi.
L'angle gouvernance : la DLP gratuite
Voici le retour sur investissement pour quiconque doit rendre des comptes à un admin de plateforme. Parce que votre serveur MCP s'est enregistré comme custom connector, il apparaît dans le Power Platform Admin Center sous Policies > Data policies comme n'importe quel connecteur. Un admin peut le classer en Business, Non-Business ou Blocked, et le placer dans un groupe DLP aux côtés de SharePoint, Dataverse et le reste.
C'est la différence entre du shadow IT et une capacité gouvernable. Le même serveur qui donne à votre agent un tool sur mesure donne aussi à votre admin une ligne dans une politique DLP. Vous n'avez pas à choisir entre capacité et contrôle.
Du POC à la production : ce qui change vraiment
Le POC ci-dessus est honnête sur son statut de POC. Trois choses changent avant que cela devienne quelque chose que vous feriez tourner pour de vrais utilisateurs :
- Host. Le tunnel
trycloudflare.comest éphémère et régénère son URL à chaque run. La production a besoin d'un host stable (une container app, un named tunnel ou un App Service) avec une URL qui ne bouge pas, pour que la configuration du connecteur reste valide. - Authentification. "None" convient à des données de démo fictives. Les vrais tools ont besoin d'OAuth 2.0, que Copilot Studio prend en charge via la dynamic client registration. Personne ne devrait pouvoir appeler vos tools de capacité ou d'assignation de façon anonyme sur l'internet ouvert.
- Allowlist et DLP. Placez le connecteur dans le bon groupe DLP délibérément, restreignez les environnements qui peuvent l'utiliser, et traitez les changements de tools comme des changements d'un actif gouverné, parce que c'est ce qu'ils sont.
Le code du serveur change à peine entre POC et production. Ce qui change, c'est tout ce qui l'entoure : où il tourne, qui peut l'appeler et comment il est gouverné. Faites d'abord marcher les trente lignes, prouvez le handshake, faites tourner la démo de dynamic discovery, puis durcissez.
La raison d'apprendre cela maintenant, c'est que "ajouter une source de connaissance" est une compétence que n'importe qui acquiert en un après-midi, et "construire le tool que l'agent appelle" est celle qui fait de vous la personne qui conçoit le système plutôt que celle qui le configure.