Cursor Python SDK
Mit dem Paket cursor-sdk kannst du den Cursor-Agent aus deinem eigenen Python-Code aufrufen. Derselbe Agent, der in der Cursor-IDE, der CLI und der Web-App läuft, lässt sich über Python mit Sync-Client und Async-Client, typisierten Dataclasses sowie gewöhnlicher Iteration über Streams und Seiten programmatisch steuern. Führe in Cursor den Skill /sdk aus, um loszulegen.
Informationen zur REST API findest du in der Cloud Agents API. Informationen zu anderen Sprachen findest du in der SDK Bridge.
Übersicht
Das SDK bietet über eine einheitliche Schnittstelle Zugriff auf lokale und Cloud-Runtimes. Du schreibst denselben Code, unabhängig davon, wo der Agent läuft.
| Runtime | Funktion | Wann verwenden |
|---|---|---|
| Local | Führt den Agenten mit lokalen Dateien auf dem Datenträger aus. | Entwicklungs-Skripte und CI-Checks in einem Working Tree. |
| Cloud (Cursor-hosted) | Läuft in einer isolierten VM, in die dein Repo geklont wurde. Cursor betreibt die VMs. | Wenn der Aufrufer das Repo nicht hat, du viele Agenten parallel nutzen möchtest oder Ausführungen auch nach einer Verbindungsunterbrechung des Aufrufers fortgesetzt werden müssen. |
Lege die Runtime fest, indem du local oder cloud an Agent.create() übergibst.
Authentifizierung
Setze CURSOR_API_KEY oder übergib api_key, bevor du einen Agenten erstellst.
Das SDK akzeptiert User API Keys und Servicekonto-API-Schlüssel für lokale Ausführungen und Cloud-Runs. Team Admin API Keys werden noch nicht unterstützt.
- User API Key im Cursor Dashboard -> API Keys
- Servicekonto-API-Schlüssel in den Team settings. Siehe Service accounts
export CURSOR_API_KEY="your-key"Nutzung und Abrechnung
SDK-Ausführungen unterliegen denselben Preis-, Request-Pool- und Privacy-Mode-Regeln wie Ausführungen in der IDE und durch Cloud Agents. Die Ausgaben deines Teams werden im Nutzungs-Dashboard unter dem SDK-Tag angezeigt.
Wie du die Token-Anzahl pro Ausführung im Code abfragst, erfährst du unter Token-Nutzung. Wie du die abgerechnete Nutzung und die Kosten in US-Dollar für die Ausführungen eines Agenten abrufst, erfährst du unter agent.get_usage().
Grundkonzepte
| Konzept | Beschreibung |
|---|---|
| Agent | Dauerhaftes Handle, das den Zustand der Unterhaltung, die Workspace-Konfiguration, die Modellauswahl und Einstellungen speichert. Bleibt über mehrere Prompts hinweg erhalten. |
| Ausführung | Eine Prompt-Übermittlung. Verfügt über eigenen Stream, Status, Ergebnis, Unterhaltung und Abbruchmöglichkeit. |
| SDKMessage | Typisierte Stream-Nachricht, die während einer Ausführung ausgegeben wird. Hat in lokalen und Cloud-Runtimes dieselbe Struktur. |
| CursorClient | Expliziter Client zur Steuerung des Lebenszyklus, für benutzerdefinierte HTTP-Optionen oder mehrere Workspaces in einem Prozess. Client ist ein Alias. |
| AsyncClient | Asynchrones Gegenstück zum Client. Für alle asynchronen Operationen erforderlich. |
Installation
pip install cursor-sdkErfordert Python 3.10 oder neuer.
Schnellstart
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())Stream-Events zeigt, wie sich Assistententext extrahieren, Tool-Aufrufe verarbeiten und der Run-Status auslesen lassen. Für einen einmaligen Prompt (erstellen, ausführen, abschließen) siehe Agent.prompt().
Cloud-Schnellstart
Das Python SDK unterstützt die Cloud Agents von Cursor nativ. Du kannst verbundene Repositories auflisten, einen Agent für eines davon starten, auf dessen Abschluss warten und das Endergebnis prüfen.
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())Cloud Agents, die über das SDK gestartet wurden, werden in der Standard-Agentenliste nicht angezeigt. Um sie in Cursor Web oder im Cursor-Agentenfenster anzuzeigen, klicken Sie auf Filter > Quelle > SDK.
Asynchrone Nutzung
Der Async-Client bietet dieselbe API wie der Sync-Client und wird für Server, Bots und die parallele Orchestrierung von Agenten empfohlen. AsyncAgent, AsyncClient, AsyncRun und AsyncCursor werden sowohl aus cursor_sdk als auch aus cursor_sdk.asyncio exportiert.
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())Es gibt keinen globalen Async-Standardclient. Erstellen Sie AsyncClient explizit oder verwenden Sie AsyncClient.launch_bridge(...) als asynchronen Kontextmanager, damit jede Ereignisschleife über einen eigenen Client verfügt. Verwenden Sie Sync- und Async-Clients nicht im selben Codepfad.
Direkte Klassenmethoden von AsyncAgent erfordern client=. Verwenden Sie
await client.agents.create(...) oder
await AsyncAgent.create(..., client=client).
| Sync | Async |
|---|---|
CursorClient / Client | AsyncClient / AsyncCursorClient |
Agent | AsyncAgent |
Run | AsyncRun |
Cursor | AsyncCursor |
ListResult | AsyncListResult |
DefaultHttpxClient | DefaultAsyncHttpxClient |
Agenten erstellen
Agent.create() validiert die Optionen und gibt sofort ein Handle zurück. Übergeben Sie entweder local oder cloud, um die Laufzeitumgebung auszuwählen.
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 wird sofort gesetzt. Lokale Agenten erhalten eine ID im Format agent-<uuid>, Cloud Agents eine ID im Format bc-<uuid>. agent.model ist eine typisierte ModelSelection, daher können Sie agent.model.id und agent.model.params direkt verwenden.
Vom SDK gestartete Cloud Agents werden in der Standard-Agentenliste nicht angezeigt. Um sie in Cursor Web oder einem Cursor-Agentenfenster anzuzeigen, klicken Sie auf Filter > Quelle > SDK.
Cloud Agents ohne Repo
Cloud Agents können auf einer leeren VM ohne Repository ausgeführt werden. Übergeben Sie cloud mit einer leeren repos-Liste oder lassen Sie repos ganz weg. Wenn Sie cloud weglassen, wird stattdessen die lokale Runtime verwendet.
from cursor_sdk import Agent, CloudAgentOptionswith Agent.create(cloud=CloudAgentOptions(repos=[])) as agent: run = agent.send("Research the top 3 Python testing frameworks and summarize.") print(run.wait().result)Agenten ohne Repo müssen für dein Konto oder dein Team aktiviert sein. Repository-spezifische API-Schlüssel können sie nicht erstellen. Verwende stattdessen einen uneingeschränkten Service-Account-Schlüssel oder einen User API Key.
Session-Umgebungsvariablen
Übergeben Sie bei Cloud Agents env_vars, wenn eine Ausführung kurzlebige Zugangsdaten oder andere Werte benötigt, die nur für diesen Agenten gelten sollen.
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"], }, ),)Diese Werte werden verschlüsselt gespeichert, in die Shell des Cloud Agents eingefügt und mit dem Agenten gelöscht. env_vars kann nicht mit einer vom Aufrufer bereitgestellten agent_id verwendet werden; lasse agent_id weg und lies die vom Server geprägte ID aus agent.agent_id. Variablennamen dürfen nicht mit CURSOR_ beginnen.
Für Werte, die nur während einer einzelnen Ausführung vorhanden sein sollen, übergib sie stattdessen an agent.send(). Siehe Umgebungsvariablen pro Ausführung.
Agent-Metadaten
Füge einem Cloud-Agenten beim Erstellen eigene Kennungen hinzu. Mit Metadaten kannst du einen Agenten mit einem Nutzer, Mandanten, Workflow oder Ticket in deinem System verknüpfen. Sie werden über SDKAgentInfo.metadata von client.agents.get() und client.agents.list() zurückgegeben.
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)Metadaten können Cloud-Agents bei der Erstellung hinzugefügt werden. Du kannst bis zu 50 Schlüssel-Wert-Paare anhängen. Schlüssel dürfen nicht leer sein und maximal 255 Zeichen lang sein. Werte müssen Strings mit maximal 4096 Byte sein. Leere String-Werte sind zulässig, und eine leere Zuordnung wird wie keine Metadaten behandelt.
Wenn Metadaten für das Konto des API-Schlüssels nicht aktiviert sind, gibt das Erstellen eines Agents mit einer
nicht leeren Zuordnung 403 feature_unavailable zurück.
Modellparameter
Verwende ModelSelection.params, um modellspezifische Optionen wie den Denkaufwand oder optimize_for des Cursor Routers zu übergeben. Parameter-IDs und -werte variieren je nach Modell. Mit Cursor.models.list() kannst du unterstützte Parameter und vordefinierte Varianten für dein Konto ermitteln.
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="."),)Verwenden Sie Cursor.models.list(), um die Parameter-IDs und vordefinierten Varianten eines bestimmten Modells zu ermitteln. Informationen zum Auswahlvertrag für auto-smart finden Sie unter Cursor Router.
Cursor Router
Cursor Router wählt für jede Auto-Anfrage ein Modell aus. Im SDK ist Router das Modell auto-smart mit dem Parameter optimize_for. Es ist für Teams und Enterprise verfügbar. Enterprise-Admins müssen Router für das Team aktivieren, bevor auto-smart im Katalog erscheint.
Das Cursor SDK ist ein Agent-SDK und keine eigenständige API für Modellinferenz oder Chat-Vervollständigungen. Router wählt Modelle für Cursor-Agent-Ausführungen aus, die einen Workspace analysieren, Tools aufrufen, Befehle ausführen und Dateien bearbeiten können. Cursor dokumentiert derzeit keinen direkten Router-Endpunkt für beliebige Modellaufrufe.
Cost, Balance oder Intelligence auswählen
Übergeben Sie auto-smart und setzen Sie optimize_for explizit:
| Produktbezeichnung | SDK-Wert |
|---|---|
| Cost | cost |
| Balance | balanced |
| Intelligence | intelligence |
Verwenden Sie Balance in Produkttexten. Verwenden Sie balanced nur als SDK-Wert für die Übertragung.
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)Übergeben Sie stets optimize_for. Lassen Sie es nicht weg und senden Sie keinen Legacy-Wert für default; die Ermittlung über den Katalog ist der unterstützte Vertrag.
Router im Modellkatalog entdecken
Cursor.models.list() gibt die für das aktuelle Konto und Team des API-Schlüssels verfügbaren Modelle, Parameterdefinitionen und vordefinierten Varianten zurück. Cursor Router wird als auto-smart angezeigt, sofern Router verfügbar ist. Teamadministratoren können Router deaktivieren oder einschränken, welche Optimierungsmodi Teammitglieder auswählen dürfen.
Nutze den Katalog als maßgebliche Quelle, bevor du eine Auswahl fest codierst:
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)],)Modi für einzelne Ausführungen wechseln
Überschreibe das Modell in agent.send(), um den Router-Modus für eine Ausführung zu ändern:
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")], ), ),)Modellüberschreibungen pro Ausführung bleiben für spätere Ausführungen erhalten. Spätere Sends ohne Überschreibung verwenden weiterhin die neue Auswahl. Siehe Modellüberschreibung pro Ausführung.
Modell-IDs: auto-smart, auto und default
| Auswahl | Bedeutung |
|---|---|
auto-smart mit optimize_for | Cursor Router. Verwende dies für Cost, Balance oder Intelligence. |
ModelSelection(id="auto") | Vom Server ausgewählter Auto-Fallback, wenn ein bestimmtes Modell im Katalog fehlt. Verwende auto-smart, wenn du einen expliziten Router-Modus benötigst. |
Weglassen von optimize_for oder Senden von default | Kein unterstützter Router-Vertrag. Ermittle stets die zulässigen Werte und übergib cost, balanced oder intelligence. |
Abrechnung und Routing-Pool
- Cost folgt dem klassischen Auto-Verhalten und der gebündelten Auto-Preisgestaltung.
- Balance und Intelligence verwenden Cursor Router und werden gemäß deinem Plan oder Vertrag zum Preis des gerouteten Modells abgerechnet.
- Das zugrunde liegende Modell kann sich zwischen Anfragen ändern. Verwende eine feste Modell-ID, wenn du reproduzierbare Vergleiche benötigst.
- Enterprise-Modell-Positivlisten bestimmen den Routing-Pool. Das Blockieren erforderlicher Modelle kann Router deaktivieren.
Aktuelle Preise und den Routing-Pool findest du unter Cursor Router und Modelle & Preise.
Fehlerbehebung bei fehlendem Router
Wenn auto-smart fehlt oder ein Optimierungsmodus abgelehnt wird:
- Rufe
Cursor.models.list()auf. - Stelle sicher, dass
auto-smartim Ergebnis enthalten ist. - Stelle sicher, dass
optimize_forden gewünschten Wert enthält (cost,balancedoderintelligence). - Stelle sicher, dass Router für das mit dem API-Schlüssel verknüpfte Team aktiviert ist.
- Wenn du mehreren Teams angehörst, stelle sicher, dass der Schlüssel im vorgesehenen Teamkontext verwendet wird.
- Überprüfe die Modellzugriffsrichtlinie des Teams, wenn Router nicht verfügbar ist oder kein gültiges zugrunde liegendes Modell auswählen kann.
Rohe Dictionaries
Für Anwendungscode sind typisierte Dataclasses vorzuziehen, da IDE-Autovervollständigung und Typprüfung besser funktionieren. Das SDK akzeptiert auch einfache Dictionaries für kurze Skripte oder extern bereitgestelltes JSON. Schlüssel im Snake-Case werden normalisiert.
from cursor_sdk import Agentwith Agent.create( { "api_key": "crsr_key", "model": {"id": "composer-2.5"}, "local": {"cwd": "."}, }) as agent: ...Agent
Das von Agent.create(), Agent.resume(), client.agents.create() und client.agents.resume() zurückgegebene Handle.
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: ...| Mitglied | Beschreibung |
|---|---|
agent_id | Stabile Agent-ID. agent-<uuid> für lokal, bc-<uuid> für die Cloud. |
model | Aktuelle typisierte Modellauswahl. Wird nach einem erfolgreichen Senden mit einer Modellüberschreibung aktualisiert. |
send | Startet eine neue Ausführung mit dem angegebenen Prompt. Gibt ein Run-Handle zurück. |
reload | Liest die Dateisystemkonfiguration (Hooks, Projekt-MCP, Subagents) erneut ein, ohne Ressourcen freizugeben. |
close | Schließt den Agent und gibt Ressourcen frei. |
list_messages | Listet den Nachrichtenverlauf des Agent auf. |
list_artifacts | Listet die vom Agent erzeugten Dateien auf (nur Cloud; lokal wird eine leere Liste zurückgegeben). |
download_artifact | Lädt eine Datei über ihren Pfad herunter (nur Cloud; lokal wird ein Fehler ausgelöst). |
get_usage | Ruft die abgerechnete Tokennutzung und die Kosten in US-Dollar für den Agent ab. |
archive / unarchive / delete | Verwaltet den Lebenszyklus von Cloud-Agenten. |
Verwenden Sie einen Kontextmanager für die automatische Bereinigung:
with Agent.create(model="composer-2.5", local=LocalAgentOptions(cwd=".")) as agent: print(agent.send("Explain this repository").text())Wenn du die synchronen Agent.*- oder Cursor.*-Hilfsfunktionen ohne client= nutzt, startet das SDK einen Standard-Client auf Modulebene oder verwendet ihn erneut. Er wird beim Beenden des Prozesses automatisch geschlossen, kann aber auch explizit geschlossen werden:
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,) -> RunResultPraktische One-Shot-Methode: Erstellt einen Agenten, sendet einen einzelnen Prompt, wartet, bis der Run abgeschlossen ist, und gibt ihn frei.
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)Asynchrone Variante (setzt voraus, dass du bereits einen AsyncClient geöffnet hast):
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
Verwende CursorClient, wenn du den Lebenszyklus explizit steuern, einen eigenen Bridge-Endpunkt oder eigene HTTP-Optionen verwenden oder mehrere Workspaces in einem Prozess nutzen möchtest. Client ist weiterhin als Alias verfügbar.
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())Ressourcen
Explizite Clients bieten Ressourcen-Namespaces:
| Ressource | Beispiele für synchrone Methoden | Beispiele für asynchrone Methoden |
|---|---|---|
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() |
Top-Level-Methoden wie client.create_agent(...) und client.list_agents(...) bleiben verfügbar, für Anwendungscode werden jedoch Ressourcen-Namespaces bevorzugt.
Benutzerdefinierte HTTP-Clients
Sowohl Sync- als auch Async-Clients unterstützen einen benutzerdefinierten httpx-Client für Proxys, Transports und andere erweiterte HTTP-Konfigurationen:
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 und DefaultAsyncHttpxClient behalten das Standard-Timeout und das Weiterleitungsverhalten des SDK bei. httpx.Client und httpx.AsyncClient verwenden stattdessen die httpx-Standardeinstellungen.
Timeouts und Wiederholungsversuche konfigurieren
Beide Clients bieten with_options(...), das eine flache Kopie mit denselben Verbindungseinstellungen zurückgibt und Standardwerte überschreibt. Verwenden Sie timeout für alle Anfragen oder legen Sie unary_timeout und stream_timeout separat fest. max_retries steuert Wiederholungsversuche des Clients:
short = client.with_options(timeout=5.0, max_retries=2)agent = short.agents.create(model="composer-2.5", local=LocalAgentOptions(cwd="."))Asynchrones Äquivalent:
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="."))Nachrichten senden
Jeder Aufruf von agent.send() gibt einen Run zurück. Jeder Aufruf von await async_agent.send() gibt einen AsyncRun zurück. Der Agent behält den Gesprächskontext über mehrere Ausführungen hinweg bei; eine Ausführung ist die Arbeitseinheit für einen Prompt.
print(agent.send("Find the bug in src/auth.py").text())# Derselbe Agent, der vollständige Unterhaltungskontext bleibt erhalten.print(agent.send("Fix it and add a regression test").text())Asynchrone Entsprechung:
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())Um Bilder zusammen mit Text zu senden:
run = agent.send( { "text": "What's in this screenshot?", "images": [{"data": base64_png, "mime_type": "image/png"}], })Du kannst auch Hilfs-Dataclasses verwenden. SDKImage.from_file(path) liest die Datei von der Festplatte und übernimmt die Base64-Kodierung für dich:
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) und SDKImage.url_image(url) stehen auch Aufrufern zur Verfügung, die bereits kodierte Bytes oder eine Remote-URL haben.
Ausführung
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 # kumulativ; Property am aktiven Handle 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() ist ein Alias für run.messages(). Die direkte Iteration über run liefert RunStreamEvent-Ereignisrahmen – genauso wie run.events().
AsyncRun bietet dieselben Statusfelder, einschließlich usage. Methoden mit I/O sind async: 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() und 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}")Ein Ausführungsstream kann nur einmal verarbeitet werden. run.messages(), run.events() und run.iter_text() greifen alle auf denselben zugrunde liegenden Stream zu und lesen ihn weiter. Nach Abschluss des Streams enthält die Ausführung das Endergebnis (run.result, run.status, run.usage, run.git, ...). Rufen Sie run.wait() auf, um alle verbleibenden Ereignisse zu verarbeiten und das typisierte RunResult zurückzugeben.
Warten ohne Streaming
result = run.wait()print(result.status) # "finished" | "error" | "cancelled" | "expired"print(result.result) # finaler Assistententext, sofern vorhandenprint(result.model) # aufgelöste ModelSelection für diesen Runprint(result.duration_ms)print(result.usage) # kumulierte TokenUsage oder None, wenn nicht verfügbarprint(result.git) # RunGitInfo in der CloudAsynchrone Entsprechung:
result = await run.wait()Tokennutzung
Ausführungen melden die Tokennutzung, wenn die Runtime sie bereitstellt. Die kumulierte Summe findest du in run.usage auf dem Live-Handle (während des Streamings oder nach wait()) oder in result.usage im von run.wait() zurückgegebenen RunResult. Beide enthalten eine TokenUsage, die über alle Runden mit gemeldeter Nutzung summiert wird, und sind None, wenn keine Runde eine Nutzung gemeldet hat – etwa bei einer abgebrochenen Ausführung, die keine Runde abgeschlossen hat, bei einer Runtime, die keine Nutzung bereitstellt, oder bei einem entkoppelten Cloud-Snapshot, dessen Nutzung noch nicht abgeglichen wurde.
@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| Feld | Beschreibung |
|---|---|
input_tokens | An das Modell gesendete Prompt-Tokens. |
output_tokens | Vom Modell generierte Tokens. |
cache_read_tokens | Aus dem Prompt-Cache abgerufene Tokens. |
cache_write_tokens | In den Prompt-Cache geschriebene Tokens. |
total_tokens | input_tokens + output_tokens + cache_read_tokens + cache_write_tokens. Ohne reasoning_tokens. |
reasoning_tokens | Denk-Tokens, eine Teilmenge von output_tokens. None, wenn das Modell oder die Laufzeit sie nicht gemeldet hat. |
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 ist bereits in output_tokens enthalten, daher lässt total_tokens sie weg, um Doppelzählungen zu vermeiden.
Für Werte pro Runde während des Streamings verarbeiten Sie das usage-Stream-Event (SDKUsageMessage). Es wird am Ende jeder Runde, die Nutzung gemeldet hat, einmal ausgelöst und enthält die TokenUsage dieser Runde. run.usage und result.usage bleiben über die gesamte Ausführung hinweg kumulativ. Nach Stream-Runden bevorzugt das Handle diese aufsummierten Gesamtwerte; andernfalls verwendet es die Nutzungsdaten aus wait() oder aus einem get_run- / list_runs-Snapshot, sofern die Bridge sie bereitstellt.
for message in run.messages(): if message.type == "usage": print(f"turn used {message.usage.total_tokens} tokens")# Oder nach wait / ohne die Nachrichten selbst zu verarbeiten:result = run.wait()print(run.usage, result.usage)Asynchrone Entsprechung: async for message in run.messages() und await run.wait(). run.usage ist bei AsyncRun weiterhin eine synchrone Property.
TokenUsage wird aus cursor_sdk exportiert (zusätzlich to_token_usage / sum_token_usage für fortgeschrittene Aufrufer). Das Wire-JSON verwendet camelCase (inputTokens, …); die Python-Dataclasses verwenden snake_case.
Die Token-Anzahlen entsprechen den Angaben der Runtime und sagen nichts über die Kosten aus. Informationen zur abgerechneten Nutzung und zu den Dollarkosten der Ausführungen eines Agenten erhalten Sie mit agent.get_usage().
Textausgabe lesen
iter_text() liefert Assistententext, während dieser gestreamt wird. text() gibt den endgültigen Terminaltext zurück und wartet auf wait(), falls die Ausführung noch läuft.
for chunk in run.iter_text(): print(chunk, end="")final_text = run.text()Asynchrones Äquivalent:
async for chunk in run.iter_text(): print(chunk, end="")final_text = await run.text()Eine Ausführung abbrechen
run.cancel()Asynchrone Entsprechung:
await run.cancel()run.cancel() fordert den Abbruch einer aktiven Ausführung an. Der Status wechselt zu "cancelled", der Live-Stream wird beendet, laufende Tool-Aufrufe werden abgebrochen und run.wait() wird mit status: "cancelled" aufgelöst. Die Teilausgabe (der bisher geschriebene Assistententext) bleibt im Run-Objekt erhalten.
Das Abbrechen einer bereits abgeschlossenen Ausführung ("finished", "error", "cancelled", "expired") löst UnsupportedRunOperationError aus. Prüfe im Zweifel run.status:
if run.status == "running": run.cancel()Run-Status abrufen
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() # Listener entfernenturns = run.conversation()run.conversation() gibt eine typisierte list[ConversationTurn] zurück. Nutzen Sie sie, um den strukturierten Verlauf darzustellen oder dauerhaft zu speichern, ohne den Live-Stream zu abonnieren. run.conversation_json() gibt den unverarbeiteten JSON-String zurück.
Verwenden Sie für asynchrone Ausführungen await run.conversation() und await run.conversation_json().
Modellüberschreibung pro Run
Das model, das du an agent.send() übergibst, überschreibt die Modellauswahl des Agenten für diesen Run und bleibt anschließend aktiv: Bei nachfolgenden Sends ohne Überschreibung wird weiterhin das neue Modell verwendet. Um zurückzuwechseln, übergib eine weitere model-Überschreibung oder lies die aktuelle Auswahl über agent.model aus.
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 und result.model geben die für diesen Run verwendete Auswahl wieder und sind nach dem Start des Runs unveränderlich.
Umgebungsvariablen pro Ausführung
Cloud Agents können auch Umgebungsvariablen für eine einzelne Ausführung erhalten. Übergib cloud.env_vars in SendOptions; die Werte werden nur für diese Ausführung in die Shell des Agents eingefügt. Nach Abschluss der Ausführung werden sie von der VM entfernt und sind für die nächste Ausführung nicht verfügbar. Das eignet sich für Zugangsdaten, die sich zwischen Runden ändern, etwa ein kurzlebiges Deploy-Token, das du direkt vor der Aufforderung an den Agent, es zu verwenden, prägst.
from cursor_sdk import CloudSendOptions, SendOptionsrun = agent.send( "Deploy the preview environment", SendOptions( cloud=CloudSendOptions(env_vars={"DEPLOY_TOKEN": mint_short_lived_token()}), ),)Wenn eine ausführungsspezifische Variable denselben Namen wie eine agentbezogene Variable aus env_vars in CloudAgentOptions hat, hat der ausführungsspezifische Wert für diese Ausführung Vorrang. Bei der nächsten Ausführung gilt wieder der agentbezogene Wert.
Ausführungsspezifische Variablen funktionieren auch beim ersten Senden. Das SDK übergibt sie bei der Erstellung des Agenten und beschränkt sie auf die erste Ausführung, sodass sie nicht im Agenten gespeichert werden. Wie agentbezogene Variablen werden sie verschlüsselt gespeichert, und ihre Namen dürfen nicht mit CURSOR_ beginnen.
Ausführungsspezifische Umgebungsvariablen sind nur für Cloud Agents verfügbar und nicht für Agents, die mit öffentlichen Repositories arbeiten. Bei lokalen Agents übernimmt der Agent-Prozess deine eigene Umgebung. Lege Variablen daher für den Prozess fest, bevor du send() aufrufst.
Unterhaltungsmodus
Übergeben Sie mode="plan" oder mode="agent", um festzulegen, ob eine Ausführung zunächst den Kontext erkundet und plant oder Änderungen direkt umsetzt. Weitere Informationen zum Plan-Modus im Produkt finden Sie unter Plan-Modus.
Legen Sie mode in den an Agent.create() übergebenen AgentOptions fest, um die erste Ausführung zu initialisieren. Lassen Sie bei nachfolgenden Aufrufen von agent.send() mode weg, um den aktuellen Unterhaltungsmodus beizubehalten, oder übergeben Sie mode, um ihn nur für diese Ausführung zu ändern.
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()Streaming von raw-Deltas
Übergeben Sie die Callbacks on_delta und on_step in SendOptions, um Updates auf niedrigerer Ebene zu erhalten. Synchrone Callbacks werden inline aufgerufen. Asynchrone Callbacks können synchron oder asynchron sein; awaitable Rückgabewerte werden abgewartet, bevor das nächste Event verarbeitet wird.
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()Die konkreten Unterklassen für Updates und Schritte befinden sich in cursor_sdk.events:
from cursor_sdk.events import TextDeltaUpdate, ToolCallStartedUpdateif isinstance(update, TextDeltaUpdate): print(update.text)Sie können aus Gründen der Abwärtskompatibilität weiterhin aus cursor_sdk importiert werden, neuer Code sollte sie jedoch aus cursor_sdk.events importieren.
SendOptions
| Eigenschaft | Typ | Beschreibung |
|---|---|---|
model | str | ModelSelection | Mapping[str, Any] | Modellüberschreibung pro Sendevorgang. Wenn nicht angegeben, wird agent.model verwendet. Bleibt nach einem erfolgreichen Sendevorgang bestehen. |
mode | "agent" | "plan" | Überschreibung des Unterhaltungsmodus pro Sendevorgang. Wenn bei Folgeanfragen nicht angegeben, bleibt der aktuelle Unterhaltungsmodus erhalten. |
mcp_servers | Mapping[str, McpServerConfig] | Inline-MCP-Serverdefinitionen. Ersetzt die beim Erstellen festgelegten Server für diese Ausführung vollständig. |
cloud.env_vars | Mapping[str, str] | Nur für Cloud Agents. Umgebungsvariablen pro Ausführung, die für diese Ausführung gesetzt und nach ihrem Abschluss entfernt werden. Überschreibt die agentbezogenen env_vars für diese Ausführung nur anhand des Namens. |
local.force | bool | Nur für lokale Agenten. Standardmäßig None (nicht gesetzt). Setzen Sie True, um eine festgefahrene aktive Ausführung zu beenden, bevor diese Nachricht gestartet wird. Cloud gibt serverseitig 409 agent_busy zurück, daher ist kein Äquivalent erforderlich. |
idempotency_key | str | Optionaler, vom Client generierter Idempotenzschlüssel für den Sendevorgang. |
on_step | Callable[[ConversationStep], Any] | Callback nach jedem abgeschlossenen Unterhaltungsschritt (Text, Thinking oder Tool-Batch). |
on_delta | Callable[[InteractionUpdate], Any] | Callback für jedes rohe InteractionUpdate. |
Die nächsten drei Abschnitte enthalten eine detaillierte Referenz zu SDKMessage, InteractionUpdate und ConversationTurn. Überfliegen oder überspringen Sie sie beim ersten Lesen; Agent wieder aufnehmen setzt die Einführung fort.
Stream-Events
run.messages() liefert typisierte SDK-Nachrichten-Dataclasses. Unterscheiden Sie anhand von message.type. Alle Nachrichten enthalten agent_id und run_id, sofern die Laufzeitumgebung sie bereitstellt.
SDKMessage = ( SDKSystemMessage | SDKUserMessageEvent | SDKAssistantMessage | SDKThinkingMessage | SDKToolUseMessage | SDKStatusMessage | SDKTaskMessage | SDKRequestMessage | SDKUsageMessage | Mapping[str, Any])type | Dataclass | Schlüsselfelder |
|---|---|---|
"system" | SDKSystemMessage | subtype, model, tools |
"user" | SDKUserMessageEvent | message.content |
"assistant" | SDKAssistantMessage | message.content mit TextBlock- und ToolUseBlock-Werten |
"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 wird für die meisten Tool-Aufrufe zweimal ausgegeben: zuerst mit status="running" und ausgefülltem args, dann nach Abschluss erneut mit status="completed" (oder "error") und ausgefülltem result. truncated gibt an, ob das SDK args oder result gekürzt hat, weil die Nutzlast zu groß war.
SDKUsageMessage wird am Ende jeder Runde mit gemeldeter Token-Nutzung einmal ausgegeben und enthält die TokenUsage dieser Runde. Die kumulierte Nutzung über alle Runden hinweg bleibt in run.usage und result.usage. Siehe Token-Nutzung.
@dataclass(frozen=True)class SDKUsageMessage: type: Literal["usage"] agent_id: str run_id: str usage: TokenUsageErgebnisdaten (endgültiger Text, Modell, Dauer, kumulative Token-Nutzung, Git-Metadaten) befinden sich nach Abschluss des Streams im Run-Objekt. Verwende run.wait(), um sie auszulesen, einschließlich result.usage, sofern die Laufzeitumgebung diese Daten gemeldet hat.
Das Tool-call-Schema ist nicht stabil. Die
args- undresult-Payloads beitool_call-Ereignissen entsprechen der internen Struktur des jeweiligen Tools und können sich weiterentwickeln, wenn sich die Tools ändern. Tool-Namen können ebenfalls umbenannt oder ersetzt werden. Behandleargsundresultals untypisierte Daten und parse sie defensiv. Der Ereignisrahmen (type,call_id,name,status) ist stabil.
run.events() liefert RunStreamEvent-Ereignisrahmen auf niedrigerer Ebene. Verwende sie, wenn du Offsets, Ereignisrahmen mit Endergebnissen oder rohe Interaktionsaktualisierungen benötigst:
for event in run.events(): print(event.kind, event.offset)Interaktionsupdates
InteractionUpdate ist der rohe Delta-Typ, der beim Aufruf von agent.send() an den on_delta-Callback übergeben wird. Updates sind feingranularer als SDKMessage-Events: Text wird Token für Token gestreamt, und Tool-Aufrufe melden Teilzustände, während sich Argumente ansammeln.
InteractionUpdate = ( TextDeltaUpdate | ThinkingDeltaUpdate | ThinkingCompletedUpdate | ToolCallStartedUpdate | ToolCallCompletedUpdate | PartialToolCallUpdate | TokenDeltaUpdate | StepStartedUpdate | StepCompletedUpdate | TurnEndedUpdate | UserMessageAppendedUpdate | SummaryUpdate | SummaryStartedUpdate | SummaryCompletedUpdate | ShellOutputDeltaUpdate | UnknownInteractionUpdate | Mapping[str, Any])PartialToolCallUpdate wird ausgegeben, während das Modell Argumente in einen Tool-Aufruf streamt, bevor dieser festgeschrieben wird. Der gleiche Stabilitätshinweis wie für SDKToolUseMessage.args gilt auch hier.
Unterhaltungstypen
Die strukturierte Ansicht der einzelnen Runden einer Ausführung, die von run.conversation() zurückgegeben wird. Jedes Element ist ein Wrapper, der den type-Diskriminator der Runde sowie die typisierte Nutzlast in turn enthält.
@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])Unterscheide nach turn.type und lies die Nutzdaten über turn.turn aus:
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() wird in on_step-Callbacks pro ConversationStep, nicht pro Runde ausgelöst. Tool-Call-Konversationsschritte enthalten eine Mapping[str, Any]-Nutzlast. Behandeln Sie Details von Tool-Call-Nutzlasten als untypisierte Daten; siehe den Stabilitätshinweis unter Stream-Events.
Agent wieder aufnehmen
Agent.resume( agent_id: str, options: AgentOptions | Mapping[str, Any] | None = None, *, client: CursorClient | None = None,) -> AgentVerwenden Sie Agent.resume() oder client.agents.resume(), um anhand der ID die Verbindung zu einem bestehenden Agenten wiederherzustellen. Häufige Anwendungsfälle: erneutes Verbinden mit einem zuvor gestarteten lang laufenden Cloud-Agenten oder Fortsetzen einer Unterhaltung nach einem Neustart des lokalen Prozesses. Die Laufzeitumgebung wird anhand des ID-Präfixes automatisch erkannt (bc- steht für Cloud, alles andere für lokal).
agent = Agent.resume("bc-abc123")run = agent.send("Also update the changelog")run.wait()Asynchrone Entsprechung:
agent = await client.agents.resume("bc-abc123")run = await agent.send("Also update the changelog")await run.wait()agent.model ist beim Fortsetzen None, sofern du model nicht erneut übergibst. Inline-MCP-Server werden beim Fortsetzen nicht gespeichert; sie enthalten häufig Secrets und sind nur im Arbeitsspeicher vorhanden. Übergib sie beim Fortsetzen erneut oder verwende eine dateibasierte MCP-Konfiguration (.cursor/mcp.json plus local.setting_sources) für Server, die dauerhaft verfügbar sein sollen.
Lokale Persistenz
Lokale Agenten speichern den Zustand der Unterhaltung und Metadaten von Ausführungen über die Bridge, sodass Follow-ups und Agent.resume() einen Prozessneustart überstehen. Standardmäßig legt die Bridge diese Daten auf dem Datenträger in einem arbeitsbereichsspezifischen Zustandsverzeichnis ab. Cloud Agents speichern serverseitig, sodass beim Fortsetzen eines Cloud Agents von überall dieselbe Unterhaltung zurückgegeben wird.
Die lokale Persistenz ist an den Arbeitsbereich gebunden. Wenn die Bridge als langlebiger Sidecar oder Unterprozess läuft, geben Sie ihr denselben Arbeitsbereich wie dem Agenten, damit lokale List-, Get- und Resume-Aufrufe die richtigen Agenten finden. Legen Sie ihn einmal im Client fest und übergeben Sie cwd an die lokalen List- und Get-Aufrufe:
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")Agenten und Ausführungen prüfen
Verwenden Sie CursorClient für Listen-, Abruf- und Paginierungs-APIs.
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)Asynchrones Äquivalent:
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)Verwende agent.list_messages() mit einem Agent-Handle, um den Nachrichtenverlauf abzurufen. Agent.messages.list(agent_id) ist eine praktische Alternative mit typisierten Attributen für denselben Aufruf, wenn du nur eine ID hast.
Verwende Agent.get_run(run_id) oder client.agents.get_run(run_id), um eine Ausführung
ohne Agent-Handle abzurufen. Brich sie mit
Agent.cancel_run(run_id, agent_id=...) oder
client.agents.cancel_run(run_id, agent_id=...) ab. Die Methoden des Async-Clients sind
awaitable und verwenden dieselben Argumente.
AgentMessage unterscheidet sich von einer gestreamten SDKMessage:
@dataclass(frozen=True)class AgentMessage: type: str uuid: str agent_id: str message: Any = NoneList-Endpunkte geben ListResult[T] zurück. Verwende .items und .next_cursor direkt, iteriere mit for item in page über die aktuelle Seite oder mit .auto_paging_iter() über alle Seiten. Asynchrone List-Endpunkte geben AsyncListResult[T] zurück; async for item in page durchläuft die aktuelle Seite und async for item in page.auto_paging_iter() alle Seiten im Ergebnissatz.
SDKAgentInfo
Die von Agent.list(), Agent.get(), client.agents.list() und client.agents.get() zurückgegebene Metadatenstruktur.
@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] = {} # aus CloudAgentOptions.metadata; leer bei lokalen AgentsLebenszyklus von Cloud Agents
Cloud Agents bleiben im Arbeitsbereich deines Teams, bis du sie archivierst oder löschst. client.agents.list(runtime="cloud") blendet archivierte Agents standardmäßig aus; übergib include_archived=True, um sie anzuzeigen. Filtere nach pr_url, um den Agent zu finden, der einen bestimmten Pull Request erstellt hat.
# Per ID, kein Agent-Handle erforderlich:Agent.archive(agent_id)Agent.unarchive(agent_id)Agent.delete(agent_id)# Über einen expliziten Client:client.agents.archive(agent_id)client.agents.unarchive(agent_id)client.agents.delete(agent_id)# Auf einem vorhandenen Agent-Handle:agent.archive()agent.unarchive()agent.delete()archive archiviert den Agenten, sodass das Transkript lesbar bleibt. unarchive stellt ihn wieder her. delete löscht ihn dauerhaft; nachfolgende Lesevorgänge geben NotFoundError zurück.
Asynchrone Lifecycle-Methoden haben dieselben Namen und sind awaitable.
agent.get_usage()
Ruft die abgerechnete Token-Nutzung und die Kosten in US-Dollar für die Ausführungen eines Agenten ab. Cloud Agents geben eine Aufschlüsselung pro Ausführung zurück, lokale Agenten eine Aufschlüsselung pro Runde. Übergeben Sie run_id, um das Ergebnis auf einen Eintrag zu beschränken: für Cloud Agents eine run-<uuid>-Ausführungs-ID, für lokale Agenten eine ID aus einem vorherigen 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 # summiert über alle `runs` runs: Sequence[RunUsage] = () cost: UsageCost | None = None # summiert über alle `runs`@dataclass(frozen=True)class RunUsage: run_id: str usage: TokenUsage cost: UsageCost | None = None@dataclass(frozen=True)class UsageCost: raw_cost_cents: float # Modell-Token-Kosten ohne Rabatte; 0 bei anfragebasierter Nutzung charged_cents: float # berechneter Betrag, inklusive Rabatten und Cursor-Token-RateKosten enthalten Rabatte und werden möglicherweise erst kurz nach Ende einer Ausführung endgültig abgerechnet; bis dahin ist cost None. charged_cents ist bei im Plan enthaltener Nutzung, BYOK und durch Gutschriften gewährter Nutzung 0.0.
Dies ist eine andere Ansicht als Token usage: run.usage ist die aktuelle Tokenanzahl für eine einzelne Ausführung, während get_usage() den abgerechneten Datensatz über die Ausführungen des Agenten hinweg liefert. Bei asynchronen Agenten entspricht await agent.get_usage() diesem Wert. AgentUsage, RunUsage und UsageCost werden aus cursor_sdk exportiert.
Der Cursor-Namespace
Lesevorgänge auf Konto- und Katalogebene. Sync-Methoden akzeptieren optional api_key; andernfalls verwenden sie CURSOR_API_KEY.
from cursor_sdk import Cursorme = Cursor.me()models = Cursor.models.list()repositories = Cursor.repositories.list()Entsprechende Variante mit explizitem Client:
me = client.me()models = client.models.list()repositories = client.repositories.list()Asynchrone Entsprechung:
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() gibt einen SDKUser mit den Feldern api_key_name, created_at sowie den optionalen Feldern user_id, user_email, user_first_name und user_last_name zurück.
Verwenden Sie Cursor.models.list(), um gültige Modell-IDs und modellspezifische Parameter zu ermitteln, bevor Sie Agent.create() oder agent.send() aufrufen. Parameter sind modellspezifisch. Häufige Beispiele sind Denkaufwand und Cursor Routers optimize_for für auto-smart.
Der Katalog ist konto- und teamspezifisch. Cursor Router wird nur als auto-smart angezeigt, wenn Router für das Team des API-Schlüssels verfügbar ist. Siehe 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"),# ),# ),# ]Voreingestellte variants jedes SDKModel enthalten bereits gültige params, sodass du sie in eine ModelSelection kopieren kannst.
Bevorzuge eine explizite Router-Auswahl (auto-smart + optimize_for), wenn ein Zielmodell fehlt und du Cost, Balance oder Intelligence verwenden möchtest. Greife nur auf ModelSelection(id="auto") zurück, wenn du serverseitig ausgewähltes Auto ohne Wahl eines Router-Modus verwenden möchtest. Übergib für Cursor Router optimize_for stets explizit.
Cursor.repositories.list() gibt die SCM-Repos zurück (GitHub, GitLab, Bitbucket, Azure DevOps – je nachdem, was verbunden ist), die für Cloud Agents im Konto oder Team des aufrufenden Nutzers verfügbar sind. Jedes Element stellt eine url bereit. Nutze diese, um CloudAgentOptions.repos zu befüllen.
MCP-Server
Je nach Laufzeitumgebung können Agenten MCP-Server aus Inline-Definitionen, Projekt- und Nutzereinstellungen, Plugins sowie Dashboard-verwalteten Konfigurationen übernehmen.
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", "."], ), }, ))Flache Dictionaries ({"type": "http", "url": ...} und {"type": "stdio", "command": ...}) werden auch als praktische Kurzform für Skripte akzeptiert.
Was geladen wird
Lokale Agenten laden Server aus bis zu fünf Quellen. Bei Namenskonflikten hat die zuerst gefundene Quelle Vorrang:
mcp_serversinagent.send(). Ersetzt die beim Erstellen festgelegten Server für diese Ausführung vollständig (keine Zusammenführung).mcp_serversinAgent.create(). Wird verwendet, wenn kein Override für den einzelnen Sendevorgang angegeben ist.- Plugin-Server, wenn
local.setting_sources"plugins"enthält. - Projekt-Server aus
.cursor/mcp.json, wennlocal.setting_sources"project"enthält. - Nutzer-Server aus
~/.cursor/mcp.json, wennlocal.setting_sources"user"enthält.
Ohne local.setting_sources werden nur Inline-Server geladen. Wenn ein lokaler MCP-Server eine OAuth-Anmeldung erfordert, kann das SDK eine gespeicherte Anmeldung aus der Cursor-App wiederverwenden, aber keinen Browser öffnen, um dich anzumelden.
Cloud Agents laden Server aus:
mcp_serversinagent.send(). Ersetzt die beim Erstellen festgelegten Server für diese Ausführung vollständig (keine Zusammenführung).mcp_serversinAgent.create(). Wird verwendet, wenn kein Override für den einzelnen Sendevorgang angegeben ist.- Deinen Nutzer- und Team-MCP-Servern auf cursor.com/agents.
Wenn ein Inline-Server weder auth noch headers enthält und du diese Server-URL zuvor auf cursor.com/agents autorisiert hast, verwenden mit einem persönlichen API-Token authentifizierte Ausführungen diese OAuth-Token automatisch wieder. Servicekonto-API-Schlüssel können nicht auf Nutzerauthentifizierung zurückgreifen, da sie keinem Nutzer zugeordnet sind.
local.setting_sources gilt nicht für Cloud Agents.
Cloud
Cloud Agents akzeptieren auch authentifizierte MCP-Konfigurationen inline. Cloud MCP unterstützt HTTP- und stdio-Transporte. Verwenden Sie HTTP-headers für statische API-Schlüssel oder Bearer-Tokens. Verwenden Sie HTTP-auth für OAuth-geschützte Server. Verwenden Sie stdio-env, wenn der Server auf der Cloud-VM läuft und Zugangsdaten aus Umgebungsvariablen liest.
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"}, ), }, ))- HTTP-
headersundauthwerden von Cursors Backend verarbeitet. Sensible Felder werden geschwärzt und gelangen nicht in die VM. - Stdio-
env-Werte werden in die VM übergeben, da der Server dort läuft. Behandeln Sie sie wie jedes andere Laufzeit-Secret. - OAuth für MCP-Server, die auf cursor.com/agents konfiguriert sind, bleibt nutzerbezogen – auch bei Servern auf Teamebene.
Unter MCP finden Sie das vollständige Konfigurationsformat sowie unter Cloud-Agent-Funktionen cloud-spezifisches Verhalten.
Subagenten
Definieren Sie benannte Subagenten, die der Hauptagent mit dem Tool Agent starten kann. Übergeben Sie sie inline:
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.", ), }, ))Subagents, die im Repo unter .cursor/agents/*.md committed sind (mit name, description und optionalem model im Frontmatter), werden ebenfalls berücksichtigt. Inline-Definitionen überschreiben dateibasierte Definitionen mit demselben Namen.
Verschachtelte Subagenten
Subagenten können bis zu einer bestimmten Verschachtelungstiefe eigene Subagenten starten. Wenn ein Subagent das Tool Agent verwendet, greift er auf denselben Subagent-Executor wie der übergeordnete Agent zu. Dadurch kann ein übergeordneter Agent an einen Subagenten delegieren, der wiederum weiterdelegiert. Auf jeder Ebene ist dieselbe Gruppe benannter Subagenten verfügbar. Der Agent der obersten Ebene und seine direkten Subagenten können Subagenten starten, aber ein von einem anderen Subagenten gestarteter Subagent kann keine weiteren Subagenten starten.
Einschränkung des Toolsets
tools legt eine Allowlist für die integrierten Tools fest, die dem Modell zur Verfügung stehen; disallowed_tools entfernt Tools und lässt die übrigen zu, einschließlich der Tools, die nach Veröffentlichung deiner SDK-Version zur Plattform hinzugefügt wurden. Beide Optionen sind derzeit nur für lokale Agenten verfügbar und werden nicht im Agenten gespeichert: Übergib sie beim Fortsetzen erneut, um die Einschränkung beizubehalten.
from cursor_sdk import Agent, AgentOptions, LocalAgentOptions# Read-only-Agent: Nur diese Tools werden angeboten.reader = Agent.create( AgentOptions( model="composer-2.5", tools=["read", "grep", "glob", "ls"], local=LocalAgentOptions(cwd="."), ))# Alles außer Shell-Zugriff.no_shell = Agent.create( AgentOptions( model="composer-2.5", disallowed_tools=["shell"], local=LocalAgentOptions(cwd="."), ))- Wenn
toolsweggelassen wird, steht das Standard-Toolset des ausgewählten Modells zur Verfügung;tools=[]stellt keine integrierten Tools bereit, sodass das Modell nur mit Text antworten kann. - Beide Felder akzeptieren öffentliche Namen (
"read","edit","task","webSearch", ...) sowie die Fähigkeitsgruppen"shell"und"mcp". Unbekannte Namen lösen beim Erstellen einenBadRequestErroraus. - Deny hat Vorrang: Damit ein Tool verfügbar ist, muss es in
toolsenthalten sein (falls gesetzt) und darf nicht indisallowed_toolsstehen. - Das Deaktivieren von
"mcp"entfernt auch benutzerdefinierte Tools. Das Deaktivieren von"task"verhindert Subagenten; andernfalls behalten Subagenten ihre eigenen kuratierten Toolsets.
Benutzerdefinierte Tools
Mit benutzerdefinierten Tools kannst du Python-Funktionen lokalen Agenten verfügbar machen, ohne einen separaten MCP-Server einzurichten. Übergib sie in 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 erhält die geparsten Argumente und, sofern verfügbar, einen CustomToolContext mit tool_call_id. Es kann einen String, einen JSON-kompatiblen Wert oder eine Zuordnung mit einer content-Liste zurückgeben. Benutzerdefinierte Tools sind ausschließlich für lokale Agenten verfügbar.
Hooks
Hooks sind ausschließlich dateibasiert. Es gibt keinen programmatischen Hook-Callback. Hooks bilden eine Richtliniengrenze auf Projektebene und sind keine Einstellung pro Ausführung.
- Local: Füge
.cursor/hooks.jsonzum Repo hinzu, das alslocal.cwdübergeben wird, oder füge~/.cursor/hooks.jsonfür Hooks auf Nutzerebene hinzu. - Cloud: Übertrage
.cursor/hooks.jsonund die zugehörigen Skripte in das incloud.reposübergebene Repo. Vom SDK erstellte Cloud Agents laden Projekt-Hooks automatisch. In Enterprise-Plänen führen sie außerdem Team-Hooks und Enterprise-verwaltete Hooks aus.
Unter Hooks findest du das Konfigurationsformat und unter Hook-Unterstützung für Cloud Agents Informationen zum Cloud-Verhalten.
Artefakte
Dateien aus dem Arbeitsbereich des Agenten auflisten und herunterladen.
@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)# Ein einzelnes Artifact auf die Festplatte herunterladen.content = agent.download_artifact(artifacts[0].path)Path("review.md").write_bytes(content)Asynchrone Agenten bieten await agent.list_artifacts() und await agent.download_artifact(path).
Die Unterstützung für Artefakte hängt von der Laufzeitumgebung ab. Lokale SDK-Agenten geben bei list_artifacts() eine leere Liste zurück und lösen bei download_artifact() eine Ausnahme aus.
Ressourcenverwaltung
Schließen Sie Agents stets, wenn Sie sie nicht mehr benötigen. Das sauberste Muster für die Synchronisierung ist ein Kontextmanager:
from cursor_sdk import Agent, LocalAgentOptionswith Agent.create(model="composer-2.5", local=LocalAgentOptions(cwd=".")) as agent: agent.send("Summarize the repository").wait()Zur expliziten Freigabe:
agent.close()Asynchrone Agenten und Clients unterstützen asynchrone Kontextmanager und Bereinigung mit 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()Zur expliziten Freigabe:
await agent.close()await client.aclose()Der standardmäßige synchrone Client auf Modulebene wird beim Beenden des Prozesses automatisch geschlossen. Lang laufende Prozesse können ihn explizit schließen und zurücksetzen:
from cursor_sdk import close_default_clientclose_default_client()Konfigurationsreferenz
Das Python SDK akzeptiert Hilfs-Dataclasses und einfache Dictionaries. Dataclasses verwenden Python-Felder im snake_case-Format und werden für Anwendungscode empfohlen.
AgentOptions
| Eigenschaft | Typ | Standard | Beschreibung |
|---|---|---|---|
model | str | ModelSelection | Mapping[str, Any] | Für Local erforderlich; bei Cloud wird der vom Server aufgelöste Standard verwendet | Zu verwendendes Modell. Siehe ModelSelection. |
api_key | str | Umgebungsvariable CURSOR_API_KEY | User API Key oder Service-Account-Key. Team-Admin-Keys werden noch nicht unterstützt. |
name | str | Automatisch generiert | Menschenlesbarer Agent-Name, der in client.agents.list() / client.agents.get() angezeigt wird. |
local | LocalAgentOptions | Mapping[str, Any] | None | Konfiguration für Local-Agent. Übergeben Sie diese, um einen Local-Agent zu erstellen. |
cloud | CloudAgentOptions | Mapping[str, Any] | None | Konfiguration für Cloud-Agent. Übergeben Sie diese, um einen Cloud-Agent zu erstellen. |
mcp_servers | Mapping[str, McpServerConfig] | None | Inline-MCP-Server-Definitionen. |
agents | Mapping[str, AgentDefinition | Mapping[str, Any]] | None | Subagent-Definitionen. |
tools | Sequence[str] | Standard-Toolset | Dem Modell werden nur die aufgeführten integrierten Tools zur Verfügung gestellt. [] bedeutet keine integrierten Tools; das Modell kann nur mit Text antworten. Nur für Local-Agents. |
disallowed_tools | Sequence[str] | None | Entfernt die aufgeführten integrierten Tools; alle anderen bleiben verfügbar. Bei Kombination mit tools hat Deny Vorrang. Nur für Local-Agents. |
agent_id | str | Automatisch generiert | Dauerhafte Agent-ID. Übergeben Sie diese, um eine stabile ID über mehrere Aufrufe hinweg beizubehalten. |
idempotency_key | str | Für Cloud automatisch generiert | Optionaler, vom Client generierter Idempotenzschlüssel. Nur für Cloud. |
mode | "agent" | "plan" | None | Anfänglicher Unterhaltungsmodus für die erste Ausführung des Agent. Wenn nicht angegeben, startet der Server im Agent-Modus. Siehe Unterhaltungsmodus. |
LocalAgentOptions
| Eigenschaft | Typ | Standard | Beschreibung |
|---|---|---|---|
cwd | str | os.PathLike | None | Primäres Arbeitsverzeichnis. Listen mit mehreren Einträgen werden abgelehnt; für mehrere Wurzeln dirs verwenden. |
dirs | Sequence[str | os.PathLike] | None | Zusätzliche Workspace-Ordner für Setups mit mehreren Wurzeln. Wird mit cwd zusammengeführt, sodass Regeln, Skills und Workspace-Kontext aus jedem Pfad geladen werden. |
setting_sources | Sequence[SettingSource] | None | Einstellungsebenen aus der Umgebung: "project", "user", "team", "mdm", "plugins" oder "all". |
sandbox_options | SandboxOptions | Mapping[str, Any] | None | Lokale Sandbox-Optionen. |
store | LocalAgentStoreConfig | Mapping[str, Any] | None | Lokale Store-Konfiguration, die an die Bridge übergeben wird. |
auto_review | bool | None | Lokale Tool-Aufrufe über Auto-review leiten, wenn das verbundene Backend dies unterstützt. |
custom_tools | Mapping[str, CustomTool | Mapping[str, Any]] | None | Benutzerdefinierte Tools, die lokalen Agenten bereitgestellt werden. |
CloudAgentOptions
| Eigenschaft | Typ | Standard | Beschreibung |
|---|---|---|---|
env | CloudEnvironment | Mapping[str, Any] | None | Ausführungsumgebung. Wenn nicht angegeben, verwendet der Server von Cursor gehostete Cloud-VMs. pool und machine beziehen sich auf selbstgehostete Worker, die Sie betreiben. |
repos | Sequence[CloudRepository | Mapping[str, Any]] | None | Repositories, die in die VM geklont werden. Lassen Sie die Angabe weg oder übergeben Sie [], um einen Agent ohne Repo mit leerem Workspace zu erstellen. Übergeben Sie für ein Repo pr_url, um den Agent an eine bestehende PR anzuhängen. |
work_on_current_branch | bool | None | Überträgt Commits auf den bestehenden Branch statt auf einen neuen. Der Server behandelt einen nicht angegebenen Wert als False. |
auto_create_pr | bool | None | Öffnet eine PR, wenn die Ausführung abgeschlossen ist. Der Server behandelt einen nicht angegebenen Wert als False. |
open_as_cursor_github_app | bool | True für Service-Konto-Schlüssel, False für Nutzerschlüssel | Öffnet PRs als Cursor GitHub App statt als Besitzer des API-Schlüssels. Der aufgelöste Wert wird beim Erstellen, Abrufen und Auflisten zurückgegeben. |
skip_reviewer_request | bool | None | Überspringt die Anfrage, den aufrufenden Nutzer als Reviewer für die PR hinzuzufügen. Der Server behandelt einen nicht angegebenen Wert als False. |
env_vars | Mapping[str, str] | None | Auf die Session beschränkte Umgebungsvariablen für Cloud Agents. |
metadata | Mapping[str, str] | None | String-Tags des Aufrufers, die auf dem Cloud-Agent gespeichert werden. Siehe Agent-Metadaten. |
AgentDefinition
| Eigenschaft | Typ | Standard | Beschreibung |
|---|---|---|---|
description | str | erforderlich | Wann dieser Subagent verwendet werden soll. Wird dem übergeordneten Agenten angezeigt, damit dieser weiß, wann er ihn starten soll. |
prompt | str | erforderlich | Systemprompt für den Subagenten. |
model | str | ModelSelection | Mapping[str, Any] | "inherit" | None | Modellüberschreibung. None und "inherit" verwenden beide die Auswahl des übergeordneten Agenten. |
mcp_servers | Sequence[str | AgentDefinitionMcpServer | Mapping[str, Any]] | None | MCP-Server, die diesem Subagenten zur Verfügung stehen. Die Namen verweisen auf Server aus den mcp_servers des übergeordneten Agenten. |
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 ist die Modellkennung (zum Beispiel "composer-2.5" oder "auto-smart"). params enthält modellspezifische Parameter wie den Denkaufwand oder optimize_for des Routers. Verwende Cursor.models.list(), um gültige IDs, Parameterdefinitionen und vordefinierte Varianten für dein Konto zu ermitteln. Informationen zum Auswahlvertrag des Routers findest du unter Cursor 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 # nur lokal; Cloud lehnt dieses Feld ab@dataclass(frozen=True)class McpAuth: client_id: str client_secret: str | None = None scopes: Sequence[str] = ()Bei HTTP-Servern in der Cloud werden headers und auth vom Cursor-Backend verarbeitet. Sensible Felder werden geschwärzt, bevor die VM sie sieht. Bei stdio-Servern in der Cloud werden env-Werte an die VM übergeben (behandeln Sie sie wie jedes andere Laufzeit-Secret).
Nutzernachricht
@dataclass(frozen=True)class UserMessage: text: str images: Sequence[SDKImage | Mapping[str, Any]] | None = NoneDie strukturierte Form des Nachrichtenarguments von agent.send(). Verwenden Sie sie, um Bilder zusammen mit Text zu senden.
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: ...Übergeben Sie entweder eine Remote-url oder base64-data mit einem mime_type. from_data() akzeptiert Bytes oder einen base64-String. from_file() liest eine Datei von der Festplatte und base64-kodiert sie.
SettingSource
SettingSource ist in cursor_sdk.types verfügbar.
from cursor_sdk.types import SettingSourceSteuert, welche Einstellungsebenen im Dateisystem ein lokaler Agent lädt. Cloud Agents laden stets project, team und plugins und ignorieren dieses Feld.
| Wert | Quelle |
|---|---|
"project" | .cursor/ im Workspace |
"user" | ~/.cursor/ |
"team" | Team-Einstellungen, die mit dem Dashboard synchronisiert werden |
"mdm" | Von MDM verwaltete Enterprise-Einstellungen |
"plugins" | Von Plugins bereitgestellte Einstellungen |
"all" | Kurzform für alle oben genannten |
ListResult
@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]: ...Wird von client.agents.list(), client.agents.list_runs() und Agent.list() zurückgegeben. next_cursor ist leer, wenn keine weiteren Seiten verfügbar sind. Asynchrone List-Endpunkte geben AsyncListResult[T] mit awaitbaren Varianten zurück.
Fehler
Alle SDK-Fehler leiten sich von CursorAgentError ab. CursorSDKError ist der abwärtskompatible Alias als Basisklasse für ältere Aufrufer. Verwenden Sie is_retryable und retry_after, um die Wiederholungslogik zu steuern.
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| Fehler | Wann |
|---|---|
AuthenticationError | Ungültiger API-Schlüssel oder nicht angemeldet. |
PermissionDeniedError | Der authentifizierte Aufrufer hat keine Berechtigung für die angeforderte Operation. |
RateLimitError | Zu viele Anfragen oder Nutzungslimits überschritten. |
ConfigurationError | Ungültiges Modell, fehlende erforderliche Konfiguration oder ungültige Anfrageparameter. |
AgentBusyError | Senden einer Folgeanfrage, während der Agent bereits eine Ausführung im Status CREATING oder RUNNING hat (HTTP 409, Code agent_busy). |
BadRequestError | Anfrage ist fehlerhaft. |
IntegrationNotConnectedError | Erstellen eines Cloud-Agenten für ein Repo, dessen SCM-Anbieter nicht verbunden ist. |
NetworkError | Dienst nicht verfügbar oder Netzwerkfehler. |
APITimeoutError | Zeitüberschreitung bei der Anfrage. |
InternalServerError | Der Cursor-Dienst hat einen Serverfehler zurückgegeben. |
NotFoundError | Angeforderte Ressource wurde nicht gefunden. |
AgentNotFoundError | Der Agent existiert nicht oder ist im aktuellen Arbeitsverzeichnis nicht sichtbar. |
UnsupportedRunOperationError | Die Ausführungsoperation wird für den aktuellen Run-Status nicht unterstützt. |
Wiederholungsversuche mit Backoff
is_retryable und retry_after steuern die Wiederholungslogik auf der Aufruferseite. retry_after ist ein HTTP-ähnlicher String (Sekunden oder ein HTTP-Datum), der vom Server bereitgestellt wird, wenn er gesetzt ist.
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)Jeder CursorAgentError enthält request_id, wenn der Server eine zurückgegeben hat. Protokolliere sie immer, wenn du einen Fehler anzeigst, damit der Support den Fehler nachvollziehen kann.
IntegrationNotConnectedError
class IntegrationNotConnectedError(ConfigurationError): provider: str # e.g. "github", "gitlab", "azuredevops" help_url: str # Dashboard-Link zum erneuten VerbindenVerwenden Sie help_url, um Nutzer zum richtigen Ablauf für die erneute Verbindung zu führen. Neue Anbieter können ohne SDK-Release hinzugefügt werden.
AgentBusyError
Cloud Agents erlauben jeweils nur eine aktive Ausführung. AgentBusyError wird ausgelöst, wenn du agent.send() aufrufst (oder auf andere Weise eine Ausführung erstellst), während eine andere Ausführung auf demselben Agent noch den Status CREATING oder RUNNING hat.
is_retryable ist False. Ein sofortiger erneuter Versuch schlägt weiterhin fehl, bis die aktive Ausführung einen Endstatus erreicht oder du sie abbrichst. Andere 409-Antworten wie agent_archived lösen stattdessen ConfigurationError aus.
Warte, bis die aktive Ausführung abgeschlossen ist, brich sie mit run.cancel() ab oder frage vor dem erneuten Senden mit Agent.list_runs() regelmäßig den Status ab:
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.")Lokale Agenten lösen keinen AgentBusyError aus. Übergeben Sie local={"force": True} an send(), um einen hängenden lokalen Lauf zu beenden, bevor Sie einen neuen starten.
UnsupportedRunOperationError
class UnsupportedRunOperationError(ConfigurationError): operation: strWird ausgelöst, wenn eine Run-Operation für die aktuelle Ausführung nicht zulässig ist. Der häufigste Fall ist run.cancel() bei einer bereits abgeschlossenen Ausführung.
run.supports(operation) und run.unsupported_reason(operation) geben an, ob eine Operation auf SDK-Ebene unterstützt wird ("stream", "wait", "cancel", "conversation"), und prüfen nicht den Run-Status. Lesen Sie run.status, bevor Sie statusabhängige Aufrufe ausführen.
Fehlerbehebung
Setzen Sie CURSOR_SDK_LOG=debug (oder info), um dem eigenen Logger des SDK einen stderr-Handler hinzuzufügen. Das SDK konfiguriert nur seinen eigenen cursor_sdk-Logger, sodass die Logging-Konfiguration der Hostanwendung nicht beeinträchtigt wird.
CURSOR_SDK_LOG=debug python my_script.pyDie mitgelieferte Bridge-Binärdatei wird zusammen mit dem Paket als cursor-sdk-bridge im PATH installiert. Führe sie direkt aus, um zu prüfen, ob dein Wheel den erwarteten Build enthält:
cursor-sdk-bridge --helpBekannte Einschränkungen
- Schemas für Tool-Call-Payloads sind absichtlich nicht stark typisiert.
- Inline-MCP-Server werden nicht über
Agent.resume()hinweg gespeichert. Übergeben Sie sie bei Bedarf beim Fortsetzen erneut. - Benutzerdefinierte Tools (
local.custom_tools) und Toolset-Einschränkungen (tools,disallowed_tools) sind nur für lokale Agenten verfügbar. Die Einschränkungen werden nicht im Agenten gespeichert; übergeben Sie sie beim Fortsetzen erneut. - Der Download von Artefakten ist für lokale Agenten nicht implementiert.
local.setting_sources(und die dadurch gesteuerten dateibasierten MCP- und Subagent-Pfade) gilt nicht für Cloud Agents. Cloud lädt stetsproject,teamundplugins.- Hooks sind nur dateibasiert (
.cursor/hooks.json). Programmatische Callbacks werden nicht unterstützt.