Run a Cursor SDK agent in CI on a self-hosted runner
Deployment
Run a Cursor SDK agent in CI on a self-hosted runner
Stand up a CI job that invokes a Cursor SDK agent on a self-hosted runner on Quake AI. The job runs on pull requests, reviews the checked-out diff, and posts the result as a pull request comment. You supply the Cursor API key as a CI secret and the runner from Deploy a self-hosted CI runner.
Monthly total for the required template above. Use the configurator below to add optional pieces and see the total update.
What each resource is for
s1a.small
s1a.small · 2 shared vCPU, 2 GiB RAM, 0.5 Gbps
$16.50/mo
Compute shown per role at custom-package rates ($29/dedicated vCPU, $7.25/shared vCPU, $1/GiB RAM). The headline above is the billed total: the cheaper of a named plan and the custom package, plus add-ons.
Private networks, subnets, Neutron routers, and security groups are included with the plan.
VPC objects are usually free to create elsewhere, but NAT gateways bill hourly plus per-GB processed. Quake AI uses router SNAT with no separate NAT line item.
$0.00
Control-plane API requests
OpenStack API calls for provisioning and management are included.
Some managed services on other clouds meter API calls or charge for premium control-plane features.
$0.00
Pricing data last validated: . For current rates, check quake.ai/pricing.
Click to zoom
Pull-request workflow on a self-hosted runner: a one-shot Cursor SDK agent reviews the diff and posts a PR comment
A self-hosted runner already registered against your repository, see Deploy a self-hosted CI runner. The examples below assume a GitHub Actions runner labeled quake-ai; the GitLab Runner equivalent is the same shape with tags instead of runs-on.
A Cursor API key. User keys live at Cursor Dashboard → Integrations; team service-account keys live in Team Settings → Service accounts.
Node.js available in the workflow, either preinstalled on the runner or added with a setup step.
Admin access to the repository to add a CI secret.
Add the key to your repository's secret store. Never commit it to a workflow file or to the runner's filesystem outside the CI secret mechanism.
For GitHub Actions, go to your repository's Settings > Secrets and variables > Actions > New repository secret, name it CURSOR_API_KEY, and paste the key.
For GitLab CI/CD, go to Settings > CI/CD > Variables > Add variable, name it CURSOR_API_KEY, and mark it Masked and Protected.
Add scripts/agent-pr-review.mjs to the repository:
JavaScript
import { Agent, CursorAgentError } from "@cursor/sdk";const baseRef = process.env.PR_BASE_REF ?? "main";const prompt = [ `Run \`git diff origin/${baseRef}...HEAD\` in this repository.`, "Write a concise code-review comment: summarize the change, flag", "correctness or security risks you see, and note missing tests.", "Output only the comment text, no preamble.",].join(" ");try { const result = await Agent.prompt(prompt, { apiKey: process.env.CURSOR_API_KEY, model: { id: "composer-2.5" }, local: { cwd: process.cwd() }, }); if (result.status === "error") { console.error(`run failed: ${result.id}`); process.exit(2); } process.stdout.write(result.result ?? "");} catch (err) { if (err instanceof CursorAgentError) { console.error(`startup failed: ${err.message}, retryable=${err.isRetryable}`); process.exit(1); } throw err;}
The script uses the one-shot Agent.prompt(...) pattern: no streaming, no follow-up turns, the process exits once the agent finishes. The agent runs locally against the runner's checked-out repository (local: { cwd: process.cwd() }), so it reads the diff itself with its own tool calls rather than the script shelling out to git diff. A thrown CursorAgentError means the run never started (exit code 1); result.status === "error" means the run started and failed midway (exit code 2). See the Cursor SDK skill for the full pattern reference.
fetch-depth: 0 is required: the agent's git diff call needs the base branch's history, which a shallow checkout does not include. The create-or-update-comment step posts the captured output as a PR comment using the workflow's built-in GITHUB_TOKEN permissions; it needs no extra secret.
For GitLab CI/CD, the equivalent job uses tags instead of runs-on and the GitLab API to post a merge request note:
GITLAB_API_TOKEN is a separate masked, protected CI/CD variable holding a project or personal access token with API scope; GitLab does not expose an equivalent of GitHub's automatic GITHUB_TOKEN for posting notes from a job.
Open a test pull request against the repository. Confirm the workflow runs on the self-hosted runner and check the job logs from your CI provider's pipeline UI.
A successful run ends with the comment posted on the pull request. If the job exits with code 1, the run never started; check that CURSOR_API_KEY is set and the runner has outbound network access to reach the Cursor API. If it exits with code 2, the run started and the agent's transcript failed midway; rerun with the run ID logged by the script for further investigation.
This is a CI job pattern on infrastructure you operate, not a hosted review product. You are responsible for the runner's uptime, the CI secret's rotation, and any cost the Cursor API key's plan accrues per run. Keep each job's prompt narrow and reproducible so reviewers can predict what the agent does on every run.
Remove the workflow file (.github/workflows/agent-review.yml or the GitLab agent-review job) and the scripts/agent-pr-review.mjs script from the repository. Delete the CURSOR_API_KEY secret from your CI provider's secret store, and revoke the key from Cursor Dashboard → Integrations if you created it solely for this deployment. The self-hosted runner itself is unaffected; follow Deploy a self-hosted CI runner's clean-up section if you also want to deregister it.