[Go to site: main page, start]

Skip to main content

Command Palette

Search for a command to run...

SDK

Cursor Python SDK

O pacote cursor-sdk permite chamar o agente do Cursor a partir do seu próprio código em Python. O mesmo agente executado na IDE do Cursor, na CLI e no app Web pode ser programado em Python com clientes síncronos e assíncronos, dataclasses tipadas e iteração comum para streams e páginas. Execute a skill /sdk no Cursor para começar.

Para a REST API, consulte a Cloud Agents API. Para outras linguagens, consulte a Ponte do SDK.

Visão geral

O SDK unifica os tempos de execução local e na nuvem em uma única interface. Você escreve o mesmo código independentemente de onde o agente é executado.

Tempo de execuçãoO que fazQuando usar
LocalExecuta o agente em arquivos locais no disco.Scripts de desenvolvimento e verificações de CI em uma árvore de trabalho.
Cloud (Cursor-hosted)Executa em uma VM isolada com seu repositório clonado. O Cursor executa as VMs.Quando o chamador não tem o repositório, você quer vários agentes em paralelo ou as execuções precisam continuar mesmo que o chamador se desconecte.

Defina o tempo de execução passando local ou cloud para Agent.create().

Autenticação

Defina CURSOR_API_KEY ou forneça api_key antes de criar um agente.

O SDK aceita chaves de API de usuário e de conta de serviço para execuções locais e na nuvem. As chaves de API de administrador da equipe ainda não são compatíveis.

export CURSOR_API_KEY="your-key"

Uso e faturamento

As execuções do SDK seguem as mesmas regras de preço, pools de solicitações e Privacy Mode que as execuções no IDE e pelos Cloud Agents. Os gastos aparecem no dashboard de uso da sua equipe com a tag SDK.

Para obter a contagem de tokens por execução no código, consulte Uso de tokens. Para consultar o uso faturado e o custo em dólares das execuções de um agente, consulte agent.get_usage().

Conceitos principais

ConceitoDescrição
AgenteHandle durável que armazena o estado da conversa, a configuração do espaço de trabalho, a seleção de modelo e outras configurações. Persiste entre vários prompts.
ExecuçãoUm envio de prompt. Tem seu próprio stream, status, resultado, conversa e cancelamento.
SDKMessageMensagem de stream tipada emitida durante uma execução. Tem a mesma estrutura em tempos de execução locais e na nuvem.
CursorClientCliente explícito para controle do ciclo de vida, opções HTTP personalizadas ou vários espaços de trabalho em um único processo. Client é um alias.
AsyncClientCliente equivalente assíncrono. Obrigatório para todas as operações assíncronas.

Instalação

pip install cursor-sdk

Requer Python 3.10 ou superior.

Início 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 stream mostra como extrair o texto do assistente, lidar com chamadas de ferramenta e consultar o estado da execução. Para um prompt único (criar, executar, concluir), consulte Agent.prompt().

Início rápido do Cloud

O Python SDK oferece suporte nativo aos agentes em nuvem do Cursor. Você pode listar os repositórios conectados, iniciar um agente em um deles, aguardar a execução e revisar o 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())

Os agentes em nuvem iniciados pelo SDK não são exibidos na lista de agentes padrão. Para visualizá-los no Cursor Web ou na Janela de Agentes do Cursor, clique em Filtrar > Fonte > SDK.

Uso assíncrono

O cliente assíncrono oferece a mesma interface do cliente síncrono e é recomendado para servidores, bots e orquestração concorrente de agentes. AsyncAgent, AsyncClient, AsyncRun e AsyncCursor são exportados por cursor_sdk e 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())

Não há um cliente padrão assíncrono global. Instancie AsyncClient explicitamente ou use AsyncClient.launch_bridge(...) como gerenciador de contexto assíncrono para que cada loop de eventos tenha seu próprio cliente. Não misture clientes síncronos e assíncronos no mesmo fluxo de código.

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

Criar agentes

Agent.create() valida as opções e retorna um handle imediatamente. Passe local ou cloud para escolher o tempo de execução.

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 é preenchido imediatamente. Agentes locais recebem um ID agent-<uuid>; agentes em nuvem recebem um ID bc-<uuid>. agent.model é um ModelSelection tipado, portanto agent.model.id e agent.model.params funcionam diretamente.

Variáveis de ambiente da sessão

Para agentes em nuvem, passe env_vars quando uma execução precisar de credenciais temporárias ou outros valores que devem ficar disponíveis apenas para esse agente.

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

Esses valores são criptografados quando armazenados, injetados no shell do agente em nuvem e excluídos com o agente. Não é possível usar env_vars com um agent_id fornecido pelo chamador; omita agent_id e leia o ID emitido pelo servidor em agent.agent_id. Os nomes das variáveis não podem começar com CURSOR_.

Para valores que devem existir apenas durante uma única execução, passe-os em agent.send(). Consulte Variáveis de ambiente por execução.

Metadados do agente

Adicione seus próprios identificadores a um agente em nuvem ao criá-lo. Os metadados podem vincular um agente a um usuário, locatário, fluxo de trabalho ou ticket no seu sistema e são retornados em SDKAgentInfo.metadata por client.agents.get() e client.agents.list().

from cursor_sdk import Agent, CloudAgentOptions, CloudRepositorywith Agent.create(    model="composer-2.5",    cloud=CloudAgentOptions(        repos=[CloudRepository(url="https://github.com/your-org/your-repo")],        metadata={            "end_user_id": "user-123",            "ticket_id": "ENG-456",        },    ),) as agent:    print(agent.agent_id)

Os metadados estão disponíveis para agentes em nuvem no momento da criação. É possível anexar até 50 pares de chave-valor. As chaves não podem estar vazias e devem ter no máximo 255 caracteres. Os valores devem ser strings de até 4096 bytes. Valores de string vazia são permitidos, e um mapeamento vazio é tratado como ausência de metadados.

Parâmetros do modelo

Use ModelSelection.params para passar opções específicas de cada modelo, como o esforço de raciocínio ou optimize_for do Cursor Router. Os IDs e valores dos parâmetros variam conforme o modelo. Use Cursor.models.list() para conhecer os parâmetros compatíveis e as variantes predefinidas disponíveis para sua conta.

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

Use Cursor.models.list() para descobrir os IDs dos parâmetros e as variantes predefinidas de um modelo. Consulte o Cursor Router para conhecer o contrato de seleção auto-smart.

Cursor Router

O Cursor Router seleciona um modelo para cada solicitação Auto. No SDK, o Router é o modelo auto-smart, com um parâmetro optimize_for. Ele está disponível nos planos Teams e Enterprise. Os administradores do Enterprise precisam habilitar o Router para a equipe antes que auto-smart apareça no catálogo.

O Cursor SDK é um SDK para agentes, não uma API independente de inferência de modelos ou conclusões de chat. O Router escolhe modelos para execuções de agentes do Cursor capazes de raciocinar sobre um espaço de trabalho, chamar ferramentas, executar comandos e editar arquivos. Atualmente, o Cursor não documenta um endpoint raw do Router para chamadas arbitrárias de modelos.

Selecione Custo, Balance ou Inteligência

Passe auto-smart e defina optimize_for explicitamente:

Rótulo do produtoValor do SDK
Custocost
Balancebalanced
Inteligênciaintelligence

Use Balance nos textos do produto. Use balanced apenas como valor transmitido pelo 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)

Sempre informe optimize_for. Não o omita nem envie um valor default legado; a descoberta pelo catálogo é o contrato compatível.

Conheça o Router no catálogo de modelos

Cursor.models.list() retorna os modelos, as definições de parâmetros e as variantes predefinidas disponíveis para a conta e a equipe associadas à chave de API. O Cursor Router aparece como auto-smart quando está disponível. Administradores da equipe podem desativar o Router ou restringir os modos de otimização que os membros podem selecionar.

Use o catálogo como fonte de referência antes de codificar uma seleção:

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

Alterne os modos por execução

Substitua o modelo em agent.send() para alterar o modo Router em uma execução:

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

As substituições de modelo por Run persistem. Envios posteriores sem substituição continuam usando a nova seleção. Consulte Substituição de modelo por Run.

IDs de modelo: auto-smart, auto e default

SeleçãoSignificado
auto-smart com optimize_forCursor Router. Use quando quiser Custo, Balance ou Inteligência.
ModelSelection(id="auto")Fallback Auto selecionado pelo servidor quando um modelo específico não estiver no catálogo. Prefira auto-smart quando precisar de um modo Router explícito.
Omitir optimize_for ou enviar defaultNão é um contrato do Router compatível. Sempre descubra os valores permitidos e passe cost, balanced ou intelligence.

Faturamento e pool de roteamento

  • Custo segue o comportamento clássico do Auto e o preço agrupado do Auto.
  • Balance e Intelligence usam o Cursor Router e são cobrados conforme o preço do modelo roteado no seu plano ou contrato.
  • O modelo subjacente pode mudar entre solicitações. Prefira um ID de modelo fixo quando precisar de comparações reproduzíveis.
  • As listas de permissão de modelos corporativos definem o pool de roteamento. Bloquear modelos necessários pode desativar o Router.

Para consultar os preços atuais e o pool de roteamento, veja Cursor Router e Modelos e preços.

Solução de problemas: Router ausente

Se auto-smart não estiver disponível ou um modo de otimização for rejeitado:

  1. Chame Cursor.models.list().
  2. Confirme que auto-smart está no resultado.
  3. Confirme que optimize_for inclui o valor desejado (cost, balanced ou intelligence).
  4. Confirme que o Router está habilitado para a equipe vinculada à chave de API.
  5. Se você fizer parte de várias equipes, confirme que a chave está operando no contexto da equipe desejada.
  6. Verifique a política de acesso a modelos da equipe se o Router não estiver disponível ou não conseguir escolher um modelo subjacente válido.

Dicionários brutos

Dataclasses tipadas são preferíveis no código da aplicação, pois o preenchimento automático da IDE e a verificação de tipos funcionam melhor. O SDK também aceita dicionários simples para scripts curtos ou JSON fornecido externamente. Chaves em snake_case são normalizadas.

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

Agente

O identificador retornado por Agent.create(), Agent.resume(), client.agents.create() e 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 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: ...
MembroDescrição
agent_idIdentificador estável do agente. agent-<uuid> para local, bc-<uuid> para nuvem.
modelSeleção de modelo tipada atual. É atualizada após um envio bem-sucedido com uma substituição de modelo.
sendInicia uma nova execução com o prompt fornecido. Retorna um handle Run.
reloadRelê a configuração do sistema de arquivos (hooks, MCP do projeto, subagentes) sem encerrar o agente.
closeFecha o agente e libera recursos.
list_messagesLista o histórico de mensagens do agente.
list_artifactsLista os arquivos produzidos pelo agente (somente em nuvem; local retorna vazio).
download_artifactBaixa um arquivo pelo caminho (somente em nuvem; local gera uma exceção).
archive / unarchive / deleteGerencia o ciclo de vida do agente em nuvem.

Use um gerenciador de contexto para limpeza automática:

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

Ao usar os auxiliares sync Agent.* ou Cursor.* sem informar client=, o SDK inicia ou reutiliza um cliente padrão no nível do módulo. Ele é fechado automaticamente quando o processo é encerrado, mas você também pode fechá-lo explicitamente:

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

Atalho one-shot: cria um agente, envia um único prompt, aguarda a execução terminar e libera os recursos.

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 assíncrono (pressupondo que você já tenha um AsyncClient aberto):

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

Use CursorClient quando precisar de controle explícito do ciclo de vida, um endpoint de ponte personalizado, opções HTTP personalizadas ou vários espaços de trabalho em um único processo. Client continua disponível 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

Clientes explícitos expõem namespaces de recursos:

RecursoExemplos de métodos síncronosExemplos de métodos assí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()

Métodos de nível superior, como client.create_agent(...) e client.list_agents(...), continuam disponíveis, mas o uso de namespaces de recursos é o formato preferencial no código da aplicação.

Clientes HTTP personalizados

Os clientes síncronos e assíncronos aceitam um cliente httpx personalizado para usar proxies, transportes e outras configurações HTTP avançadas:

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 e DefaultAsyncHttpxClient mantêm o tempo limite e o comportamento de redirecionamento padrão do SDK. Já httpx.Client e httpx.AsyncClient usam os valores padrão do httpx.

Configurando tempos limite e novas tentativas

Ambos os clientes expõem with_options(...), que retorna uma cópia superficial, compartilhando as configurações de conexão e substituindo os valores padrão:

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

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

Envio de mensagens

Cada agent.send() retorna um Run. Cada await async_agent.send() retorna um AsyncRun. O agente mantém o contexto da conversa entre execuções; uma execução é a unidade de trabalho de um prompt.

print(agent.send("Find the bug in src/auth.py").text())# Mesmo agente, todo o contexto da conversa é preservado.print(agent.send("Fix it and add a regression test").text())

Equivalente assí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 imagens junto com texto:

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

Você também pode usar dataclasses auxiliares. SDKImage.from_file(path) lê o arquivo do disco e faz a codificação em base64 para você:

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) e SDKImage.url_image(url) também estão disponíveis para chamadores que já têm bytes codificados ou uma URL remota.

Run

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  # cumulativo; propriedade do handle ativo    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() é um alias de run.messages(). Iterar diretamente por run produz envelopes RunStreamEvent, assim como run.events().

AsyncRun expõe os mesmos campos de estado, incluindo usage. Os métodos que realizam operações de E/S são assíncronos: 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() e 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}")

Um stream de execução só pode ser consumido uma vez. run.messages(), run.events() e run.iter_text() usam o mesmo stream subjacente e o avançam. Quando o stream é concluído, a execução contém o resultado final (run.result, run.status, run.usage, run.git, ...). Chame run.wait() para consumir os eventos restantes e retornar o RunResult tipado.

Aguardar sem streaming

result = run.wait()print(result.status)       # "finished" | "error" | "cancelled" | "expired"print(result.result)       # texto final do assistente, se houverprint(result.model)        # ModelSelection resolvido usado nesta execuçãoprint(result.duration_ms)print(result.usage)        # TokenUsage acumulado, ou None se indisponívelprint(result.git)          # RunGitInfo na nuvem

Equivalente assíncrono:

result = await run.wait()

Uso de tokens

As execuções relatam o uso de tokens quando o tempo de execução o disponibiliza. Leia o total acumulado em run.usage no handle ativo (durante o streaming ou após wait()), ou em result.usage no RunResult retornado por run.wait(). Ambos contêm um TokenUsage que soma o uso relatado em todos os turnos e são None quando nenhum turno relatou uso — por exemplo, em uma execução cancelada que nunca concluiu um turno, em um tempo de execução que não expõe o uso ou em um snapshot da Cloud desvinculado que ainda não reconciliou o uso.

@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
CampoDescrição
input_tokensTokens do prompt enviados ao modelo.
output_tokensTokens gerados pelo modelo.
cache_read_tokensTokens obtidos do cache de prompt.
cache_write_tokensTokens gravados no cache de prompt.
total_tokensinput_tokens + output_tokens + cache_read_tokens + cache_write_tokens. Não inclui reasoning_tokens.
reasoning_tokensTokens de raciocínio, um subconjunto de output_tokens. None quando o modelo ou o tempo de execução não os informou.
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 já é contabilizado em output_tokens, portanto total_tokens o exclui para evitar contagem em duplicidade.

Para obter números por turno à medida que são transmitidos, trate o evento de stream usage (SDKUsageMessage). Ele é acionado uma vez ao fim de cada turno que reportou uso e inclui o TokenUsage desse turno. run.usage e result.usage permanecem cumulativos ao longo da execução. Após os turnos de stream, o handle prioriza esses totais somados; caso contrário, usa o uso retornado por wait() ou por um snapshot de get_run / list_runs quando fornecido pela ponte.

for message in run.messages():    if message.type == "usage":        print(f"turn used {message.usage.total_tokens} tokens")# Ou após o wait / sem consumir as mensagens por conta própria:result = run.wait()print(run.usage, result.usage)

Equivalente assíncrono: async for message in run.messages() e await run.wait(). run.usage continua sendo uma propriedade síncrona de AsyncRun.

TokenUsage é exportado de cursor_sdk (além de to_token_usage / sum_token_usage para chamadores avançados). O JSON transmitido usa camelCase (inputTokens, …); as dataclasses do Python usam snake_case.

As contagens de tokens são as reportadas pelo tempo de execução; elas não informam nada sobre o custo. Para consultar o uso faturado e o custo em dólares das execuções de um agente, chame agent.get_usage().

Leitura da saída de texto

iter_text() produz o texto do assistente à medida que é transmitido. text() retorna o texto final no terminal, aguardando wait() se a execução ainda estiver em andamento.

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

Equivalente assíncrono:

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

Cancelando uma execução

run.cancel()

Equivalente assíncrono:

await run.cancel()

run.cancel() solicita o cancelamento de uma execução ativa. O status muda para "cancelled", a transmissão ao vivo é interrompida, as chamadas de ferramenta em andamento param, e run.wait() é concluído com status: "cancelled". A saída parcial (texto do assistente gerado até então) permanece no objeto Run.

Cancelar uma execução que já está em um estado terminal ("finished", "error", "cancelled", "expired") gera UnsupportedRunOperationError. Em caso de dúvida, verifique run.status:

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

Consultando o estado da execução

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()  # remove o listenerturns = run.conversation()

run.conversation() retorna uma list[ConversationTurn] tipada. Use-a para renderizar ou persistir o histórico estruturado sem precisar assinar o stream em tempo real. run.conversation_json() retorna a string JSON bruta.

Para execuções assíncronas, use await run.conversation() e await run.conversation_json().

Substituição de modelo por Run

O model passado para agent.send() substitui a seleção do agente para essa execução e permanece ativo: envios subsequentes sem uma substituição continuam usando o novo modelo. Para voltar ao modelo anterior, passe outra substituição de model ou consulte a seleção atual em 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 e result.model refletem a seleção usada nesta execução e são imutáveis após seu início.

Variáveis de ambiente por execução

Agentes em nuvem também podem receber variáveis de ambiente para uma única execução. Passe cloud.env_vars em SendOptions, e os valores serão injetados no shell do agente apenas nessa execução — quando ela terminar, serão removidos da VM, e a execução seguinte não terá acesso a eles. Esse é o formato ideal para credenciais que são rotacionadas entre turnos, como um token de deploy de curta duração emitido imediatamente antes de pedir ao agente para usá-lo.

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

Se uma variável com escopo de execução tiver o mesmo nome de uma variável com escopo de agente em env_vars no CloudAgentOptions, o valor com escopo de execução prevalecerá nessa execução, e o valor com escopo de agente voltará a ser usado na execução seguinte.

As variáveis por execução também funcionam no primeiro envio. O SDK as envia junto com a criação do agente, limitadas à execução inicial, portanto não são persistidas no agente. Assim como as variáveis com escopo de agente, elas são criptografadas quando armazenadas, e os nomes não podem começar com CURSOR_.

As variáveis de ambiente por execução estão disponíveis apenas para agentes em nuvem e não podem ser usadas por agentes executados em repositórios públicos. Para agentes locais, o processo do agente herda seu próprio ambiente. Portanto, defina as variáveis no processo antes de chamar send().

Modo de conversa

Passe mode="plan" ou mode="agent" para definir se uma execução primeiro explora e planeja ou implementa alterações diretamente. Consulte o modo Plan para saber como ele funciona no produto.

Defina mode em AgentOptions passado para Agent.create() para definir o modo da primeira execução. Nas chamadas de acompanhamento a agent.send(), omita mode para manter o modo atual da conversa ou passe mode para alterná-lo apenas naquela execução.

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

Transmissão de deltas brutos

Forneça os callbacks on_delta e on_step em SendOptions para receber atualizações de nível mais baixo. Os callbacks síncronos são chamados inline. Os callbacks assíncronos podem ser síncronos ou assíncronos; valores de retorno aguardáveis são esperados antes do processamento do próximo 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()

As subclasses concretas de update e step ficam em cursor_sdk.events:

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

Eles ainda podem ser importados de cursor_sdk para manter a compatibilidade com versões anteriores, mas os novos códigos devem ser importados de cursor_sdk.events.

SendOptions

PropriedadeTipoDescrição
modelstr | ModelSelection | Mapping[str, Any]Substituição de modelo por envio. Se omitido, usa agent.model. Permanece em vigor após um envio bem-sucedido.
mode"agent" | "plan"Substituição do modo de conversa por envio. Se omitido em mensagens de acompanhamento, mantém o modo atual da conversa.
mcp_serversMapping[str, McpServerConfig]Definições de servidor MCP inline. Substitui completamente os servidores definidos na criação para esta execução.
cloud.env_varsMapping[str, str]Apenas para agentes em nuvem. Variáveis de ambiente por execução injetadas nesta execução e removidas quando ela termina. Substitui env_vars no escopo do agente por nome apenas nesta execução.
local.forceboolApenas para agentes locais. O padrão é None (não definido). Defina como True para expirar uma execução ativa travada antes de iniciar esta mensagem. Na nuvem, o servidor retorna 409 agent_busy, portanto não é necessário equivalente.
idempotency_keystrChave de idempotência opcional gerada pelo cliente para o envio.
on_stepCallable[[ConversationStep], Any]Callback após cada etapa concluída da conversa (texto, raciocínio ou lote de ferramentas).
on_deltaCallable[[InteractionUpdate], Any]Callback para cada InteractionUpdate bruto.

As próximas três seções trazem referências detalhadas sobre SDKMessage, InteractionUpdate e ConversationTurn. Leia superficialmente ou pule-as na primeira leitura; Retomar agentes retoma a explicação.

Eventos de stream

run.messages() retorna dataclasses tipadas de mensagens do SDK. Use message.type para diferenciá-las. Todas as mensagens incluem agent_id e run_id quando fornecidos pelo tempo de execução.

SDKMessage = (    SDKSystemMessage    | SDKUserMessageEvent    | SDKAssistantMessage    | SDKThinkingMessage    | SDKToolUseMessage    | SDKStatusMessage    | SDKTaskMessage    | SDKRequestMessage    | SDKUsageMessage    | Mapping[str, Any])
typeDataclassCampos principais
"system"SDKSystemMessagesubtype, model, tools
"user"SDKUserMessageEventmessage.content
"assistant"SDKAssistantMessagemessage.content com valores TextBlock e 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 é emitido duas vezes para a maioria das chamadas de ferramenta: primeiro com status="running" e args preenchido e, depois, na conclusão, com status="completed" (ou "error") e result preenchido. truncated indica se o SDK truncou args ou result porque o payload era grande demais.

SDKUsageMessage é emitido uma vez ao final de cada turno em que houve uso de tokens, contendo o TokenUsage desse turno. O total acumulado entre os turnos permanece em run.usage e result.usage. Consulte Uso de tokens.

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

Os dados do resultado (texto final, modelo, duração, uso acumulado de tokens e metadados do Git) ficam no objeto Run após a conclusão do stream. Use run.wait() para acessá-los, incluindo result.usage quando o tempo de execução o informar.

O esquema de chamada de ferramenta não é estável. Os payloads de args e result nos eventos tool_call refletem a estrutura interna de cada ferramenta e podem mudar conforme as ferramentas evoluem. Os nomes das ferramentas também podem ser renomeados ou substituídos. Trate args e result como dados sem tipo e faça o parsing de forma defensiva. O envelope do evento (type, call_id, name, status) é estável.

run.events() gera envelopes RunStreamEvent de nível inferior. Use-o quando precisar de offsets, envelopes de resultado final ou atualizações brutas de interação:

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

Atualizações de interação

InteractionUpdate é o tipo delta bruto passado ao callback on_delta em agent.send(). As atualizações são mais detalhadas que os eventos SDKMessage: o texto é transmitido token por token, e as chamadas de ferramenta informam o estado parcial à medida que os args se acumulam.

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

PartialToolCallUpdate é emitido à medida que o modelo transmite argumentos para uma chamada de ferramenta antes de confirmá-la. O mesmo aviso sobre estabilidade aplicável a SDKToolUseMessage.args se aplica aqui.

Tipos de conversa

A visualização estruturada por turno de uma execução, retornada por run.conversation(). Cada item é um contêiner que inclui o discriminador type do turno e o payload tipado em 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])

Diferencie por turn.type e leia a carga útil usando 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() em callbacks on_step é acionado para cada ConversationStep, não para cada turno. As etapas de conversa de chamada de ferramenta têm um payload Mapping[str, Any]. Trate os detalhes do payload de chamada de ferramenta como dados sem tipo; consulte a nota de estabilidade em Eventos de stream.

Retomar agentes

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

Use Agent.resume() ou client.agents.resume() para reconectar-se a um agente existente pelo ID. Fluxos comuns: reconectar-se a um agente em nuvem de longa duração iniciado anteriormente ou continuar uma conversa após o reinício do processo local. O tempo de execução é detectado automaticamente pelo prefixo do ID (bc- indica nuvem; qualquer outro indica local).

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

Equivalente assíncrono:

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

agent.model é None ao retomar, a menos que você informe model novamente. Servidores MCP inline não são mantidos entre retomadas; geralmente contêm segredos e existem apenas na memória. Informe-os novamente ao retomar ou use uma configuração MCP baseada em arquivo (.cursor/mcp.json e local.setting_sources) para servidores que precisam ser mantidos.

Persistência local

Agentes locais persistem o estado da conversa e os metadados da execução por meio da ponte, para que mensagens de acompanhamento e Agent.resume() sobrevivam ao reinício do processo. Por padrão, a ponte armazena esses dados em disco, em uma raiz de estado por espaço de trabalho. Agentes em nuvem persistem no servidor; portanto, retomar um agente em nuvem de qualquer lugar retorna a mesma conversa.

A persistência local é restrita ao espaço de trabalho. Quando a ponte é executada como um processo auxiliar ou subprocesso de longa duração, use o mesmo espaço de trabalho do agente para que as chamadas locais de listagem, obtenção e retomada encontrem os agentes corretos. Configure isso uma vez no cliente e passe cwd às chamadas locais de listagem e obtenção:

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

Como inspecionar agentes e execuções

Use CursorClient para as APIs de listagem, obtenção e paginação.

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 assí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)

Use agent.list_messages() em um identificador de agente para ler o histórico de mensagens. Agent.messages.list(agent_id) é um atalho com atributo tipado para a mesma chamada quando você só tem um ID.

Endpoints de lista retornam ListResult[T]. Use .items e .next_cursor diretamente, percorra a página atual com for item in page ou todas as páginas com .auto_paging_iter(). Endpoints de lista assíncronos retornam AsyncListResult[T]; async for item in page percorre a página atual, e async for item in page.auto_paging_iter() percorre todas as páginas do conjunto de resultados.

SDKAgentInfo

O formato dos metadados retornados por Agent.list(), Agent.get(), client.agents.list() e 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; vazio para agentes locais

Ciclo de vida dos agentes em nuvem

Os agentes em nuvem permanecem no espaço de trabalho da sua equipe até serem arquivados ou excluídos. client.agents.list(runtime="cloud") oculta agentes arquivados por padrão; passe include_archived=True para vê-los. Filtre por pr_url para encontrar o agente que abriu uma solicitação de pull específica.

# Por ID, sem precisar de um handle de agente:Agent.archive(agent_id)Agent.unarchive(agent_id)Agent.delete(agent_id)# Por meio de um client explícito:client.agents.archive(agent_id)client.agents.unarchive(agent_id)client.agents.delete(agent_id)# Em um handle de agente já existente:agent.archive()agent.unarchive()agent.delete()

archive exclui logicamente o agente, mantendo a transcrição legível. unarchive o restaura. delete é permanente; leituras posteriores retornam NotFoundError.

Os métodos assíncronos do ciclo de vida usam os mesmos nomes e podem ser usados com await.

agent.get_usage()

Consulta o uso de tokens faturados e o custo em dólares das execuções de um agente. Agentes em nuvem retornam um detalhamento por execução; agentes locais retornam um detalhamento por turno. Passe run_id para restringir o resultado a uma única entrada: para agentes em nuvem, um ID de execução run-<uuid>; para agentes locais, um ID de um 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              # somado entre os `runs`    runs: Sequence[RunUsage] = ()    cost: UsageCost | None = None  # somado entre os `runs`@dataclass(frozen=True)class RunUsage:    run_id: str    usage: TokenUsage    cost: UsageCost | None = None@dataclass(frozen=True)class UsageCost:    raw_cost_cents: float  # custo de tokens do modelo sem desconto; 0 para uso cobrado por solicitação    charged_cents: float   # valor cobrado, já com descontos e a Taxa de Tokens do Cursor

O custo inclui descontos e pode levar um momento para ser calculado após o término de uma execução; cost é None até isso acontecer. charged_cents é 0.0 para uso incluído no plano, BYOK e créditos concedidos.

Esta é uma visualização diferente de Uso de tokens: run.usage é a contagem de tokens em tempo real de uma execução, enquanto get_usage() é o registro faturado de todas as execuções do agente. Para agentes assíncronos, use await agent.get_usage(). AgentUsage, RunUsage e UsageCost são exportados de cursor_sdk.

O namespace Cursor

Leituras no nível da conta e do catálogo. Os métodos de sincronização aceitam api_key opcional e, caso contrário, usam CURSOR_API_KEY.

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

Equivalente usando cliente explícito:

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

Equivalente assí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)

Use Cursor.models.list() para descobrir IDs de modelos válidos e parâmetros de cada modelo antes de chamar Agent.create() ou agent.send(). Os parâmetros são específicos de cada modelo. Exemplos comuns incluem o esforço de raciocínio e optimize_for do Cursor Router em auto-smart.

O catálogo é específico de cada conta e equipe. O Cursor Router aparece como auto-smart somente quando está disponível para a equipe da chave de API. Veja 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"),#       ),#   ),# ]

As variants predefinidas de cada SDKModel já contêm params válidos, você pode copiá-las para uma ModelSelection.

Prefira uma seleção explícita do Router (auto-smart + optimize_for) quando um modelo de destino não estiver disponível e você quiser Cost, Balance ou Intelligence. Use ModelSelection(id="auto") como alternativa apenas quando quiser usar Auto selecionado pelo servidor, sem escolher um modo do Router. Para o Cursor Router, sempre informe optimize_for explicitamente.

Cursor.repositories.list() retorna os repositórios de SCM (GitHub, GitLab, Bitbucket, Azure DevOps, dependendo do que estiver conectado) disponíveis para agentes em nuvem na conta ou equipe que fez a chamada. Cada item expõe uma url. Use-as para preencher CloudAgentOptions.repos.

Servidores MCP

Dependendo do tempo de execução, os agentes podem obter servidores MCP de definições inline, configurações de projeto ou de usuário, plugins e configurações gerenciadas pelo dashboard.

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

Dicionários planos ({"type": "http", "url": ...} e {"type": "stdio", "command": ...}) também são aceitos como uma forma prática de criar scripts rapidamente.

O que é carregado

Agentes locais carregam servidores de até cinco fontes, com precedência da primeira correspondência em caso de nomes conflitantes:

  1. mcp_servers em agent.send(). Substitui completamente os servidores definidos na criação para essa execução (não são mesclados).
  2. mcp_servers em Agent.create(). Usado quando nenhuma substituição por envio é fornecida.
  3. Servidores de plugin, se local.setting_sources incluir "plugins".
  4. Servidores do projeto em .cursor/mcp.json, se local.setting_sources incluir "project".
  5. Servidores do usuário em ~/.cursor/mcp.json, se local.setting_sources incluir "user".

Sem local.setting_sources, apenas servidores inline são carregados. Se um servidor MCP local exigir login via OAuth, o SDK poderá reutilizar um login salvo no app Cursor, mas não poderá abrir um navegador para fazer login.

Agentes em nuvem carregam servidores das seguintes fontes:

  1. mcp_servers em agent.send(). Substitui completamente os servidores definidos na criação para essa execução (não são mesclados).
  2. mcp_servers em Agent.create(). Usado quando nenhuma substituição por envio é fornecida.
  3. Seus servidores MCP de usuário e de equipe em cursor.com/agents.

Se um servidor inline não incluir auth nem headers e você já tiver autorizado a URL desse servidor em cursor.com/agents, as execuções autenticadas com um token pessoal de API reutilizarão automaticamente esses tokens OAuth. Chaves de API de conta de serviço não podem recorrer à autenticação do usuário, pois não estão associadas a um usuário.

local.setting_sources não se aplica a agentes em nuvem.

Nuvem

Agentes em nuvem também aceitam configurações MCP autenticadas inline. O MCP em nuvem oferece suporte aos transportes HTTP e stdio. Use headers em HTTP para chaves de API estáticas ou tokens Bearer. Use auth em HTTP para servidores protegidos por OAuth. Use env em stdio quando o servidor é executado na VM em nuvem e lê credenciais de variáveis de ambiente.

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"},            ),        },    ))
  • Os headers HTTP e a auth são processados pelo backend do Cursor. Campos confidenciais são ocultados e não chegam à VM.
  • Os valores de env do Stdio são passados para a VM porque o servidor é executado nela. Trate-os como qualquer outro segredo de tempo de execução.
  • O OAuth para servidores MCP configurados em cursor.com/agents permanece por usuário, mesmo para servidores em nível de equipe.

Consulte MCP para ver o formato completo da configuração e recursos do Cloud Agent para saber mais sobre comportamentos específicos da nuvem.

Subagentes

Defina subagentes nomeados que o agente principal pode criar com a ferramenta Agent. Passe-os diretamente:

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

Subagentes commitados no repositório em .cursor/agents/*.md (com frontmatter de name, description e model opcional) também são considerados. Definições inline substituem as baseadas em arquivo com o mesmo nome.

Subagentes aninhados

Os subagentes podem criar seus próprios subagentes, respeitando um limite de aninhamento. Quando um subagente usa a ferramenta Agent, ele acessa o mesmo executor de subagentes do agente pai, permitindo que um agente pai delegue a um subagente que, por sua vez, delega a outros. Cada nível vê o mesmo conjunto de subagentes nomeados. O agente de nível superior e seus subagentes diretos podem iniciar subagentes, mas um subagente iniciado por outro subagente não pode iniciar outros.

Restringir o conjunto de ferramentas

tools define uma lista de permissão para as ferramentas integradas disponibilizadas ao modelo; disallowed_tools remove ferramentas e mantém as demais, inclusive as adicionadas à plataforma após o lançamento da sua versão do SDK. Por enquanto, ambos funcionam apenas com agentes locais e não persistem no agente: passe-os novamente ao retomar para manter a restrição.

from cursor_sdk import Agent, AgentOptions, LocalAgentOptions# Agente somente leitura: apenas estas ferramentas são disponibilizadas.reader = Agent.create(    AgentOptions(        model="composer-2.5",        tools=["read", "grep", "glob", "ls"],        local=LocalAgentOptions(cwd="."),    ))# Tudo, exceto acesso ao shell.no_shell = Agent.create(    AgentOptions(        model="composer-2.5",        disallowed_tools=["shell"],        local=LocalAgentOptions(cwd="."),    ))
  • Omitir tools disponibiliza o conjunto de ferramentas padrão do modelo selecionado; tools=[] não disponibiliza ferramentas integradas, portanto o modelo só pode responder com texto.
  • Ambos os campos aceitam nomes públicos ("read", "edit", "task", "webSearch", ...) e os grupos de capacidades "shell" e "mcp". Nomes desconhecidos geram BadRequestError durante a criação.
  • A negação prevalece: para ser disponibilizada, uma ferramenta precisa estar em tools (quando definido) e não estar em disallowed_tools.
  • Desabilitar "mcp" também remove as ferramentas personalizadas. Desabilitar "task" impede o uso de subagentes; caso contrário, os subagentes mantêm seus próprios conjuntos de ferramentas selecionados.

Ferramentas personalizadas

As ferramentas personalizadas permitem disponibilizar funções Python para agentes locais sem precisar configurar um servidor MCP separado. Passe-as em 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 recebe os argumentos analisados e um CustomToolContext com tool_call_id, quando disponível. Ele pode retornar uma string, um valor compatível com JSON ou um mapeamento com uma lista content. As ferramentas personalizadas estão disponíveis apenas para agentes locais.

Hooks

Os hooks são baseados exclusivamente em arquivos. Não há callback programático de hook. Os hooks definem um limite de política do projeto, não uma configuração por execução.

  • Local: Adicione .cursor/hooks.json ao repositório informado em local.cwd ou adicione ~/.cursor/hooks.json para hooks no nível do usuário.
  • Cloud: Faça commit de .cursor/hooks.json e de seus scripts no repositório informado em cloud.repos. Agentes em nuvem criados pelo SDK carregam automaticamente os hooks do projeto. Nos planos corporativos, eles também executam hooks de equipe e hooks gerenciados pela empresa.

Consulte Hooks para ver o formato de configuração e suporte a hooks do Cloud Agents para conhecer o comportamento na nuvem.

Artefatos

Liste e baixe arquivos do espaço de trabalho do 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)# Baixa um único artifact para o disco.content = agent.download_artifact(artifacts[0].path)Path("review.md").write_bytes(content)

Agentes assíncronos expõem await agent.list_artifacts() e await agent.download_artifact(path).

O suporte a artefatos depende do tempo de execução. Agentes locais do SDK retornam uma lista vazia em list_artifacts() e lançam uma exceção em download_artifact().

Gerenciamento de recursos

Sempre feche os agentes ao concluir. A forma mais simples de sincronizar é usar um gerenciador 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 explicitamente:

agent.close()

Agentes e clientes assíncronos oferecem suporte a gerenciadores de contexto assíncronos e à limpeza com 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 explicitamente:

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

O cliente padrão síncrono no nível do módulo é fechado automaticamente ao encerrar o processo. Processos de longa execução podem fechá-lo e redefini-lo explicitamente:

from cursor_sdk import close_default_clientclose_default_client()

Referência de configuração

O SDK Python aceita dataclasses auxiliares e dicionários brutos. As dataclasses usam campos em snake_case do Python e são recomendadas para o código da aplicação.

AgentOptions

PropriedadeTipoPadrãoDescrição
modelstr | ModelSelection | Mapping[str, Any]Obrigatório para local; na nuvem, usa o padrão resolvido pelo servidorModelo a usar. Consulte ModelSelection.
api_keystrVariável de ambiente CURSOR_API_KEYChave de API do usuário ou chave de conta de serviço. Chaves de administrador da equipe ainda não são compatíveis.
namestrGerado automaticamenteNome legível do agente exibido em client.agents.list() / client.agents.get().
localLocalAgentOptions | Mapping[str, Any]NoneConfiguração do agente local. Use para criar um agente local.
cloudCloudAgentOptions | Mapping[str, Any]NoneConfiguração do agente em nuvem. Use para criar um agente em nuvem.
mcp_serversMapping[str, McpServerConfig]NoneDefinições inline de servidores MCP.
agentsMapping[str, AgentDefinition | Mapping[str, Any]]NoneDefinições de subagentes.
toolsSequence[str]Conjunto de ferramentas padrãoApenas as ferramentas integradas listadas são disponibilizadas ao modelo. [] significa que não há ferramentas integradas; o modelo só pode responder com texto. Apenas para agentes locais.
disallowed_toolsSequence[str]NoneRemove as ferramentas integradas listadas; todas as demais permanecem disponíveis. A negação prevalece quando combinada com tools. Apenas para agentes locais.
agent_idstrGerado automaticamenteID durável do agente. Use para manter um ID estável entre invocações.
idempotency_keystrGerado automaticamente na nuvemChave de idempotência opcional gerada pelo cliente. Apenas na nuvem.
mode"agent" | "plan"NoneModo inicial da conversa na primeira execução do agente. Quando omitido, o servidor inicia no modo agente. Consulte Modo de conversa.

LocalAgentOptions

PropriedadeTipoPadrãoDescrição
cwdstr | os.PathLikeNoneDiretório de trabalho principal. Listas com várias entradas não são aceitas; use dirs para vários diretórios raiz.
dirsSequence[str | os.PathLike]NonePastas adicionais do espaço de trabalho para configurações com vários diretórios raiz. Mescladas com cwd para carregar regras, skills e o contexto do espaço de trabalho de todos os caminhos.
setting_sourcesSequence[SettingSource]NoneCamadas de configuração disponíveis no ambiente: "project", "user", "team", "mdm", "plugins" ou "all".
sandbox_optionsSandboxOptions | Mapping[str, Any]NoneOpções do sandbox local.
storeLocalAgentStoreConfig | Mapping[str, Any]NoneConfiguração do store local passada para a ponte.
auto_reviewboolNoneEncaminha chamadas de ferramenta locais pelo Auto-review quando o backend conectado oferecer suporte.
custom_toolsMapping[str, CustomTool | Mapping[str, Any]]NoneFerramentas personalizadas expostas a agentes locais.

CloudAgentOptions

PropriedadeTipoPadrãoDescrição
envCloudEnvironment | Mapping[str, Any]NoneAmbiente de execução. Quando omitido, o servidor usa VMs em nuvem hospedadas pelo Cursor. pool e machine direcionam para workers autogerenciados executados por você.
reposSequence[CloudRepository | Mapping[str, Any]]NoneRepositórios a serem clonados na VM. Omita repos e env para criar um agente sem repositório e com um espaço de trabalho vazio. Passe pr_url em um repositório para associar o agente a uma PR existente.
work_on_current_branchboolNoneFaça push dos commits para a branch existente em vez de criar uma nova. O servidor considera um valor omitido como False.
auto_create_prboolNoneAbra uma PR quando a execução terminar. O servidor considera um valor omitido como False.
open_as_cursor_github_appboolTrue para chaves de conta de serviço, False para chaves de usuárioAbra PRs como o app do Cursor no GitHub, em vez de como o proprietário da chave de API. O valor resolvido é retornado nas operações de criação, obtenção e listagem.
skip_reviewer_requestboolNoneNão solicite o usuário chamador como revisor da PR. O servidor considera um valor omitido como False.
env_varsMapping[str, str]NoneVariáveis de ambiente com escopo de sessão para agentes em nuvem.
metadataMapping[str, str]NoneTags de texto do chamador persistidas no agente em nuvem. Consulte Metadados do agente.

AgentDefinition

PropriedadeTipoPadrãoDescrição
descriptionstrobrigatórioQuando usar este subagente. É exibida ao agente pai para que ele saiba quando criá-lo.
promptstrobrigatórioPrompt de sistema para o subagente.
modelstr | ModelSelection | Mapping[str, Any] | "inherit"NoneSubstituição de modelo. Tanto None quanto "inherit" usam a seleção do agente pai.
mcp_serversSequence[str | AgentDefinitionMcpServer | Mapping[str, Any]]NoneServidores MCP disponíveis para este subagente. Os nomes fazem referência aos servidores em mcp_servers do agente pai.

CustomTool

@dataclassclass CustomTool:    execute: Callable[[Mapping[str, Any], CustomToolContext], Any]    description: str | None = None    input_schema: Mapping[str, Any] | None = Noneclass CustomToolContext:    tool_call_id: str | None = None

ModelSelection

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

id é o identificador do modelo (por exemplo, "composer-2.5" ou "auto-smart"). params contém parâmetros específicos do modelo, como esforço de raciocínio ou optimize_for do Router. Use Cursor.models.list() para consultar IDs válidos, definições de parâmetros e variantes predefinidas para sua conta. Consulte Cursor Router para conhecer o contrato de seleção do 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  # somente local; a cloud rejeita este campo@dataclass(frozen=True)class McpAuth:    client_id: str    client_secret: str | None = None    scopes: Sequence[str] = ()

Para servidores HTTP executados na nuvem, headers e auth são tratados pelo backend do Cursor. Os campos sensíveis são ocultados antes de chegarem à VM. Para servidores stdio na nuvem, os valores de env são passados para a VM (trate-os como qualquer outro secret de tempo de execução).

UserMessage

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

A forma estruturada do argumento message de agent.send(). Use-a para enviar imagens junto com 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: ...

Forneça uma url remota ou data em base64 com um mime_type. from_data() aceita bytes ou uma string em base64. from_file() lê um arquivo do disco e o codifica em base64.

SettingSource

SettingSource está disponível em cursor_sdk.types.

from cursor_sdk.types import SettingSource

Controla quais camadas de configuração armazenadas em disco um agente local carrega. Agentes em nuvem sempre carregam project, team e plugins e ignoram este campo.

ValorOrigem
"project".cursor/ no espaço de trabalho
"user"~/.cursor/
"team"Configurações da equipe sincronizadas do dashboard
"mdm"Configurações corporativas gerenciadas por MDM
"plugins"Configurações fornecidas por plugins
"all"Abreviação para todas as opções acima

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

Retornado por client.agents.list(), client.agents.list_runs() e Agent.list(). next_cursor fica vazio quando não há mais páginas. Os endpoints de listagem assíncronos retornam AsyncListResult[T] com equivalentes que podem ser aguardados.

Erros

Todos os erros do SDK estendem CursorAgentError. CursorSDKError é o alias raiz compatível com versões anteriores para chamadores mais antigos. Use is_retryable e retry_after para definir a lógica de novas tentativas.

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    request_id: str | None    headers: Mapping[str, str]    retry_after: str | None
ErroQuando
AuthenticationErrorChave de API inválida ou usuário não autenticado.
PermissionDeniedErrorO chamador autenticado não tem permissão para a operação solicitada.
RateLimitErrorNúmero excessivo de solicitações ou limites de uso excedidos.
ConfigurationErrorModelo inválido, configuração obrigatória ausente ou parâmetros de solicitação inválidos.
AgentBusyErrorEnvio de uma mensagem de acompanhamento enquanto o agente já tem uma execução no estado CREATING ou RUNNING (HTTP 409, código agent_busy).
BadRequestErrorA solicitação está malformada.
IntegrationNotConnectedErrorCriação de um agente em nuvem para um repositório cujo provedor de SCM não está conectado.
NetworkErrorServiço indisponível ou falha de rede.
APITimeoutErrorA solicitação excedeu o tempo limite.
InternalServerErrorO serviço do Cursor retornou um erro de servidor.
NotFoundErrorO recurso solicitado não foi encontrado.
AgentNotFoundErrorO agente não existe ou não está visível no diretório de trabalho atual.
UnsupportedRunOperationErrorA operação de execução não é compatível com o estado da execução atual.

Novas tentativas com espera progressiva

is_retryable e retry_after controlam a lógica de novas tentativas no chamador. retry_after é uma string no formato HTTP (em segundos ou como uma data HTTP) fornecida pelo servidor quando 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)

Todo CursorAgentError inclui request_id quando o servidor retorna um. Registre-o sempre que apresentar um erro, para que o suporte possa identificar a falha.

IntegrationNotConnectedError

class IntegrationNotConnectedError(ConfigurationError):    provider: str   # ex.: "github", "gitlab", "azuredevops"    help_url: str   # link do dashboard para reconectar

Use help_url para direcionar o usuário ao fluxo de reconexão adequado. Novos provedores podem ser adicionados sem uma nova versão do SDK.

AgentBusyError

Agentes em nuvem permitem apenas uma execução ativa por vez. AgentBusyError é gerado quando você chama agent.send() (ou cria uma execução de outra forma) enquanto outra execução no mesmo agente ainda está com o status CREATING ou RUNNING.

is_retryable é False. Tentar novamente imediatamente continuará falhando até que a execução ativa atinja um status terminal ou seja cancelada. Outras respostas 409, como agent_archived, geram ConfigurationError.

Aguarde a execução ativa terminar, cancele-a com run.cancel() ou consulte Agent.list_runs() antes de enviar novamente:

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

Agentes locais não geram AgentBusyError. Passe local={"force": True} em send() para encerrar uma execução local travada antes de iniciar outra.

UnsupportedRunOperationError

class UnsupportedRunOperationError(ConfigurationError):    operation: str

Gerado quando uma operação de Run não é permitida na execução atual. O caso mais comum é chamar run.cancel() em uma execução que já está em estado terminal.

run.supports(operation) e run.unsupported_reason(operation) informam se o SDK oferece suporte a um nome de operação ("stream", "wait", "cancel", "conversation") e não verificam o estado da execução. Leia run.status antes de fazer chamadas que dependem do estado.

Solução de problemas

Defina CURSOR_SDK_LOG=debug (ou info) para adicionar um manipulador de stderr ao logger do próprio SDK. O SDK configura apenas o logger cursor_sdk, portanto isso não interfere na configuração de logging da aplicação host.

CURSOR_SDK_LOG=debug python my_script.py

O binário da ponte incluída é instalado como cursor-sdk-bridge no PATH junto com o pacote. Execute-o diretamente para confirmar a build incluída no seu wheel:

cursor-sdk-bridge --help

Limitações conhecidas

  • Os schemas de payload de chamadas de ferramenta não são intencionalmente fortemente tipados.
  • Servidores MCP inline não são persistidos entre chamadas a Agent.resume(). Passe-os novamente ao retomar, se necessário.
  • Ferramentas personalizadas (local.custom_tools) e restrições de conjunto de ferramentas (tools, disallowed_tools) são exclusivas de agentes locais. As restrições não persistem no agente; passe-as novamente ao retomar.
  • O download de artefatos não está implementado para agentes locais.
  • local.setting_sources (e os caminhos baseados em arquivos para MCP e subagentes que ele controla) não se aplica a agentes em nuvem. A nuvem sempre carrega project, team e plugins.
  • Hooks são compatíveis apenas com arquivos (.cursor/hooks.json). Não há callbacks programáticos.