Skip to content

MCP Server

Connect Claude, Cursor, and other MCP clients to your sprintrr projects, tasks, and milestones — or call the HTTP API directly.

The sprintrr Model Context Protocol (MCP) server lets AI assistants and your own tooling read and modify your projects, tasks, and milestones with a scoped API key. There are two ways to use it:

  1. The @sprintrr/mcp-server package — a stdio MCP server you wire into Claude Desktop, Claude Code, or Cursor. This is what most people want.
  2. The hosted HTTP API — a plain REST-ish interface under https://www.sprintrr.ai/api/mcp for custom integrations.

Both authenticate with the same sk_live_… API key and enforce the same permission scopes.

Everything the MCP server can touch is scoped to your account. A key never sees another user's data, and every change it makes is attributed to that key in your Activity feed.

Create an API key

  1. Sign in to sprintrr and open Settings → Integrations → MCP API Keys.
  2. Click Create New Key, give it a recognizable name (e.g. "Claude Desktop – laptop"), and optionally set an expiry.
  3. Copy the key immediately — it is shown once and only the prefix (sk_live_xxxx…) is stored afterward.

You can hold up to 10 keys per account. Each row shows the key name, prefix, last-used time, request count, and expiry. Revoke a key any time with the trash icon; revocation is immediate.

Keys are generated as sk_live_ followed by 24 random bytes, stored only as a SHA‑256 hash, and (optionally) expire after the number of days you choose (1–365).

Quickstart by client

Pick your MCP client below — each section has the exact config-file path for macOS and Windows, the JSON snippet that works for that client, and how to verify the integration is live. After any change, restart the client (Antigravity auto-reloads).

The in-app key dialog has a Copy config button that produces a ready-to-paste JSON snippet with your key already filled in.

Claude Desktop

Claude Desktop only supports stdio MCP servers, so the sprintrr config uses the @sprintrr/mcp-server npm package.

1. Open the config. In Claude Desktop, click the Claude menu in the top bar (macOS) or the system tray (Windows) → Settings…Developer tab → Edit Config. The file opens in your default editor and gets created if it doesn't exist.

Direct paths if you'd rather edit by hand:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\Claude\claude_desktop_config.json (resolves to C:\Users\<USERNAME>\AppData\Roaming\Claude\claude_desktop_config.json)

2. Paste this JSON (merge with any existing mcpServers you have):

{
  "mcpServers": {
    "sprintrr": {
      "command": "npx",
      "args": ["-y", "@sprintrr/mcp-server"],
      "env": {
        "SPRINTRR_API_KEY": "sk_live_your_key_here"
      }
    }
  }
}

3. Restart Claude Desktop fully (quit + reopen). An MCP indicator appears at the bottom-right of the conversation input — click it to see the sprintrr tools listed.

4. Verify. Ask Claude: "List my sprintrr projects." You should see it call list_projects and return a list.

Claude Code uses the same stdio shape and the same @sprintrr/mcp-server package. From the Claude Code CLI, run claude mcp add sprintrr -- npx -y @sprintrr/mcp-server and set SPRINTRR_API_KEY in your shell environment. The CLI manages the config file for you.

Cursor

Cursor supports remote HTTP MCP servers natively — recommended for sprintrr because it skips the npm install. The stdio shape from Claude Desktop also works if you'd rather mirror your other client setups.

1. Open the config. In Cursor, **Settings → Features → MCP →

  • Add New MCP Server**. The form writes to mcp.json automatically. Or edit by hand:
  • Global (applies everywhere)
    • macOS: ~/.cursor/mcp.json
    • Windows: %USERPROFILE%\.cursor\mcp.json (resolves to C:\Users\<USERNAME>\.cursor\mcp.json)
  • Project (in the repo root): <repo>/.cursor/mcp.json. Wins over global when both define the same server.

2. Paste this JSON (recommended — direct HTTP, no npm install):

{
  "mcpServers": {
    "sprintrr": {
      "url": "https://www.sprintrr.ai/api/mcp",
      "headers": {
        "Authorization": "Bearer sk_live_your_key_here"
      }
    }
  }
}

Or the stdio variant if you prefer:

{
  "mcpServers": {
    "sprintrr": {
      "command": "npx",
      "args": ["-y", "@sprintrr/mcp-server"],
      "env": {
        "SPRINTRR_API_KEY": "sk_live_your_key_here"
      }
    }
  }
}

3. Restart Cursor. In Settings → Tools & MCP, a green dot next to sprintrr means the server is connected. Red dot = check the key and the URL.

4. Verify. Open the chat sidebar and ask: "List my sprintrr projects." The tools panel should show the list_projects call.

Antigravity (Google)

Antigravity's MCP transport is similar to Cursor's but with one critical difference: HTTP servers use serverUrl, not url.

Antigravity requires serverUrl, not url. Copying a Cursor or generic MCP snippet directly will fail silently — the server just won't appear in the agent panel. The stdio (command/args/env) shape works without modification if you'd rather use the npm package.

1. Open the config. In Antigravity, click the "…" (Additional Options) menu at the top of the Agent panelMCP ServersManage MCP ServersView raw config. This opens mcp_config.json directly in the editor; Antigravity auto-reloads on save.

Direct paths if you'd rather edit by hand:

  • macOS: ~/.gemini/antigravity/mcp_config.json
  • Windows: C:\Users\<USERNAME>\.gemini\antigravity\mcp_config.json

2. Paste this JSON (note serverUrl):

{
  "mcpServers": {
    "sprintrr": {
      "serverUrl": "https://www.sprintrr.ai/api/mcp",
      "headers": {
        "Authorization": "Bearer sk_live_your_key_here"
      }
    }
  }
}

3. Save the file. Antigravity reloads MCP servers automatically — no restart needed.

4. Verify. Open the agent panel, ask: "List my sprintrr projects." The sprintrr tools should appear in the agent's available-tools list.

For deeper Antigravity-specific details, see the Antigravity MCP docs.

Environment variables

VariableRequiredDefaultDescription
SPRINTRR_API_KEYYesYour sk_live_… MCP key
SPRINTRR_API_URLNohttps://www.sprintrr.aiAPI base URL (override for self-hosted/staging)

The package is published as @sprintrr/mcp-server (binary sprintrr-mcp, Node ≥ 18). Install it globally with npm install -g @sprintrr/mcp-server or just let npx fetch it as shown above.

Tools

The MCP server registers 30 tools. * marks required arguments.

Projects

ToolArgumentsReturns
list_projectsteamId?Active project list (excludes archived)
get_projectprojectId*Project + analytics (task counts, completion %, hours)
create_projectname*, description?, startDate?, targetLaunchDate?, totalSprints?, teamId?Created project
update_projectprojectId*, name?, description?, startDate?, targetLaunchDate?, totalSprints?Updated project
delete_projectprojectId*{ success: true } — also deletes its tasks & milestones
list_archived_projectsteamId?Archived projects (hidden from list_projects)
archive_projectprojectId*Updated project with archivedAt set. Reversible — tasks/milestones/folder are preserved
unarchive_projectprojectId*Updated project, restored to its original folder if it still exists
move_project_to_folderprojectId*, folderId* (string or null)Updated project. null moves to top level

Tasks

ToolArgumentsReturns
list_tasksprojectId*, status?, priority?, assignedTo?Task list
get_tasktaskId*Task (incl. time-tracking fields)
create_taskprojectId*, title*, description?, category?, priority?, status?, estimatedHours?, dueDate?, sprintWeek?, milestoneId?, assignedTo?Created task
update_tasktaskId*, plus any of the create fields + actualHours?Updated task
delete_tasktaskId*{ success: true }

Milestones

ToolArgumentsReturns
list_milestonesprojectId*Milestone list (with progress %)
get_milestonemilestoneId*Milestone + its tasks
create_milestoneprojectId*, title*, description?, startDate?, targetDate?Created milestone
update_milestonemilestoneId*, title?, description?, startDate?, targetDate?, status?Updated milestone
assign_task_to_milestonetaskId*, milestoneId? (omit/empty to unassign)Updated task

Comments

ToolArgumentsReturns
list_commentstaskId*Comment list, threaded one level deep (top-level comments carry their replies), enriched with author identity
create_commenttaskId*, body*, parentId?Created comment (201)
update_commentcommentId*, body*Updated comment
delete_commentcommentId*{ success: true } — deleting a top-level comment also removes its replies

Comments behave exactly like the in-app feature. Replies are one level deepparentId must point at a top-level comment. Mention a teammate by putting an @[Display Name](userId) token in body; they receive a task_mention notification. Creating a comment also emits the same notifications as the web app (comment / reply / mention) and an MCP-attributed activity-feed event scoped to the parent task (the key name is recorded). Editing does not re-send mention notifications.

Folders

ToolArgumentsReturns
list_foldersteamId?Folder list (folders are team-scoped in a team workspace, personal otherwise)
create_foldername*, parentId?, teamId?Created folder (201). Pass parentId to create a subfolder under a root folder
update_folderfolderId*, name*Renamed folder
delete_folderfolderId*{ success: true } — subfolders cascade; projects inside fall back to the top level

Folders are 2 levels deep at most (folder → subfolder → projects); a third level is rejected by both the API and the database. Folder scope must match the project scope when moving (team folder for team projects, personal otherwise). Deleting a folder never deletes projects — they always fall back to the top level via ON DELETE SET NULL.

User & utility

ToolArgumentsReturns
get_user_profile{ id, email, fullName, avatarUrl }
get_credits{ availableCredits, usedCredits, totalCredits, planType, resetDate }
search_tasksquery*, projectId?Matching tasks (title/description, max 50)

Enums. status: Not Started, In Progress, Completed, Blocked. priority: Low, Medium, High. category: Development, Design, Testing, Research, Planning, Marketing, Strategy, Communication, Administration, Finance, Legal, Operations, Misc.

When you omit optional fields on create, the server fills sensible defaults: project totalSprints = 4, start = today, target = +28 days; task category = Development, priority = Medium, status = Not Started, due = +7 days; milestone target = +14 days.

Date cascades — direct HTTP only

Atomic date shifts for projects and milestones are reachable via direct HTTP (POST with Authorization: Bearer sk_live_...), not yet through the npm wrapper's named tools. Two endpoints, both gated by the write:projects scope that's in the default permissions for every key created in Settings → API Keys.

Shift a project start (cascades to every non-completed milestone + task, optionally the launch date too):

curl -X POST https://www.sprintrr.ai/api/mcp/projects/<PROJECT_ID>/shift-dates \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{"deltaDays": 7, "shiftLaunch": true}'

Move a milestone, optionally cascading the rest (shiftScope defaults to "cascade"; pass "only" to move just the pivot):

curl -X POST https://www.sprintrr.ai/api/mcp/milestones/<MILESTONE_ID>/shift-cascade \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{"deltaDays": -3, "shiftScope": "cascade"}'

Both return:

{
  "milestones_shifted": 4,
  "tasks_shifted": 17,
  "completed_skipped": 2,
  "min_task_date_after_shift": "2026-06-12",
  "project_start_after_shift": "2026-06-05",
  "dry_run": false
}

Preview mode. Add "dryRun": true to either body. The endpoint returns the same payload — counts, post-shift dates — but the database is untouched and no activity-feed event is recorded. Lets an agent describe consequences before the user OKs the real apply. Re-POST without the flag (or with "dryRun": false) to commit.

# What would happen if I shifted the project by +14 days?
curl -X POST https://www.sprintrr.ai/api/mcp/projects/<PROJECT_ID>/shift-dates \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{"deltaDays": 14, "dryRun": true}'

Completed milestones and tasks are always skipped (their dates are historical). For project-scope shifts, tasks without a milestone shift along with the project; for milestone cascades, only tasks bound to shifted milestones move.

Blog (admin-only)

The MCP route also exposes endpoints under /blog/* for the Sprintrr-team CMS — create, update, publish, and delete posts and categories. These use the same Bearer auth and the same scope machinery, with the scopes read:blog / write:blog.

Admin-only. Every blog handler additionally re-checks isAdminUser() server-side before mutating. Keys belonging to non-admin users get 403 Forbidden — blog requires admin even if their permissions array claims write:blog. Public MCP keys never include blog scopes by default — these endpoints exist for the Sprintrr team's internal authoring workflow via the sprintrr-blog Claude Code skill, not for the public MCP surface.

If you're an admin and want to drive your own automation against the blog (rather than the bundled skill), the endpoints are:

MethodPathScope
GET/api/mcp/blog/postsread:blog
GET/api/mcp/blog/posts/{slugOrId}read:blog
POST/api/mcp/blog/postswrite:blog
PUT/api/mcp/blog/posts/{id}write:blog
POST/api/mcp/blog/posts/{id}/publishwrite:blog
POST/api/mcp/blog/posts/{id}/unpublishwrite:blog
DELETE/api/mcp/blog/posts/{id}write:blog
GET/api/mcp/blog/categoriesread:blog
POST/api/mcp/blog/categorieswrite:blog
PUT/api/mcp/blog/categories/{id}write:blog
DELETE/api/mcp/blog/categories/{id}write:blog

Resources

The server also exposes read-only MCP resources:

sprintrr://projects
sprintrr://projects/archived
sprintrr://projects/{id}
sprintrr://projects/{id}/tasks
sprintrr://projects/{id}/milestones
sprintrr://tasks/{id}
sprintrr://tasks/{id}/comments
sprintrr://milestones/{id}
sprintrr://folders
sprintrr://user/profile
sprintrr://user/credits

HTTP API reference

For custom integrations, call the API directly. Base URL:

https://www.sprintrr.ai/api/mcp

Every request must send:

Authorization: Bearer sk_live_your_key_here

Responses are JSON. Errors are { "error": "message" } with the status codes listed under Errors.

GET

PathQueryScopeReturns
/projectsteamId?read:projectsProject[] — excludes archived
/projects/archivedteamId?read:projectsArchived Project[]
/projects/:idread:projectsProject + analytics
/projects/:id/tasksstatus?, priority?read:tasksTask[]
/tasks/:idread:tasksTask
/tasks/:id/commentsread:commentsComment[] (threaded one level deep)
/projects/:id/milestonesread:milestonesMilestone[]
/milestones/:idread:milestonesMilestone + tasks
/foldersteamId?read:foldersFolder[]
/user/profileread:user{ id, email, fullName, avatarUrl }
/user/creditsread:user{ availableCredits, usedCredits, totalCredits, planType, resetDate }
/search/tasksq*, projectId?read:tasksTask[] (max 50)

analytics is { totalTasks, completedTasks, inProgressTasks, blockedTasks, completionPercentage, totalEstimatedHours, totalActualHours }.

POST

PathBodyScopeStatus
/projects{ name*, description?, startDate?, targetLaunchDate?, totalSprints?, teamId? }write:projects201
/projects/:id/tasks{ title*, description?, category?, priority?, status?, estimatedHours?, dueDate?, sprintWeek?, milestoneId?, assignedTo? }write:tasks201
/projects/:id/milestones{ title*, description?, startDate?, targetDate?, status? }write:milestones201
/projects/:id/archiveemptywrite:projects200
/projects/:id/unarchiveemptywrite:projects200
/tasks/:id/comments{ body*, parentId? }write:comments201
/folders{ name*, parentId?, teamId? }write:folders201

PUT

PathBodyScope
/projects/:id{ name?, description?, startDate?, targetLaunchDate?, totalSprints?, techStack?, folderId? }write:projects
/tasks/:id{ title?, description?, category?, priority?, status?, estimatedHours?, actualHours?, dueDate?, sprintWeek?, milestoneId?, assignedTo? }write:tasks
/milestones/:id{ title?, description?, startDate?, targetDate?, status? }write:milestones
/comments/:id{ body* }write:comments
/folders/:id{ name* }write:folders

Pass folderId: null on /projects/:id to move a project to the top level, or a folder ID to move it into that folder. The scope-consistency trigger rejects cross-scope moves (e.g. personal project into a team folder).

DELETE

PathScopeReturns
/projects/:idwrite:projects{ success: true }
/tasks/:idwrite:tasks{ success: true }
/milestones/:idwrite:milestones{ success: true }
/comments/:idwrite:comments{ success: true }
/folders/:idwrite:folders{ success: true } — projects inside move to top level

Example

# List projects
curl https://www.sprintrr.ai/api/mcp/projects \
  -H "Authorization: Bearer sk_live_your_key_here"
 
# Create a task
curl -X POST https://www.sprintrr.ai/api/mcp/projects/PROJECT_ID/tasks \
  -H "Authorization: Bearer sk_live_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{ "title": "Fix login bug", "priority": "High" }'
{
  "id": "…",
  "title": "Fix login bug",
  "status": "Not Started",
  "priority": "High",
  "category": "Development"
}

Permissions & scopes

Permissions are strings of the form action:resource. The valid set is:

read:projects     write:projects
read:tasks        write:tasks
read:milestones   write:milestones
read:comments     write:comments
read:folders      write:folders
read:user
read:blog         write:blog   (admin-only — see "Blog (admin-only)" above)

Wildcards are supported when matching: * (everything), *:projects (any action on projects), read:* (any read). A new key is created with all eleven non-blog permissions by default; pass a permissions array to POST /api/user/mcp-keys to issue a narrower key (for example, a read-only key for reporting), or to grant read:blog / write:blog on a key that belongs to an admin user. Non-admin keys never see the blog endpoints regardless of the scopes they claim, thanks to the isAdminUser() server-side re-check. Archive and move-to-folder operations on projects fall under write:projects (no separate scope).

Managing keys programmatically

These endpoints use your app session (not an MCP key) and live under /api/user/mcp-keys:

MethodBodyResult
GET{ keys: [{ id, name, key_prefix, permissions, last_used_at, use_count, expires_at, created_at }] }
POST{ name (1–100)*, permissions?: string[], expiresInDays?: 1–365 }{ key: { …, apiKey }, message }apiKey is the full key, shown once
DELETE{ keyId: <uuid> }{ success: true }

A maximum of 10 keys per account is enforced on create.

Activity attribution

Every create/update/delete made through the MCP server is recorded in your Activity feed with source: mcp and the name of the key that made the change, so MCP-driven edits are always distinguishable from manual ones. Status transitions map to specific actions: moving a task/milestone to Completed/Done records completed; another status change records status_changed; changing assignees records assigned; otherwise updated.

Errors

StatusMeaning
400Invalid JSON body, or a required query param (e.g. q) is missing
401Missing, malformed, invalid, or expired API key
403The key lacks the required permission scope
404The project/task/milestone/user was not found (or not yours)
500Unexpected server error

Troubleshooting

  • 401 Invalid or expired API key — the header must be exactly Authorization: Bearer sk_live_…. Confirm the key hasn't expired or been revoked; if in doubt, create a new one.
  • 403 Permission denied — the key was issued without the scope this call needs. Recreate it with the default (full) permissions or the specific scope.
  • "I lost my key" — keys are unrecoverable by design (only a hash is stored). Revoke the old one and create a new key.
  • Pointing at staging/self-hosted — set SPRINTRR_API_URL in the MCP client env block.