[Go to site: main page, start]

Skip to main content

Command Palette

Search for a command to run...

SDK

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écutionDescriptionQuand l’utiliser
LocalExé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.

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

ConceptDescription
AgentRé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.
RunSoumission unique d’un prompt. Possède son propre stream, état, résultat, conversation et mécanisme d’annulation.
SDKMessageMessage de stream typé émis lors d’une exécution. Même structure dans les environnements d’exécution locaux et cloud.
CursorClientClient 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.
AsyncClientClient miroir asynchrone. Requis pour toutes les opérations asynchrones.

Installation

pip install cursor-sdk

Né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).

SyncAsync
CursorClient / ClientAsyncClient / AsyncCursorClient
AgentAsyncAgent
RunAsyncRun
CursorAsyncCursor
ListResultAsyncListResult
DefaultHttpxClientDefaultAsyncHttpxClient

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.

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.

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 produitValeur SDK
Coûtcost
Balancebalanced
Intelligenceintelligence

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électionSignification
auto-smart avec optimize_forCursor 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 defaultCe 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é :

  1. Appelez Cursor.models.list().
  2. Vérifiez que auto-smart figure dans le résultat.
  3. Vérifiez que optimize_for inclut la valeur souhaitée (cost, balanced ou intelligence).
  4. Vérifiez que Router est activé pour l’équipe associée à la clé API.
  5. Si vous appartenez à plusieurs équipes, vérifiez que la clé est utilisée dans le contexte de l’équipe prévue.
  6. 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: ...
MembreDescription
agent_idIdentifiant stable de l’agent. agent-<uuid> en local, bc-<uuid> dans le cloud.
modelSélection de modèle typée actuelle. Mise à jour après un envoi réussi avec un remplacement de modèle.
sendDémarre un nouveau run avec le prompt fourni. Renvoie une référence Run.
reloadRelit la configuration du système de fichiers (hooks, MCP du projet, sous-agents) sans libérer les ressources.
closeFerme l’agent et libère les ressources.
list_messagesRépertorie l’historique des messages de l’agent.
list_artifactsRépertorie les fichiers produits par l’agent (cloud uniquement ; local renvoie une liste vide).
download_artifactTélécharge un fichier selon son chemin (cloud uniquement ; local génère une erreur).
get_usageRécupère l’usage de tokens facturé et le coût en dollars pour l’agent.
archive / unarchive / deleteGè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,) -> RunResult

Pratique 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 :

RessourceExemples de méthodes synchronesExemples de méthodes asynchrones
agentsclient.agents.create(...), client.agents.list(...), client.agents.get(...)await client.agents.create(...), await client.agents.list(...)
modelsclient.models.list()await client.models.list()
repositoriesclient.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
ChampDescription
input_tokensTokens du prompt envoyés au modèle.
output_tokensTokens générés par le modèle.
cache_read_tokensTokens récupérés depuis le cache du prompt.
cache_write_tokensTokens écrits dans le cache du prompt.
total_tokensinput_tokens + output_tokens + cache_read_tokens + cache_write_tokens. N’inclut pas reasoning_tokens.
reasoning_tokensTokens 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éTypeDescription
modelstr | 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_serversMapping[str, McpServerConfig]Définitions de serveurs MCP en ligne. Remplace entièrement les serveurs définis à la création pour ce run.
cloud.env_varsMapping[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.forceboolAgents 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_keystrClé d’idempotence facultative générée par le client pour l’envoi.
on_stepCallable[[ConversationStep], Any]Callback après chaque étape de conversation terminée (texte, réflexion ou lot d’outils).
on_deltaCallable[[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])
typeDataclassChamps clés
"system"SDKSystemMessagesubtype, model, tools
"user"SDKUserMessageEventmessage.content
"assistant"SDKAssistantMessagemessage.content avec des valeurs TextBlock et ToolUseBlock
"thinking"SDKThinkingMessagetext, thinking_duration_ms
"tool_call"SDKToolUseMessagecall_id, name, status, args, result, truncated
"status"SDKStatusMessagestatus, message
"task"SDKTaskMessagestatus, text
"request"SDKRequestMessagerequest_id
"usage"SDKUsageMessageusage (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: TokenUsage

Les 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 args et result des événements tool_call reflè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. Traitez args et result comme 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,) -> Agent

Utilisez 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 = None

Les 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 locaux

Cycle 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 inclus

Le 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 :

  1. mcp_servers dans agent.send(). Remplace entièrement les serveurs définis lors de la création pour cette exécution (sans fusion).
  2. mcp_servers dans Agent.create(). Utilisé lorsqu’aucun remplacement n’est fourni lors de l’envoi.
  3. Les serveurs de plugins, si local.setting_sources inclut "plugins".
  4. Les serveurs du projet dans .cursor/mcp.json, si local.setting_sources inclut "project".
  5. Les serveurs utilisateur dans ~/.cursor/mcp.json, si local.setting_sources inclut "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 :

  1. mcp_servers dans agent.send(). Remplace entièrement les serveurs définis lors de la création pour cette exécution (sans fusion).
  2. mcp_servers dans Agent.create(). Utilisé lorsqu’aucun remplacement n’est fourni lors de l’envoi.
  3. 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 headers HTTP et l’auth sont gérés par le backend de Cursor. Les champs sensibles sont masqués et ne sont pas transmis à la VM.
  • Les valeurs env de 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 une BadRequestError à 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 dans disallowed_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.json au dépôt indiqué dans local.cwd, ou ajoutez ~/.cursor/hooks.json pour les hooks au niveau utilisateur.
  • Cloud : validez .cursor/hooks.json et ses scripts dans le dépôt indiqué dans cloud.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éTypePar défautDescription
modelstr | ModelSelection | Mapping[str, Any]Obligatoire en local ; pour le cloud, valeur par défaut résolue par le serveurModèle à utiliser. Voir ModelSelection.
api_keystrVariable d’environnement CURSOR_API_KEYClé API utilisateur ou clé de compte de service. Les clés Team Admin ne sont pas encore prises en charge.
namestrGénéré automatiquementNom d’agent lisible par l’utilisateur, renvoyé par client.agents.list() / client.agents.get().
localLocalAgentOptions | Mapping[str, Any]NoneConfiguration de l’agent local. À transmettre pour créer un agent local.
cloudCloudAgentOptions | Mapping[str, Any]NoneConfiguration de l’agent cloud. À transmettre pour créer un agent cloud.
mcp_serversMapping[str, McpServerConfig]NoneDéfinitions de serveurs MCP en ligne.
agentsMapping[str, AgentDefinition | Mapping[str, Any]]NoneDéfinitions de sous-agents.
toolsSequence[str]Jeu d’outils par défautSeuls 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_toolsSequence[str]NoneSupprime 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_idstrGénéré automatiquementID d’agent persistant. À transmettre pour conserver un ID stable entre les invocations.
idempotency_keystrGénéré automatiquement pour le cloudClé d’idempotence facultative générée par le client. Cloud uniquement.
mode"agent" | "plan"NoneMode 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éTypePar défautDescription
cwdstr | os.PathLikeNoneRépertoire de travail principal. Les listes comportant plusieurs entrées sont refusées ; utilisez dirs pour plusieurs racines.
dirsSequence[str | os.PathLike]NoneDossiers 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_sourcesSequence[SettingSource]NoneCouches de paramètres ambiantes : "project", "user", "team", "mdm", "plugins" ou "all".
sandbox_optionsSandboxOptions | Mapping[str, Any]NoneOptions du sandbox local.
storeLocalAgentStoreConfig | Mapping[str, Any]NoneConfiguration du store local transmise au bridge.
auto_reviewboolNoneAchemine les appels d’outils locaux via la révision automatique lorsque le backend connecté le prend en charge.
custom_toolsMapping[str, CustomTool | Mapping[str, Any]]NoneOutils personnalisés exposés aux agents locaux.

CloudAgentOptions

PropriétéTypePar défautDescription
envCloudEnvironment | Mapping[str, Any]NoneEnvironnement 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.
reposSequence[CloudRepository | Mapping[str, Any]]NoneDé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_branchboolNoneEnvoyer 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_prboolNoneOuvrir une PR à la fin de l’exécution. Le serveur considère une valeur omise comme False.
open_as_cursor_github_appboolTrue pour les clés de compte de service, False pour les clés utilisateurOuvrir 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_requestboolNoneNe pas demander à l’utilisateur appelant de passer en revue la PR. Le serveur considère une valeur omise comme False.
env_varsMapping[str, str]NoneVariables d’environnement limitées à la session pour les agents cloud.
metadataMapping[str, str]NoneBalises de chaîne appartenant à l’appelant et persistées sur l’agent cloud. Voir Métadonnées de l’agent.

AgentDefinition

PropriétéTypePar défautDescription
descriptionstrobligatoireIndique quand utiliser ce sous-agent. S’affiche à l’agent parent afin qu’il sache quand le lancer.
promptstrobligatoirePrompt système du sous-agent.
modelstr | ModelSelection | Mapping[str, Any] | "inherit"NoneRemplace le modèle. None et "inherit" utilisent tous deux la sélection du parent.
mcp_serversSequence[str | AgentDefinitionMcpServer | Mapping[str, Any]]NoneServeurs 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 = None

ModelSelection

@dataclass(frozen=True)class ModelSelection:    id: str    params: Sequence[ModelParameterValue] = ()@dataclass(frozen=True)class ModelParameterValue:    id: str    value: str

id 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 = None

Forme 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 SettingSource

Dé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.

ValeurSource
"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
ErreurCas
AuthenticationErrorClé API non valide ou absence de connexion.
PermissionDeniedErrorL’appelant authentifié n’a pas l’autorisation d’effectuer l’opération demandée.
RateLimitErrorTrop de requêtes ou limites d’usage dépassées.
ConfigurationErrorModèle non valide, configuration requise manquante ou paramètres de requête incorrects.
AgentBusyErrorEnvoi 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).
BadRequestErrorLa requête est mal formée.
IntegrationNotConnectedErrorCréation d’un agent cloud pour un dépôt dont le fournisseur SCM n’est pas connecté.
NetworkErrorService indisponible ou défaillance du réseau.
APITimeoutErrorLa requête a expiré.
InternalServerErrorLe service Cursor a renvoyé une erreur serveur.
NotFoundErrorLa ressource demandée est introuvable.
AgentNotFoundErrorL’agent n’existe pas ou n’est pas visible dans le répertoire de travail actuel.
UnsupportedRunOperationErrorL’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 reconnecter

Utilisez 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: str

Levé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.py

Le 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 --help

Limitations 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 toujours project, team et plugins.
  • Les hooks sont uniquement basés sur des fichiers (.cursor/hooks.json). Aucun callback programmatique.