Skip to content
Sign in

MCP server

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

on this page

    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.

    note:

    Everything the MCP server can touch is scoped to your account: your personal workspace and the teams you belong to right now. A key never reaches anyone else's personal workspace or a team you're not in, and every change it makes is attributed to that key in your Activity feed. See Teams and access.

    Create an API key

    1. Sign in to sprintrr and open Settings → sprintrr API & MCP keys.
    2. Name the key after where it lives (e.g. "Claude Code · work laptop") and click Create key.
    3. Copy the key immediately — it is shown once and only the prefix (sk_live_xxxx…) is stored afterward. The same panel shows a ready-made claude_desktop_config.json and the Claude Code command with your key filled in.

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

    Keys are generated as sk_live_ followed by 24 random bytes and stored only as a SHA‑256 hash.

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

    tip:

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

    json
    {
      "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.

    note:

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

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

    Or the stdio variant if you prefer:

    json
    {
      "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.

    warning:

    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 panel → MCP Servers → Manage MCP Servers → View 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):

    json
    {
      "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 (opens in a new tab).

    Environment variables

    VariableRequiredDefaultDescription
    SPRINTRR_API_KEYYes—Your 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. In a team, only the project's creator or the team owner
    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 deep — parentId 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, purchasedCredits, planType, resetDate, trialExpired } — your own balance, not a team's pool. availableCredits is what the next extraction can spend (on the trial at most its allowance: 3, or 5 after a referral; packs included on a paid plan)
    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 → sprintrr API & MCP keys.

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

    bash
    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):

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

    json
    {
      "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.

    bash
    # 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.

    warning:

    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:

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

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

    Every request must send:

    text
    Authorization: Bearer sk_live_your_key_here

    Responses are JSON. Errors are { "error": "message" }, plus a code where the cause is specific, with the status codes listed under Errors.

    GET

    PathQueryScopeReturns
    /projectsteamId?read:projectsProject[] — excludes archived
    /projects/archivedteamId?read:projectsArchived Project[]
    /projects/:id—read:projectsProject + analytics
    /projects/:id/tasksstatus?, priority?read:tasksTask[]
    /tasks/:id—read:tasksTask
    /tasks/:id/comments—read:commentsComment[] (threaded one level deep)
    /projects/:id/milestones—read:milestonesMilestone[]
    /milestones/:id—read:milestonesMilestone + tasks
    /foldersteamId?read:foldersFolder[]
    /user/profile—read:user{ id, email, fullName, avatarUrl }
    /user/credits—read:user{ availableCredits, usedCredits, totalCredits, purchasedCredits, planType, resetDate, trialExpired } (your own pool; availableCredits = spendable now)
    /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

    bash
    # 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" }'
    json
    {
      "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:

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

    Teams and access

    A key acts as the account that created it, with the same reach as that account in the app:

    • Workspaces. Personal projects you own, plus the projects of every team you're a member of now. When you leave a team, or its owner removes you, your keys stop reaching it on the next request.

    • Anything else answers 404. A project, task, milestone, comment or folder outside your reach looks exactly like one that doesn't exist. Passing a teamId you're not a member of (on create_project, create_folder, list_*) is a 404 too.

    • Team rows stay in their team. Tasks and milestones take their team from their project; you can't move a project between workspaces, and a project only moves into a folder of its own workspace.

    • Deletes follow the app's rule. In a team, only the row's creator or the team owner can delete a project, task, milestone or folder. Anyone else gets 403 with code: "delete_forbidden".

    • Assignees must be in the workspace. assignedTo on a team task may name only team members (on a personal task, only you). Anyone else is a 400 with code: "assignee_not_in_workspace".

    • Your plan must be active. Every call checks that your account has access right now: an active plan or trial, or a seat in a paid team. Without it the API answers 402:

      json
      {
        "error": "Your plan doesn't cover this right now.",
        "code": "access-required",
        "reason": "trial_expired"
      }

      reason says why (for example trial_expired, or team_seat_lapsed with the team's id and name). Pick a plan in the app and the key works again within about a minute.

    Transcript extraction is charged

    POST /api/mcp/transcripts/extract (body { projectId*, transcript* }, scope write:tasks) proposes task changes from a meeting transcript, like Transcripts → tasks in the app, and costs 1 AI Credit: from the team's pool when you hold a seat in the project's team, otherwise your own. The credit is taken before the AI call and given back if it fails. With your own AI key nothing is charged. The other MCP calls cost nothing.

    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, a required query param (e.g. q) is missing, or code: "assignee_not_in_workspace" / "invalid_folder" / "invalid_milestone"
    401Missing, malformed, invalid, or expired API key
    402code: "access-required": your plan gives no access right now (see Teams and access); for transcript extraction, also "trial-expired" or "credits-exhausted"
    403The key lacks the required permission scope, or code: "delete_forbidden" (only the creator or the team owner can delete it)
    404Not found, or outside your workspaces (the two look the same)
    500Unexpected server error
    503code: "credits-unavailable": credits couldn't be checked, nothing was charged; retry in a minute

    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.
    • 402 access-required — your trial or plan has ended (or your team seat lapsed). Pick a plan in the app; the key works again within about a minute.
    • A team project suddenly 404s — you're no longer a member of that team. Ask its owner to invite you again.
    • "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.

    Something unclear or out of date? Email support@sprintrr.ai — the subject line already names this page.