Skip to main content

Command Palette

Search for a command to run...

SDK

Cursor Python SDK

El paquete cursor-sdk te permite invocar el agente de Cursor desde tu propio código de Python. El mismo agente que se ejecuta en el IDE de Cursor, la CLI y la aplicación web se puede controlar mediante scripts de Python con clientes síncronos y asíncronos, dataclasses tipadas e iteración estándar para flujos y páginas. Ejecuta la habilidad /sdk en Cursor para empezar.

Para la API REST, consulta la API de Cloud Agents. Para otros lenguajes, consulta SDK Bridge.

Descripción general

El SDK unifica los entornos de ejecución locales y en la nube mediante una sola interfaz. Escribes el mismo código independientemente de dónde se ejecute el agente.

Entorno de ejecuciónQué haceCuándo usarlo
LocalEjecuta el agente con archivos locales en disco.Scripts de desarrollo y comprobaciones de CI sobre un árbol de trabajo.
Cloud (Cursor-hosted)Se ejecuta en una VM aislada con tu repositorio clonado. Cursor ejecuta las VM.Cuando quien llama no tiene el repositorio, necesitas muchos agentes en paralelo o las ejecuciones deben continuar aunque quien llama se desconecte.

Establece el entorno de ejecución pasando local o cloud a Agent.create().

Autenticación

Establece CURSOR_API_KEY o proporciona api_key antes de crear un agente.

El SDK admite claves de API de usuario y de cuentas de servicio para ejecuciones tanto locales como en la nube. Las claves de API de administrador de equipo aún no son compatibles.

export CURSOR_API_KEY="your-key"

Consumo y facturación

Las ejecuciones del SDK siguen las mismas reglas de precios, pools de solicitudes y modo de privacidad que las ejecuciones desde el IDE y los agentes en la nube. El gasto aparece en el panel de control de consumo de tu equipo con la etiqueta SDK.

Para consultar en el código el número de tokens por ejecución, consulta Consumo de tokens. Para obtener el consumo facturado y el coste en dólares de las ejecuciones de un agente de programación, consulta agent.get_usage().

Conceptos principales

ConceptoDescripción
AgenteIdentificador duradero que conserva el estado de la conversación, la configuración del espacio de trabajo, la selección de modelo y los ajustes. Persiste entre varias instrucciones.
EjecuciónUn envío de instrucción. Tiene su propio flujo, estado, resultado, conversación y cancelación.
SDKMessageMensaje tipado del flujo emitido durante una ejecución. Tiene la misma estructura en los entornos de ejecución locales y en la nube.
CursorClientCliente explícito para controlar el ciclo de vida, usar opciones HTTP personalizadas o gestionar varios espacios de trabajo en un proceso. Client es un alias.
AsyncClientCliente asíncrono equivalente. Obligatorio para todas las operaciones asíncronas.

Instalación

pip install cursor-sdk

Requiere Python 3.10 o una versión posterior.

Inicio rápido

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())

Eventos de flujo muestra cómo extraer texto del asistente, gestionar llamadas a herramientas y consultar el estado de la ejecución. Para una instrucción de una sola vez (crear, ejecutar, finalizar), consulta Agent.prompt().

Inicio rápido en la nube

El SDK de Python ofrece compatibilidad nativa con los agentes en la nube de Cursor. Puede listar los repositorios conectados, iniciar un agente en uno de ellos, esperar a que finalice la ejecución y revisar el resultado 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())

Los agentes en la nube iniciados mediante el SDK no aparecen en la lista de agentes predeterminada. Para consultarlos en Cursor Web o en la ventana de agentes de programación de Cursor, haz clic en Filtro > Fuente > SDK.

Uso asíncrono

El cliente asíncrono ofrece la misma interfaz que el cliente síncrono y se recomienda para servidores, bots y la orquestación concurrente de agentes de programación. AsyncAgent, AsyncClient, AsyncRun y AsyncCursor se exportan desde cursor_sdk y 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())

No hay un cliente asíncrono predeterminado global. Cree una instancia de AsyncClient explícitamente o use AsyncClient.launch_bridge(...) como gestor de contexto asíncrono para que cada bucle de eventos tenga su propio cliente. No mezcle clientes síncronos y asíncronos en la misma ruta de código.

Los métodos directos de la clase AsyncAgent requieren client=. Use await client.agents.create(...) o await AsyncAgent.create(..., client=client).

SíncronoAsíncrono
CursorClient / ClientAsyncClient / AsyncCursorClient
AgentAsyncAgent
RunAsyncRun
CursorAsyncCursor
ListResultAsyncListResult
DefaultHttpxClientDefaultAsyncHttpxClient

Crear agentes de programación

Agent.create() valida las opciones y devuelve un controlador de inmediato. Pasa local o cloud para elegir el entorno de ejecución.

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 se asigna de inmediato. Los agentes locales reciben un ID agent-<uuid>; los agentes en la nube reciben un ID bc-<uuid>. agent.model es un ModelSelection con tipos definidos, por lo que agent.model.id y agent.model.params funcionan directamente.

Agentes en la nube sin repositorio

Los agentes en la nube pueden ejecutarse en una VM vacía sin repositorio. Pase cloud con una lista repos vacía u omita repos por completo. Si omite cloud, se seleccionará el entorno de ejecución local.

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)

Los agentes sin repositorio deben estar activados en tu cuenta o equipo. No se pueden crear con claves de API limitadas a un repositorio; usa una clave de cuenta de servicio sin restricciones o una clave de API de usuario.

Variables de entorno de sesión

Para los agentes de programación en la nube, pasa env_vars cuando una ejecución necesite credenciales de corta duración u otros valores que solo deban estar disponibles para ese agente de programación.

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"],        },    ),)

Estos valores se cifran en reposo, se inyectan en la shell del agente de programación en la nube y se eliminan con el agente de programación. No se puede usar env_vars con un agent_id proporcionado por quien llama; omite agent_id y lee el ID emitido por el servidor desde agent.agent_id. Los nombres de las variables no pueden empezar por CURSOR_.

Para valores que solo deban existir durante una única ejecución, pásalos en agent.send(). Consulta Variables de entorno por ejecución.

Metadatos del agente de programación

Asigna tus propios identificadores a un agente en la nube al crearlo. Los metadatos pueden vincular un agente de programación con un usuario, inquilino, flujo de trabajo o ticket de tu sistema, y se devuelven en SDKAgentInfo.metadata mediante client.agents.get() y client.agents.list(). Estas etiquetas no son la API de metadatos del agente de programación dentro de la VM, que expone el id, propietario, turno y espacio de trabajo de la ejecución actual desde dentro de la VM.

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)

Los metadatos están disponibles para los agentes en la nube al crearlos. Puedes adjuntar hasta 50 pares clave-valor. Las claves no pueden estar vacías ni superar los 255 caracteres. Los valores deben ser cadenas de no más de 4096 bytes. Se permiten cadenas vacías, y un mapa vacío se considera equivalente a no incluir metadatos.

Parámetros del modelo

Usa ModelSelection.params para pasar opciones específicas de cada modelo, como el esfuerzo de razonamiento o optimize_for de Cursor Router. Los ID y valores de los parámetros varían según el modelo. Usa Cursor.models.list() para consultar los parámetros y las variantes predefinidas compatibles con tu cuenta.

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="."),)

Usa Cursor.models.list() para consultar los ID de parámetros y las variantes predefinidas de un modelo determinado. Consulta Cursor Router para conocer el contrato de selección de auto-smart.

Cursor Router

Cursor Router selecciona un modelo para cada solicitud de Auto. En el SDK, Router es el modelo auto-smart con un parámetro optimize_for. Está disponible en Teams y Enterprise. Los administradores de Enterprise deben activar Router para el equipo antes de que auto-smart aparezca en el catálogo.

El SDK de Cursor es un SDK para agentes, no una API independiente de inferencia de modelos ni de completado de chat. Router selecciona modelos para ejecuciones de agentes de Cursor que pueden razonar sobre un espacio de trabajo, usar herramientas, ejecutar comandos y editar archivos. Actualmente, Cursor no documenta un endpoint de Router sin procesar para llamadas arbitrarias a modelos.

Selecciona Coste, Equilibrio o Inteligencia

Pasa auto-smart y establece optimize_for de forma explícita:

Etiqueta de productoValor del SDK
Costecost
Equilibriobalanced
Inteligenciaintelligence

Usa Equilibrio en los textos del producto. Usa balanced solo como valor de transmisión del 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)

Pasa siempre optimize_for. No lo omitas ni envíes un valor default heredado; el descubrimiento a través del catálogo es el contrato compatible.

Descubre Router en el catálogo de modelos

Cursor.models.list() devuelve los modelos, las definiciones de parámetros y las variantes predefinidas disponibles para la cuenta y el equipo actuales de la clave de API. Cursor Router aparece como auto-smart cuando Router está disponible. Los administradores de equipo pueden desactivar Router o restringir los modos de optimización que pueden seleccionar los miembros.

Consulta el catálogo como fuente de referencia antes de codificar una selección de forma fija:

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)],)

Cambiar de modo en cada ejecución

Sobrescribe el modelo en agent.send() para cambiar el modo Router de una ejecución:

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")],        ),    ),)

Las anulaciones de modelo por ejecución se mantienen. Los envíos posteriores sin una anulación siguen usando la nueva selección. Consulta Anulación de modelo por ejecución.

ID de modelo: auto-smart, auto y default

SelecciónSignificado
auto-smart con optimize_forCursor Router. Úsalo para Coste, Equilibrio o Inteligencia.
ModelSelection(id="auto")Alternativa Auto seleccionada por el servidor cuando un modelo específico no está en el catálogo. Prefiere auto-smart si necesitas un modo de Router explícito.
Omitir optimize_for o enviar defaultNo es un contrato de Router compatible. Consulta siempre los valores permitidos y pasa cost, balanced o intelligence.

Facturación y pool de enrutamiento

  • El coste sigue el comportamiento clásico de Auto y sus precios combinados.
  • Equilibrio e Inteligencia usan Cursor Router y se facturan a la tarifa del modelo enrutado según tu plan o contrato.
  • El modelo subyacente puede cambiar entre solicitudes. Prefiere un ID de modelo fijo si necesitas comparaciones reproducibles.
  • Las listas de permitidos de modelos Enterprise definen el pool de enrutamiento. Bloquear modelos necesarios puede desactivar Router.

Para consultar las tarifas actuales y el pool de enrutamiento, consulta Cursor Router y Modelos y precios.

Solución de problemas si falta Router

Si falta auto-smart o se rechaza un modo de optimización:

  1. Llame a Cursor.models.list().
  2. Confirme que auto-smart aparezca en el resultado.
  3. Confirme que optimize_for incluya el valor que desea (cost, balanced o intelligence).
  4. Confirme que Router esté activado para el equipo asociado a la clave de API.
  5. Si pertenece a varios equipos, confirme que la clave se esté usando en el contexto del equipo previsto.
  6. Compruebe la política de acceso a modelos del equipo si Router no está disponible o no puede elegir un modelo subyacente válido.

Diccionarios sin procesar

Se prefieren las dataclasses tipadas para el código de la aplicación, ya que el autocompletado del IDE y la comprobación de tipos funcionan mejor. El SDK también acepta diccionarios simples para scripts cortos o JSON proporcionado externamente. Las claves en snake_case se normalizan.

from cursor_sdk import Agentwith Agent.create(    {        "api_key": "crsr_key",        "model": {"id": "composer-2.5"},        "local": {"cwd": "."},    }) as agent:    ...

Agente de programación

El controlador devuelto por Agent.create(), Agent.resume(), client.agents.create() y 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: ...
MiembroDescripción
agent_idIdentificador estable del agente de programación. agent-<uuid> para local, bc-<uuid> para la nube.
modelSelección de modelo tipada actual. Se actualiza tras un envío exitoso con una anulación de modelo.
sendInicia una nueva ejecución con la instrucción proporcionada. Devuelve un controlador de Run.
reloadVuelve a leer la configuración del sistema de archivos (hooks, MCP del proyecto, subagentes de programación) sin cerrar el agente de programación.
closeCierra el agente de programación y libera recursos.
list_messagesEnumera el historial de mensajes del agente de programación.
list_artifactsEnumera los archivos generados por el agente de programación (solo en la nube; local devuelve una lista vacía).
download_artifactDescarga un archivo por ruta (solo en la nube; local genera una excepción).
get_usageObtiene el consumo de tokens facturado y el coste en dólares del agente de programación.
archive / unarchive / deleteGestiona el ciclo de vida del agente de programación en la nube.

Usa un gestor de contexto para la limpieza automática:

with Agent.create(model="composer-2.5", local=LocalAgentOptions(cwd=".")) as agent:    print(agent.send("Explain this repository").text())

Cuando usas las funciones auxiliares síncronas Agent.* o Cursor.* sin especificar client=, el SDK inicia o reutiliza un cliente predeterminado a nivel de módulo. Se cierra automáticamente al finalizar el proceso, pero también puedes cerrarlo explícitamente:

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

Comodidad de ejecución única: crea un agente, envía una sola instrucción, espera a que finalice la ejecución y lo libera.

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)

Equivalente asíncrono (se asume que ya tienes un AsyncClient abierto):

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

Usa CursorClient si necesitas controlar explícitamente el ciclo de vida, un endpoint de bridge personalizado, opciones HTTP personalizadas o varios espacios de trabajo en un mismo proceso. Client sigue estando disponible como 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())

Recursos

Los clientes explícitos exponen espacios de nombres de recursos:

RecursoEjemplos de métodos síncronosEjemplos de métodos asíncronos
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()

Los métodos de nivel superior, como client.create_agent(...) y client.list_agents(...), siguen estando disponibles, pero los espacios de nombres de recursos son la estructura recomendada para el código de la aplicación.

Clientes HTTP personalizados

Los clientes síncronos y asíncronos aceptan un cliente httpx personalizado para usar proxies, transportes y otras opciones avanzadas de configuración HTTP:

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 y DefaultAsyncHttpxClient mantienen el tiempo de espera y el comportamiento de redirección predeterminados del SDK. En cambio, httpx.Client y httpx.AsyncClient sin personalizar usan los valores predeterminados de httpx.

Configuración de tiempos de espera y reintentos

Ambos clientes exponen with_options(...), que devuelve una copia superficial que comparte la configuración de conexión y anula los valores predeterminados. Usa timeout para todas las solicitudes o establece unary_timeout y stream_timeout por separado. max_retries controla los reintentos del cliente:

short = client.with_options(timeout=5.0, max_retries=2)agent = short.agents.create(model="composer-2.5", local=LocalAgentOptions(cwd="."))

Equivalente asíncrono:

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="."))

Envío de mensajes

Cada agent.send() devuelve un Run. Cada await async_agent.send() devuelve un AsyncRun. El agente conserva el contexto de la conversación entre ejecuciones; una ejecución es la unidad de trabajo de una instrucción.

print(agent.send("Find the bug in src/auth.py").text())# El mismo agente conserva todo el contexto de la conversación.print(agent.send("Fix it and add a regression test").text())

Equivalente asíncrono:

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())

Para enviar imágenes junto con texto:

run = agent.send(    {        "text": "What's in this screenshot?",        "images": [{"data": base64_png, "mime_type": "image/png"}],    })

También puedes usar dataclasses auxiliares. SDKImage.from_file(path) lee el archivo del disco y se encarga de codificarlo en 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) y SDKImage.url_image(url) también están disponibles para quienes ya tengan bytes codificados o una URL remota.

Ejecución

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  # acumulado; propiedad del identificador activo    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() es un alias de run.messages(). Al iterar directamente sobre run, se obtienen envelopes de RunStreamEvent, igual que con run.events().

AsyncRun expone los mismos campos de estado, incluido usage. Los métodos que realizan operaciones de E/S son 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() y async for event in run.observe().

Flujo

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 flujo de ejecución solo se puede consumir una vez. run.messages(), run.events() y run.iter_text() extraen datos del mismo flujo subyacente y lo avanzan. Cuando el flujo finaliza, la ejecución contiene el resultado final (run.result, run.status, run.usage, run.git, ...). Llame a run.wait() para agotar los eventos restantes y devolver el RunResult tipado.

Esperar sin transmitir en flujo

result = run.wait()print(result.status)       # "finalizado" | "error" | "cancelado" | "vencido"print(result.result)       # texto final generado por el asistente, si lo hayprint(result.model)        # ModelSelection resuelto utilizado en esta ejecuciónprint(result.duration_ms)print(result.usage)        # TokenUsage acumulado, o None si no está disponibleprint(result.git)          # RunGitInfo en la nube

Equivalente asíncrono:

result = await run.wait()

Consumo de tokens

Las ejecuciones informan del consumo de tokens cuando el entorno de ejecución lo proporciona. Lee el total acumulado en run.usage del handle activo (durante el streaming o después de wait()), o en result.usage del RunResult que devuelve run.wait(). Ambos contienen un TokenUsage que suma el consumo de todos los turnos que lo informaron y tienen el valor None cuando ningún turno lo hizo; por ejemplo, en una ejecución cancelada que no terminó ningún turno, un entorno de ejecución que no muestra el consumo o una instantánea de Cloud desvinculada que aún no ha sincronizado el consumo.

@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
CampoDescripción
input_tokensTokens de instrucción enviados al modelo.
output_tokensTokens generados por el modelo.
cache_read_tokensTokens obtenidos de la caché de instrucciones.
cache_write_tokensTokens escritos en la caché de instrucciones.
total_tokensinput_tokens + output_tokens + cache_read_tokens + cache_write_tokens. No incluye reasoning_tokens.
reasoning_tokensTokens de razonamiento, un subconjunto de output_tokens. None si el modelo o el entorno de ejecución no los informó.
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 ya se contabiliza dentro de output_tokens, por lo que total_tokens lo excluye para evitar contabilizarlo dos veces.

Para obtener cifras por interacción a medida que se transmiten, maneja el evento de flujo stream event usage (SDKUsageMessage). Se emite una vez al final de cada interacción que informó consumo y contiene el TokenUsage de esa interacción. run.usage y result.usage se mantienen acumulados durante toda la ejecución. Tras las interacciones del flujo, el handle prefiere esos totales acumulados; de lo contrario, usa el consumo de wait() o de una instantánea de get_run / list_runs cuando el bridge lo proporciona.

for message in run.messages():    if message.type == "usage":        print(f"turn used {message.usage.total_tokens} tokens")# O después de wait(), sin consumir los mensajes manualmente:result = run.wait()print(run.usage, result.usage)

Equivalente asíncrono: async for message in run.messages() y await run.wait(). run.usage sigue siendo una propiedad síncrona de AsyncRun.

TokenUsage se exporta desde cursor_sdk (junto con to_token_usage / sum_token_usage para invocadores avanzados). El JSON en la conexión usa camelCase (inputTokens, …); las dataclasses de Python usan snake_case.

Los recuentos de tokens son los que informa el tiempo de ejecución; no indican nada sobre el coste. Para consultar el consumo facturado y el coste en dólares de las ejecuciones de un agente, llama a agent.get_usage().

Lectura de la salida de texto

iter_text() proporciona el texto del asistente a medida que se transmite. text() devuelve el texto final del terminal y espera en wait() si la ejecución aún está en curso.

for chunk in run.iter_text():    print(chunk, end="")final_text = run.text()

Equivalente asíncrono:

async for chunk in run.iter_text():    print(chunk, end="")final_text = await run.text()

Cancelar una ejecución

run.cancel()

Equivalente asíncrono:

await run.cancel()

run.cancel() solicita la cancelación de una ejecución activa. El estado cambia a "cancelled", se detiene el flujo en tiempo real, se detienen las llamadas a herramientas en curso y run.wait() se resuelve con status: "cancelled". La salida parcial (el texto del asistente generado hasta ese momento) permanece en el objeto Run.

Cancelar una ejecución que ya está en un estado terminal ("finished", "error", "cancelled", "expired") genera UnsupportedRunOperationError. En caso de duda, comprueba run.status:

if run.status == "running":    run.cancel()

Consultar el estado de ejecución

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()  # eliminar el detectorturns = run.conversation()

run.conversation() devuelve una list[ConversationTurn] tipada. Úsala para mostrar o conservar el historial estructurado sin suscribirte al flujo en tiempo real. run.conversation_json() devuelve la cadena JSON sin procesar.

Para ejecuciones asíncronas, usa await run.conversation() y await run.conversation_json().

Anulación de modelo por ejecución

El model que pasas a agent.send() reemplaza la selección del agente para esa ejecución y permanece activo: los envíos posteriores sin una anulación seguirán usando el nuevo modelo. Para volver al anterior, pasa otra anulación de model o consulta la selección actual en 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 y result.model reflejan el modelo seleccionado para esta ejecución y son inmutables una vez iniciada.

Variables de entorno por ejecución

Los agentes en la nube también pueden recibir variables de entorno para una única ejecución. Pasa cloud.env_vars en SendOptions; los valores se inyectan en el shell del agente solo durante esa ejecución. Al finalizar, se eliminan de la VM y no estarán disponibles en la siguiente ejecución. Esto resulta adecuado para credenciales que rotan entre interacciones, como un token de implementación de corta duración que emites justo antes de pedirle al agente que lo use.

from cursor_sdk import CloudSendOptions, SendOptionsrun = agent.send(    "Deploy the preview environment",    SendOptions(        cloud=CloudSendOptions(env_vars={"DEPLOY_TOKEN": mint_short_lived_token()}),    ),)

Si una variable con ámbito de ejecución tiene el mismo nombre que una variable con ámbito de agente de env_vars en CloudAgentOptions, el valor con ámbito de ejecución prevalece en esa ejecución y el valor con ámbito de agente vuelve a aplicarse en la siguiente.

Las variables por ejecución también funcionan en el primer envío. El SDK las pasa al crear el agente, con ámbito de la ejecución inicial, por lo que no se conservan en el agente. Al igual que las variables con ámbito de agente, se cifran en reposo y sus nombres no pueden comenzar con CURSOR_.

Las variables de entorno por ejecución solo están disponibles para agentes en la nube y no para agentes que se ejecutan en repositorios públicos. En el caso de los agentes locales, el proceso del agente hereda tu propio entorno, así que establece las variables en el proceso antes de llamar a send().

Modo de conversación

Pasa mode="plan" o mode="agent" para controlar si una ejecución primero explora y planifica, o implementa los cambios directamente. Consulta el modo Plan para ver qué hace este modo en el producto.

Establece mode en las AgentOptions que se pasan a Agent.create() para definir la ejecución inicial. En las llamadas posteriores a agent.send(), omite mode para mantener el modo actual de la conversación o pásalo para cambiarlo solo en esa ejecución.

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()

Flujo de deltas sin procesar

Pasa los callbacks on_delta y on_step en SendOptions para obtener actualizaciones de bajo nivel. Los callbacks síncronos se ejecutan en línea. Los callbacks asíncronos pueden ser síncronos o asíncronos; los valores de retorno esperables se esperan antes de procesar el siguiente evento.

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()

Las subclases concretas de update y step se encuentran en cursor_sdk.events:

from cursor_sdk.events import TextDeltaUpdate, ToolCallStartedUpdateif isinstance(update, TextDeltaUpdate):    print(update.text)

Siguen pudiendo importarse desde cursor_sdk por compatibilidad con versiones anteriores, pero el código nuevo debe importarlos desde cursor_sdk.events.

SendOptions

PropiedadTipoDescripción
modelstr | ModelSelection | Mapping[str, Any]Anulación de modelo por envío. Si se omite, usa agent.model. Se mantiene tras un envío correcto.
mode"agent" | "plan"Anulación del modo de conversación por envío. Si se omite en los mensajes de seguimiento, se conserva el modo actual de la conversación.
mcp_serversMapping[str, McpServerConfig]Definiciones de servidores MCP en línea. Reemplaza por completo los servidores configurados al crear esta ejecución.
cloud.env_varsMapping[str, str]Solo para agentes en la nube. Variables de entorno por ejecución inyectadas para esta ejecución y eliminadas cuando finaliza. Anula las env_vars con alcance de agente del mismo nombre solo para esta ejecución.
local.forceboolSolo para agentes locales. El valor predeterminado es None (sin establecer). Establece True para expirar una ejecución activa bloqueada antes de iniciar este mensaje. Cloud devuelve 409 agent_busy en el servidor, por lo que no se necesita un equivalente.
idempotency_keystrClave de idempotencia opcional generada por el cliente para el envío.
on_stepCallable[[ConversationStep], Any]Callback después de cada paso de conversación completado (texto, razonamiento o lote de herramientas).
on_deltaCallable[[InteractionUpdate], Any]Callback por cada InteractionUpdate sin procesar.

Las siguientes tres secciones son referencias detalladas de SDKMessage, InteractionUpdate y ConversationTurn. Revísalas rápidamente u omítelas en la primera lectura; Reanudar agente de programación retoma la explicación.

Eventos de flujo

run.messages() devuelve dataclasses de mensajes del SDK tipados. Diferéncielos según message.type. Todos los mensajes incluyen agent_id y run_id cuando el runtime los proporciona.

SDKMessage = (    SDKSystemMessage    | SDKUserMessageEvent    | SDKAssistantMessage    | SDKThinkingMessage    | SDKToolUseMessage    | SDKStatusMessage    | SDKTaskMessage    | SDKRequestMessage    | SDKUsageMessage    | Mapping[str, Any])
typeClase de datosCampos clave
"system"SDKSystemMessagesubtype, model, tools
"user"SDKUserMessageEventmessage.content
"assistant"SDKAssistantMessagemessage.content con valores TextBlock y 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 se emite dos veces en la mayoría de las llamadas a herramientas: primero con status="running" y args rellenado y, al completarse, de nuevo con status="completed" (o "error") y result rellenado. truncated indica si el SDK truncó args o result porque el payload era demasiado grande.

SDKUsageMessage se emite una vez al final de cada interacción que registró consumo de tokens e incluye el TokenUsage de esa interacción. El total acumulado de todas las interacciones se mantiene en run.usage y result.usage. Consulte Consumo de tokens.

@dataclass(frozen=True)class SDKUsageMessage:    type: Literal["usage"]    agent_id: str    run_id: str    usage: TokenUsage

Los datos del resultado (texto final, modelo, duración, consumo acumulado de tokens y metadatos de Git) están en el objeto Run cuando finaliza el flujo. Usa run.wait() para consultarlos, incluido result.usage si el entorno de ejecución lo informó.

El esquema de las llamadas a herramientas no es estable. Los payloads de args y result de los eventos tool_call reflejan la estructura interna de cada herramienta y pueden cambiar a medida que estas evolucionan. Los nombres de las herramientas también pueden cambiar o sustituirse. Trata args y result como datos sin tipo y analízalos de forma defensiva. El contenedor del evento (type, call_id, name, status) es estable.

run.events() genera contenedores RunStreamEvent de nivel inferior. Úsalo cuando necesites desplazamientos, contenedores de resultados finales o actualizaciones de interacción sin procesar:

for event in run.events():    print(event.kind, event.offset)

Actualizaciones de interacción

InteractionUpdate es el tipo delta sin procesar que se pasa al callback on_delta de agent.send(). Las actualizaciones son más detalladas que los eventos SDKMessage: el texto se transmite token por token y las llamadas a herramientas informan de estados parciales a medida que se acumulan los args.

InteractionUpdate = (    TextDeltaUpdate    | ThinkingDeltaUpdate    | ThinkingCompletedUpdate    | ToolCallStartedUpdate    | ToolCallCompletedUpdate    | PartialToolCallUpdate    | TokenDeltaUpdate    | StepStartedUpdate    | StepCompletedUpdate    | TurnEndedUpdate    | UserMessageAppendedUpdate    | SummaryUpdate    | SummaryStartedUpdate    | SummaryCompletedUpdate    | ShellOutputDeltaUpdate    | UnknownInteractionUpdate    | Mapping[str, Any])

PartialToolCallUpdate se emite mientras el modelo transmite argumentos a una llamada a herramienta antes de confirmarla. La misma advertencia sobre estabilidad aplicable a SDKToolUseMessage.args también se aplica aquí.

Tipos de conversación

La vista estructurada por turno de una ejecución, devuelta por run.conversation(). Cada elemento es un contenedor que incluye el discriminador type del turno junto con el payload tipado en 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])

Distingue por turn.type y lee el payload mediante 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() en los callbacks de on_step se invoca por cada ConversationStep, no por turno. Los pasos de conversación de llamadas a herramientas incluyen un payload Mapping[str, Any]. Trata los detalles del payload de las llamadas a herramientas como datos no tipados; consulta la nota sobre estabilidad en Eventos de flujo.

Reanudar agentes de programación

Agent.resume(    agent_id: str,    options: AgentOptions | Mapping[str, Any] | None = None,    *,    client: CursorClient | None = None,) -> Agent

Usa Agent.resume() o client.agents.resume() para volver a adjuntarte a un agente existente mediante su ID. Flujos habituales: reconectarte a un agente en la nube de larga duración iniciado anteriormente o continuar una conversación tras reiniciar el proceso local. El entorno de ejecución se detecta automáticamente a partir del prefijo del ID (bc- corresponde a la nube; cualquier otro, a local).

agent = Agent.resume("bc-abc123")run = agent.send("Also update the changelog")run.wait()

Equivalente asíncrono:

agent = await client.agents.resume("bc-abc123")run = await agent.send("Also update the changelog")await run.wait()

agent.model es None al reanudar, a menos que vuelvas a pasar model. Los servidores MCP en línea no persisten al reanudar; suelen contener secretos y solo existen en memoria. Vuelve a pasarlos al reanudar o usa una configuración de MCP basada en archivos (.cursor/mcp.json y local.setting_sources) para los servidores que deban persistir.

Persistencia local

Los agentes locales conservan el estado de la conversación y los metadatos de ejecución mediante el bridge, de modo que los mensajes de seguimiento y Agent.resume() se mantienen tras reiniciar el proceso. De forma predeterminada, el bridge los guarda en disco, en una raíz de estado independiente para cada espacio de trabajo. Los agentes en la nube conservan los datos del lado del servidor, por lo que al reanudar un agente en la nube desde cualquier lugar se recupera la misma conversación.

La persistencia local se limita al espacio de trabajo. Cuando el bridge se ejecuta como proceso auxiliar o subproceso de larga duración, asígnale el mismo espacio de trabajo que al agente para que las llamadas locales a lista, obtención y reanudación resuelvan los agentes correctos. Establécelo una vez en el cliente y pasa cwd a las llamadas locales de lista y obtención:

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")

Consultar agentes de programación y ejecuciones

Usa CursorClient para las API de listado, obtención y paginación.

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)

Equivalente asíncrono:

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)

Usa agent.list_messages() con una referencia al agente para consultar el historial de mensajes. Agent.messages.list(agent_id) ofrece acceso simplificado mediante atributos tipados a la misma llamada cuando solo dispones de un ID.

Usa Agent.get_run(run_id) o client.agents.get_run(run_id) para obtener una ejecución sin una referencia al agente. Cancélala con Agent.cancel_run(run_id, agent_id=...) o client.agents.cancel_run(run_id, agent_id=...). Los métodos del cliente asíncrono son awaitables y usan los mismos argumentos.

AgentMessage es distinto de un SDKMessage transmitido en flujo:

@dataclass(frozen=True)class AgentMessage:    type: str    uuid: str    agent_id: str    message: Any = None

Los endpoints de lista devuelven ListResult[T]. Usa .items y .next_cursor directamente, recorre la página actual con for item in page o todas las páginas con .auto_paging_iter(). Los endpoints de lista asíncronos devuelven AsyncListResult[T]; async for item in page recorre la página actual y async for item in page.auto_paging_iter() recorre todas las páginas del conjunto de resultados.

SDKAgentInfo

La estructura de los metadatos que devuelven Agent.list(), Agent.get(), client.agents.list() y 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] = {}  # de CloudAgentOptions.metadata; vacío para agentes locales

Ciclo de vida de los agentes en la nube

Los agentes en la nube permanecen en el espacio de trabajo de tu equipo hasta que los archives o elimines. client.agents.list(runtime="cloud") oculta los agentes archivados de forma predeterminada; pasa include_archived=True para verlos. Filtra por pr_url para encontrar el agente que abrió una solicitud de extracción concreta.

# Por ID, no se necesita una referencia al agente:Agent.archive(agent_id)Agent.unarchive(agent_id)Agent.delete(agent_id)# Mediante un cliente explícito:client.agents.archive(agent_id)client.agents.unarchive(agent_id)client.agents.delete(agent_id)# En una referencia a un agente existente:agent.archive()agent.unarchive()agent.delete()

archive elimina el agente de forma reversible para que la transcripción siga siendo legible. unarchive lo restaura. delete es permanente; las lecturas posteriores devuelven NotFoundError.

Los métodos async del ciclo de vida tienen los mismos nombres y se pueden usar con await.

agent.get_usage()

Obtiene el consumo facturado de tokens y el coste en dólares de las ejecuciones de un agente de programación. Los agentes en la nube devuelven un desglose por ejecución; los agentes locales, un desglose por turno. Pasa run_id para limitar el resultado a una sola entrada: para los agentes en la nube, un ID de ejecución run-<uuid>; para los agentes locales, un ID obtenido de un get_usage().runs[].run_id anterior.

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              # suma de todas las ejecuciones    runs: Sequence[RunUsage] = ()    cost: UsageCost | None = None  # suma de todas las ejecuciones@dataclass(frozen=True)class RunUsage:    run_id: str    usage: TokenUsage    cost: UsageCost | None = None@dataclass(frozen=True)class UsageCost:    raw_cost_cents: float  # coste de tokens del modelo sin descuentos; 0 para consumo con precio por solicitud    charged_cents: float   # importe cobrado, incluidos los descuentos y la tasa de tokens de Cursor

El coste incluye descuentos y puede tardar un momento en actualizarse tras finalizar una ejecución; cost es None hasta entonces. charged_cents es 0.0 para el consumo incluido en el plan, BYOK (Bring Your Own Key) y el consumo cubierto por créditos concedidos.

Esta es una vista distinta de Consumo de tokens: run.usage muestra el recuento de tokens en tiempo real de una ejecución, mientras que get_usage() muestra el registro facturado de todas las ejecuciones del agente de programación. En los agentes asíncronos, await agent.get_usage() devuelve lo mismo. AgentUsage, RunUsage y UsageCost se exportan desde cursor_sdk.

El espacio de nombres de Cursor

Permite consultar la cuenta y el catálogo. Los métodos de sincronización aceptan el parámetro opcional api_key; de lo contrario, usan CURSOR_API_KEY.

from cursor_sdk import Cursorme = Cursor.me()models = Cursor.models.list()repositories = Cursor.repositories.list()

Equivalente con cliente explícito:

me = client.me()models = client.models.list()repositories = client.repositories.list()

Equivalente asíncrono:

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() devuelve un SDKUser con los campos api_key_name, created_at y los campos opcionales user_id, user_email, user_first_name y user_last_name.

Usa Cursor.models.list() para consultar los ID de modelo válidos y los parámetros de cada modelo antes de llamar a Agent.create() o agent.send(). Los parámetros son específicos de cada modelo. Algunos ejemplos habituales son el esfuerzo de razonamiento y optimize_for de Cursor Router en auto-smart.

El catálogo es específico de la cuenta y el equipo. Cursor Router solo aparece como auto-smart cuando Router está disponible para el equipo asociado a la clave de API. Consulta 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"),#       ),#   ),# ]

Las variants preestablecidas de cada SDKModel ya contienen params válidos, por lo que puedes copiarlos en una ModelSelection.

Prefiere una selección explícita del Router (auto-smart + optimize_for) si falta un modelo de destino y quieres priorizar el Coste, el Equilibrio o la Inteligencia. Recurre a ModelSelection(id="auto") solo si quieres que el servidor seleccione Auto sin elegir un modo del Router. Para Cursor Router, especifica siempre optimize_for explícitamente.

Cursor.repositories.list() devuelve los repositorios SCM (GitHub, GitLab, Bitbucket o Azure DevOps, según lo que esté conectado) disponibles para los agentes en la nube de la cuenta o el equipo que realiza la llamada. Cada elemento expone una url. Úsalas para rellenar CloudAgentOptions.repos.

Servidores MCP

Los agentes de programación pueden obtener servidores MCP a partir de definiciones en línea, ajustes del proyecto o del usuario, plugins y configuración gestionada desde el Panel de control, según el entorno de ejecución.

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", "."],            ),        },    ))

También se aceptan diccionarios planos ({"type": "http", "url": ...} y {"type": "stdio", "command": ...}) para crear scripts rápidamente.

Qué se carga

Los agentes locales cargan servidores de hasta cinco fuentes. Si hay nombres en conflicto, prevalece la primera coincidencia:

  1. mcp_servers en agent.send(). Sustituye por completo los servidores configurados al crear el agente para esa ejecución (no se fusionan).
  2. mcp_servers en Agent.create(). Se usa cuando no se proporciona una anulación por envío.
  3. Servidores de plugins, si local.setting_sources incluye "plugins".
  4. Servidores del proyecto de .cursor/mcp.json, si local.setting_sources incluye "project".
  5. Servidores del usuario de ~/.cursor/mcp.json, si local.setting_sources incluye "user".

Sin local.setting_sources, solo se cargan los servidores en línea. Si un servidor MCP local requiere iniciar sesión con OAuth, el SDK puede reutilizar un inicio de sesión guardado en la app de Cursor, pero no puede abrir un navegador para iniciar sesión.

Los agentes en la nube cargan servidores desde:

  1. mcp_servers en agent.send(). Sustituye por completo los servidores configurados al crear el agente para esa ejecución (no se fusionan).
  2. mcp_servers en Agent.create(). Se usa cuando no se proporciona una anulación por envío.
  3. Tus servidores MCP de usuario y de equipo desde cursor.com/agents.

Si un servidor en línea no incluye auth ni headers y autorizaste previamente la URL de ese servidor en cursor.com/agents, las ejecuciones autenticadas con un token de API personal reutilizan automáticamente esos tokens de OAuth. Las claves de API de cuentas de servicio no pueden recurrir a la autenticación de usuario, ya que no están asociadas a ningún usuario.

local.setting_sources no se aplica a los agentes en la nube.

Nube

Los agentes en la nube también aceptan configuraciones de MCP autenticadas insertadas directamente. MCP en la nube es compatible con los transportes HTTP y stdio. Usa los headers HTTP para claves de API estáticas o tokens Bearer. Usa auth de HTTP para servidores protegidos con OAuth. Usa env de stdio cuando el servidor se ejecuta dentro de la VM en la nube y obtiene las credenciales de variables de entorno.

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"},            ),        },    ))
  • Los headers HTTP y la auth los gestiona el backend de Cursor. Los campos confidenciales se ocultan y no llegan a la VM.
  • Los valores de env de Stdio se pasan a la VM porque el servidor se ejecuta allí. Trátalos como cualquier otro secreto de tiempo de ejecución.
  • OAuth para servidores MCP configurados en cursor.com/agents se mantiene por usuario, incluso en servidores a nivel de equipo.

Consulta MCP para ver el formato completo de configuración y capacidades del agente en la nube para conocer el comportamiento específico de la nube.

Subagentes

Define subagentes con nombre que el agente principal puede iniciar mediante la herramienta Agent. Pásalos directamente:

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.",            ),        },    ))

También se detectan los subagentes confirmados en el repositorio en .cursor/agents/*.md (con frontmatter de name, description y model opcional). Las definiciones inline tienen prioridad sobre las basadas en archivos con el mismo nombre.

Subagentes anidados

Los subagentes pueden crear sus propios subagentes, dentro de un límite de anidamiento. Cuando un subagente usa la herramienta Agent, accede al mismo ejecutor de subagentes que el agente principal, por lo que este puede delegar en un subagente que, a su vez, delega en otro. Cada nivel ve el mismo conjunto de subagentes con nombre. El agente de nivel superior y sus subagentes directos pueden iniciar subagentes, pero un subagente iniciado por otro subagente no puede iniciar otros.

Restringir el conjunto de herramientas

tools crea una lista de permitidos de las herramientas integradas disponibles para el modelo; disallowed_tools elimina herramientas y conserva las demás, incluidas las añadidas a la plataforma después del lanzamiento de tu versión del SDK. Por ahora, ambas opciones solo están disponibles para agentes locales y ninguna se conserva en el agente: vuelve a pasarlas al reanudar para mantener la restricción.

from cursor_sdk import Agent, AgentOptions, LocalAgentOptions# Agente de solo lectura: solo dispone de estas herramientas.reader = Agent.create(    AgentOptions(        model="composer-2.5",        tools=["read", "grep", "glob", "ls"],        local=LocalAgentOptions(cwd="."),    ))# Todo excepto el acceso a la shell.no_shell = Agent.create(    AgentOptions(        model="composer-2.5",        disallowed_tools=["shell"],        local=LocalAgentOptions(cwd="."),    ))
  • Si se omite tools, se ofrece el conjunto de herramientas estándar para el modelo seleccionado; tools=[] no ofrece herramientas integradas, por lo que el modelo solo puede responder con texto.
  • Ambos campos aceptan nombres públicos ("read", "edit", "task", "webSearch", ...) y los grupos de funcionalidades "shell" y "mcp". Los nombres desconocidos generan un BadRequestError al crear la solicitud.
  • La denegación prevalece: para ofrecerse, una herramienta debe estar en tools (si se ha establecido) y no estar en disallowed_tools.
  • Deshabilitar "mcp" también elimina las herramientas personalizadas. Deshabilitar "task" impide los subagentes; de lo contrario, los subagentes conservan sus propios conjuntos de herramientas seleccionados.

Herramientas personalizadas

Las herramientas personalizadas permiten exponer funciones de Python a los agentes locales sin necesidad de configurar un servidor MCP independiente. Pásalas mediante 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 recibe los argumentos analizados y un CustomToolContext con tool_call_id cuando está disponible. Puede devolver una cadena, un valor compatible con JSON o un mapeo con una lista de content. Las herramientas personalizadas solo están disponibles para agentes locales.

Hooks

Los hooks solo se configuran mediante archivos. No hay callbacks de hooks programáticos. Los hooks establecen límites de políticas a nivel de proyecto, no son una opción por ejecución.

  • Local: Añade .cursor/hooks.json al repositorio indicado en local.cwd o añade ~/.cursor/hooks.json para hooks de nivel de usuario.
  • Cloud: Confirma .cursor/hooks.json y sus scripts en el repositorio indicado en cloud.repos. Los agentes en la nube creados mediante el SDK cargan automáticamente los hooks del proyecto. En los planes Enterprise, también ejecutan hooks de equipo y hooks gestionados por la empresa.

Consulta Hooks para ver el formato de configuración y compatibilidad de hooks de Cloud Agents para conocer el comportamiento en la nube.

Artefactos

Enumera y descarga archivos del espacio de trabajo del agente.

@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)# Descarga un solo artefacto al disco.content = agent.download_artifact(artifacts[0].path)Path("review.md").write_bytes(content)

Los agentes asíncronos exponen await agent.list_artifacts() y await agent.download_artifact(path).

La compatibilidad con artefactos depende del entorno de ejecución. Los agentes locales del SDK devuelven una lista vacía mediante list_artifacts() y producen una excepción mediante download_artifact().

Gestión de recursos

Cierra siempre los agentes al terminar. El patrón de sincronización más limpio es un administrador de contexto:

from cursor_sdk import Agent, LocalAgentOptionswith Agent.create(model="composer-2.5", local=LocalAgentOptions(cwd=".")) as agent:    agent.send("Summarize the repository").wait()

Para liberar recursos explícitamente:

agent.close()

Los agentes y clientes asíncronos admiten administradores de contexto asíncronos y la limpieza con 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()

Para liberar recursos explícitamente:

await agent.close()await client.aclose()

El cliente sync predeterminado del módulo se cierra automáticamente al finalizar el proceso. Los procesos de larga duración pueden cerrarlo y restablecerlo explícitamente:

from cursor_sdk import close_default_clientclose_default_client()

Referencia de configuración

El SDK de Python admite dataclasses auxiliares y diccionarios sin procesar. Las dataclasses usan campos de Python en snake_case y se recomiendan para el código de la aplicación.

AgentOptions

PropiedadTipoPredeterminadoDescripción
modelstr | ModelSelection | Mapping[str, Any]Obligatorio para local; en la nube se usa el valor predeterminado resuelto por el servidorModelo que se usará. Consulta ModelSelection.
api_keystrVariable de entorno CURSOR_API_KEYClave de API de usuario o clave de cuenta de servicio. Las claves de administrador de equipo aún no son compatibles.
namestrGenerado automáticamenteNombre legible del agente mostrado en client.agents.list() / client.agents.get().
localLocalAgentOptions | Mapping[str, Any]NoneConfiguración del agente local. Úsala para crear un agente local.
cloudCloudAgentOptions | Mapping[str, Any]NoneConfiguración del agente en la nube. Úsala para crear un agente en la nube.
mcp_serversMapping[str, McpServerConfig]NoneDefiniciones de servidores MCP en línea.
agentsMapping[str, AgentDefinition | Mapping[str, Any]]NoneDefiniciones de subagentes.
toolsSequence[str]Conjunto de herramientas predeterminadoSolo se ofrecen al modelo las herramientas integradas indicadas. [] significa que no hay herramientas integradas; el modelo solo puede responder con texto. Solo para agentes locales.
disallowed_toolsSequence[str]NoneElimina las herramientas integradas indicadas; las demás siguen disponibles. Deny prevalece cuando se combina con tools. Solo para agentes locales.
agent_idstrGenerado automáticamenteID de agente persistente. Úsalo para mantener un ID estable entre invocaciones.
idempotency_keystrGenerado automáticamente para la nubeClave de idempotencia opcional generada por el cliente. Solo para la nube.
mode"agent" | "plan"NoneModo de conversación inicial para la primera ejecución del agente. Si se omite, el servidor se inicia en modo agente. Consulta Modo de conversación.

LocalAgentOptions

PropiedadTipoPredeterminadoDescripción
cwdstr | os.PathLikeNoneDirectorio de trabajo principal. No se admiten listas con varias entradas; usa dirs para varias raíces.
dirsSequence[str | os.PathLike]NoneCarpetas adicionales del espacio de trabajo para configuraciones con varias raíces. Se combina con cwd para cargar las reglas, skills y el contexto del espacio de trabajo desde cada ruta.
setting_sourcesSequence[SettingSource]NoneCapas de ajustes disponibles: "project", "user", "team", "mdm", "plugins" o "all".
sandbox_optionsSandboxOptions | Mapping[str, Any]NoneOpciones del sandbox local.
storeLocalAgentStoreConfig | Mapping[str, Any]NoneConfiguración del store local que se pasa al bridge.
auto_reviewboolNoneEnruta las llamadas a herramientas locales a través de Auto-review si el backend conectado es compatible.
custom_toolsMapping[str, CustomTool | Mapping[str, Any]]NoneHerramientas personalizadas expuestas a los agentes locales.

CloudAgentOptions

PropiedadTipoPredeterminadoDescripción
envCloudEnvironment | Mapping[str, Any]NoneEntorno de ejecución. Si se omite, el servidor usa VM en la nube alojadas por Cursor. pool y machine se dirigen a workers autohospedados que ejecutas.
reposSequence[CloudRepository | Mapping[str, Any]]NoneRepositorios que se clonarán en la VM. Omite o pasa [] para un agente sin repositorio con un espacio de trabajo vacío. Pasa pr_url en un repositorio para asociar el agente a un PR existente.
work_on_current_branchboolNoneEnvía los commits a la rama existente en lugar de crear una nueva. El servidor interpreta un valor omitido como False.
auto_create_prboolNoneAbre un PR cuando finaliza la ejecución. El servidor interpreta un valor omitido como False.
open_as_cursor_github_appboolTrue para claves de cuenta de servicio, False para claves de usuarioAbre los PR como la aplicación de GitHub de Cursor en lugar de como el propietario de la clave de API. El valor resuelto se devuelve al crear, obtener y listar.
skip_reviewer_requestboolNoneOmite solicitar que el usuario que realiza la llamada sea revisor del PR. El servidor interpreta un valor omitido como False.
env_varsMapping[str, str]NoneVariables de entorno con alcance de sesión para agentes en la nube.
metadataMapping[str, str]NoneEtiquetas de cadena del llamador persistidas en el agente en la nube. Consulta Metadatos del agente.

AgentDefinition

PropiedadTipoPredeterminadoDescripción
descriptionstrobligatorioCuándo usar este subagente. Se muestra al agente principal para que sepa cuándo iniciarlo.
promptstrobligatorioInstrucción del sistema para el subagente.
modelstr | ModelSelection | Mapping[str, Any] | "inherit"NoneAnulación de modelo. Tanto None como "inherit" usan la selección del agente principal.
mcp_serversSequence[str | AgentDefinitionMcpServer | Mapping[str, Any]]NoneServidores MCP disponibles para este subagente. Los nombres hacen referencia a servidores de mcp_servers del agente principal.

Herramienta personalizada

@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 es el identificador del modelo (por ejemplo, "composer-2.5" o "auto-smart"). params incluye parámetros específicos del modelo, como el esfuerzo de razonamiento o optimize_for de Router. Usa Cursor.models.list() para consultar los ID válidos, las definiciones de parámetros y las variantes predefinidas disponibles para tu cuenta. Consulta Cursor Router para conocer el contrato de selección de 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  # solo Local; Cloud rechaza este campo@dataclass(frozen=True)class McpAuth:    client_id: str    client_secret: str | None = None    scopes: Sequence[str] = ()

En los servidores HTTP que se ejecutan en la nube, el backend de Cursor gestiona headers y auth. Los campos confidenciales se ocultan antes de que la VM los procese. En los servidores stdio en la nube, los valores de env se pasan a la VM (trátalos como cualquier otro secreto de tiempo de ejecución).

UserMessage

@dataclass(frozen=True)class UserMessage:    text: str    images: Sequence[SDKImage | Mapping[str, Any]] | None = None

La forma estructurada del argumento message de agent.send(). Úsala para enviar imágenes junto con texto.

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

Pasa una url remota o datos data codificados en base64 con un mime_type. from_data() acepta bytes o una cadena codificada en base64. from_file() lee un archivo del disco y lo codifica en base64.

SettingSource

SettingSource está disponible en cursor_sdk.types.

from cursor_sdk.types import SettingSource

Controla qué capas de ajustes almacenados en disco carga un agente local. Los agentes en la nube siempre cargan project, team y plugins e ignoran este campo.

ValorOrigen
"project".cursor/ en el espacio de trabajo
"user"~/.cursor/
"team"Ajustes de equipo sincronizados desde el panel de control
"mdm"Ajustes empresariales gestionados mediante MDM
"plugins"Ajustes proporcionados por plugins
"all"Abreviatura de todo lo anterior

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]: ...

Devuelto por client.agents.list(), client.agents.list_runs() y Agent.list(). next_cursor está vacío cuando no hay más páginas. Los endpoints de lista asíncronos devuelven AsyncListResult[T] con equivalentes esperables.

Errores

Todos los errores del SDK heredan de CursorAgentError. CursorSDKError es el alias de raíz compatible con versiones anteriores para clientes antiguos. Usa is_retryable y retry_after para implementar la lógica de reintento.

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
ErrorCuándo
AuthenticationErrorClave de API no válida o sesión no iniciada.
PermissionDeniedErrorEl cliente autenticado no tiene permiso para realizar la operación solicitada.
RateLimitErrorDemasiadas solicitudes o se superaron los límites de consumo.
ConfigurationErrorModelo no válido, falta la configuración obligatoria o parámetros de solicitud incorrectos.
AgentBusyErrorSe envía un seguimiento mientras el agente ya tiene una ejecución en estado CREATING o RUNNING (HTTP 409, código agent_busy).
BadRequestErrorLa solicitud tiene un formato incorrecto.
IntegrationNotConnectedErrorSe crea un agente en la nube para un repositorio cuyo proveedor de SCM no está conectado.
NetworkErrorServicio no disponible o error de red.
APITimeoutErrorSe agotó el tiempo de espera de la solicitud.
InternalServerErrorEl servicio de Cursor devolvió un error de servidor.
NotFoundErrorNo se encontró el recurso solicitado.
AgentNotFoundErrorEl agente no existe o no es visible en el directorio de trabajo actual.
UnsupportedRunOperationErrorLa operación de ejecución no es compatible con el estado actual de la ejecución.

Reintentos con espera progresiva

is_retryable y retry_after determinan la lógica de reintento del cliente. retry_after es una cadena con formato HTTP (segundos o una fecha HTTP) que proporciona el servidor cuando está definido.

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)

Cada CursorAgentError incluye request_id cuando el servidor proporciona uno. Regístralo siempre que muestres un error para que el equipo de soporte pueda identificar el fallo.

IntegrationNotConnectedError

class IntegrationNotConnectedError(ConfigurationError):    provider: str   # p. ej., "github", "gitlab", "azuredevops"    help_url: str   # enlace del Panel de control para reconectar

Usa help_url para dirigir al usuario al flujo de reconexión correcto. Se pueden añadir nuevos proveedores sin publicar una nueva versión del SDK.

AgentBusyError

Los agentes en la nube solo permiten una ejecución activa a la vez. Se genera AgentBusyError al llamar a agent.send() (o al crear una ejecución de otro modo) mientras otra ejecución del mismo agente aún está en estado CREATING o RUNNING.

is_retryable es False. Si reintentas de inmediato, seguirá fallando hasta que la ejecución activa alcance un estado terminal o la canceles. En cambio, otras respuestas 409, como agent_archived, generan ConfigurationError.

Espera a que finalice la ejecución activa, cancélala con run.cancel() o consulta periódicamente Agent.list_runs() antes de volver a enviar:

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.")

Los agentes locales no generan AgentBusyError. Pase local={"force": True} a send() para finalizar una ejecución local bloqueada antes de iniciar otra.

UnsupportedRunOperationError

class UnsupportedRunOperationError(ConfigurationError):    operation: str

Se produce cuando no se permite una operación de Run en la ejecución actual. El caso más habitual es llamar a run.cancel() en una ejecución que ya ha finalizado.

run.supports(operation) y run.unsupported_reason(operation) indican si el SDK es compatible con un nombre de operación ("stream", "wait", "cancel", "conversation") y no comprueban el estado de la ejecución. Consulta run.status para proteger las llamadas que dependen del estado.

Solución de problemas

Establece CURSOR_SDK_LOG=debug (o info) para añadir un controlador de stderr al registrador del propio SDK. El SDK solo configura su propio registrador cursor_sdk, por lo que esto no interferirá con la configuración de registro de la aplicación anfitriona.

CURSOR_SDK_LOG=debug python my_script.py

El binario de bridge incluido se instala como cursor-sdk-bridge en el PATH junto con el paquete. Ejecútalo directamente para confirmar la compilación distribuida con tu wheel:

cursor-sdk-bridge --help

Limitaciones conocidas

  • Los esquemas de payload de las llamadas a herramientas no están tipados de forma estricta intencionadamente.
  • Los servidores MCP en línea no se conservan entre llamadas a Agent.resume(). Vuelve a incluirlos al reanudar si es necesario.
  • Las herramientas personalizadas (local.custom_tools) y las restricciones del conjunto de herramientas (tools, disallowed_tools) solo están disponibles para agentes locales. Las restricciones no se conservan en el agente; vuelve a incluirlas al reanudar.
  • La descarga de artefactos no está implementada para agentes locales.
  • local.setting_sources (y las rutas de MCP y subagentes basadas en archivos que controla) no se aplica a los agentes en la nube. La nube siempre carga project, team y plugins.
  • Los hooks solo se definen mediante archivos (.cursor/hooks.json). No hay callbacks programáticos.