Cursor Python SDK
Le package cursor-sdk vous permet d'appeler l'agent de Cursor depuis votre code Python. Le même agent qui s'exécute dans l'IDE Cursor, la CLI et l'application web peut être piloté par script depuis Python grâce à des clients sync et async, des dataclasses typées et une itération classique sur les streams et les pages. Exécutez la compétence /sdk dans Cursor pour commencer.
Pour l'API REST, voir l'API des Cloud Agents. Pour les autres langages, voir le SDK Bridge.
Aperçu
Le SDK regroupe les environnements d’exécution locaux et cloud derrière une interface unique. Vous écrivez le même code, quel que soit l’endroit où l’agent s’exécute.
| Environnement d’exécution | Description | Quand l’utiliser |
|---|---|---|
| Local | Exécute l’agent sur des fichiers locaux présents sur le disque. | Scripts de développement et vérifications CI sur une copie de travail. |
| Cloud (Cursor-hosted) | S’exécute dans une VM isolée avec votre repo cloné. Cursor exécute les VM. | Lorsque l’appelant n’a pas le repo, que vous souhaitez exécuter de nombreux agents en parallèle ou que les exécutions doivent se poursuivre après la déconnexion de l’appelant. |
Définissez l’environnement d’exécution en passant local ou cloud à Agent.create().
Authentification
Définissez CURSOR_API_KEY ou transmettez api_key avant de créer un Agent.
Le SDK accepte les clés API utilisateur et les clés API de compte de service pour les exécutions locales et dans le cloud. Les clés API Team Admin ne sont pas encore prises en charge.
- Clé API utilisateur depuis Cursor Dashboard -> API Keys
- Clé API de compte de service depuis les Paramètres d’équipe. Voir Comptes de service
export CURSOR_API_KEY="your-key"Usage et facturation
Les exécutions via le SDK suivent les mêmes règles de tarification, de pools de requêtes et de mode de confidentialité que les exécutions depuis l’IDE et les agents cloud. Les dépenses de votre équipe apparaissent dans le tableau de bord d’usage, sous le tag SDK.
Pour consulter le nombre de tokens par exécution dans le code, voir Usage des tokens. Pour récupérer l’usage facturé et le coût en dollars des exécutions d’un agent, voir agent.get_usage().
Concepts fondamentaux
| Concept | Description |
|---|---|
| Agent | Référence durable qui conserve l’état de la conversation, la configuration de l’espace de travail, la sélection du modèle et les paramètres. Persiste d’un prompt à l’autre. |
| Run | Soumission unique d’un prompt. Possède son propre stream, état, résultat, conversation et mécanisme d’annulation. |
| SDKMessage | Message de stream typé émis lors d’une exécution. Même structure dans les environnements d’exécution locaux et cloud. |
| CursorClient | Client explicite permettant de contrôler le cycle de vie, de personnaliser les options HTTP ou de gérer plusieurs espaces de travail dans un même processus. Client est un alias. |
| AsyncClient | Client miroir asynchrone. Requis pour toutes les opérations asynchrones. |
Installation
pip install cursor-sdkNécessite Python 3.10 ou version ultérieure.
Démarrage rapide
import osfrom cursor_sdk import Agent, LocalAgentOptionswith Agent.create( model="composer-2.5", api_key="crsr_key", local=LocalAgentOptions(cwd=os.getcwd()),) as agent: print(agent.send("Summarize what this repository does").text())Événements de flux explique comment extraire le texte de l’assistant, gérer les appels d’outils et consulter l’état de l’exécution. Pour un prompt en une seule opération (créer, exécuter, terminer), voir Agent.prompt().
Démarrage rapide avec les agents cloud
Le SDK Python prend en charge nativement les agents cloud de Cursor. Vous pouvez lister les dépôts connectés, démarrer un agent sur l’un d’eux, attendre la fin de l’exécution et passer en revue le résultat final.
from cursor_sdk import Agent, CloudAgentOptions, CloudRepositorywith Agent.create( model="composer-2.5", api_key="crsr_key", cloud=CloudAgentOptions( repos=[CloudRepository(url="https://github.com/your-org/your-repo", starting_ref="main")], auto_create_pr=True, ),) as agent: print(agent.send("Add structured logging to the auth middleware").text())Les agents cloud démarrés par le SDK n’apparaissent pas dans la liste d’agents par défaut. Pour les afficher dans Cursor Web ou la fenêtre Agents, cliquez sur Filtrer > Source > SDK.
Utilisation asynchrone
Le client async offre la même interface que le client sync et est recommandé pour les serveurs, les bots et l’orchestration concurrente d’agents. AsyncAgent, AsyncClient, AsyncRun et AsyncCursor sont exportés par cursor_sdk et cursor_sdk.asyncio.
import asyncioimport osfrom cursor_sdk import AsyncClient, LocalAgentOptionsasync def main(): async with await AsyncClient.launch_bridge(workspace=os.getcwd()) as client: async with await client.agents.create( model="composer-2.5", api_key="crsr_key", local=LocalAgentOptions(cwd=os.getcwd()), ) as agent: run = await agent.send("Summarize what this repository does") print(await run.text())asyncio.run(main())Il n’existe pas de client async global par défaut. Instanciez explicitement AsyncClient ou utilisez AsyncClient.launch_bridge(...) comme gestionnaire de contexte async afin que chaque boucle d’événements dispose de son propre client. Ne mélangez pas les clients sync et async dans le même chemin de code.
Les méthodes de classe directes d’AsyncAgent nécessitent client=. Utilisez
await client.agents.create(...) ou
await AsyncAgent.create(..., client=client).
| Sync | Async |
|---|---|
CursorClient / Client | AsyncClient / AsyncCursorClient |
Agent | AsyncAgent |
Run | AsyncRun |
Cursor | AsyncCursor |
ListResult | AsyncListResult |
DefaultHttpxClient | DefaultAsyncHttpxClient |
Création d’agents
Agent.create() valide les options et renvoie immédiatement une référence. Indiquez local ou cloud pour choisir un environnement d’exécution.
from cursor_sdk import Agent, CloudAgentOptions, CloudRepository, LocalAgentOptionsagent = Agent.create( model="composer-2.5", local=LocalAgentOptions(cwd="."),)cloud_agent = Agent.create( model="composer-2.5", cloud=CloudAgentOptions( repos=[CloudRepository(url="https://github.com/your-org/your-repo", starting_ref="main")], auto_create_pr=True, ),)agent.agent_id est renseigné immédiatement. Les agents locaux reçoivent un ID agent-<uuid> ; les agents cloud reçoivent un ID bc-<uuid>. agent.model est un ModelSelection typé, donc agent.model.id et agent.model.params sont directement utilisables.
Les agents cloud démarrés par le SDK sont exclus de la liste d’agents par défaut. Pour les afficher dans Cursor Web ou dans une fenêtre d’agent Cursor, cliquez sur Filtrer > Source > SDK.
Variables d’environnement de session
Pour les agents cloud, transmettez env_vars lorsqu’une exécution nécessite des identifiants de courte durée ou d’autres valeurs qui ne doivent être disponibles que pour cet agent.
import osagent = Agent.create( model="composer-2.5", cloud=CloudAgentOptions( repos=[CloudRepository(url="https://github.com/your-org/your-repo")], env_vars={ "STAGING_API_TOKEN": os.environ["STAGING_API_TOKEN"], }, ),)Ces valeurs sont chiffrées au repos, injectées dans le shell de l’agent cloud et supprimées avec l’agent. env_vars ne peut pas être utilisé avec un agent_id fourni par l’appelant ; omettez agent_id et récupérez l’ID généré par le serveur via agent.agent_id. Les noms de variables ne peuvent pas commencer par CURSOR_.
Pour les valeurs qui ne doivent exister que pendant une seule exécution, transmettez-les plutôt via agent.send(). Voir Variables d’environnement par run.
Métadonnées de l’agent
Associez vos propres identifiants à un agent cloud lors de sa création. Les métadonnées permettent d’associer un agent à un utilisateur, un tenant, un flux de travail ou un ticket de votre système. Elles sont renvoyées dans SDKAgentInfo.metadata par client.agents.get() et client.agents.list().
from cursor_sdk import Agent, CloudAgentOptions, CloudRepositorywith Agent.create( model="composer-2.5", cloud=CloudAgentOptions( repos=[CloudRepository(url="https://github.com/your-org/your-repo")], metadata={ "end_user_id": "user-123", "ticket_id": "ENG-456", }, ),) as agent: print(agent.agent_id)Les métadonnées peuvent être définies pour les agents cloud lors de leur création. Vous pouvez associer jusqu’à 50 paires clé-valeur. Les clés ne doivent pas être vides ni dépasser 255 caractères. Les valeurs doivent être des chaînes de 4 096 octets maximum. Les chaînes vides sont autorisées, et un mapping vide est traité comme l’absence de métadonnées.
Si les métadonnées ne sont pas activées pour le compte associé à la clé API, la création d’un agent avec une
map non vide renvoie 403 feature_unavailable.
Paramètres du modèle
Utilisez ModelSelection.params pour transmettre des options propres à chaque modèle, telles que l’effort de raisonnement ou optimize_for de Cursor Router. Les ID et les valeurs des paramètres varient selon le modèle. Utilisez Cursor.models.list() pour découvrir les paramètres pris en charge et les variantes prédéfinies disponibles pour votre compte.
from cursor_sdk import Agent, LocalAgentOptions, ModelParameterValue, ModelSelectionagent = Agent.create( model=ModelSelection( id="composer-2.5", params=[ModelParameterValue(id="fast", value="true")], ), local=LocalAgentOptions(cwd="."),)Utilisez Cursor.models.list() pour connaître les ID des paramètres et les variantes prédéfinies d’un modèle donné. Consultez Cursor Router pour le contrat de sélection auto-smart.
Cursor Router
Cursor Router sélectionne un modèle pour chaque requête Auto. Dans le SDK, Router est le modèle auto-smart avec un paramètre optimize_for. Il est disponible avec Teams et Enterprise. Les administrateurs Enterprise doivent activer Router pour l’équipe afin que auto-smart apparaisse dans le catalogue.
Le SDK Cursor est un SDK d’agents, et non une API autonome d’inférence de modèles ou de complétions de chat. Router sélectionne des modèles pour les exécutions d’agents Cursor, qui peuvent raisonner sur un espace de travail, appeler des outils, exécuter des commandes et modifier des fichiers. Cursor ne documente actuellement aucun endpoint Router brut permettant d’effectuer des appels de modèles arbitraires.
Sélectionnez Coût, Balance ou Intelligence
Transmettez auto-smart et définissez explicitement optimize_for :
| Libellé du produit | Valeur SDK |
|---|---|
| Coût | cost |
| Balance | balanced |
| Intelligence | intelligence |
Utilisez Balance dans les textes du produit. Utilisez balanced uniquement comme valeur transmise par le SDK.
import osfrom cursor_sdk import Agent, LocalAgentOptions, ModelParameterValue, ModelSelectionwith Agent.create( model=ModelSelection( id="auto-smart", params=[ModelParameterValue(id="optimize_for", value="balanced")], ), local=LocalAgentOptions(cwd=os.getcwd()),) as agent: run = agent.send("Find and fix the failing authentication test") result = run.wait() print(result.status)Transmettez toujours optimize_for. Ne l’omettez pas et n’envoyez pas la valeur default héritée ; la découverte via le catalogue est le contrat pris en charge.
Découvrir Router dans le catalogue de modèles
Cursor.models.list() renvoie les modèles, les définitions de paramètres et les variantes prédéfinies disponibles pour le compte et l’équipe associés à la clé API. Cursor Router apparaît sous la forme de auto-smart lorsqu’il est disponible. Les administrateurs de l’équipe peuvent désactiver Router ou restreindre les modes d’optimisation que les membres peuvent sélectionner.
Considérez le catalogue comme la source de référence avant d’intégrer une sélection en dur :
from cursor_sdk import Cursor, ModelParameterValue, ModelSelectionmodels = Cursor.models.list()router = next((model for model in models if model.id == "auto-smart"), None)optimize_for = next( ( parameter for parameter in (router.parameters if router else []) if parameter.id == "optimize_for" ), None,)if router is None or optimize_for is None: raise RuntimeError( "Cursor Router is not available for this API key. " "Verify that Router is enabled for the key's team." )requested_mode = "balanced"allowed_values = {entry.value for entry in optimize_for.values}if requested_mode not in allowed_values: raise RuntimeError( f'Router mode "{requested_mode}" is not enabled for this team.' )model = ModelSelection( id=router.id, params=[ModelParameterValue(id=optimize_for.id, value=requested_mode)],)Changer de mode pour chaque exécution
Remplacez le modèle dans agent.send() pour changer le mode Router d’une exécution :
from cursor_sdk import ModelParameterValue, ModelSelection, SendOptionsrun = agent.send( "Handle this complex migration", SendOptions( model=ModelSelection( id="auto-smart", params=[ModelParameterValue(id="optimize_for", value="intelligence")], ), ),)Les remplacements de modèle par run restent actifs. Les envois ultérieurs sans remplacement continuent d’utiliser la nouvelle sélection. Voir Remplacement de modèle par run.
Identifiants de modèle : auto-smart, auto et default
| Sélection | Signification |
|---|---|
auto-smart avec optimize_for | Cursor Router. À utiliser lorsque vous souhaitez Cost, Balance ou Intelligence. |
ModelSelection(id="auto") | Mode de secours Auto sélectionné par le serveur lorsqu’un modèle spécifique est absent du catalogue. Préférez auto-smart si vous avez besoin d’un mode Router explicite. |
Omettre optimize_for ou envoyer default | Ce n’est pas un contrat Router pris en charge. Identifiez toujours les valeurs autorisées et transmettez cost, balanced ou intelligence. |
Facturation et pool de routage
- Cost suit le comportement Auto classique et la tarification Auto groupée.
- Balance et Intelligence utilisent Cursor Router et sont facturés au tarif du modèle sélectionné par le routeur, conformément à votre forfait ou contrat.
- Le modèle sous-jacent peut changer d’une requête à l’autre. Préférez un ID de modèle fixe si vous avez besoin de comparaisons reproductibles.
- Les listes d’autorisation de modèles Enterprise définissent le pool de routage. Bloquer des modèles requis peut désactiver Router.
Pour connaître les tarifs actuels et le pool de routage, voir Cursor Router et Modèles et tarifs.
Dépannage : Router absent
Si auto-smart est absent ou si un mode d’optimisation est refusé :
- Appelez
Cursor.models.list(). - Vérifiez que
auto-smartfigure dans le résultat. - Vérifiez que
optimize_forinclut la valeur souhaitée (cost,balancedouintelligence). - Vérifiez que Router est activé pour l’équipe associée à la clé API.
- Si vous appartenez à plusieurs équipes, vérifiez que la clé est utilisée dans le contexte de l’équipe prévue.
- Vérifiez la politique d’accès aux modèles de l’équipe si Router est indisponible ou ne peut pas sélectionner de modèle sous-jacent valide.
Dictionnaires bruts
Les dataclasses typées sont préférables dans le code d’application, car l’autocomplétion de l’IDE et la vérification des types fonctionnent mieux. Le SDK accepte également des dictionnaires simples pour les scripts courts ou le JSON fourni par des sources externes. Les clés en snake_case sont normalisées.
from cursor_sdk import Agentwith Agent.create( { "api_key": "crsr_key", "model": {"id": "composer-2.5"}, "local": {"cwd": "."}, }) as agent: ...Agent
La référence renvoyée par Agent.create(), Agent.resume(), client.agents.create() et client.agents.resume().
class Agent: agent_id: str model: ModelSelection | None client: CursorClient def send( self, message: str | Mapping[str, Any] | UserMessage, options: SendOptions | Mapping[str, Any] | None = None, *, idempotency_key: str | None = None, ) -> Run: ... def reload(self) -> None: ... def close(self) -> None: ... def list_messages( self, options: Mapping[str, Any] | None = None ) -> list[AgentMessage]: ... def list_artifacts(self) -> list[SDKArtifact]: ... def download_artifact(self, path: str) -> bytes: ... def get_usage(self, *, run_id: str | None = None) -> AgentUsage: ... def archive(self, options: Mapping[str, Any] | None = None) -> None: ... def unarchive(self, options: Mapping[str, Any] | None = None) -> None: ... def delete(self, options: Mapping[str, Any] | None = None) -> None: ...| Membre | Description |
|---|---|
agent_id | Identifiant stable de l’agent. agent-<uuid> en local, bc-<uuid> dans le cloud. |
model | Sélection de modèle typée actuelle. Mise à jour après un envoi réussi avec un remplacement de modèle. |
send | Démarre un nouveau run avec le prompt fourni. Renvoie une référence Run. |
reload | Relit la configuration du système de fichiers (hooks, MCP du projet, sous-agents) sans libérer les ressources. |
close | Ferme l’agent et libère les ressources. |
list_messages | Répertorie l’historique des messages de l’agent. |
list_artifacts | Répertorie les fichiers produits par l’agent (cloud uniquement ; local renvoie une liste vide). |
download_artifact | Télécharge un fichier selon son chemin (cloud uniquement ; local génère une erreur). |
get_usage | Récupère l’usage de tokens facturé et le coût en dollars pour l’agent. |
archive / unarchive / delete | Gère le cycle de vie de l’agent cloud. |
Utilisez un gestionnaire de contexte pour un nettoyage automatique :
with Agent.create(model="composer-2.5", local=LocalAgentOptions(cwd=".")) as agent: print(agent.send("Explain this repository").text())Lorsque vous utilisez les utilitaires sync Agent.* ou Cursor.* sans spécifier client=, le SDK crée ou réutilise un client par défaut au niveau du module. Celui-ci est automatiquement fermé à la fin du processus, mais vous pouvez également le fermer explicitement :
from cursor_sdk import close_default_clientclose_default_client()Agent.prompt()
Agent.prompt( message: str | Mapping[str, Any] | UserMessage, options: AgentOptions | Mapping[str, Any] | None = None, *, client: CursorClient | None = None,) -> RunResultPratique en une seule opération : crée un agent, envoie un prompt unique, attend la fin de l’exécution et libère les ressources.
from cursor_sdk import Agent, AgentOptions, LocalAgentOptionsresult = Agent.prompt( "What does the auth middleware do?", AgentOptions(model="composer-2.5", local=LocalAgentOptions(cwd=".")),)print(result.result)Équivalent asynchrone (en supposant qu’un AsyncClient est déjà ouvert) :
from cursor_sdk import AgentOptions, AsyncAgent, LocalAgentOptionsresult = await AsyncAgent.prompt( "What does the auth middleware do?", AgentOptions(model="composer-2.5", local=LocalAgentOptions(cwd=".")), client=client,)CursorClient
Utilisez CursorClient lorsque vous souhaitez contrôler explicitement le cycle de vie, utiliser un endpoint de bridge personnalisé, définir des options HTTP personnalisées ou gérer plusieurs espaces de travail au sein d’un même processus. Client reste disponible comme alias.
from cursor_sdk import CursorClient, LocalAgentOptionswith CursorClient.launch_bridge(workspace=".") as client: with client.agents.create( model="composer-2.5", api_key="crsr_key", local=LocalAgentOptions(cwd="."), ) as agent: print(agent.send("Summarize what this repository does").text())Ressources
Les clients explicites exposent des espaces de noms pour les ressources :
| Ressource | Exemples de méthodes synchrones | Exemples de méthodes asynchrones |
|---|---|---|
agents | client.agents.create(...), client.agents.list(...), client.agents.get(...) | await client.agents.create(...), await client.agents.list(...) |
models | client.models.list() | await client.models.list() |
repositories | client.repositories.list() | await client.repositories.list() |
Les méthodes de niveau supérieur telles que client.create_agent(...) et client.list_agents(...) restent disponibles, mais il est préférable d'utiliser les espaces de noms de ressources dans le code d'application.
Clients HTTP personnalisés
Les clients synchrones et asynchrones acceptent un client httpx personnalisé pour les proxys, les transports et d’autres configurations HTTP avancées :
from cursor_sdk import CursorClient, DefaultHttpxClientwith CursorClient.launch_bridge( workspace=".", http_client=DefaultHttpxClient(proxy="http://proxy.example.com"),) as client: ...from cursor_sdk import AsyncClient, DefaultAsyncHttpxClientasync with await AsyncClient.launch_bridge( workspace=".", http_client=DefaultAsyncHttpxClient(proxy="http://proxy.example.com"),) as client: ...DefaultHttpxClient et DefaultAsyncHttpxClient conservent le délai d’expiration et le comportement de redirection par défaut du SDK. Les clients httpx.Client et httpx.AsyncClient utilisent quant à eux les valeurs par défaut de httpx.
Configuration des délais d’expiration et des nouvelles tentatives
Les deux clients exposent with_options(...), qui renvoie une copie superficielle partageant les paramètres de connexion et remplaçant les valeurs par défaut. Utilisez timeout pour toutes les requêtes, ou définissez séparément unary_timeout et stream_timeout. max_retries contrôle les nouvelles tentatives du client :
short = client.with_options(timeout=5.0, max_retries=2)agent = short.agents.create(model="composer-2.5", local=LocalAgentOptions(cwd="."))Équivalent asynchrone :
short_async = async_client.with_options(timeout=5.0, max_retries=2)agent = await short_async.agents.create(model="composer-2.5", local=LocalAgentOptions(cwd="."))Envoi de messages
Chaque agent.send() renvoie un Run. Chaque await async_agent.send() renvoie un AsyncRun. L’agent conserve le contexte conversationnel d’un run à l’autre ; le run constitue l’unité de travail pour un prompt.
print(agent.send("Find the bug in src/auth.py").text())# Même agent, l’intégralité du contexte de la conversation est conservée.print(agent.send("Fix it and add a regression test").text())Équivalent asynchrone :
run = await agent.send("Find the bug in src/auth.py")print(await run.text())run = await agent.send("Fix it and add a regression test")print(await run.text())Pour envoyer des images avec du texte :
run = agent.send( { "text": "What's in this screenshot?", "images": [{"data": base64_png, "mime_type": "image/png"}], })Vous pouvez également utiliser des dataclasses utilitaires. SDKImage.from_file(path) lit le fichier depuis le disque et s’occupe de l’encodage base64 :
from cursor_sdk import SDKImage, UserMessagerun = agent.send( UserMessage( text="What's in this screenshot?", images=[SDKImage.from_file("screenshot.png")], ))SDKImage.data_image(base64_data, mime_type) et SDKImage.url_image(url) sont également disponibles pour les appelants disposant déjà d’octets encodés ou d’une URL distante.
Exécuter
class Run: id: str agent_id: str status: str # "running" | "finished" | "error" | "cancelled" | "expired" result: str model: ModelSelection | None duration_ms: int git: RunGitInfo | None created_at: str | None usage: TokenUsage | None # cumulée ; propriété de la référence active def stream(self) -> Iterator[SDKMessage]: ... def messages(self) -> Iterator[SDKMessage]: ... def events(self) -> Iterator[RunStreamEvent]: ... def iter_text(self) -> Iterator[str]: ... def text(self) -> str: ... def wait(self) -> RunResult: ... def cancel(self) -> None: ... def conversation(self) -> list[ConversationTurn]: ... def conversation_json(self) -> str: ... def observe(self, *, after_offset: str | None = None) -> Iterator[RunStreamEvent]: ... def supports(self, operation: str) -> bool: ... def unsupported_reason(self, operation: str) -> str | None: ... def on_did_change_status( self, listener: Callable[[str], None] ) -> Callable[[], None]: ...run.stream() est un alias de run.messages(). L’itération directe sur run produit des enveloppes RunStreamEvent, comme run.events().
AsyncRun expose les mêmes champs d’état, dont usage. Les méthodes qui effectuent des E/S sont asynchrones : async for message in run.stream(), async for message in run.messages(), async for event in run.events(), async for text in run.iter_text(), await run.text(), await run.wait(), await run.cancel(), await run.conversation(), await run.conversation_json(), et async for event in run.observe().
Streaming
run = agent.send("Find the bug in src/auth.py")for message in run.messages(): if message.type == "assistant": for block in message.message.content: if block.type == "text": print(block.text, end="") elif message.type == "thinking": print(message.text, end="") elif message.type == "tool_call": print(f"[tool] {message.name}: {message.status}") elif message.type == "status": print(f"[status] {message.status}") elif message.type == "usage": print(f"[usage] turn total={message.usage.total_tokens}")Un stream d’exécution ne peut être consommé qu’une seule fois. run.messages(), run.events() et run.iter_text() utilisent tous le même stream sous-jacent et le font avancer. Une fois le stream terminé, l’exécution contient le résultat final (run.result, run.status, run.usage, run.git, ...). Appelez run.wait() pour traiter les événements restants et renvoyer le RunResult typé.
Attente sans diffusion en continu
result = run.wait()print(result.status) # "finished" | "error" | "cancelled" | "expired"print(result.result) # texte final de l’assistant, le cas échéantprint(result.model) # ModelSelection résolu pour cette exécutionprint(result.duration_ms)print(result.usage) # TokenUsage cumulé, ou None s’il n’est pas disponibleprint(result.git) # RunGitInfo dans le CloudÉquivalent asynchrone :
result = await run.wait()Usage des tokens
Les exécutions indiquent l’usage des tokens lorsque l’environnement d’exécution la fournit. Consultez le total cumulé dans run.usage sur la référence active (pendant le streaming ou après wait()), ou dans result.usage sur le RunResult renvoyé par run.wait(). Les deux contiennent un TokenUsage cumulant les données de chaque tour ayant indiqué un usage, et valent None lorsqu’aucun tour ne l’a fait : par exemple, pour une exécution annulée qui n’a terminé aucun tour, un environnement d’exécution qui n’expose pas l’usage ou un instantané cloud détaché dont l’usage n’a pas encore été rapprochée.
@dataclass(frozen=True)class TokenUsage: input_tokens: int output_tokens: int cache_read_tokens: int cache_write_tokens: int total_tokens: int reasoning_tokens: int | None = None| Champ | Description |
|---|---|
input_tokens | Tokens du prompt envoyés au modèle. |
output_tokens | Tokens générés par le modèle. |
cache_read_tokens | Tokens récupérés depuis le cache du prompt. |
cache_write_tokens | Tokens écrits dans le cache du prompt. |
total_tokens | input_tokens + output_tokens + cache_read_tokens + cache_write_tokens. N’inclut pas reasoning_tokens. |
reasoning_tokens | Tokens de raisonnement, un sous-ensemble de output_tokens. None lorsque le modèle ou l’environnement d’exécution ne les a pas signalés. |
result = run.wait()if result.usage is not None: print(f"total: {result.usage.total_tokens}") print(f"in: {result.usage.input_tokens}, out: {result.usage.output_tokens}") print( f"cache read/write: {result.usage.cache_read_tokens}/{result.usage.cache_write_tokens}" )else: print("no usage reported for this run")reasoning_tokens est déjà comptabilisé dans output_tokens ; total_tokens l’exclut donc afin d’éviter un double comptage.
Pour obtenir les valeurs par tour au fil de leur diffusion, gérez l’événement de stream usage (SDKUsageMessage). Il est émis une fois à la fin de chaque tour ayant signalé un usage et contient le TokenUsage de ce tour. run.usage et result.usage restent cumulés sur l’ensemble de l’exécution. Après les tours de stream, la référence privilégie ces totaux cumulés ; sinon, elle utilise l’usage renvoyé par wait() ou par un instantané get_run / list_runs lorsque le bridge la fournit.
for message in run.messages(): if message.type == "usage": print(f"turn used {message.usage.total_tokens} tokens")# Ou après wait(), sans consommer les messages vous-même :result = run.wait()print(run.usage, result.usage)Équivalent async : async for message in run.messages() et await run.wait(). run.usage reste une propriété sync de AsyncRun.
TokenUsage est exporté par cursor_sdk (ainsi que to_token_usage / sum_token_usage pour les appelants avancés). Le JSON échangé utilise le format camelCase (inputTokens, …) ; les dataclasses Python utilisent snake_case.
Le nombre de tokens correspond à ce que l’environnement d’exécution rapporte ; il ne dit rien du coût. Pour connaître l'usage facturé et le coût en dollars des exécutions d'un agent, appelez agent.get_usage().
Lire la sortie texte
iter_text() produit le texte de l’assistant au fil du stream. text() renvoie le texte final dans le terminal et bloque sur wait() si l’exécution est toujours en cours.
for chunk in run.iter_text(): print(chunk, end="")final_text = run.text()Équivalent asynchrone :
async for chunk in run.iter_text(): print(chunk, end="")final_text = await run.text()Annuler une exécution
run.cancel()Équivalent asynchrone :
await run.cancel()run.cancel() demande l’annulation d’une exécution en cours. Le statut passe à "cancelled", le flux en direct s’arrête, les appels d’outils en cours sont interrompus et run.wait() se résout avec status: "cancelled". La sortie partielle (le texte produit jusque-là par l’assistant) reste sur l’objet Run.
L’annulation d’une exécution déjà terminée ("finished", "error", "cancelled", "expired") génère une erreur UnsupportedRunOperationError. En cas de doute, vérifiez run.status :
if run.status == "running": run.cancel()Consultation de l’état d’exécution
print(run.id)print(run.status) # "running" | "finished" | "error" | "cancelled" | "expired"stop = run.on_did_change_status(lambda status: print(f"status changed to {status}"))stop() # supprimer l’écouteurturns = run.conversation()run.conversation() renvoie une list[ConversationTurn] typée. Utilisez-la pour afficher ou conserver un historique structuré sans vous abonner au flux en direct. run.conversation_json() renvoie la chaîne JSON brute.
Pour les exécutions asynchrones, utilisez await run.conversation() et await run.conversation_json().
Remplacement du modèle pour un run
Le model transmis à agent.send() remplace le modèle sélectionné par l’agent pour ce run, puis reste actif : les envois suivants sans remplacement continuent d’utiliser le nouveau modèle. Pour revenir au modèle précédent, transmettez un autre model ou consultez la sélection actuelle dans agent.model.
from cursor_sdk import ModelParameterValue, ModelSelection, SendOptionsrun = agent.send( "Plan the refactor", SendOptions( model=ModelSelection( id="composer-2.5", params=[ModelParameterValue(id="fast", value="true")], ), ),)run.model et result.model reflètent le modèle sélectionné pour cette exécution et ne peuvent plus être modifiés une fois l’exécution lancée.
Variables d’environnement par run
Les agents cloud peuvent également recevoir des variables d’environnement pour un seul run. Passez cloud.env_vars dans SendOptions : les valeurs sont injectées dans le shell de l’agent uniquement pour ce run. Une fois le run terminé, elles sont supprimées de la VM et ne sont pas accessibles lors du run suivant. Cette approche convient aux identifiants qui changent entre les tours, comme un token de déploiement de courte durée généré juste avant de demander à l’agent de l’utiliser.
from cursor_sdk import CloudSendOptions, SendOptionsrun = agent.send( "Deploy the preview environment", SendOptions( cloud=CloudSendOptions(env_vars={"DEPLOY_TOKEN": mint_short_lived_token()}), ),)Si une variable par run porte le même nom qu’une variable associée à l’agent définie dans env_vars de CloudAgentOptions, la valeur par run prévaut pour ce run, puis la valeur associée à l’agent reprend effet au run suivant.
Les variables par run fonctionnent également lors du premier envoi. Le SDK les transmet lors de la création de l’agent, en les limitant au run initial afin qu’elles ne soient pas persistées sur l’agent. Comme les variables associées à l’agent, elles sont chiffrées au repos et leurs noms ne peuvent pas commencer par CURSOR_.
Les variables d’environnement par run sont réservées aux agents cloud et ne sont pas disponibles pour les agents exécutés sur des dépôts publics. Pour les agents locaux, le processus de l’agent hérite de votre environnement. Définissez donc les variables sur le processus avant d’appeler send().
Mode de conversation
Passez mode="plan" ou mode="agent" pour contrôler si une exécution doit d’abord explorer et planifier, ou implémenter directement les modifications. Consultez le mode Plan pour savoir comment fonctionne le mode Plan dans le produit.
Définissez mode dans les AgentOptions transmis à Agent.create() pour définir le mode de la première exécution. Lors des appels agent.send() de suivi, omettez mode pour conserver le mode actuel de la conversation, ou transmettez mode pour changer de mode uniquement pour cette exécution.
from cursor_sdk import Agent, AgentOptions, CloudAgentOptions, CloudRepository, SendOptionswith Agent.create( AgentOptions( model="composer-2.5", mode="plan", cloud=CloudAgentOptions( repos=[CloudRepository(url="https://github.com/your-org/your-repo")], ), )) as agent: agent.send("Design the auth refactor").wait() agent.send( "Looks good, start building", SendOptions(mode="agent"), ).wait()Diffusion en continu des deltas bruts
Passez les callbacks on_delta et on_step à SendOptions pour obtenir des mises à jour de bas niveau. Les callbacks synchrones sont appelés en ligne. Les callbacks asynchrones peuvent être synchrones ou asynchrones ; les valeurs de retour awaitables sont attendues avant le traitement de l’événement suivant.
from cursor_sdk import SendOptionsdef on_delta(update): if update.type in ("text-delta", "thinking-delta"): print(update.text, end="")run = agent.send( "Refactor the utils module", SendOptions(on_delta=on_delta, on_step=lambda step: print(f"[step] {step.type}")),)run.wait()Les sous-classes concrètes d’update et de step se trouvent dans cursor_sdk.events :
from cursor_sdk.events import TextDeltaUpdate, ToolCallStartedUpdateif isinstance(update, TextDeltaUpdate): print(update.text)Ils peuvent toujours être importés depuis cursor_sdk pour préserver la compatibilité descendante, mais le nouveau code doit les importer depuis cursor_sdk.events.
SendOptions
| Propriété | Type | Description |
|---|---|---|
model | str | ModelSelection | Mapping[str, Any] | Remplacement du modèle pour cet envoi. S’il est omis, utilise agent.model. Persiste après un envoi réussi. |
mode | "agent" | "plan" | Remplacement du mode de conversation pour cet envoi. S’il est omis dans les messages de suivi, conserve le mode actuel de la conversation. |
mcp_servers | Mapping[str, McpServerConfig] | Définitions de serveurs MCP en ligne. Remplace entièrement les serveurs définis à la création pour ce run. |
cloud.env_vars | Mapping[str, str] | Agents cloud uniquement. Variables d’environnement par run injectées pour ce run et supprimées à sa fin. Remplace, pour ce run uniquement, les env_vars associés à l’agent portant le même nom. |
local.force | bool | Agents locaux uniquement. Par défaut, None (non défini). Définissez True pour expirer une exécution en cours bloquée avant d’envoyer ce message. Cloud renvoie 409 agent_busy côté serveur, aucun équivalent n’est donc nécessaire. |
idempotency_key | str | Clé d’idempotence facultative générée par le client pour l’envoi. |
on_step | Callable[[ConversationStep], Any] | Callback après chaque étape de conversation terminée (texte, réflexion ou lot d’outils). |
on_delta | Callable[[InteractionUpdate], Any] | Callback pour chaque InteractionUpdate brut. |
Les trois sections suivantes fournissent une référence détaillée pour SDKMessage, InteractionUpdate et ConversationTurn. Parcourez-les rapidement ou ignorez-les lors d’une première lecture ; reprendre des agents reprend ensuite le fil.
Événements du flux
run.messages() renvoie des dataclasses de messages SDK typés. Utilisez message.type pour les distinguer. Tous les messages incluent agent_id et run_id lorsque l’environnement d’exécution les fournit.
SDKMessage = ( SDKSystemMessage | SDKUserMessageEvent | SDKAssistantMessage | SDKThinkingMessage | SDKToolUseMessage | SDKStatusMessage | SDKTaskMessage | SDKRequestMessage | SDKUsageMessage | Mapping[str, Any])type | Dataclass | Champs clés |
|---|---|---|
"system" | SDKSystemMessage | subtype, model, tools |
"user" | SDKUserMessageEvent | message.content |
"assistant" | SDKAssistantMessage | message.content avec des valeurs TextBlock et ToolUseBlock |
"thinking" | SDKThinkingMessage | text, thinking_duration_ms |
"tool_call" | SDKToolUseMessage | call_id, name, status, args, result, truncated |
"status" | SDKStatusMessage | status, message |
"task" | SDKTaskMessage | status, text |
"request" | SDKRequestMessage | request_id |
"usage" | SDKUsageMessage | usage (TokenUsage) |
SDKToolUseMessage est émis deux fois pour la plupart des appels d’outils : une première fois avec status="running" et args renseigné, puis à la fin avec status="completed" (ou "error") et result renseigné. truncated indique si le SDK a tronqué args ou result parce que le payload était trop volumineux.
SDKUsageMessage est émis une fois à la fin de chaque tour ayant rapporté un usage de tokens et contient le TokenUsage de ce tour. Le total cumulé sur l’ensemble des tours est conservé dans run.usage et result.usage. Voir Utilisation des tokens.
@dataclass(frozen=True)class SDKUsageMessage: type: Literal["usage"] agent_id: str run_id: str usage: TokenUsageLes données de résultat (texte final, modèle, durée, usage cumulé de tokens, métadonnées Git) sont disponibles sur l’objet Run une fois le flux terminé. Utilisez run.wait() pour les récupérer, y compris result.usage lorsque l’environnement d’exécution le renvoie.
Le schéma des appels d’outils n’est pas stable. Les payloads
argsetresultdes événementstool_callreflètent la structure interne de chaque outil et peuvent évoluer avec les outils. Les noms des outils peuvent également être renommés ou remplacés. Traitezargsetresultcomme des données non typées et analysez-les de manière défensive. L’enveloppe d’événement (type,call_id,name,status) est stable.
run.events() produit des enveloppes RunStreamEvent de plus bas niveau. Utilisez-le lorsque vous avez besoin de décalages, d’enveloppes de résultat final ou de mises à jour brutes des interactions :
for event in run.events(): print(event.kind, event.offset)Mises à jour d’interaction
InteractionUpdate est le type de delta brut transmis au callback on_delta de agent.send(). Les mises à jour sont plus détaillées que les événements SDKMessage : le texte est transmis token par token et les appels d’outils signalent un état partiel à mesure que les args s’accumulent.
InteractionUpdate = ( TextDeltaUpdate | ThinkingDeltaUpdate | ThinkingCompletedUpdate | ToolCallStartedUpdate | ToolCallCompletedUpdate | PartialToolCallUpdate | TokenDeltaUpdate | StepStartedUpdate | StepCompletedUpdate | TurnEndedUpdate | UserMessageAppendedUpdate | SummaryUpdate | SummaryStartedUpdate | SummaryCompletedUpdate | ShellOutputDeltaUpdate | UnknownInteractionUpdate | Mapping[str, Any])PartialToolCallUpdate est émis lorsque le modèle transmet progressivement les arguments d’un appel d’outil, avant de le valider. Le même avertissement de stabilité que pour SDKToolUseMessage.args s’applique ici.
Types de conversation
Vue structurée de chaque tour d’une exécution, renvoyée par run.conversation(). Chaque élément est un encapsuleur contenant le discriminateur type du tour, ainsi que le payload typé dans turn.
@dataclass(frozen=True)class ConversationTurn: type: str # "agentConversationTurn" | "shellConversationTurn" turn: AgentConversationTurn | ShellConversationTurn | Mapping[str, Any]@dataclass(frozen=True)class AgentConversationTurn: user_message: Mapping[str, Any] | None = None steps: Sequence[ConversationStep] = ()@dataclass(frozen=True)class ShellConversationTurn: shell_command: ShellCommand | None = None shell_output: ShellOutput | None = NoneConversationStep = ( AssistantConversationStep | ToolCallConversationStep | ThinkingConversationStep | Mapping[str, Any])Faites la distinction selon turn.type et lisez le payload via turn.turn :
for turn in run.conversation(): if turn.type == "agentConversationTurn": for step in turn.turn.steps: print(step.type) elif turn.type == "shellConversationTurn": print(turn.turn.shell_command, turn.turn.shell_output)run.conversation() appelée depuis les callbacks on_step se déclenche pour chaque ConversationStep, et non pour chaque tour. Les étapes de conversation d’appel d’outil contiennent un payload Mapping[str, Any]. Traitez les détails du payload d’appel d’outil comme des données non typées ; voir la note de stabilité dans la section événements du flux.
Reprendre des agents
Agent.resume( agent_id: str, options: AgentOptions | Mapping[str, Any] | None = None, *, client: CursorClient | None = None,) -> AgentUtilisez Agent.resume() ou client.agents.resume() pour vous rattacher à un agent existant à l’aide de son ID. Cas courants : se reconnecter à un agent cloud de longue durée lancé auparavant ou poursuivre une conversation après le redémarrage du processus local. L’environnement d’exécution est détecté automatiquement à partir du préfixe de l’ID (bc- indique le cloud, tout autre préfixe indique le local).
agent = Agent.resume("bc-abc123")run = agent.send("Also update the changelog")run.wait()Équivalent asynchrone :
agent = await client.agents.resume("bc-abc123")run = await agent.send("Also update the changelog")await run.wait()agent.model est None lors de la reprise, sauf si vous passez à nouveau model. Les serveurs MCP en ligne ne sont pas conservés d'une reprise à l'autre ; ils contiennent souvent des secrets et ne résident qu'en mémoire. Passez-les à nouveau lors de la reprise, ou utilisez une configuration MCP basée sur des fichiers (.cursor/mcp.json et local.setting_sources) pour les serveurs qui doivent être conservés.
Persistance locale
Les agents locaux conservent l’état de la conversation et les métadonnées d’exécution via le bridge, afin que les messages de suivi et Agent.resume() soient préservés après le redémarrage d’un processus. Par défaut, le bridge stocke ces données sur disque, dans une racine d’état propre à chaque espace de travail. Les agents cloud conservent leurs données côté serveur : reprendre un agent cloud depuis n’importe où renvoie donc la même conversation.
La persistance locale est limitée à l’espace de travail. Lorsque le bridge s’exécute en tant que processus compagnon ou sous-processus de longue durée, utilisez le même espace de travail que pour l’agent afin que les appels locaux de liste, d’obtention et de reprise ciblent les bons agents. Définissez-le une seule fois sur le client et transmettez cwd aux appels locaux de liste et d’obtention :
from cursor_sdk import CursorClientwith CursorClient.launch_bridge(workspace="/path/to/repo") as client: agents = client.agents.list(runtime="local", cwd="/path/to/repo") info = client.agents.get(agents.items[0].agent_id, cwd="/path/to/repo")Consultation des agents et des exécutions
Utilisez CursorClient pour les API de liste, de récupération et de pagination.
from cursor_sdk import CursorClientwith CursorClient.launch_bridge(workspace=".") as client: agents = client.agents.list(runtime="local", cwd=".") for agent_info in agents.auto_paging_iter(): print(agent_info.agent_id) info = client.agents.get(agents.items[0].agent_id) runs = client.agents.list_runs(info.agent_id) run = client.agents.get_run(runs.items[0].id)Équivalent asynchrone :
agents = await client.agents.list(runtime="local", cwd=".")async for agent_info in agents.auto_paging_iter(): print(agent_info.agent_id)info = await client.agents.get(agents.items[0].agent_id)runs = await client.agents.list_runs(info.agent_id)run = await client.agents.get_run(runs.items[0].id)Utilisez agent.list_messages() sur une référence d’agent pour consulter l’historique des messages. Agent.messages.list(agent_id) est un raccourci avec attributs typés pour le même appel lorsque vous ne disposez que d’un ID.
Utilisez Agent.get_run(run_id) ou client.agents.get_run(run_id) pour récupérer une exécution
sans référence d’agent. Annulez-la avec
Agent.cancel_run(run_id, agent_id=...) ou
client.agents.cancel_run(run_id, agent_id=...). Les méthodes du client asynchrone peuvent être
utilisées avec await et acceptent les mêmes arguments.
AgentMessage est distinct d’un SDKMessage transmis en flux :
@dataclass(frozen=True)class AgentMessage: type: str uuid: str agent_id: str message: Any = NoneLes endpoints de liste renvoient ListResult[T]. Utilisez directement .items et .next_cursor, itérez sur la page actuelle avec for item in page ou sur toutes les pages avec .auto_paging_iter(). Les endpoints de liste asynchrones renvoient AsyncListResult[T] ; async for item in page itère sur la page actuelle, tandis que async for item in page.auto_paging_iter() parcourt toutes les pages de l’ensemble de résultats.
SDKAgentInfo
La structure des métadonnées renvoyée par Agent.list(), Agent.get(), client.agents.list() et client.agents.get().
@dataclass(frozen=True)class SDKAgentInfo: agent_id: str name: str summary: str last_modified: str | None = None status: str | None = None # "running" | "finished" | "error" created_at: str | None = None archived: bool = False runtime: Literal["local", "cloud"] | None = None cwd: str = "" env: CloudEnvironment | None = None repos: Sequence[str] = () metadata: Mapping[str, str] = {} # issu de CloudAgentOptions.metadata ; vide pour les agents locauxCycle de vie des agents cloud
Les agents cloud restent dans l’espace de travail de votre équipe jusqu’à ce que vous les archiviez ou les supprimiez. client.agents.list(runtime="cloud") masque les agents archivés par défaut ; utilisez include_archived=True pour les afficher. Filtrez par pr_url pour trouver l’agent qui a ouvert une pull request donnée.
# Par ID, sans référence d’agent :Agent.archive(agent_id)Agent.unarchive(agent_id)Agent.delete(agent_id)# Via un client explicite :client.agents.archive(agent_id)client.agents.unarchive(agent_id)client.agents.delete(agent_id)# Sur une référence d’agent existante :agent.archive()agent.unarchive()agent.delete()archive archive l’agent tout en laissant le transcript lisible. unarchive le restaure. delete est définitif ; les lectures ultérieures renvoient NotFoundError.
Les méthodes async du cycle de vie portent les mêmes noms et peuvent être utilisées avec await.
agent.get_usage()
Récupère le nombre de tokens facturés et le coût en dollars des exécutions d’un agent. Les agents cloud renvoient une ventilation par exécution ; les agents locaux, par tour. Passez run_id pour limiter le résultat à une seule entrée : pour les agents cloud, un ID d’exécution run-<uuid> ; pour les agents locaux, un ID provenant d’un précédent get_usage().runs[].run_id.
usage = agent.get_usage()print(f"tokens: {usage.usage.total_tokens}")if usage.cost is not None: print(f"charged: ${usage.cost.charged_cents / 100:.2f}")for run in usage.runs: print(run.run_id, run.usage.total_tokens)@dataclass(frozen=True)class AgentUsage: usage: TokenUsage # totalisé sur l’ensemble des `runs` runs: Sequence[RunUsage] = () cost: UsageCost | None = None # totalisé sur l’ensemble des `runs`@dataclass(frozen=True)class RunUsage: run_id: str usage: TokenUsage cost: UsageCost | None = None@dataclass(frozen=True)class UsageCost: raw_cost_cents: float # coût des tokens du modèle hors remise ; 0 pour l’usage facturé à la requête charged_cents: float # montant facturé, remises et tarif des tokens Cursor inclusLe coût tient compte des remises et peut mettre un moment à être finalisé après la fin d’une exécution ; cost est None en attendant. charged_cents vaut 0.0 pour l’usage inclus dans le forfait, l’usage BYOK et les crédits accordés.
Cette vue diffère de l’utilisation des tokens : run.usage correspond au décompte de tokens en direct pour une exécution, tandis que get_usage() fournit l’enregistrement facturé pour l’ensemble des exécutions de l’agent. Pour les agents async, await agent.get_usage() renvoie le même résultat. AgentUsage, RunUsage et UsageCost sont exportés par cursor_sdk.
L’espace de noms Cursor
Lecture des données du compte et du catalogue. Les méthodes de synchronisation acceptent une api_key facultative et utilisent sinon CURSOR_API_KEY.
from cursor_sdk import Cursorme = Cursor.me()models = Cursor.models.list()repositories = Cursor.repositories.list()Équivalent avec un client explicite :
me = client.me()models = client.models.list()repositories = client.repositories.list()Équivalent asynchrone :
from cursor_sdk import AsyncCursorme = await AsyncCursor.me(client=client)models = await AsyncCursor.models.list(client=client)repositories = await AsyncCursor.repositories.list(client=client)Cursor.me() renvoie un SDKUser avec les champs api_key_name, created_at et
facultatifs user_id, user_email, user_first_name et user_last_name.
Utilisez Cursor.models.list() pour obtenir les ID de modèle valides et les paramètres propres à chaque modèle avant d’appeler Agent.create() ou agent.send(). Les paramètres dépendent du modèle. Les exemples courants incluent l’effort de raisonnement et optimize_for de Cursor Router pour auto-smart.
Le catalogue dépend du compte et de l’équipe. Cursor Router n’apparaît sous la forme auto-smart que lorsque Router est disponible pour l’équipe associée à la clé API. Voir Cursor Router.
models = Cursor.models.list()composer = next((model for model in models if model.id == "composer-2.5"), None)print(composer.parameters if composer else [])# [# ModelParameterDefinition(# id="fast",# display_name="Fast",# values=(# ModelParameterDefinitionValue(value="false"),# ModelParameterDefinitionValue(value="true", display_name="Fast"),# ),# ),# ]Les variants prédéfinis de chaque SDKModel contiennent déjà des params valides ; vous pouvez donc les copier dans un ModelSelection.
Privilégiez une sélection explicite du Router (auto-smart + optimize_for) lorsqu’un modèle cible est absent et que vous souhaitez utiliser Cost, Balance ou Intelligence. N’utilisez ModelSelection(id="auto") que si vous souhaitez laisser le serveur sélectionner Auto sans choisir de mode Router. Pour Cursor Router, transmettez toujours optimize_for explicitement.
Cursor.repositories.list() renvoie les dépôts SCM (GitHub, GitLab, Bitbucket, Azure DevOps, selon les services connectés) disponibles pour les agents cloud du compte ou de l’équipe appelante. Chaque élément expose une url. Utilisez-les pour renseigner CloudAgentOptions.repos.
Serveurs MCP
Selon l’environnement d’exécution, les Agents peuvent récupérer des serveurs MCP à partir de définitions en ligne, de paramètres de projet ou d’utilisateur, de plugins et d’une configuration gérée depuis le tableau de bord.
from cursor_sdk import ( Agent, AgentOptions, HttpMcpServerConfig, LocalAgentOptions, McpAuth, StdioMcpServerConfig,)agent = Agent.create( AgentOptions( model="composer-2.5", local=LocalAgentOptions(cwd="."), mcp_servers={ "docs": HttpMcpServerConfig( url="https://example.com/mcp", auth=McpAuth(client_id="client-id", scopes=["read", "write"]), ), "filesystem": StdioMcpServerConfig( command="npx", args=["-y", "@modelcontextprotocol/server-filesystem", "."], ), }, ))Les dictionnaires plats ({"type": "http", "url": ...} et {"type": "stdio", "command": ...}) sont également acceptés pour simplifier les scripts rapides.
Éléments chargés
Les agents locaux chargent les serveurs depuis cinq sources au maximum. En cas de noms en conflit, la première correspondance est prioritaire :
mcp_serversdansagent.send(). Remplace entièrement les serveurs définis lors de la création pour cette exécution (sans fusion).mcp_serversdansAgent.create(). Utilisé lorsqu’aucun remplacement n’est fourni lors de l’envoi.- Les serveurs de plugins, si
local.setting_sourcesinclut"plugins". - Les serveurs du projet dans
.cursor/mcp.json, silocal.setting_sourcesinclut"project". - Les serveurs utilisateur dans
~/.cursor/mcp.json, silocal.setting_sourcesinclut"user".
Sans local.setting_sources, seuls les serveurs en ligne sont chargés. Si un serveur MCP local nécessite une connexion OAuth, le SDK peut réutiliser une connexion enregistrée dans l’application Cursor, mais il ne peut pas ouvrir un navigateur pour vous connecter.
Les agents cloud chargent les serveurs depuis :
mcp_serversdansagent.send(). Remplace entièrement les serveurs définis lors de la création pour cette exécution (sans fusion).mcp_serversdansAgent.create(). Utilisé lorsqu’aucun remplacement n’est fourni lors de l’envoi.- Vos serveurs MCP utilisateur et d’équipe depuis cursor.com/agents.
Si un serveur en ligne n’inclut ni auth ni headers et que vous avez déjà autorisé l’URL de ce serveur sur cursor.com/agents, les exécutions authentifiées avec un token API personnel réutilisent automatiquement ces tokens OAuth. Les clés API de compte de service ne peuvent pas utiliser l’authentification utilisateur comme solution de repli, car elles ne sont pas associées à un utilisateur.
local.setting_sources ne s’applique pas aux agents cloud.
Cloud
Les agents cloud acceptent également les configurations MCP authentifiées en ligne. MCP Cloud prend en charge les transports HTTP et stdio. Utilisez les headers HTTP pour les clés API statiques ou les tokens Bearer. Utilisez l’auth HTTP pour les serveurs protégés par OAuth. Utilisez env pour stdio lorsque le serveur s’exécute dans la VM cloud et lit les identifiants depuis les variables d’environnement.
from cursor_sdk import ( Agent, AgentOptions, CloudAgentOptions, CloudRepository, HttpMcpServerConfig, StdioMcpServerConfig,)agent = Agent.create( AgentOptions( model="composer-2.5", cloud=CloudAgentOptions( repos=[CloudRepository(url="https://github.com/your-org/your-repo")], ), mcp_servers={ "linear": HttpMcpServerConfig( url="https://mcp.linear.app/mcp", headers={"Authorization": "Bearer linear_pat_xxx"}, ), "github": StdioMcpServerConfig( command="npx", args=["-y", "@modelcontextprotocol/server-github"], env={"GITHUB_TOKEN": "ghp_xxx"}, ), }, ))- Les
headersHTTP et l’authsont gérés par le backend de Cursor. Les champs sensibles sont masqués et ne sont pas transmis à la VM. - Les valeurs
envde Stdio sont transmises à la VM, car le serveur s’y exécute. Traitez-les comme n’importe quel autre secret d’exécution. - OAuth pour les serveurs MCP configurés sur cursor.com/agents reste propre à chaque utilisateur, même pour les serveurs au niveau de l’équipe.
Voir MCP pour le format de configuration complet et les fonctionnalités de l’agent cloud pour le comportement spécifique au cloud.
Sous-agents
Définissez des sous-agents nommés que l’agent principal peut créer via l’outil Agent. Définissez-les directement en ligne :
from cursor_sdk import Agent, AgentDefinition, AgentOptions, LocalAgentOptionsagent = Agent.create( AgentOptions( model="composer-2.5", local=LocalAgentOptions(cwd="."), agents={ "code-reviewer": AgentDefinition( description="Expert code reviewer for quality and security.", prompt="Review code for bugs, security issues, and proven approaches.", model="inherit", ), "test-writer": AgentDefinition( description="Writes tests for code changes.", prompt="Write comprehensive tests for the given code.", ), }, ))Les sous-agents enregistrés dans le dépôt sous .cursor/agents/*.md (avec un en-tête YAML name, description et model facultatif) sont également pris en compte. Les définitions en ligne remplacent celles définies dans des fichiers du même nom.
Sous-agents imbriqués
Les sous-agents peuvent créer leurs propres sous-agents, dans la limite d’un certain niveau d’imbrication. Lorsqu’un sous-agent utilise l’outil Agent, il accède au même exécuteur de sous-agents que son parent. Un agent parent peut donc déléguer à un sous-agent, qui peut lui-même déléguer davantage. Chaque niveau voit le même ensemble de sous-agents nommés. L’agent de niveau supérieur et ses sous-agents directs peuvent lancer des sous-agents, mais un sous-agent lancé par un autre sous-agent ne peut pas en lancer d’autres.
Restreindre l’ensemble d’outils
tools définit une liste d’autorisation des outils intégrés proposés au modèle ; disallowed_tools supprime des outils et conserve les autres, y compris ceux ajoutés à la plateforme après la publication de votre version du SDK. Ces deux options sont pour l’instant réservées aux agents locaux et ne persistent pas sur l’agent : transmettez-les à nouveau lors de la reprise pour conserver la restriction.
from cursor_sdk import Agent, AgentOptions, LocalAgentOptions# Agent en lecture seule : seuls ces outils sont disponibles.reader = Agent.create( AgentOptions( model="composer-2.5", tools=["read", "grep", "glob", "ls"], local=LocalAgentOptions(cwd="."), ))# Tous les outils sauf le shell.no_shell = Agent.create( AgentOptions( model="composer-2.5", disallowed_tools=["shell"], local=LocalAgentOptions(cwd="."), ))- Si vous omettez
tools, le modèle sélectionné reçoit l’ensemble d’outils standard ;tools=[]n’offre aucun outil intégré, le modèle ne peut donc répondre qu’avec du texte. - Les deux champs acceptent des noms publics (
"read","edit","task","webSearch", ...) et les groupes de capacités"shell"et"mcp". Les noms inconnus génèrent uneBadRequestErrorà la création. - Le refus l’emporte : pour être proposé, un outil doit figurer dans
tools(lorsqu’il est défini) et ne pas figurer dansdisallowed_tools. - Interdire
"mcp"supprime également les outils personnalisés. Interdire"task"empêche les sous-agents ; sinon, les sous-agents conservent leurs propres ensembles d’outils sélectionnés.
Outils personnalisés
Les outils personnalisés permettent d’exposer des fonctions Python aux agents locaux sans avoir à déployer un serveur MCP distinct. Passez-les via LocalAgentOptions.custom_tools.
from cursor_sdk import Agent, CustomTool, CustomToolContext, LocalAgentOptionsdef get_deployment_status(args, context: CustomToolContext): service = args["service"] return f"Service {service} is healthy."with Agent.create( model="composer-2.5", local=LocalAgentOptions( cwd=".", custom_tools={ "get_deployment_status": CustomTool( description="Look up the current deployment status for a service.", input_schema={ "type": "object", "properties": { "service": {"type": "string", "description": "Service name"}, }, "required": ["service"], }, execute=get_deployment_status, ), }, ),) as agent: agent.send("Is the checkout service healthy?").wait()execute reçoit les arguments analysés et un CustomToolContext avec tool_call_id lorsqu’il est disponible. Il peut renvoyer une chaîne, une valeur compatible avec JSON ou un mapping contenant une liste content. Les outils personnalisés sont uniquement pris en charge par les agents locaux.
Hooks
Les hooks sont uniquement définis dans des fichiers. Il n’existe pas de callback de hook programmatique. Les hooks délimitent la politique d’un projet ; ils ne constituent pas un paramètre par run.
- Local : ajoutez
.cursor/hooks.jsonau dépôt indiqué danslocal.cwd, ou ajoutez~/.cursor/hooks.jsonpour les hooks au niveau utilisateur. - Cloud : validez
.cursor/hooks.jsonet ses scripts dans le dépôt indiqué danscloud.repos. Les agents cloud créés par le SDK chargent automatiquement les hooks de projet. Avec les forfaits Enterprise, ils exécutent également les hooks d’équipe et les hooks gérés par l’entreprise.
Voir Hooks pour le format de configuration et prise en charge des hooks des agents cloud pour le comportement dans le cloud.
Artefacts
Répertoriez et téléchargez les fichiers de l’espace de travail de l’agent.
@dataclass(frozen=True)class SDKArtifact: path: str size_bytes: int = 0 updated_at: str = ""from pathlib import Pathartifacts = agent.list_artifacts()for artifact in artifacts: print(artifact.path, artifact.size_bytes)# Télécharge un artefact sur le disque.content = agent.download_artifact(artifacts[0].path)Path("review.md").write_bytes(content)Les agents asynchrones exposent await agent.list_artifacts() et await agent.download_artifact(path).
La prise en charge des artefacts dépend de l’environnement d’exécution. Les agents SDK locaux renvoient une liste vide avec list_artifacts() et lèvent une exception avec download_artifact().
Gestion des ressources
Fermez toujours les Agents une fois leur utilisation terminée. Le schéma synchrone le plus propre consiste à utiliser un gestionnaire de contexte :
from cursor_sdk import Agent, LocalAgentOptionswith Agent.create(model="composer-2.5", local=LocalAgentOptions(cwd=".")) as agent: agent.send("Summarize the repository").wait()Pour libérer explicitement :
agent.close()Les agents et clients asynchrones prennent en charge les gestionnaires de contexte asynchrones et le nettoyage via await :
from cursor_sdk import AsyncClient, LocalAgentOptionsasync with await AsyncClient.launch_bridge(workspace=".") as client: async with await client.agents.create( model="composer-2.5", local=LocalAgentOptions(cwd="."), ) as agent: run = await agent.send("Summarize the repository") await run.wait()Pour libérer explicitement :
await agent.close()await client.aclose()Le client synchrone par défaut du module est automatiquement fermé à la fin du processus. Les processus de longue durée peuvent le fermer et le réinitialiser explicitement :
from cursor_sdk import close_default_clientclose_default_client()Référence de configuration
Le SDK Python accepte des dataclasses d’aide et des dictionnaires bruts. Les dataclasses utilisent des champs Python en snake_case et sont recommandées pour le code d’application.
AgentOptions
| Propriété | Type | Par défaut | Description |
|---|---|---|---|
model | str | ModelSelection | Mapping[str, Any] | Obligatoire en local ; pour le cloud, valeur par défaut résolue par le serveur | Modèle à utiliser. Voir ModelSelection. |
api_key | str | Variable d’environnement CURSOR_API_KEY | Clé API utilisateur ou clé de compte de service. Les clés Team Admin ne sont pas encore prises en charge. |
name | str | Généré automatiquement | Nom d’agent lisible par l’utilisateur, renvoyé par client.agents.list() / client.agents.get(). |
local | LocalAgentOptions | Mapping[str, Any] | None | Configuration de l’agent local. À transmettre pour créer un agent local. |
cloud | CloudAgentOptions | Mapping[str, Any] | None | Configuration de l’agent cloud. À transmettre pour créer un agent cloud. |
mcp_servers | Mapping[str, McpServerConfig] | None | Définitions de serveurs MCP en ligne. |
agents | Mapping[str, AgentDefinition | Mapping[str, Any]] | None | Définitions de sous-agents. |
tools | Sequence[str] | Jeu d’outils par défaut | Seuls les outils intégrés répertoriés sont proposés au modèle. [] signifie qu’aucun outil intégré n’est disponible ; le modèle ne peut répondre qu’avec du texte. Agents locaux uniquement. |
disallowed_tools | Sequence[str] | None | Supprime les outils intégrés répertoriés ; tous les autres restent disponibles. L’interdiction prévaut lorsqu’elle est combinée avec tools. Agents locaux uniquement. |
agent_id | str | Généré automatiquement | ID d’agent persistant. À transmettre pour conserver un ID stable entre les invocations. |
idempotency_key | str | Généré automatiquement pour le cloud | Clé d’idempotence facultative générée par le client. Cloud uniquement. |
mode | "agent" | "plan" | None | Mode de conversation initial de la première exécution de l’agent. S’il est omis, le serveur démarre en mode Agent. Voir Mode de conversation. |
LocalAgentOptions
| Propriété | Type | Par défaut | Description |
|---|---|---|---|
cwd | str | os.PathLike | None | Répertoire de travail principal. Les listes comportant plusieurs entrées sont refusées ; utilisez dirs pour plusieurs racines. |
dirs | Sequence[str | os.PathLike] | None | Dossiers d’espace de travail supplémentaires pour les configurations à plusieurs racines. Fusionnés avec cwd afin que les règles, les compétences et le contexte de l’espace de travail soient chargés depuis chaque chemin. |
setting_sources | Sequence[SettingSource] | None | Couches de paramètres ambiantes : "project", "user", "team", "mdm", "plugins" ou "all". |
sandbox_options | SandboxOptions | Mapping[str, Any] | None | Options du sandbox local. |
store | LocalAgentStoreConfig | Mapping[str, Any] | None | Configuration du store local transmise au bridge. |
auto_review | bool | None | Achemine les appels d’outils locaux via la révision automatique lorsque le backend connecté le prend en charge. |
custom_tools | Mapping[str, CustomTool | Mapping[str, Any]] | None | Outils personnalisés exposés aux agents locaux. |
CloudAgentOptions
| Propriété | Type | Par défaut | Description |
|---|---|---|---|
env | CloudEnvironment | Mapping[str, Any] | None | Environnement d’exécution. S’il est omis, le serveur utilise des VM cloud hébergées par Cursor. pool et machine ciblent les workers auto-hébergés que vous exécutez. |
repos | Sequence[CloudRepository | Mapping[str, Any]] | None | Dépôts à cloner dans la VM. Omettez repos et env pour créer un agent sans dépôt avec un espace de travail vide. Transmettez pr_url pour un dépôt afin d’associer l’agent à une PR existante. |
work_on_current_branch | bool | None | Envoyer les commits vers la branche existante plutôt que d’en créer une nouvelle. Le serveur considère une valeur omise comme False. |
auto_create_pr | bool | None | Ouvrir une PR à la fin de l’exécution. Le serveur considère une valeur omise comme False. |
open_as_cursor_github_app | bool | True pour les clés de compte de service, False pour les clés utilisateur | Ouvrir les PR avec l’application GitHub de Cursor plutôt qu’au nom du propriétaire de la clé API. La valeur résolue est renvoyée lors de la création, de la récupération et du listage. |
skip_reviewer_request | bool | None | Ne pas demander à l’utilisateur appelant de passer en revue la PR. Le serveur considère une valeur omise comme False. |
env_vars | Mapping[str, str] | None | Variables d’environnement limitées à la session pour les agents cloud. |
metadata | Mapping[str, str] | None | Balises de chaîne appartenant à l’appelant et persistées sur l’agent cloud. Voir Métadonnées de l’agent. |
AgentDefinition
| Propriété | Type | Par défaut | Description |
|---|---|---|---|
description | str | obligatoire | Indique quand utiliser ce sous-agent. S’affiche à l’agent parent afin qu’il sache quand le lancer. |
prompt | str | obligatoire | Prompt système du sous-agent. |
model | str | ModelSelection | Mapping[str, Any] | "inherit" | None | Remplace le modèle. None et "inherit" utilisent tous deux la sélection du parent. |
mcp_servers | Sequence[str | AgentDefinitionMcpServer | Mapping[str, Any]] | None | Serveurs MCP disponibles pour ce sous-agent. Les noms font référence aux serveurs définis dans les mcp_servers du parent. |
CustomTool
@dataclassclass CustomTool: execute: Callable[[Mapping[str, Any], CustomToolContext], Any] description: str | None = None input_schema: Mapping[str, Any] | None = Noneclass CustomToolContext: tool_call_id: str | None = NoneModelSelection
@dataclass(frozen=True)class ModelSelection: id: str params: Sequence[ModelParameterValue] = ()@dataclass(frozen=True)class ModelParameterValue: id: str value: strid est l’identifiant du modèle (par exemple, "composer-2.5" ou "auto-smart"). params contient des paramètres propres au modèle, tels que l’effort de raisonnement ou optimize_for du Router. Utilisez Cursor.models.list() pour connaître les ID valides, la définition des paramètres et les variantes prédéfinies disponibles pour votre compte. Voir Cursor Router pour le contrat de sélection du Router.
McpServerConfig
from cursor_sdk.types import McpServerConfig@dataclass(frozen=True)class HttpMcpServerConfig: url: str type: Literal["http", "sse"] | str = "http" headers: Mapping[str, str] | None = None auth: McpAuth | Mapping[str, Any] | None = None@dataclass(frozen=True)class SseMcpServerConfig(HttpMcpServerConfig): type: Literal["sse"] = "sse"@dataclass(frozen=True)class StdioMcpServerConfig: command: str args: Sequence[str] | None = None env: Mapping[str, str] | None = None cwd: str | os.PathLike | None = None # uniquement en local ; le cloud rejette ce champ@dataclass(frozen=True)class McpAuth: client_id: str client_secret: str | None = None scopes: Sequence[str] = ()Pour les serveurs HTTP exécutés dans le cloud, les headers et l’auth sont gérés par le backend de Cursor. Les champs sensibles sont masqués avant d’être transmis à la VM. Pour les serveurs stdio dans le cloud, les valeurs env sont transmises à la VM (traitez-les comme tout autre secret d’exécution).
MessageUtilisateur
@dataclass(frozen=True)class UserMessage: text: str images: Sequence[SDKImage | Mapping[str, Any]] | None = NoneForme structurée de l’argument message de agent.send(). Utilisez-la pour envoyer des images avec du texte.
SDKImage
@dataclass(frozen=True)class SDKImage: url: str | None = None data: str | None = None mime_type: str | None = None dimension: SDKImageDimension | Mapping[str, Any] | None = None @classmethod def from_url(cls, url: str, dimension=None) -> SDKImage: ... @classmethod def from_data(cls, data: bytes | str, mime_type: str, dimension=None) -> SDKImage: ... @classmethod def url_image(cls, url: str, dimension=None) -> SDKImage: ... @classmethod def data_image(cls, data: str, mime_type: str, dimension=None) -> SDKImage: ... @classmethod def from_file(cls, path, *, mime_type=None, dimension=None) -> SDKImage: ...Transmettez une url distante ou des data encodées en base64 avec un mime_type. from_data() accepte des octets ou une chaîne base64. from_file() lit un fichier sur le disque et l’encode en base64.
SettingSource
SettingSource est accessible depuis cursor_sdk.types.
from cursor_sdk.types import SettingSourceDétermine les couches de paramètres sur disque qu’un agent local charge. Les agents cloud chargent toujours project, team et plugins, et ignorent ce champ.
| Valeur | Source |
|---|---|
"project" | .cursor/ dans l’espace de travail |
"user" | ~/.cursor/ |
"team" | Paramètres d’équipe synchronisés depuis le tableau de bord |
"mdm" | Paramètres Enterprise gérés par MDM |
"plugins" | Paramètres fournis par les plugins |
"all" | Raccourci pour tous les éléments ci-dessus |
Résultat de liste
@dataclass(frozen=True)class ListResult(Generic[T]): items: list[T] next_cursor: str = "" def to_dict(self) -> dict[str, Any]: ... def has_next_page(self) -> bool: ... def next_page_info(self) -> dict[str, str]: ... def get_next_page(self) -> ListResult[T]: ... def auto_paging_iter(self) -> Iterator[T]: ...Renvoyé par client.agents.list(), client.agents.list_runs() et Agent.list(). next_cursor est vide lorsqu’il n’y a plus de pages. Les endpoints de liste asynchrones renvoient AsyncListResult[T] avec des équivalents pouvant être utilisés avec await.
Erreurs
Toutes les erreurs du SDK héritent de CursorAgentError. CursorSDKError est l'alias racine rétrocompatible destiné aux anciens appelants. Utilisez is_retryable et retry_after pour gérer les nouvelles tentatives.
class CursorAgentError(Exception): message: str code: str | None status: int | None status_code: int | None details: list[Mapping[str, Any]] is_retryable: bool cause: BaseException | None proto_error_code: str | None request_id: str | None headers: Mapping[str, str] retry_after: str | None| Erreur | Cas |
|---|---|
AuthenticationError | Clé API non valide ou absence de connexion. |
PermissionDeniedError | L’appelant authentifié n’a pas l’autorisation d’effectuer l’opération demandée. |
RateLimitError | Trop de requêtes ou limites d’usage dépassées. |
ConfigurationError | Modèle non valide, configuration requise manquante ou paramètres de requête incorrects. |
AgentBusyError | Envoi d’un message de suivi alors que l’agent a déjà une exécution à l’état CREATING ou RUNNING (HTTP 409, code agent_busy). |
BadRequestError | La requête est mal formée. |
IntegrationNotConnectedError | Création d’un agent cloud pour un dépôt dont le fournisseur SCM n’est pas connecté. |
NetworkError | Service indisponible ou défaillance du réseau. |
APITimeoutError | La requête a expiré. |
InternalServerError | Le service Cursor a renvoyé une erreur serveur. |
NotFoundError | La ressource demandée est introuvable. |
AgentNotFoundError | L’agent n’existe pas ou n’est pas visible dans le répertoire de travail actuel. |
UnsupportedRunOperationError | L’opération d’exécution n’est pas prise en charge pour l’état actuel de l’exécution. |
Nouvelles tentatives avec temporisation exponentielle
is_retryable et retry_after déterminent la logique de nouvelle tentative côté appelant. retry_after est une chaîne au format HTTP (en secondes ou sous forme de date HTTP) fournie par le serveur lorsqu’elle est définie.
import timefrom cursor_sdk import Agent, AgentOptions, CursorAgentError, LocalAgentOptions, RateLimitErrorfor attempt in range(3): try: result = Agent.prompt( "Audit the auth middleware for missing input validation", AgentOptions(model="composer-2.5", local=LocalAgentOptions(cwd=".")), ) break except RateLimitError as err: time.sleep(float(err.retry_after) if err.retry_after else 2**attempt) except CursorAgentError as err: if not err.is_retryable: raise time.sleep(2**attempt)Chaque CursorAgentError inclut request_id si le serveur en a renvoyé un. Consignez-le chaque fois que vous affichez une erreur, afin que le support puisse identifier l’origine de l’échec.
IntegrationNotConnectedError
class IntegrationNotConnectedError(ConfigurationError): provider: str # p. ex. "github", "gitlab", "azuredevops" help_url: str # lien vers le tableau de bord pour se reconnecterUtilisez help_url pour guider l’utilisateur vers la procédure de reconnexion appropriée. De nouveaux fournisseurs peuvent être ajoutés sans nouvelle version du SDK.
AgentBusyError
Les agents cloud n’autorisent qu’une seule exécution en cours à la fois. AgentBusyError est levée lorsque vous appelez agent.send() (ou créez une exécution par un autre moyen) alors qu’une autre exécution du même agent est encore à l’état CREATING ou RUNNING.
is_retryable est défini sur False. Toute nouvelle tentative immédiate échouera tant que l’exécution en cours n’aura pas atteint un état terminal ou que vous ne l’aurez pas annulée. Les autres réponses 409, telles que agent_archived, lèvent quant à elles ConfigurationError.
Attendez la fin de l’exécution en cours, annulez-la avec run.cancel(), ou interrogez régulièrement Agent.list_runs() avant d’envoyer une nouvelle requête :
from cursor_sdk import Agent, AgentBusyErroragent = Agent.resume("bc-00000000-0000-0000-0000-000000000001")try: agent.send("Also add tests for the auth middleware.")except AgentBusyError: runs = Agent.list_runs(agent.agent_id, {"runtime": "cloud", "limit": 1}) active = runs.items[0] if runs.items else None if active is not None and active.status == "running": active.cancel() agent.send("Also add tests for the auth middleware.")Les agents locaux ne génèrent pas d’AgentBusyError. Passez local={"force": True} à send() pour mettre fin à une exécution locale bloquée avant d’en démarrer une autre.
UnsupportedRunOperationError
class UnsupportedRunOperationError(ConfigurationError): operation: strLevée lorsqu’une opération Run n’est pas autorisée pour l’exécution en cours. Le cas le plus courant est l’appel à run.cancel() sur une exécution déjà terminée.
run.supports(operation) et run.unsupported_reason(operation) indiquent si une opération est prise en charge au niveau du SDK ("stream", "wait", "cancel", "conversation") et ne vérifient pas l’état de l’exécution. Consultez run.status avant d’effectuer des appels dépendant de l’état.
Dépannage
Définissez CURSOR_SDK_LOG=debug (ou info) afin d’ajouter un gestionnaire stderr au journaliseur du SDK. Le SDK configure uniquement son propre journaliseur cursor_sdk, ce qui n’interfère pas avec la configuration de journalisation de l’application hôte.
CURSOR_SDK_LOG=debug python my_script.pyLe binaire bridge fourni est installé sous le nom cursor-sdk-bridge dans le PATH, avec le package. Exécutez-le directement pour vérifier la build incluse dans votre wheel :
cursor-sdk-bridge --helpLimitations connues
- Les schémas de payload des appels d’outils ne sont volontairement pas fortement typés.
- Les serveurs MCP en ligne ne sont pas persistés entre les appels à
Agent.resume(). Transmettez-les à nouveau lors de la reprise si nécessaire. - Les outils personnalisés (
local.custom_tools) et les restrictions d’outils (tools,disallowed_tools) sont réservés aux agents locaux. Les restrictions ne sont pas persistées sur l’agent ; transmettez-les à nouveau lors de la reprise. - Le téléchargement d’artefacts n’est pas implémenté pour les agents locaux.
local.setting_sources(ainsi que les chemins des serveurs MCP et des sous-agents basés sur des fichiers qu’il contrôle) ne s’applique pas aux agents cloud. Le cloud charge toujoursproject,teametplugins.- Les hooks sont uniquement basés sur des fichiers (
.cursor/hooks.json). Aucun callback programmatique.