Jean-Maurice Raboude
17 octobre 2026· 5 min de lecture

Ajouter MCP à une API ASP.NET Core

Comment permettre à des agents IA d'utiliser une API .NET existante grâce au SDK MCP officiel, du premier outil jusqu'à l'authentification et la connexion de Claude Code, avec des exemples tirés de mon outil de suivi de candidatures.

Cairn application timeline with an email entry logged via Claude Code

Le Model Context Protocol (MCP) permet à un agent IA d'appeler les fonctions de votre application sous forme d'outils. L'ajouter à une API ASP.NET Core demande trois choses : le SDK officiel, quelques méthodes-outils et l'authentification. Les deux premières vont vite. L'authentification prend l'essentiel du temps.

J'ai ajouté MCP à Cairn, mon outil de suivi de candidatures. Claude Code peut maintenant chercher dans mes candidatures, en enregistrer une nouvelle ou la faire passer à l'étape suivante. Les exemples ci-dessous viennent de ce travail.

Exposer un endpoint

Pour un serveur HTTP, installez ModelContextProtocol.AspNetCore. L'enregistrement ressemble à celui de n'importe quel service :

builder.Services.AddMcpServer(o => o.ServerInstructions = "Search before acting. Only pass ids returned by tools.")
    .WithHttpTransport(o => o.Stateless = true)
    .WithTools<ReadTools>();

app.MapMcp("/api/mcp");

En mode stateless, aucune session n'est liée à une instance : un redéploiement ne déconnecte personne. ServerInstructions est transmis à l'agent à la connexion, c'est donc l'endroit des règles communes à tous les outils. Cairn y précise qu'il n'existe pas d'outil de suppression et que les dates doivent inclure un décalage UTC.

Écrire les outils comme de simples méthodes

Un outil est une méthode statique. Le SDK construit le schéma d'entrée à partir des paramètres et injecte les services comme votre DbContext. Le texte des [Description] est la documentation que lit le modèle :

[McpServerTool(Name = "get_application", ReadOnly = true, UseStructuredContent = true)]
[Description("Full details of one application: company, status, dates, salary, tags.")]
public static async Task<ApplicationResponse> GetApplication(
    AppDbContext db,
    [Description("Application id from search_applications.")] Guid applicationId,
    CancellationToken ct = default) =>
    await ApplicationQueries.GetAsync(db, applicationId, ct)
        ?? throw new McpException("No application with that id.");

Le corps de la méthode appelle la même classe de requêtes que l'endpoint REST. Avant d'écrire le moindre outil, j'ai déplacé la logique des endpoints de Cairn dans des classes partagées de requêtes et d'opérations, pour que REST et MCP ne puissent pas diverger. Une McpException revient au modèle sous forme d'erreur qu'il peut lire et corriger.

Ce qui a rendu les outils plus faciles à utiliser pour un agent : des filtres qui prennent des noms plutôt que des identifiants, des réponses courtes par défaut (la description de poste seulement sur demande) et aucun outil destructif.

Choisir comment les agents s'authentifient

Un serveur local en stdio tourne avec les droits de l'utilisateur et n'a généralement pas besoin d'authentification. Un endpoint HTTP distant a deux options.

Clés d'API

Chaque utilisateur crée une clé dans votre application, et le client l'envoie dans un en-tête Authorization. Vous la validez dans un handler d'authentification, comme n'importe quel jeton bearer. C'est le plus rapide à construire, mais l'utilisateur doit copier et renouveler ses clés lui-même.

OAuth 2.1

C'est ce que décrit la spécification MCP. L'utilisateur ajoute votre URL dans son client et autorise l'accès dans le navigateur :

  1. Le client appelle l'endpoint sans jeton et reçoit un 401 qui pointe vers un document de métadonnées de la ressource protégée.
  2. Ce document désigne votre serveur d'autorisation.
  3. Le client s'y enregistre, puis déroule un flux authorization code avec PKCE dans le navigateur.
  4. Il revient avec un jeton bearer et relance sa requête.

Le SDK couvre les étapes 1 et 2. Enregistrez son schéma AddMcp à côté de ce qui valide vos jetons, et faites-en le schéma de challenge pour qu'un 401 porte l'en-tête de découverte :

builder.Services.AddAuthentication(o =>
    {
        o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        o.DefaultChallengeScheme = McpAuthenticationDefaults.AuthenticationScheme;
    })
    .AddJwtBearer(o =>
    {
        o.Authority = "https://auth.example.com";
        o.Audience = "https://example.com/api/mcp";
    })
    .AddMcp(o => o.ResourceMetadata = new ProtectedResourceMetadata
    {
        Resource = "https://example.com/api/mcp",
        AuthorizationServers = { "https://auth.example.com" },
        ScopesSupported = ["app.read", "app.write"],
    });

app.MapMcp("/api/mcp").RequireAuthorization();

Les étapes 3 et 4 reviennent au serveur d'autorisation. Vous pouvez utiliser un fournisseur d'identité que vous avez déjà, à condition qu'il laisse des clients inconnus s'enregistrer, par enregistrement dynamique ou par document de métadonnées client : les clients MCP ne sont pas connus à l'avance. Pour Cairn, OpenIddict ne gérait ni l'un ni l'autre, j'ai donc écrit un petit serveur dans l'API : quatre endpoints, quatre tables et une page de consentement qui réutilise le cookie de connexion existant.

Pour les permissions, placez [Authorize(Policy = "...")] sur les outils d'écriture et appelez .AddAuthorizationFilters() sur le builder du serveur. Les jetons sans le scope d'écriture ne voient alors plus ces outils.

Connecter Claude Code

Ajoutez le serveur depuis le terminal :

claude mcp add --transport http myapp https://example.com/api/mcp

Pour le partager avec toute l'équipe d'un dépôt, ajoutez --scope project. Claude Code l'écrit alors dans un fichier .mcp.json à la racine du dépôt, que vous pouvez commiter :

{
  "mcpServers": {
    "myapp": {
      "type": "http",
      "url": "https://example.com/api/mcp"
    }
  }
}

Avec OAuth, la configuration s'arrête là. À la première utilisation, Claude Code reçoit le 401, suit la chaîne de découverte et ouvre le navigateur pour l'autorisation. La commande /mcp dans une session affiche l'état de la connexion ou permet de se reconnecter. Avec des clés d'API, ajoutez un en-tête et gardez la clé dans une variable d'environnement, que Claude Code remplace :

"headers": { "Authorization": "Bearer ${MYAPP_API_KEY}" }

Une fois connecté, l'agent choisit lui-même ses outils. Dans Cairn, « Je viens de postuler au poste backend chez Acme, rappelle-moi de relancer vendredi » devient create_application puis create_reminder.

Tester avec un vrai client

Cairn avait 274 tests au vert quand j'ai connecté Claude Code pour la première fois, et il a rejeté tous les résultats structurés des outils. Le SDK publie un schéma de sortie qui marque les champs nullables comme obligatoires, mais ses options JSON par défaut omettent les champs nuls. Le client de test du SDK ne valide pas les résultats, donc les tests n'avaient rien vu. La correction tient en un réglage :

DefaultIgnoreCondition = JsonIgnoreCondition.Never

Le vrai client a aussi révélé une 404 sur le document de métadonnées derrière nginx, et des clients qui sautent complètement la découverte. Connectez au moins un vrai client MCP à une instance déployée avant de considérer le travail terminé.

Ajouter MCP à une API ASP.NET Core · Jean-Maurice Raboude