The MCP server.
How a coding agent uses Perfloop to find performance wins and correctness bugs: the first run, the core jobs as tool calls with example arguments, and every tool a member can call.
Your agent controls Perfloop. Model builds and experiments run on Perfloop and use Perfloop Usage. Your conversation still uses your coding agent's tokens.
Start here
Perfloop finds performance wins and correctness bugs in your code and brings back a change only when it is proven. A workspace has nothing to show until an Admin selects repos in Setup. These steps need a person; your agent does the rest:
- Get a Perfloop account: follow step 1 of the Quickstart at perfloop.ai/docs/work/quickstart, or accept an invitation to a workspace.
- An Admin connects a code host, such as GitHub, and selects repos in Setup in the Perfloop app. For GitLab.com or self-managed GitLab, follow perfloop.ai/docs/connect/gitlab; enter the dedicated bot token only in the Setup form, never in chat or an MCP call. For GitHub Enterprise Server, follow perfloop.ai/docs/connect/github-enterprise; never put the App private key in chat or an MCP call. Selected Cursor Origin repos also complete Setup when that code host is enabled and connected and at least one selected repo has a usable route. Perfloop works only on selected repos. Setup stays unfinished until at least one selected repo can enter a model build. If no model build starts after the selection, your agent can start one with buildModel, which is paid.
- Add https://app.perfloop.ai/mcp to your coding agent and sign in with your Perfloop account in the browser window it opens. The steps for each agent are under Connect on perfloop.ai/docs/work/mcp.
- If work is refused for lack of funds, an Admin can add Usage on the Usage page in the Perfloop app.
- Ask your agent to list your Perfloop Cases, or to submit your branch.
Each job below is one task: its calls in order, with example arguments, and at most one call that changes anything. Do only the job the user asked for. Replace each ID, and approve's generation, with the value a previous call returned.
Find performance hypotheses on a branch
model{"section": "repos"}: a page of repos, each repo_ ID with inScope; a new diff needs an in-scope repo. While pageInfo.hasNextPage is true, pass pageInfo.endCursor as after to read the next page.submitDiff{"repoID": "repo_…", "ref": "my-branch"}: a diff_ ID. For a pull request from a fork, executionConsentRequired is true until the member approves running that exact head: ask once with the diff's headRevision, then call submitDiff again with approvedHead set to it. A pull request from the repository itself needs no approval.diff{"id": "diff_…"}: the status; while it is tracing, discovery is still running, so read it again later. An admitted diff gives the ranked findings and nextCall for the top finding. At a fork pull-request head the member has not approved, nextCall is a consent-required template; its advisory names the exact head and the member approval step. A rejected or error diff gives its reason and relay, which you report instead.
Discovery on a new head is paid work. When your user asks to run Go benchmarks during discovery, pass measure: true to submitDiff. For a fork PR, include approvedHead only after the member approves running that exact head. This work uses public Go dependencies only and no workspace build cache, even for a PR from the repository itself. Keep https://proxy.golang.org/, https://sum.golang.org/ and https://storage.googleapis.com/proxy-golang-org-prod/ allowed in Setup under Network access. Other workspace destinations are unavailable. A later benchmark request has its own diff_ ID and leaves the completed review unchanged; repeating it reuses that diff. If the diff is still tracing when you stop waiting, give your user its ID to read later. Admitted findings link to Cases whose Evidence shows the cited samples and full benchmark receipt. These observations are not proof of a change. On a Cursor Origin repo, also pass headRevision and baseRevision; its code host cannot resolve a branch head or merge base for Perfloop.
Read a Case's status
cases{"caseID": "case_…"}: the Case's state, open reviews with their text and source links, latestSession with its recorded outcomeExplanation or stop reason, active action, delivered PR, and Case usage for Admins.
The Case result and latest Session are separate facts: a verified Case can have a later Session that ended without a Candidate. Report the open feedback and the recorded end context, not a budget cause inferred from spend. A large summary uses result pages: follow nextContinuation and check the joined text against sha256. Add sections to read several parts of the Case with the overview in one call.
See what Perfloop is working on
cases{}: the newest Cases with their state; filter with repo, state, or diffID.cases{"caseID": "case_…", "section": "evidence"}: a page of the Case's Candidates, each with its verdict and reason. While pageInfo.hasNextPage is true, pass pageInfo.endCursor as after to read the next page.cases{"caseID": "case_…", "section": "evidence", "candidateID": "cand_…"}: checks and runs for one Candidate. Official measurements are in candidate.runs[].decision; benchmarks lists only historical rows.
A Case in state verified is proven. A Case in state refuted was disproved, and its record stays. A finished Case's summary, cases {"caseID": "case_…"} with no section, carries relay, its result to report to your user; when the proof came from an earlier attempt, relay says so.
Download a Candidate's retained proof
cases{"caseID": "case_…", "section": "proof", "candidateID": "cand_…"}: candidateProofLink with url, sha256, decimal size and expiresAt for the complete retained ZIP. Fetch url as-is within ten minutes; no second login or API key is needed. Check the downloaded byte count and SHA-256 against size and sha256 before using the file.
The ZIP contains retained source and proof patches, the source bundle, and a manifest with recorded checks, raw run results and execution details. Preserved evidence includes its source history and review explanations. The link is a secret: do not log, publish or share it. It opens only while the member retains read access. If it expires or retained evidence changes, repeat the same read for a new link. API-key automation reads the Candidate's authenticated proofURL. This read starts no paid work and approves or publishes nothing.
Prove a finding
workCase{"hypothesisID": "hyp_…", "at": {"diffID": "diff_…"}}: the Case, and the started Session's ID and status. sessionError means the start outcome is unknown: read the Case's timeline section for its Sessions before you call workCase again. Pass caseID instead for an existing Case.cases{"caseID": "case_…", "section": "timeline", "sessionID": "sesh_…"}: that Session's timeline rows; sessionEndedAt is set once it has ended. While it is not set, the Session is still working: read again later. If you stop waiting, give your user the case_ and sesh_ IDs.cases{"caseID": "case_…"}: the Case's state after the Session ended. verified was proven and refuted was disproved; stopped means the Case closed without a result. latestSession identifies the newest Session: if pending or running, wait on its sesh_ ID; if ended, report its sessionResult and recorded outcomeExplanation or stop reason. A replacement may not have started yet: no live Session does not mean the Case's work is complete. Give your user the case_ and newest sesh_ IDs for a later read. perfloop.ai/docs/work/states gives the next step for each state. Report only this state and its evidence.
workCase starts paid work. Use the diff's nextCall arguments when they are exact: for a branch, a pull request from the repository itself, or a fork head the member approved in submitDiff. At any other fork head, nextCall is a consent-required template without at.approvedHead. Follow its advisory: ask the member to approve running that exact head's build and test code, including code from an outside contributor. Only after explicit consent, add at.approvedHead with the diff's headRevision. Source review is not execution consent. A different head needs new consent. When the pull request's head is in another repository (a fork), that Session gets no Network access destinations and no build cache from your workspace, and its tools read none of your connected telemetry, retained profiles, or retained research.
Steer a Case
fileFeedback{"caseID": "case_…", "feedback": "The benchmark must use the production batch size."}: recorded failing feedback on the Case, which also holds the Case's publication until the feedback is settled.
Failing feedback can let Perfloop start paid work on the Case and holds publication.
Stop a Case's running Session
stopSession{"sessionID": "sesh_…"}: the Session's status when you asked, and stopRequested: true when Perfloop requested the stop; for a Session that had already finished, stopRequested is false and nothing changes.cases{"caseID": "case_…", "section": "timeline", "sessionID": "sesh_…"}: that Session's timeline rows; sessionEndedAt is set once it has stopped. While it is not set, read again later. If you stop waiting, give your user the case_ and sesh_ IDs.
The Session ID comes from workCase's answer or from a row of the Case's timeline section. Stop ends only that Session. Automatic work can start another Session later; closeCase holds future work. A model-build, discovery, submission or research Session has no Case: stop it the same way; for a discovery Session, diff {"id": "diff_…"} shows the latest Session state in sessionStatus. stopSession's answer gives its status when you asked; stopRequested means a stop was requested, not that the Session has ended.
Close a Case
closeCase{"caseID": "case_…", "reason": "Not a priority."}: a closed Case.
Closing holds future work and keeps unanswered feedback, saved approvals and evidence. A running Session keeps going; stop it with stopSession. workCase and fileFeedback refuse a closed Case. Follow their reopenCase guidance only when they show it; some closures are final, and a live Session must end before reopening.
Approve a publication
inbox{}: a page of pending items; a publication approval has approval.action publish and completionPaths.approve with its exact arguments. While pageInfo.hasNextPage is true, pass pageInfo.endCursor as after to read the next page.inbox{"decisionID": "appr_…"}: the first page of the full proposal and its reviews, with sha256 and nextContinuation. When nextContinuation is null, this page is the whole document.inbox{"decisionID": "appr_…", "continuation": "…"}: the next page; call it only while nextContinuation is not null, passing that value, until it is null. Then join the pages, even a single one, and check them against sha256. Show your user the proposal and its reviews.approve{"approvalID": "appr_…", "generation": 2, "baseCommit": "…"}: an approved publication, which authorizes Perfloop to open the pull request, or only the issue when publicationIssue.issueOnly is true; nothing is open yet. Call it only when your user decides to approve this generation.cases{"caseID": "case_…", "section": "changes", "interventionID": "intv_…"}: the approved change, named by approve's approval.interventionID, with its phase: approved or publishing while delivery waits or runs, so read again later; approved with holdReviewID while a review holds it, which you report; open with prURL once the pull request exists, or, for an issue-only publication, open with publicationIssue.url and no prURL; failed when delivery ended without one, which Perfloop does not retry, so tell your user. deliveryFailedAt marks a failed attempt while it stands.
Approval is your user's decision, not yours; a passing sha256 check proves the document is whole, not that they agree. Copy approve's arguments from completionPaths.approve, including the full baseCommit for a source publication. Show your user that base from the decision read: a pull request keeps it, does not rebase onto the latest target, and requires a clean merge. A missing or different base is refused; read the decision again before approving. A generation that changed since you read it is refused. If your client does not list baseCommit, refresh its tool catalog. When the decision shows a publicationIssue without a url, show your user its title and body and pass openIssue: true; when issueOnly is true, no pull request follows, but still pass baseCommit as completionPaths.approve gives it. Approving a run_case Approval instead starts paid work. When a pull request opens, you review and merge it on your code host.
Check the Usage balance
usage{"balance": true}: the remaining Usage balance; an Admin always sees it, and members who can contribute see it when an Admin shares it.
Tools that start paid work say so in their description. An Admin reads the full statement with usage and adds Usage on the Usage page in the Perfloop app. Session activity includes repoID when the Session records a repo, and initiativeID for Research. These IDs identify the work's recorded scope.
What an agent must not assume
- A finding from submitDiff is not a proven change. Only a Case proves a change.
- Proof needs a Case: call workCase, wait until its Session has ended, then read the Case's state and evidence.
- Perfloop opens pull requests. The approve tool authorizes Perfloop to open one; nothing in Perfloop approves or merges a pull request on the code host, or deploys.
- An empty answer can mean Setup is not finished. Then, for a member or an Admin, an empty first page of model or cases carries extensions.setup, with the Setup page URL and the one step a person must take there; relay that step. model repos show inScope.
Connect
The server is at https://app.perfloop.ai/mcp. It requires a login: the agent signs in with your Perfloop account, and your role in the workspace decides what it can read or change. There is no anonymous access.
Works with any coding agent that supports remote MCP over HTTP with OAuth. Add the URL to your agent, then sign in. The agents below are examples, with the same steps as Setup:
Claude Code
claude mcp add --transport http perfloop https://app.perfloop.ai/mcpOpen Claude Code, run /mcp, choose perfloop, and sign in.
Cursor
{
"mcpServers": {
"perfloop": {
"url": "https://app.perfloop.ai/mcp"
}
}
}Save this as ~/.cursor/mcp.json, then open Settings → Tools & MCP and authenticate perfloop.
Antigravity
{
"mcpServers": {
"perfloop": {
"serverUrl": "https://app.perfloop.ai/mcp"
}
}
}In Antigravity's MCP Servers menu, open Manage MCP Servers → View raw config. Add this entry, save, then authenticate perfloop under Customizations.
Codex
codex mcp add perfloop --url https://app.perfloop.ai/mcp
codex mcp login perfloopThis needs Codex 0.136 or later. Codex opens your browser to finish the Perfloop sign-in.
OpenCode
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"perfloop": {
"type": "remote",
"url": "https://app.perfloop.ai/mcp"
}
}
}Add this to opencode.json, then run opencode mcp auth perfloop.
Pi
{
"mcpServers": {
"perfloop": {
"url": "https://app.perfloop.ai/mcp"
}
}
}Pi needs the pi-mcp-adapter extension. Run pi install npm:pi-mcp-adapter, save this as .mcp.json, reload Pi, then run /mcp-auth perfloop.
Amp
amp mcp add perfloop https://app.perfloop.ai/mcpRestart Amp and complete the browser sign-in.
For automation with nobody present, a member API key with read,contribute stands in for the login. Add admin to that key when the automation reads Usage or changes workspace setup. Ask Perfloop for such a key; Setup manages relay tokens only.
Install only the skill with npx skills add https://perfloop.ai. It tells your agent how to connect to this server and use it.
Sign in again
If you have no Perfloop account, follow step 1 of the Quickstart or accept an invitation first. Then sign in again from your agent. In Codex Desktop, open a new chat after signing in.
What the agent can do
Reads return the model, cases, Initiatives, the Inbox, Usage, submitted ideas, and diffs, in bounded pages. Writes start or stop Sessions, file feedback, decide Inbox items, and change an Initiative's direction. Every write is recorded under the authenticated user or API key. The server cannot record a result, and it cannot approve or merge a pull request on the code host.
Admins can add a public HTTPS prefix in Setup under Network access, or allow a requested path or its exact host from the Inbox. Each destination grants access across the workspace. A wildcard host covers its proper subdomains, not the root host. An allowed destination can receive source code or other sandbox data through the URL, headers or body.
Destinations have no count limit. Setup shows the stored URL and lets you replace or remove it. Removing a destination removes that grant. Another destination, either enabled network switch, or a separate service rule can still permit access.
Perfloop can end a case Session when required network access prevents further work. It saves its progress and a list of required destinations. Destinations that need an Admin decision appear once in the Inbox. When a Case records a need for registry credentials, an Admin must save a covering private registry in Setup; an ordinary Allow does not meet that need. Perfloop can continue in a fresh Session after all its required access is configured. Partial access starts no work. Existing Usage and case limits still apply.
Connecting a GitHub repo supplies no package credential. For private NuGet feeds, follow the GitHub Packages guide. A saved registry permits reads within its prefix without a separate destination Allow. It grants no package publication. Destination permission alone does not solve authentication. Saving a registry does not prove that its token works, and 401 or 403 errors do not pause work automatically.
Network access also has two Admin switches, off by default for a new workspace. Each change is recorded. Turning one on asks you to confirm; turning it off removes only that switch's authority on the next request. Allow downloads the repository declares permits exact URLs written on a trusted repo's default branch. Whoever can change that branch chooses these addresses. The full URL, including its path and query, can carry data out. These downloads send no client headers, body or credentials.
Allow all public destinations lets Perfloop's work choose public HTTPS destinations without an Inbox request. Requests can send source code and other data in the full URL, headers or body, including caller-supplied credentials. The proxy adds none. Ordinary reads use GET or HEAD; Buf's fixed read calls can also use POST. Read requests can cause changes at a remote site. Existing route and address checks still apply. A Reject blocks its recorded host and first path segment through either switch. An explicit allowed destination can still permit it.
The Inbox keeps at most 100 pending or allowed network requests per workspace. At capacity, new requests replace allowed rows first, then the oldest pending request whose requesting Session has ended. Saved case waits keep their pending requests and can exceed that limit. A new blocked request can return an allowed entry to pending after its grant is removed. A rejected request stays rejected while the workspace exists, so its host and first path segment do not return to the Inbox. To allow it later, add it in Setup.
Model building
Call model with section: "buildStatus", then call buildModel to start one Session. It accepts:
modelSeq(required): the model sequence returned in that build status. The build records the live model sequence at start; a stale value does not reject the request.repoID(optional): the repo to build. Omit it to let Perfloop choose the next repo.focus(optional, requiresrepoID): an exact repo-relativepathto a source file or directory. Addnamefor a case-sensitive source name as written under that path, such asServer.serve. The build stops with a named gap if the name is not written there.patternSlugs(optional, requiresrepoID): active pattern slugs from the catalog. Omit it for a broad build. An empty list, repeated slug, or unavailable pattern is rejected.
For example, use your model sequence and replace repo_… with your repo ID:
{
"modelSeq": 7,
"repoID": "repo_…",
"patternSlugs": [
"identity_collision",
"resource_leak"
]
}With selected patterns, the Session reuses fresh mapping or maps first, then hunts for each pattern over the related paths. Completed work is reused; unfinished work stays pending. Large model context does not cause the Session to skip selected work. Session budget and time limits still apply. The result reports this bounded attempt, not coverage of the whole repo. Autonomous model building does not need to be enabled.
Tools
What each tool is for, grouped by what you do with it; your agent reads each tool's inputs from the server itself. These calls can lead to a paid Session now or later: fileFeedback, saveWatch, reopenCase, approve, submitIdea, submitDiff, workCase, buildModel, startResearch, and setResearchCadence. Each tool's description explains its effect. Tools only a workspace admin can call are not listed here; the catalog marks them. Every docs page, this one included, is also a resource on the server, so an agent can read it without leaving the session.
To read a Case's timeline or attempts, pass its caseID and the section. Add sessionID for one Session, or candidateID for one attempt. A redundant first on an exact read is ignored. To page the attempts, pass the previous candidateIndex.pageInfo.endCursor as after; a row number is not a cursor. Add order: "NEWEST" for recent rows first and first: 25 for a larger page. Keep the same order when you continue a page. Use sections to read several parts with the Case overview in one call.
One attempt, Case, or Initiative decision can be larger than a tool result may be. Then the read is refused with a nextContinuation. Repeat the same read with it as continuation to get the result in pages, join each page's text, and check sha256. To read one Initiative decision, pass its assessment as artifactID with section: "decisions".
Once a Session ends, its row in the timeline carries sessionResult and sessionEndedAt. A Session that stopped early also carries sessionStopReason, a fixed code that says why. outcomeExplanation carries its recorded finish explanation when available, including when it produced no Candidate.
provider_policy_refused means the model provider refused a request under its safety policy. provider_policy_rejected means the provider rejected the request's context.
Case work can end with an explanation when no useful permitted step remains, including when proof is unfinished. This does not accept a code change or settle feedback. Work that previously waited for an answer resumes automatically when its existing permissions and limits allow it. Publication holds remain in force; read them on the Case before allowing publication.
A Case summary and its hypothesis section return the hypothesis's forecast. The model's hypotheses section returns it too: size (S, M, L, XL, or unknown), reason, sourceChanged, and evidence artifact IDs. The forecast is null when no assessment is available. sourceChanged: true means the source differs from the assessment's saved source basis. A false value can be inconclusive when that basis is missing.
If a review stops early, check its saved findings and any reason given before you submit a new review.
See the work
casesList Cases, or read one with its open reviews, latest Session and why it ended, pull request, and Case usage for Admins. Read several parts of a Case in one call, including its evidence, attempts, and feedback.modelRead the model Perfloop keeps of your system, one part at a time: its repos, services, workloads, and paths, the patterns and hypotheses on them, and whether a build is running.initiativesList the Initiatives, with the workspace research cadence, or read one: its scope, its sources, its research runs, and its Cases.inboxRead what waits for a decision, newest first: a short list, or one item in full.submissionRead what became of an idea a coding agent submitted: its Case or why it did not admit, including all outcomes of earlier split submissions.diffRead what Perfloop found in a diff you submitted: its findings, their Cases, the pull-request comment, and the call that proves the top finding.usageRead the remaining Usage and below-$25 warning when the workspace Admin shares them. Full statements and Case totals require Admin access.signoffRead the name and email that sign off pull requests you approve, whether your workspace adds that sign-off, and your Add me as co-author choice.
Steer it
fileFeedbackFailing feedback holds publication and can let Perfloop start a paid Session on its own. A pass settles open member feedback and carries no direction, so file it only when no direction remains. Close the Case to hold its work; stop a running Session separately.sendFeedbackSend feedback about Perfloop itself to the Perfloop team when you ask your agent to; it writes the message from what you ask it to send. It changes no Case or Session; to direct a Case, file Case feedback instead.editInitiativeReplace an Initiative's title, outcome, rationale, disposition, and scope, as one new version. Changed text is checked first; a refusal saves nothing and says what to change.setSignoffNameChange the name that signs off pull requests you approve from now on. It changes only your own name and turns off Add me as co-author until you choose it again.saveWatchAdd, edit or turn off a Watch with its query, threshold, window, direction and check interval; choose whole minutes from 1 to 60, no longer than the window, with 5 as the default. Members who can start work can save Watches, and setup preserves their edits. Saving starts no Session; work a Watch later fires stays within the workspace's Case limits and Usage.closeCaseHold future Case work with a reason. Saved feedback, approvals and evidence stay intact. A Session already running keeps going until you stop it.reopenCaseReopen a Case whose page shows Reopen case: one a member closed, one the admission check closed, or one closed by a decision after its pull request closed unmerged. Its saved direction becomes available again, and Perfloop can start a paid Session on its own.
Decide what waits in the Inbox
approveApprove what an Inbox item proposes, exactly as shown. When that is work, this starts it.rejectReject Candidate publication to end the Case without repair work; a reason is optional. Use Case feedback to request repair instead. Rejecting a work proposal declines that work.resolveClose a Case whose first publication, a pull request or an issue, still waits for approval, because it was handled another way. Give the reason; nothing is opened.decidePRClosureWhen a pull request Perfloop opened was closed without a merge, say whether the Case closes or continues with another approach, or name the maintainer's own pull request that merged the change instead.
Start and stop work
submitIdeaPropose a report from your coding agent: what should change, in which file, and why. Your complete report stays together in one Case if admitted, with ordinary paid work and limits. Poll submission for the outcome without submitting again.submitDiffSubmit a pushed branch or a pull request for discovery; everyone who names the same branch head or pull request gets the same diff, and for a fork pull request the member can approve running its exact head in the same call. Findings stay at the diff's head, outside the main branch's model; discovery itself starts no Case Session, and a pull request gets one comment. A head over 50,000 files is rejected, before anything is spent when GitHub lists the whole head, otherwise when the diff's Session counts its checkout.workCaseStart or continue a Case by naming the Case or its hypothesis; name the submitted diff to run at its head. A fork pull request's head needs explicit member consent to run that exact head's build and test code, given here or when the diff was submitted; a pull request from the repository itself needs none. Source review is not execution consent; a different head needs new consent.buildModelBuild the model of a repository, or bring it up to date. You can point the next pass at one file or directory, or at one name in it.startResearchStart a research Session on an Initiative, or create the Initiative from an outcome, a rationale, and a repository and start its first one. A new Initiative's text is checked first; a refusal saves nothing and says what to change. While the workspace cadence is on, an active Initiative keeps getting research Sessions on that cadence, each one paid, until you pause or settle it or turn the cadence off.setResearchCadenceSet how often research runs on its own, one cadence for the whole workspace. It starts no Session itself: a cadence above zero authorizes recurring paid research Sessions on each active Initiative after its first one, and zero stops them.stopSessionStop only the running Session, whatever it is doing: Case work, a model build, an idea being checked, or research. Close the Case to hold future work.
Also in this section: Quickstart, The loop, Case states, Pull requests, Steering and learning, Initiatives, Autonomy and limits, and Members.
Questions: hello@perfloop.ai