Skip to main content
Skip to content

Utilisation de serveurs MCP avec le SDK GitHub Copilot

Le sdk Copilot peut s'intégrer à des serveurs MCP (protocole de contexte de modèle) pour étendre les fonctionnalités de l'assistant à des outils externes. Les serveurs MCP s’exécutent en tant que processus distincts et exposent des outils (fonctions) que Copilot peuvent appeler pendant les conversations.

Remarque

Il s’agit d’une fonctionnalité en constante évolution. Consultez le problème n° 36 pour une discussion en cours.

Qu’est-ce que MCP ?

Le protocole MCP (Model Context Protocol) est un standard ouvert pour connecter des assistants IA à des outils et sources de données externes. Les serveurs MCP peuvent :

  • Exécuter du code ou des scripts
  • Interroger des bases de données
  • Accéder aux systèmes de fichiers
  • Appeler des API externes
  • Et bien plus encore

Types de serveurs

Le Kit de développement logiciel (SDK) prend en charge deux types de serveurs MCP :

TypeDescriptionCas d’usage
Local/StdioS’exécute en tant que sous-processus, communique via stdin/stdoutOutils locaux, accès aux fichiers, scripts personnalisés
HTTP/SSEServeur distant accessible via HTTPServices partagés, outils hébergés dans le cloud

Configuration

Inspectez la configuration et le cycle de vie

session.rpc.mcp.listConfigured() est une lecture passive de l’inventaire sans paramètre. Il ne démarre pas ou ne redémarre pas les serveurs, authentifie, applique la stratégie de bac à sable ou attend le démarrage du serveur. La découverte de la configuration et le filtrage de stratégie déterminent les serveurs qui apparaissent et leurs valeurs enabled.

Ce RPC expérimental nécessite une version du SDK/runtime alignée. Sa réponse préliminaire antérieure permettait d’omettre live ; les SDK utilisant le contrat de cycle de vie requis ne prennent pas en charge cette forme de réponse plus ancienne. Lorsque vous fournissez un runtime externe, mettez-le à jour en même temps que le Kit de développement logiciel (SDK). La version globale du protocole reste inchangée et n’identifie pas cette révision expérimentale du contrat.

Chaque entrée inclut live.status. Un serveur configuré sans serveur runtime matérialisé signale l’état existantnot_configured, même lorsque true est enabled. Un serveur d’exécution qui a été explicitement arrêté signale stopped. Les serveurs matérialisés signalent leur cycle de vie réel, y compris pending, connected, needs-auth, et failed; les défaillances incluent live.error lorsqu’elles sont disponibles.

La configuration et le cycle de vie décrivent différents faits. Si l’URL, l’authentification ou la source de configuration d’un serveur change pendant qu’une ancienne connexion reste active, l’inventaire signale toujours le cycle de vie réel de cette connexion. Le statut connected ne signifie pas que la dernière configuration a été appliquée. Le runtime gère cette observation ; les clients ne doivent pas remplacer l’état mis en cache uniquement en fonction du nom du serveur.

L’inventaire retente le remplacement concurrent du graphe un nombre limité de fois et retourne une erreur s’il ne peut pas obtenir un instantané cohérent. Il ne manque jamais d’utiliser live pour masquer une graph race.

Une résolution par nom uniquement startServer ou restartServer résout et initialise uniquement le serveur demandé après la reprise d’une session à froid sans appeler d’abord list. Cela inclut les configurations utilisateur et plug-in et le serveur intégré GitHub configuré via githubMcpToolConfig. Il ne démarre pas d’autres serveurs configurés et ne passe pas outre l’état désactivé, les exigences d’authentification, la stratégie de bac à sable ou la priorité de la source de configuration.

Node.js / TypeScript

import { CopilotClient } from "@github/copilot-sdk";

const client = new CopilotClient();
const session = await client.createSession({
    model: "gpt-5",
    mcpServers: {
        // Local MCP server (stdio)
        "my-local-server": {
            type: "local",
            command: "node",
            args: ["./mcp-server.js"],
            env: { DEBUG: "true" },
            cwd: "./servers",
            tools: ["*"],  // "*" = all tools, [] = none, or list specific tools
            timeout: 30000,
        },
        // Remote MCP server (HTTP)
        "github": {
            type: "http",
            url: "https://api.githubcopilot.com/mcp/",
            headers: { "Authorization": "Bearer ${TOKEN}" },
            tools: ["*"],
        },
    },
});

Python

import asyncio
from copilot import CopilotClient
from copilot.session import PermissionHandler

async def main():
    client = CopilotClient()
    await client.start()

    session = await client.create_session(on_permission_request=PermissionHandler.approve_all, model="gpt-5", mcp_servers={
        # Local MCP server (stdio)
        "my-local-server": {
            "type": "local",
            "command": "python",
            "args": ["./mcp_server.py"],
            "env": {"DEBUG": "true"},
            "cwd": "./servers",
            "tools": ["*"],
            "timeout": 30000,
        },
        # Remote MCP server (HTTP)
        "github": {
            "type": "http",
            "url": "https://api.githubcopilot.com/mcp/",
            "headers": {"Authorization": "Bearer ${TOKEN}"},
            "tools": ["*"],
        },
    })

    response = await session.send_and_wait("List my recent GitHub notifications")
    print(response.data.content)

    await client.stop()

asyncio.run(main())

Go

package main

import (
    "context"
    "log"
    copilot "github.com/github/copilot-sdk/go"
)

func main() {
    ctx := context.Background()
    client := copilot.NewClient(nil)
    if err := client.Start(ctx); err != nil {
        log.Fatal(err)
    }
    defer client.Stop()

    session, err := client.CreateSession(ctx, &copilot.SessionConfig{
        Model: "gpt-5",
        MCPServers: map[string]copilot.MCPServerConfig{
            "my-local-server": copilot.MCPStdioServerConfig{
                Command: "node",
                Args:    []string{"./mcp-server.js"},
                Tools:   []string{"*"},
            },
        },
    })
    if err != nil {
        log.Fatal(err)
    }
    defer session.Disconnect()

    // Use the session...
}

.NET

using GitHub.Copilot;

await using var client = new CopilotClient();
await using var session = await client.CreateSessionAsync(new SessionConfig
{
    Model = "gpt-5",
    McpServers = new Dictionary<string, McpServerConfig>
    {
        ["my-local-server"] = new McpStdioServerConfig
        {
            Command = "node",
            Args = new List<string> { "./mcp-server.js" },
            Tools = new List<string> { "*" },
        },
    },
});

Désactivation des serveurs configurés par session

Définissez disabledMcpServers sur les noms exacts des serveurs MCP qui ne doivent pas être exécutés dans une session. Le paramètre est limité à la demande de création ou de reprise individuelle ; elle ne modifie pas les paramètres MCP globaux ou la configuration du serveur.

const session = await client.createSession({
    mcpServers: {
        filesystem: { type: "local", command: "npx", args: ["-y", "@modelcontextprotocol/server-filesystem", "."] },
        github: { type: "http", url: "https://api.githubcopilot.com/mcp/" },
    },
    disabledMcpServers: ["github"],
});
SDKPropriété de configuration
Node.jsdisabledMcpServers
Pythondisabled_mcp_servers
GoDisabledMCPServers
.NETDisabledMcpServers
JavasetDisabledMcpServers(...)
Rustwith_disabled_mcp_servers(...)

Lors de la création de session et d’une reprise à froid , les serveurs désactivés ne sont pas démarrés et le runtime ne lance pas leur authentification. Un cv résident ne peut pas annuler un serveur que le runtime a déjà généré. Les noms sont mis en correspondance exactement.

Lister et récupérer des prompts

L’espace de noms MCP généré expérimental expose les méthodes session.mcp.prompts.list JSON-RPC sur le protocole filaire et session.mcp.prompts.get. Utilisez les accesseurs sdk spécifiques au langage ci-dessous pour les appeler. Ces méthodes ciblent un serveur connecté dans la session en cours ; elles ne combinent pas les résultats de plusieurs serveurs.

SDKRépertorier les requêtesAfficher une invite de commande
Node.jssession.rpc.mcp.prompts.list(...)session.rpc.mcp.prompts.get(...)
Pythonsession.rpc.mcp.prompts.list(...)session.rpc.mcp.prompts.get(...)
Gosession.RPC.MCP.Prompts().List(...)session.RPC.MCP.Prompts().Get(...)
.NETsession.Rpc.Mcp.Prompts.ListAsync(...)session.Rpc.Mcp.Prompts.GetAsync(...)
Javasession.getRpc().mcp.prompts.list(...)session.getRpc().mcp.prompts.get(...)
Rustsession.rpc().mcp().prompts().list(...)session.rpc().mcp().prompts().get(...)

La liste nécessite serverName et accepte une option opaque cursor. Chaque appel retourne une page de définitions d’invite typées et éventuellement nextCursor. Transmettez ce jeton de pagination dans un autre appel de liste pour demander la page suivante. Les définitions incluent des noms, des titres facultatifs et des descriptions, ainsi que des définitions d’arguments. L’indicateur d’argument omis required reste distinct de false.

L’affichage d’une invite de commande nécessite serverName et promptName, avec un dictionnaire facultatif arguments dont les valeurs sont des chaînes. Omettre arguments laisse les arguments de la requête MCP absents ; le passage de {} envoie un dictionnaire explicitement vide. Le runtime conserve cette distinction pour le serveur. Le résultat contient une description facultative et des messages ordonnés avec des rôles typés et json contentopaque. Inspectez le type de chaque bloc de contenu avant de l’interpréter ; le contenu n’est pas aplati en texte. Les ressources imbriquées, les annotations, les métadonnées, les types de contenu inconnus et les champs de contenu supplémentaires conservent leur structure JSON. Les extensions serveur sur les descripteurs, messages et enveloppes de résultats typés sont disponibles sous additionalProperties. Une extension de serveur elle-même nommée additionalProperties est conservée en tant qu’entrée à l’intérieur de cette map, et non fusionnée dans celle-ci. Le discriminateur de premier niveau resultType: "complete" du protocole est consommé par le runtime ; il n’est pas retourné en tant que champ sdk ou extension de serveur. Les requêtes échouent explicitement si le runtime ne peut pas conserver la réponse brute, y compris les trames de réponse stdio dépassant la limite de capture de 1 Mio.

Les messages de prompt retournés ne sont pas automatiquement envoyés au modèle ou utilisés pour exécuter des outils ou récupérer des ressources référencées. Votre application décide comment utiliser le résultat. Lors de la génération de ce résultat, un serveur MCP peut demander l’échantillonnage ou la sollicitation par le biais de continuations de plusieurs allers-retours. Ces demandes utilisent les mêmes répondeurs hôtes configurés que d’autres opérations MCP ; la récupération de prompt n’accorde pas d’autorisation supplémentaire et ne contourne pas la décision de l’hôte. L’absence d’un gestionnaire ou une erreur du gestionnaire entraîne l’échec de la requête, et un refus d’élicitation est retourné au serveur inchangé. Les continuations d’état uniquement n’appellent aucun des deux répondeurs.

Après un événement de session existant mcp.prompts.list_changed , répertoriez à nouveau les messages du serveur nommé pour actualiser la vue de votre application.

Application d’une stratégie de bac à sable avant le démarrage de MCP

Passez sandboxConfig à createSession ou resumeSession pour appliquer une politique de sandbox pendant la construction, avant le démarrage des serveurs MCP détectés via root-config, plugin ou configuration explicite. Il accepte la même configuration que session.rpc.options.update({ sandboxConfig }).

const sandboxConfig = {
    enabled: true,
    sandboxMcpServers: true,
    userPolicy: { network: { allowOutbound: false } },
};
const session = await client.createSession({
    onPermissionRequest: approveAll,
    sandboxConfig,
});

Réappliquez la configuration sur une reprise à froid ; l’omission préserve la politique actuelle d’une session résidente. Une reprise du résident ne peut pas annuler les processus qui ont déjà démarré. La validation de stratégie managée utilise la même sémantique d’hôte direct qu’une mise à jour d’options : une demande en conflit entraîne l’échec de la création ou de la reprise au lieu de revenir à une session sans bac à sable, avec data.code défini sur managed_sandbox_policy_conflict dans l’erreur JSON-RPC. Le runtime n’attribue pas de valeur dérivée des paramètres sandboxConfigSource à cette configuration d’hôte explicite.

Un élément identique ultérieur options.update({ sandboxConfig }) ne redémarre pas les serveurs MCP, notamment lorsque la stratégie managée a normalisé la configuration. Une autre politique effective du bac à sable MCP conserve le comportement de redémarrage habituel.

Vérifiez le runtime version renvoyé par ConnectResult (server.connect) par rapport à la version minimale du runtime requise par votre application pour cette fonctionnalité. La version du protocole SDK n’est pas un signal de capacité par fonctionnalité. Les runtimes plus anciens ignorent les champs de création/reprise inconnus, de sorte qu’une application qui les prend en charge puisse envoyer sandboxConfig à l’ouverture et le répéter dans une mise à jour ultérieure des options. Cette mise à jour est inoffensive sur les runtimes compatibles, mais les anciens runtimes peuvent toujours démarrer MCP avant l’arrivée de la mise à jour ; exigez une version de runtime compatible lorsque l’isolement dès le premier processus est obligatoire.

Configuration de l’outil

Vous pouvez contrôler les outils disponibles pour un serveur MCP à l’aide du tools champ.

Autoriser tous les outils

Permet "*" d’activer tous les outils fournis par le serveur MCP :

tools: ["*"]

Autoriser des outils spécifiques

Fournissez la liste des noms d’outils pour restreindre l’accès :

tools: ["bash", "edit"]

Seuls les outils répertoriés seront disponibles pour l’agent.

Désactiver tous les outils

Utilisez un tableau vide pour désactiver tous les outils :

tools: []

Notes

  • Le tools champ définit les outils autorisés.
  • Il n’existe pas de configuration distincte allow ou disallow : l’accès aux outils est contrôlé directement par cette liste.

Démarrage rapide : serveur MCP du système de fichiers

Voici un exemple de travail complet utilisant le serveur MCP officiel @modelcontextprotocol/server-filesystem :

import { CopilotClient } from "@github/copilot-sdk";

async function main() {
    const client = new CopilotClient();

    // Create session with filesystem MCP server
    const session = await client.createSession({
        mcpServers: {
            filesystem: {
                type: "local",
                command: "npx",
                args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"],
                tools: ["*"],
            },
        },
    });

    console.log("Session created:", session.sessionId);

    // The model can now use filesystem tools
    const result = await session.sendAndWait({
        prompt: "List the files in the allowed directory",
    });

    console.log("Response:", result?.data?.content);

    await session.disconnect();
    await client.stop();
}

main();

Output:

Session created: 18b3482b-bcba-40ba-9f02-ad2ac949a59a
Response: The allowed directory is `/tmp`, which contains various files
and subdirectories including temporary system files, log files, and
directories for different applications.

Conseil

Vous pouvez utiliser n’importe quel serveur MCP à partir du répertoire des serveurs MCP. Les options populaires incluent @modelcontextprotocol/server-github, @modelcontextprotocol/server-sqliteet @modelcontextprotocol/server-puppeteer.

Options de configuration

Serveur local/stdio

PropriétéTypeObligatoireDescription
type
"local" ou "stdio"NonType de serveur (par défaut en local)
commandstringYesCommande à exécuter
argsstring[]YesLes arguments de la commande
envobjectNonVariables d’environnement
cwdstringNonRépertoire de travail
toolsstring[]NonOutils à activer (["*"] pour tous, [] pour aucun)
timeoutnumberNonDélai d’expiration en millisecondes

Serveur distant (HTTP/SSE)

PropriétéTypeObligatoireDescription
type
"http" ou "sse"YesType de serveur
urlstringYesURL du serveur
headersobjectNonEn-têtes HTTP (par exemple, pour l’authentification)
oauthClientIdstringNonID client non vide pour un client OAuth configuré statiquement
oauthScopesstring[]NonTableau non vide de jetons de portée RFC 6749 valides. Nécessite oauthClientId; utilisé lorsque le défi du serveur omet le scope ou fournit un scope vide, avant le recours aux métadonnées de la ressource protégée
oauthPublicClientbooleanNonIndique si le client OAuth configuré est public et ne nécessite pas de clé secrète client
oauthGrantTypestringNonType d’autorisation OAuth pour le client configuré, tel que client_credentials
toolsstring[]NonOutils à activer
timeoutnumberNonDélai d’expiration en millisecondes

Propriétaire OAuth pour les serveurs distants

Choisissez un modèle de propriété OAuth pour chaque tentative d’authentification :

  1. Bouclage géré par le runtime : appel session.mcp.oauth.login sans URI de redirection. Le runtime ouvre un écouteur local pour le callback et prend en charge la découverte, PKCE, la validation d’état, l’échange de jetons, la persistance, l’actualisation et la reconnexion.
  2. Callback hébergé géré par le runtime : appel session.mcp.oauth.login avec un URI de redirection HTTPS public approuvé. Votre hôte reçoit la fonction de rappel, mais le runtime continue de posséder le protocole OAuth et les informations d’identification.
  3. OAuth géré par l’hôte : utilisez le gestionnaire des requêtes d’authentification MCP du SDK et retournez un jeton d’accès acquis par l’hôte. Votre hôte possède le jeton d’autorisation et le jeton d’actualisation.

N’enregistrez pas de gestionnaire OAuth géré par l’hôte pour une tentative d’authentification qui utilise un rappel hébergé géré par le runtime. Attendez que le serveur distant entre dans l’état needs-auth , puis appelez la méthode générée session.rpc.mcp.oauth.login directement avec le nom du serveur et l’URI de rappel :

{
    "serverName": "remote-mcp",
    "redirectUri": "https://agent.example.com/oauth/callback"
}

Le client OAuth statique, CIMD ou dynamiquement inscrit doit indiquer cet URI exact. L’URI doit utiliser HTTPS et ne doit pas contenir de requête ou de fragment. Lorsque l’interaction du navigateur est requise, le résultat contient authorizationUrl et authorizationId. Ouvrez authorizationUrl dans le navigateur de l’utilisateur et conservez authorizationId avec la session cible.

Après que le serveur d’autorisation vous a redirigé vers votre point de terminaison, appelez la méthode générée session.rpc.mcp.oauth.complete :

{
    "authorizationId": "<opaque-state>",
    "callbackUrl": "https://agent.example.com/oauth/callback?code=...&state=..."
}

Construisez callbackUrl à partir de l’origine et du chemin de redirection publique configurés, ainsi que de la chaîne de requête d’origine du callback. Ne transmettez pas d’URL de service interne et ne faites pas confiance aux en-têtes X-Forwarded-* ou Forwarded fournis par le client. Le runtime valide l’origine, le port, le chemin, l’état et la réponse OAuth avant d’accepter la livraison. L’échange de jetons, la persistance et la reconnexion MCP se reconnectent puis continuent de façon asynchrone.

Remise d’un jeton d’accès rejeté au runtime

Pour une requête OAuth hôte avec reason: "refresh" et runtimeRefreshSupported: true, votre gestionnaire peut retourner { "kind": "runtimeRefresh" } au lieu d’acquérir un jeton d’accès de remplacement. Le runtime effectue un renouvellement non interactif à l’aide de son autorisation enregistrée existante à l’intérieur de l’opération d’autorisation d’origine. Il ne démarre pas un nouveau flux de consentement ni appelle le RPC d’actualisation indépendant authenticationStateChanged . Le remplacement conserve sa provenance du grant enregistré, de sorte que l’adoption ultérieure de jeton stocké reste disponible. Un jeton fourni directement par un hôte n’est pas marqué comme un jeton stocké et ne peut pas être remplacé par ce chemin d’adoption.

Renvoyez cette réponse uniquement lorsque la demande en attente indique qu’elle est prise en charge. Une fonctionnalité manquante est définie sur faux et conserve le contrat hérité de jeton/annulation. Si un gestionnaire retourne le handoff d’une requête sans la capacité requise, le SDK envoie cancelled à la place, de sorte que la demande en attente ne reste pas ouverte. Un jeton fourni est prioritaire sur la passation, et une annulation reste une annulation.

SDKCapacitéRésultat de la passation
Node.jsrequest.runtimeRefreshSupported{ kind: "runtimeRefresh" }
Pythonrequest["runtimeRefreshSupported"]{"kind": "runtimeRefresh"}
Go
MCPAuthRuntimeRefreshHandler argument de fonctionnalitéMCPAuthResultRuntimeRefresh()
.NETMcpAuthContext.RuntimeRefreshSupportedMcpAuthResult.RefreshInRuntime()
Java
McpAuthHandler.handleWithRuntimeRefresh argument de fonctionnalitéMcpAuthResponse.runtimeRefresh()
RustMcpAuthHandler::handle_with_runtime_refreshMcpAuthResponse::RuntimeRefresh

L’acceptation de la réponse confirme le transfert, pas l’actualisation, la reconnexion ou l’achèvement de l’outil. Pour une transmission acceptée, mcp.oauth_completed est émis une seule fois, lorsque la réservation d’autorisation d’origine atteint son résultat terminal. Son outcome utilise uniquement les valeurs existantes : token quand la récupération a réussi et cancelled quand elle a été annulée ou a échoué. L’événement persistant mcp.oauth_recovery_completed comporte le même requestId et serverName et indique l’issue finale, y compris la reconnexion : succeeded, cancelled ou failed. Les versions du Kit de développement logiciel (SDK) antérieures à cet événement le reçoivent en tant que type d’événement inconnu.

Tant que le runtime n’accepte pas de réponse runtimeRefresh, les réponses existantes conservent leur signification antérieure. Une réponse de l’hôte { kind: "cancelled" } refuse la demande ; pour une refresh demande, le runtime peut ensuite demander une réauthorisation. Si aucun hôte ne répond, le runtime revient à sa gestion OAuth locale ordinaire, y compris les informations d’identification mises en cache, et n’émet aucun événement mcp.oauth_recovery_completed. Cette modification ne reclasse pas une réponse de l’hôte cancelled en tant qu’arrêt. L’invocation d’origine est conservée uniquement par le biais d’un transfert accepté runtimeRefresh.

Après l’acceptation, l’invocation d’origine peut réessayer après l’installation des identifiants et se reconnecter en passant par les limites existantes de session, de configuration, de politique et d’autorisations. L’arrêt du cycle principal ou la fin de la session annule la réservation d’autorisation d’origine ; l’invocation arrêtée ne peut pas réessayer ou publier un résultat ultérieurement. Un échange de jeton à autorisation rotative déjà émis peut terminer son remplacement après l’annulation, mais ne peut pas installer les informations d’identification obsolètes, se reconnecter ou reprendre l’invocation annulée.

Une autorisation enregistrée non disponible ou une actualisation ayant échoué met fin à l’appel d’origine sans demander de réautorisation interactive. Une reconnexion ayant échoué met également fin à l’appel ; l’obtention d’un jeton n’est pas un résultat d’outil réussi.

Les appels simultanés se joignent à la même tentative d’autorisation en cours et reçoivent son résultat final. Un serveur connecté, un jeton d’accès conservé ou une tentative réussie ultérieure n’est pas une preuve que leur tentative a réussi. Chaque appel survivant revérifie toujours son propre catalogue et sa propre autorisation d’outil avant de réessayer ; l’échec regroupé ne démarre pas une autre autorisation interactive.

Les délais HTTP OAuth s’appliquent aux requêtes individuelles, et non au transfert complet ou à la finalisation du stockage de l’autorisation. L’annulation peut se terminer avant qu’un règlement d’échange émis ne soit finalisé, et la libération de la session attend les processus de règlement suivis. Il n’y a pas de délai global de finalisation, donc les hôtes ne doivent pas traiter l’annulation, l’acceptation de relais ou un délai d’expiration HTTP comme preuve que tout le travail de stockage est terminé.

Diagnostics MCP à portée de session

Définissez diagnostics: { sources: { mcp: { level: "debug" } } } lorsque vous créez ou reprenez une session pour activer les diagnostics MCP. Chaque source est définie par défaut sur off. Les niveaux activés sont error, warning, info, debug, et trace. Ce paramètre est distinct de celui du client au niveau du processus logLevel.

Avertissement

Les entrées de diagnostic peuvent contenir du contenu fourni par l’utilisateur ou fourni par le serveur à chaque niveau activé : warning et au-dessus peuvent inclure le serveur stderr, tandis que debug et trace peuvent également inclure des charges utiles MCP, des arguments d’outil et des chemins d’accès. Traitez chaque entrée comme sensible. Ne téléchargez pas automatiquement les entrées en tant que télémétrie et ne les exportez pas sans intervention explicite de l’hôte.

L’API générée session.rpc.diagnostics permet aux hôtes de configurer et de lire les diagnostics en mémoire limités et délimités par session. MCP est la première et actuellement la seule source prise en charge. Sélectionnez-le sources: ["mcp"] explicitement lors de la lecture ; la lecture n’active pas la capture. Utilisez une boucle de lecture stoppable par surface de sortie et conservez son curseur. Une boucle de lecture possède son curseur et sa présentation ; le runtime possède la capture, le filtrage, la rétention, l’expiration du curseur et les réveils.

import { approveAll, CopilotClient } from "@github/copilot-sdk";

const client = new CopilotClient();
const stop = new AbortController();
const appendLine = (line: string) => console.log(line);

await client.start();
const session = await client.createSession({
    onPermissionRequest: approveAll,
    diagnostics: { sources: { mcp: { level: "debug" } } },
});

try {
    let cursor: string | undefined;
    while (!stop.signal.aborted) {
        const page = await session.rpc.diagnostics.read({
            sources: ["mcp"],
            cursor,
            max: 100,
            waitMs: 30_000,
        });
        if (page.cursorStatus === "expired") {
            appendLine("MCP diagnostic entries were dropped.");
        }
        for (const entry of page.entries) {
            const detail = entry.details.data ? ` ${entry.details.data}` : "";
            appendLine(
                `${entry.timestamp} [${entry.level}] [${entry.source}:${entry.details.serverName}] ${entry.message}${detail}`,
            );
        }
        cursor = page.cursor;
    }
} finally {
    await session.disconnect();
    await client.stop();
}

Le signal d’abandon arrête la boucle une fois la lecture limitée en cours terminée ; il n’est pas passé à l’appel RPC. La désactivation de la capture réveille une opération de lecture en attente.

Le démarrage MCP se fait à la demande : l’activation ou la lecture des diagnostics ne démarre pas un serveur. Configurez mcpServers lors de la création de session, puis utilisez la session normalement ou appelez session.rpc.mcp.startServer({ serverName }) pour un serveur installé afin de résoudre les problèmes d’initialisation sans effectuer de demande de modèle.

Chaque enregistrement a source: "mcp", timestamp, gravité, message et un agentId facultatif pour les agents non racines. Son details typé contient le cycle de vie, le protocole, HTTP ou la catégorie stderr, le nom du serveur, un nouveau connectionId pour chaque tentative de connexion, ainsi que la direction du protocole et les données de diagnostic facultatives. La rétention MCP est limitée à 1 024 enregistrements et à 4 MiB par session. Les enregistrements encodés individuels sont limités à 16 Kio et comportent truncated lorsqu’ils sont raccourcis. Les lectures utilisent par défaut 100 enregistrements, acceptent un maximum de 500 et ne consomment pas les données d’autres lecteurs. Utilisez droppedCount, lorsqu’il est présent sur un curseur expiré, pour afficher la perte connue. Chaque lecture réussie retourne un curseur et un état de curseur, y compris les lectures vides. Conservez le curseur retourné même lorsqu’aucun enregistrement n’est renvoyé. Un curseur appartient à sa sélection source ; démarrez sans curseur lors de la modification de cette sélection. Les sources manquantes, vides, dupliquées ou non prises en charge sont rejetées, comme les curseurs mal formés et les limites numériques non valides. Ni les enregistrements ni les paramètres de capture ne sont enregistrés dans l’historique de session.

Appelez session.rpc.diagnostics.configure({ sources: { mcp: { level: "off" } } }) pour désactiver la capture MCP et effacer sa mémoire tampon conservée. La configuration ne met à jour que les sources explicitement nommées et renvoie les niveaux de source effectifs. La configuration source vide ou inconnue est rejetée ; l’ajout d’une autre source prise en charge à l’avenir n’inscrira pas implicitement un appelant existant à celle-ci.

Arrêtez également la boucle de lecture de l’hôte ; les lectures effectuées pendant que la journalisation est désactivée retournent immédiatement plutôt que d’attendre. Une requête longue en suspens s’éveille lorsque la journalisation est désactivée. Omettre diagnostics pendant une reprise résidente conserve sa configuration actuelle ; une session chargée à froid nécessite une nouvelle activation explicite. L’activation des diagnostics à partir de l’état initial off peut commencer la capture pour une connexion MCP déjà active. Une fois les diagnostics désactivés avec configure({ sources: { mcp: { level: "off" } } }), la réactivation peut reprendre la capture pour la même connexion active et son ID de connexion existant. Les enregistrements déjà en cours de traitement lorsque les diagnostics ont été désactivés sont ignorés.

Le runtime supprime les informations d’identification configurées et structurellement reconnues des diagnostics HTTP avant que les entrées atteignent cette API, mais ce n’est pas une garantie que des secrets arbitraires fournis par le serveur sont absents. Il n’expose pas les mappages d’en-tête de demande ou de réponse. Seules les métadonnées sélectionnées, telles que Content-Type, Accept et MCP-Protocol-Version, peuvent faire l’objet d’une capture, mais leurs valeurs ne sont toujours pas fiables et peuvent contenir du contenu sensible. Les en-têtes d’authentification, les cookies, les ID de session, les valeurs de requête, l’URL userinfo et les fragments sont omis. Les chemins d’URL sont conservés pour la résolution des problèmes. Par conséquent, ne placez pas les informations d’identification dans les segments de chemin d’accès. Les charges utiles de protocole et stderr peuvent également contenir du contenu utilisateur ou des secrets arbitraires fournis par le serveur.

Troubleshooting

Les outils ne s’affichent pas ou ne sont pas appelés

  1. Vérifier que le serveur MCP démarre correctement

    • Vérifiez que la commande et les arguments sont corrects
    • Vérifier que le processus du serveur ne se bloque pas au démarrage
    • Recherchez la sortie d’erreur dans stderr
  2. Vérifier la configuration de l’outil

    • Assurez-vous que tools est défini sur ["*"] ou répertorie les outils spécifiques dont vous avez besoin
    • Un tableau [] vide signifie qu’aucun outil n’est activé
  3. Vérifier la connectivité pour les serveurs distants

    • Vérifier que l’URL est accessible
    • Vérifier que les en-têtes d’authentification sont corrects

Problèmes courants

IssueSolution
« Serveur MCP introuvable »Vérifier que le chemin de commande est correct et exécutable
« Connexion refusée » (HTTP)Vérifiez l’URL et vérifiez que le serveur est en cours d’exécution
Erreurs « Délai d’expiration »Augmenter la valeur ou vérifier les performances du timeout serveur
Les outils fonctionnent, mais ne sont pas appelésAssurez-vous que votre invite nécessite clairement la fonctionnalité de l’outil

Pour obtenir des instructions détaillées sur le débogage, consultez autoTITLE.

Voir aussi