En bref

  • Problème : Cursor ne permettait pas alors de tester proprement les modèles que je voulais via OpenRouter.
  • Ce que j’ai testé : un proxy Go qui présente une surface OpenAI-compatible à Cursor, puis remplace gpt-4o par un modèle OpenRouter côté serveur.
  • Résultat : le routage fonctionnait, les payloads devenaient inspectables, mais DeepSeek ne produisait pas toujours une réponse que Cursor savait appliquer comme modification de code. Le chemin local du repo a été revalidé ensuite avec Docker, go test, go build et un appel /v1/chat/completions en HTTP 200.
  • À retenir : une API compatible ne suffit pas si le client attend aussi un comportement implicite très précis. C’est une note historique, pas une recommandation actuelle d’intégration Cursor.
  • Repo lié : pezzos/cursor-openrouter-proxy.

Pourquoi je garde cette trace

Le point de départ était simple : permettre à Cursor de travailler avec d’autres modèles que ceux prévus nativement, en particulier DeepSeek au moment où le projet a été lancé. L’objectif n’était pas de refaire l’éditeur, mais de garder Cursor comme surface de travail tout en reprenant la main sur le modèle appelé derrière.

Le dépôt pezzos/cursor-openrouter-proxy garde cette tentative. C’était un petit serveur Go, lançable localement ou derrière Docker, qui exposait une API de type OpenAI pour relayer les requêtes de Cursor vers OpenRouter.

Je garde cette note parce que le projet est un bon petit exemple de compatibilité utile mais imparfaite. Sur le papier, un format API commun donne l’impression que tout va s’assembler proprement. En pratique, le client attend aussi des comportements implicites.

Le montage, et ce qu’il rendait visible

L’idée était de faire pointer Cursor vers une URL locale compatible OpenAI. Côté Cursor, le modèle restait gpt-4o. Côté proxy, cette demande était remplacée par le modèle configuré dans OpenRouter, par exemple deepseek/deepseek-chat, puis envoyée avec la vraie clé OpenRouter gardée côté serveur.

Pour Cursor, le point d’entrée restait familier. Pour moi, le vrai changement était double : choisir le modèle aval sans casser l’intégration existante, et voir passer les payloads réellement envoyés par l’éditeur. Les messages système, les instructions, les outils attendus, le modèle demandé et les options de streaming devenaient inspectables.

Schéma du proxy Cursor OpenRouter : Cursor envoie une requête compatible OpenAI vers le proxy local placé au milieu, le proxy permet d'observer et d'adapter le payload, puis relaie l'aller-retour avec OpenRouter.
Le proxy local reste au milieu : Cursor croit parler à une API OpenAI, tandis que le proxy inspecte et traduit l'aller-retour avec OpenRouter.
if chatReq.Model == "gpt-4o" {
	chatReq.Model = activeConfig.model
} else {
	http.Error(w, "use gpt-4o from Cursor", http.StatusBadRequest)
}

Le code utile n’était pas dans la taille du proxy. Il était dans ce petit mensonge technique : laisser Cursor croire que l’API et le modèle étaient familiers, puis reprendre la décision de routage juste avant l’appel OpenRouter.

La limite intéressante

Le proxy fonctionnait : la requête partait, OpenRouter répondait, Cursor recevait quelque chose. Mais avec DeepSeek, le résultat ne se comportait pas toujours comme attendu dans Cursor.

Le modèle pouvait produire une réponse textuelle utile sans respecter suffisamment le format implicite attendu par l’éditeur pour transformer cette réponse en modification applicable. Au lieu d’un patch ou d’une action exploitable, la suggestion restait parfois visible comme un simple message dans le chat.

La compatibilité apparente ne suffit pas quand le client attend un comportement implicite très précis.

Aujourd’hui, je ne présenterais pas ce repo comme une solution robuste pour remplacer le modèle de Cursor. Le proxy pouvait router la requête, mais il ne pouvait pas rendre n’importe quel modèle bon dans le rôle attendu par l’éditeur. Il satisfaisait le contrat HTTP, pas forcément le contrat d’édition : produire une réponse que Cursor sait relire, parser et appliquer dans les fichiers.

Le projet rendait quelque chose possible, mais il montrait en même temps à quel point les outils agents dépendent de contrats non documentés.

Pourquoi je l’ai laissé de côté

Je l’ai surtout abandonné parce que je n’en avais plus vraiment l’usage. Petit à petit, mon usage de Cursor comme agent de code a laissé plus de place à Claude Code, puis à Codex. Les abonnements et les outils dédiés ont fini par remplacer ce bricolage : je n’avais plus besoin de faire croire à Cursor qu’il parlait à une API OpenAI pour tester un autre modèle.

Aujourd’hui, je ne me sers presque plus de Cursor comme agent. Mon flux principal passe plutôt par Codex App, avec des validations Claude et des skills personnelles quand le sujet le mérite. Cursor me reste utile autrement : lire du code, naviguer vite dans des fichiers, ou manipuler du texte avec ses raccourcis clavier.

L’idée pourrait redevenir utile dans un autre contexte, mais je ne la reprendrais pas sans retester les formats attendus par le client actuel. Pour l’instant, je garde surtout cette trace comme un petit outil qui a servi à regarder sous le capot, puis à comprendre pourquoi la compatibilité d’API ne suffit pas toujours.

Après la remise au propre du repo, j’ai aussi levé un blocage plus banal : le script de test local envoyait une fausse clé qui ne ressemblait pas à une clé OpenAI. Le proxy la rejetait avant même d’utiliser la vraie clé OpenRouter côté serveur. Une fois la clé factice passée au format sk-*, le chemin Docker local a répondu en HTTP 200 avec une réponse courte Hi.. Ça valide le montage local ; ça ne valide toujours pas l’application de patchs dans Cursor.

Ce que tu peux reprendre

  • le montage proxy minimal ;
  • l’idée d’inspecter les payloads avant de conclure sur un modèle ;
  • le test qui vérifie que le modèle aval reçoit bien la requête transformée ;
  • la leçon : le contrat HTTP n’est qu’une partie du contrat réel.

Ce qu’il ne faut pas copier aveuglément

  • l’hypothèse que gpt-4o côté Cursor reste le bon déguisement ;
  • le choix de modèle OpenRouter ;
  • l’idée que toute réponse textuelle utile sera applicable par Cursor ;
  • l’usage avec des prompts, fichiers ou clés que tu ne veux pas voir passer dans un proxy local.

Le repo contient

  • un proxy Go compatible avec l’endpoint de chat attendu ;
  • une configuration Docker/Compose, dont un chemin local sur 127.0.0.1:9000 ;
  • les variables OpenRouter attendues ;
  • un script de test qui appelle /v1/chat/completions.

Il sert à

  • inspecter la forme du montage ;
  • comprendre comment un client peut croire appeler une API OpenAI pendant que le proxy route ailleurs ;
  • repartir d’un exemple minimal si tu veux refaire l’expérience.

Il ne sert pas à

  • garantir que DeepSeek, ou un autre modèle, sera exploitable dans Cursor ;
  • documenter tous les comportements implicites de Cursor ;
  • remplacer une intégration officielle ou maintenue.