> ## Content Index
> Fetch the complete content index at: https://www.sfeir.dev/llms.txt
> Use this file to discover other available public pages before exploring further.

# Erreurs OAuth d'un serveur MCP : causes et correctifs (+ 5 commandes curl pour diagnostiquer)
- URL: https://www.sfeir.dev/ia/erreurs-oauth-serveur-mcp/
- Published: 2026-10-28T10:50:00.000Z
- Updated: 2026-10-28T10:49:59.000Z
- Description: Les erreurs OAuth d'un serveur MCP (invalid_client_metadata, Unexpected token '<', 421...) : causes, correctifs et 5 commandes curl.
- Author: Baptiste PIRAULT
- Tags: IA, MCP, Agent IA, Agentic

[](https://github.com/batou9150/writing-workspace/blob/main/blog/drafts/4%5FMCP%2C%20erreurs%20OAuth%20d'un%20serveur%20et%20correctifs%20%28FR%29.md?ref=sfeir.dev#erreurs-oauth-dun-serveur-mcp--causes-et-correctifs--5-commandes-curl-pour-diagnostiquer)

Coder un serveur MCP conforme est la moitié facile du travail. La moitié difficile : chaque client (Claude Code, Codex, Gemini CLI, Copilot CLI, Antigravity...) découvre, enregistre et authentifie légèrement différemment, et chaque différence remonte sous forme d'erreur OAuth générique, depuis une couche que vous n'avez pas écrite. Voici les erreurs que j'ai rencontrées en déployant des serveurs MCP distants, avec leur cause réelle et leur correctif, puis la méthode en cinq commandes `curl` qui sépare un bug serveur d'un bug client en une minute.

## La chaîne de découverte OAuth d'un serveur MCP

[](https://github.com/batou9150/writing-workspace/blob/main/blog/drafts/4%5FMCP%2C%20erreurs%20OAuth%20d'un%20serveur%20et%20correctifs%20%28FR%29.md?ref=sfeir.dev#la-cha%C3%AEne-de-d%C3%A9couverte-oauth-dun-serveur-mcp)

Toutes les erreurs ci-dessous cassent un maillon de cette chaîne, définie par la spécification MCP Authorization :

1. Le client appelle le serveur sans token.
2. Le serveur répond `401` avec un header `WWW-Authenticate` qui pointe vers son **Protected Resource Metadata** ([RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728?ref=sfeir.dev)).
3. Ce document désigne le serveur d'autorisation.
4. Le client applique la découverte [RFC 8414](https://datatracker.ietf.org/doc/html/rfc8414?ref=sfeir.dev) sur cet identifiant pour trouver les endpoints `authorize`, `token` et `register`.

## Erreurs de découverte

[](https://github.com/batou9150/writing-workspace/blob/main/blog/drafts/4%5FMCP%2C%20erreurs%20OAuth%20d'un%20serveur%20et%20correctifs%20%28FR%29.md?ref=sfeir.dev#erreurs-de-d%C3%A9couverte)

### `SyntaxError: Unexpected token '<', "<!doctype "... is not valid JSON`

[](https://github.com/batou9150/writing-workspace/blob/main/blog/drafts/4%5FMCP%2C%20erreurs%20OAuth%20d'un%20serveur%20et%20correctifs%20%28FR%29.md?ref=sfeir.dev#syntaxerror-unexpected-token--doctype--is-not-valid-json)

**Cause :** le client a demandé un document de métadonnées OAuth (`/.well-known/oauth-authorization-server` ou `/.well-known/openid-configuration`) et a reçu une page HTML en `200`. Typiquement, le catch-all d'un frontend SPA ou une page de login répond sur ce chemin.

Certains clients ne suivent pas le challenge `401` : ils sondent l'**origine** du serveur, prennent la première réponse qui n'est pas en 4xx et la parsent. Trois rapports de bug distincts se sont révélés être ce seul fait.

**Correctif :** répondre `404` sur les chemins `/.well-known/*` que vous ne servez pas. Un 404 vaut mieux qu'un 200 que vous n'aviez pas prévu : le client passe au candidat suivant.

### `ZodError` sur `jwks_uri` (`"path": ["jwks_uri"]`, `"message": "Required"`)

[](https://github.com/batou9150/writing-workspace/blob/main/blog/drafts/4%5FMCP%2C%20erreurs%20OAuth%20d'un%20serveur%20et%20correctifs%20%28FR%29.md?ref=sfeir.dev#zoderror-sur-jwks%5Furi-path-jwks%5Furi-message-required)

```
ZodError: [
  {
    "code": "invalid_type",
    "expected": "string",
    "received": "undefined",
    "path": ["jwks_uri"],
    "message": "Required"
  }
]

```

**Cause :** le client a parsé vos métadonnées OAuth avec le schéma **OpenID Connect**, qui exige `jwks_uri`. Vous servez `/.well-known/openid-configuration` alors que votre serveur est un serveur OAuth 2.0 qui n'émet pas d'ID token.

**Correctif :** ne servez pas `openid-configuration` si vous n'êtes pas un fournisseur OpenID. Servir un document que vous ne pouvez pas honorer est pire que ne rien servir.

### `404` sur l'URL `resource_metadata` annoncée

[](https://github.com/batou9150/writing-workspace/blob/main/blog/drafts/4%5FMCP%2C%20erreurs%20OAuth%20d'un%20serveur%20et%20correctifs%20%28FR%29.md?ref=sfeir.dev#404-sur-lurl-resource%5Fmetadata-annonc%C3%A9e)

**Cause :** le préfixe de chemin est doublé. L'URL de base contient déjà `/mcp`, et le code y ajoute à nouveau le chemin (`/.well-known/oauth-protected-resource/mcp/mcp`).

**Correctif :** comparer l'URL du header `WWW-Authenticate` avec celle réellement servie (commande 1 et 2 plus bas).

### `403` en HTML sur `/register` ou `/.well-known/...` (Cloud Run)

[](https://github.com/batou9150/writing-workspace/blob/main/blog/drafts/4%5FMCP%2C%20erreurs%20OAuth%20d'un%20serveur%20et%20correctifs%20%28FR%29.md?ref=sfeir.dev#403-en-html-sur-register-ou-well-known-cloud-run)

**Cause :** sur **Cloud Run**, le binding IAM `allUsers` → `roles/run.invoker` a été perdu, souvent après un redéploiement sans `--allow-unauthenticated`. C'est Google Front End qui répond, en HTML, avant votre code.

**Correctif :**

```
gcloud run services add-iam-policy-binding my-mcp-server --region europe-west1 \
  --member="allUsers" --role="roles/run.invoker"
```

Votre serveur gère lui-même l'authentification OAuth : il doit être joignable sans IAM. Réaffirmez ce binding dans votre CI pour que le cas ne se reproduise pas.

## Erreurs d'enregistrement et de scopes

[](https://github.com/batou9150/writing-workspace/blob/main/blog/drafts/4%5FMCP%2C%20erreurs%20OAuth%20d'un%20serveur%20et%20correctifs%20%28FR%29.md?ref=sfeir.dev#erreurs-denregistrement-et-de-scopes)

### `invalid_client_metadata: Requested scopes are not valid: email, profile`

[](https://github.com/batou9150/writing-workspace/blob/main/blog/drafts/4%5FMCP%2C%20erreurs%20OAuth%20d'un%20serveur%20et%20correctifs%20%28FR%29.md?ref=sfeir.dev#invalid%5Fclient%5Fmetadata-requested-scopes-are-not-valid-email-profile)

**Cause :** le client s'enregistre (Dynamic Client Registration) avec l'orthographe courte des scopes Google (`email`, `profile`), alors que votre serveur n'accepte que la forme canonique (`https://www.googleapis.com/auth/userinfo.email`). Le fournisseur considère les deux comme équivalentes, et votre bibliothèque normalise ce que **vous** configurez, mais pas ce que **le client** demande.

**Correctif :** déclarer les deux orthographes dans les scopes acceptés à l'enregistrement.

### `invalid_scope: Client was not registered with scope email`

[](https://github.com/batou9150/writing-workspace/blob/main/blog/drafts/4%5FMCP%2C%20erreurs%20OAuth%20d'un%20serveur%20et%20correctifs%20%28FR%29.md?ref=sfeir.dev#invalid%5Fscope-client-was-not-registered-with-scope-email)

**Cause :** même famille, autre porte. Le client s'est enregistré **sans** scope, a reçu par défaut la forme canonique, puis demande `email` au moment de l'autorisation. C'est le *grant* stocké pour ce client qui est vérifié à `/authorize`.

**Correctif :** élargir la liste des scopes accordés par défaut, en plus de celle des scopes acceptés. J'ai corrigé ce même symptôme deux fois, une fois par porte : élargissez les deux en même temps.

### `invalid_client_metadata` sans nom de champ

[](https://github.com/batou9150/writing-workspace/blob/main/blog/drafts/4%5FMCP%2C%20erreurs%20OAuth%20d'un%20serveur%20et%20correctifs%20%28FR%29.md?ref=sfeir.dev#invalid%5Fclient%5Fmetadata-sans-nom-de-champ)

**Cause possible :** `application_type`, que la révision `2026-07-28` impose aux clients qui utilisent encore Dynamic Client Registration. Un serveur qui valide strictement les métadonnées d'enregistrement le rejette sans dire pourquoi.

**Correctif :** accepter explicitement `application_type` dans la validation.

### `redirect_uri_mismatch` ou `invalid_redirect_uri`

[](https://github.com/batou9150/writing-workspace/blob/main/blog/drafts/4%5FMCP%2C%20erreurs%20OAuth%20d'un%20serveur%20et%20correctifs%20%28FR%29.md?ref=sfeir.dev#redirect%5Furi%5Fmismatch-ou-invalid%5Fredirect%5Furi)

**Cause :** deux allowlists distinctes peuvent produire cette erreur :

- celle du **fournisseur d'identité** (Google, Entra ID...) : l'URI de callback de votre serveur (`https://mon-serveur/auth/callback`) n'est pas déclarée sur le client OAuth amont, ou diffère d'un caractère de l'URL de base configurée ;
- celle de **votre serveur** : l'URI de redirection du client MCP (loopback avec port aléatoire, URL de connecteur d'un éditeur) n'est pas autorisée.

**Correctif :** pour le fournisseur, aligner exactement l'URL de base et l'URI déclarée (y compris `http://localhost:<port>/auth/callback` pour les tests locaux). Pour votre serveur, autoriser en wildcard uniquement ce que le client fait réellement varier (le port loopback, l'id de connecteur) et épingler le reste.

## Erreurs de transport et de protocole

[](https://github.com/batou9150/writing-workspace/blob/main/blog/drafts/4%5FMCP%2C%20erreurs%20OAuth%20d'un%20serveur%20et%20correctifs%20%28FR%29.md?ref=sfeir.dev#erreurs-de-transport-et-de-protocole)

### `405 Method Not Allowed` sur `GET /mcp`

[](https://github.com/batou9150/writing-workspace/blob/main/blog/drafts/4%5FMCP%2C%20erreurs%20OAuth%20d'un%20serveur%20et%20correctifs%20%28FR%29.md?ref=sfeir.dev#405-method-not-allowed-sur-get-mcp)

**Cause :** aucune. C'est attendu : le transport Streamable HTTP stateless n'enregistre que `POST`, et la révision `2026-07-28` a retiré l'endpoint `GET` du transport.

### `UnsupportedProtocolVersionError`

[](https://github.com/batou9150/writing-workspace/blob/main/blog/drafts/4%5FMCP%2C%20erreurs%20OAuth%20d'un%20serveur%20et%20correctifs%20%28FR%29.md?ref=sfeir.dev#unsupportedprotocolversionerror)

**Cause :** la version du protocole envoyée dans `_meta` n'est pas acceptée par le serveur.

**Correctif :** interroger `server/discover` (commande 5 plus bas) avant de toucher au client ou au serveur. Un serveur qui ne répond pas à `server/discover` est antérieur à la révision `2026-07-28`, ce qui explique souvent le comportement d'un client récent.

### `421 Misdirected Request`

[](https://github.com/batou9150/writing-workspace/blob/main/blog/drafts/4%5FMCP%2C%20erreurs%20OAuth%20d'un%20serveur%20et%20correctifs%20%28FR%29.md?ref=sfeir.dev#421-misdirected-request)

**Cause :** la protection contre le DNS rebinding de la bibliothèque MCP rejette le header `Host` reçu, typiquement derrière un load balancer ou un domaine personnalisé.

**Correctif :** déclarer les hôtes autorisés dans la configuration de sécurité du transport de votre bibliothèque, plutôt que de désactiver la protection.

### `307 Temporary Redirect` vers `http://` (slash final)

[](https://github.com/batou9150/writing-workspace/blob/main/blog/drafts/4%5FMCP%2C%20erreurs%20OAuth%20d'un%20serveur%20et%20correctifs%20%28FR%29.md?ref=sfeir.dev#307-temporary-redirect-vers-http-slash-final)

**Cause :** le client est configuré avec `/mcp/` et le serveur redirige vers `/mcp`, mais en `http://` : derrière un load balancer qui termine TLS, le serveur ASGI ne fait pas confiance au header `X-Forwarded-Proto`. Le client suit la redirection en clair, ou la refuse.

**Correctif :** faire confiance aux headers du proxy (`forwarded_allow_ips` avec uvicorn), et documenter une seule forme d'URL.

## Le correctif est déployé, mais le client renvoie la même erreur

[](https://github.com/batou9150/writing-workspace/blob/main/blog/drafts/4%5FMCP%2C%20erreurs%20OAuth%20d'un%20serveur%20et%20correctifs%20%28FR%29.md?ref=sfeir.dev#le-correctif-est-d%C3%A9ploy%C3%A9-mais-le-client-renvoie-la-m%C3%AAme-erreur)

**Cause :** le client a mis en cache un `client_id` enregistré **avant** votre correctif. Le grant vérifié à `/authorize` est celui stocké pour ce `client_id` : il ne changera jamais. C'est la panne qui m'a fait perdre le plus de temps.

**Correctif :** vérifier d'abord le serveur avec un enregistrement neuf (`curl -X POST .../register`), puis vider le cache OAuth du client :

```
codex mcp logout my-server                 # Codex
rm ~/.copilot/mcp-oauth-config/<hash>.json # Copilot CLI (et le .verifier associé)
# Claude Code : /mcp puis "Clear authentication" sur le serveur
```

## Diagnostiquer en 5 commandes curl

[](https://github.com/batou9150/writing-workspace/blob/main/blog/drafts/4%5FMCP%2C%20erreurs%20OAuth%20d'un%20serveur%20et%20correctifs%20%28FR%29.md?ref=sfeir.dev#diagnostiquer-en-5-commandes-curl)

Sans aucun identifiant, ces cinq commandes disent si le problème est chez vous ou chez le client :

```
B=https://example.com

# 1. le challenge : doit contenir resource_metadata
curl -sD- -o /dev/null -X POST $B/mcp/ -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | grep -i www-authenticate

# 2. le Protected Resource Metadata qu'il désigne
curl -s $B/.well-known/oauth-protected-resource/mcp/ | jq .

# 3. RFC 8414 sur le serveur d'autorisation désigné : l'issuer DOIT correspondre
curl -s $B/.well-known/oauth-authorization-server/mcp | jq '.issuer, .token_endpoint'

# 4. ce que voient les clients qui sondent l'origine : 404 ou JSON, jamais du HTML
curl -sL -o /dev/null -w '%{http_code} %{content_type}\n' \
  $B/.well-known/oauth-authorization-server

# 5. les versions et capacités annoncées (révision 2026-07-28)
curl -s -X POST $B/mcp/ -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"server/discover"}' | jq .
```

Si les cinq réponses sont propres, arrêtez de modifier votre serveur et allez lire le code du client.

## Connecter chaque client MCP

[](https://github.com/batou9150/writing-workspace/blob/main/blog/drafts/4%5FMCP%2C%20erreurs%20OAuth%20d'un%20serveur%20et%20correctifs%20%28FR%29.md?ref=sfeir.dev#connecter-chaque-client-mcp)

Chaque CLI diffère assez pour qu'un snippet copié-modifié soit faux. Vérifiez avec `--help` avant de documenter :

```
claude mcp add my-server https://example.com/mcp/ --transport http   # Claude Code, puis /mcp
codex mcp add my-server --url https://example.com/mcp/               # Codex : --url
codex mcp login my-server                                            # et un login explicite
copilot mcp add my-server https://example.com/mcp/ --transport http  # Copilot CLI
gemini mcp add --transport http my-server https://example.com/mcp/   # Gemini CLI, puis /mcp auth
```

```
// Antigravity : configuration par fichier uniquement (~/.gemini/config/mcp_config.json)
{"mcpServers": {"my-server": {"serverUrl": "https://example.com/mcp/"}}}
```

## Méthode de debug

[](https://github.com/batou9150/writing-workspace/blob/main/blog/drafts/4%5FMCP%2C%20erreurs%20OAuth%20d'un%20serveur%20et%20correctifs%20%28FR%29.md?ref=sfeir.dev#m%C3%A9thode-de-debug)

- **Lire le code source du client plutôt que son message d'erreur.** Trois échecs avaient une seule cause, trouvée en vingt minutes dans la fonction qui construit l'URL de découverte du bundle JavaScript du client.
- **Reproduire avant de corriger.** Écrire l'assertion qui échoue, et la voir échouer pour la bonne raison.
- **Séparer "pas déployé" de "toujours cassé".** Vérifier la révision qui sert le trafic.
- **Quand un correctif "ne marche pas", suspecter le cache client** avant de rouvrir le code.
- **Chercher où ailleurs la même incohérence s'applique**, comme les deux portes de scopes.

## Conclusion

[](https://github.com/batou9150/writing-workspace/blob/main/blog/drafts/4%5FMCP%2C%20erreurs%20OAuth%20d'un%20serveur%20et%20correctifs%20%28FR%29.md?ref=sfeir.dev#conclusion)

Prévoyez plus de temps pour la compatibilité des clients que pour le serveur lui-même. La plupart de ces erreurs ne viennent pas de votre code OAuth, mais d'un écart entre ce que votre serveur publie et ce qu'un client précis attend. Les cinq commandes `curl` tranchent la question en une minute.

[7 pannes de serveur MCP qui ressemblent à des bugs OAuthinvalid\_client intermittent, clients déconnectés, 500 sur /authorize, 403 Cloud Run : 7 pannes de serveur MCP en production et leur vraie cause.![](https://storage.ghost.io/c/0b/31/0b31478e-a66a-46f1-8c34-d44388e21192/content/images/icon/plume-point-df4063e9-0280-4dda-b0d8-0219bdeaa4ad.png)sfeir.dev - Le média incontournable pour les passionnés de tech et d'intelligence artificielleBaptiste PIRAULT![](https://storage.ghost.io/c/0b/31/0b31478e-a66a-46f1-8c34-d44388e21192/content/images/thumbnail/mcp_series_03-hero-38c8f86f-dbd1-4dd2-9f60-4a457bf2798e.png)](https://www.sfeir.dev/ia/serveur-mcp-production-pannes-oauth/)