DidWork for Cursor

The agent edits, runs the command, reads the output and reports the task complete. Everything it knows comes from its own execution — so a deploy that exits 0 against a service that never came up reads exactly like one that worked, and a refund call that returns 200 reads exactly like a refund that settled.

DidWork gives the agent a source that isn't itself: it forms a claim, DidWork asks the system that can prove it, and the agent gates on the verdict.

Install

One command, in your project:

npx @didwork/inspect init

It writes .cursor/mcp.json so the agent has the did_verify tool, and .cursor/rules/verify-outcomes.mdc so it reaches for it — the rule being the part that actually changes behaviour. Existing config is merged rather than replaced, running it twice changes nothing, and --dry-run shows the plan first.

Then reload the Cursor window so the MCP server starts.

Your key is never written into those files. They are project files you commit, so the config references DIDWORK_API_KEY and the key itself lives outside the repo:

mkdir -p ~/.didwork
printf '%s
' 'dk_your_key' > ~/.didwork/api_key
chmod 600 ~/.didwork/api_key

The key file works where an environment variable does not: an editor launched from the Dock or Start menu never sees what your shell exports. A free key is at didwork.sh/console; without one, http.ok still verifies keylessly and nothing is stored.

Or configure it by hand

Add the MCP server to .cursor/mcp.json in your project (or ~/.cursor/mcp.json for every project), then reload the window:

{
  "mcpServers": {
    "didwork": {
      "command": "npx",
      "args": ["-y", "@didwork/mcp"],
      "env": { "DIDWORK_API_KEY": "dk_your_key" }
    }
  }
}

Creating a key at didwork.sh/console also gives you a one-click Add to Cursor link.

There is also plugin packaging carrying the same rule plus /demo, /setup and /verify commands. It is waiting on the Cursor marketplace, so init above is the supported install today — and it writes the same rule the plugin would.

See it catch a miss

Two fixture endpoints exist for exactly this. One is permanently healthy; one permanently answers 503. Ask the agent to verify the broken one — or run it yourself, with no key and no account:

curl -s https://api.didwork.sh/v1/verify   -H 'content-type: application/json'   -d '{"type":"http.ok","expected":{"url":"https://didwork.sh/demo/broken"}}'

The verdict is FAILED with STATUS_MISMATCH and GET … → 503 as the evidence. Swap /demo/broken for /demo/healthy and the same check returns VERIFIED. That contrast is the product: the same check, two honest answers, neither self-reported.

What the plugin adds

PieceWhat it does
MCP serverdid_verify, did_get, did_list, did_watch, did_watches, did_unwatch, did_usage, did_inspect_tool
Rulerules/verify-outcomes.mdc — verify external side effects before reporting success
SkillThe claim types and the verify → gate workflow
Commands/demo, /setup, /verify (plugin only)

Cursor has no Stop-hook equivalent, so the rule does the work here. On Claude Code a Stop gate also blocks ending a turn when a side effect went unverified.

The rule

verify-outcomes.mdc is always applied. In substance it asks the agent for three things: verify each externally visible outcome before calling it done, gate on the verdict rather than on its own output, and never read UNKNOWN as success — poll or escalate instead.

That last one matters more than it looks. "I could not establish this" and "this is true" are the two states most reporting collapses into one.

Point it at your project

Ask the agent to look at what the repo actually touches — dependency manifests, .env.example variable names, CI workflows, a deployed URL — and map that to claim types. Stripe in the dependencies means stripe.refund and stripe.subscription_active are available to it; a GitHub remote means github.pr_merged and github.workflow_passed are.

Then connect those providers at didwork.sh/console/providers with read-only credentials. With the plugin installed, /setup does this walk for you and backfills verdicts over your recent merged PRs and CI runs.

What it can verify

Nearly forty claim types across Stripe, GitHub, GitLab, Linear, Jira, Sentry, Slack, email, and any public URL — the full table is in the docs. What each one refuses to call success is the useful part:

ClaimWhat it refuses to call success
github.pr_mergedclosed but not merged
stripe.refundpending, wrong amount, or a duplicate
linear.issue_completedcancelled — which is not resolved
github.release_publishedstill a draft
http.okany status you did not expect

The rule, if you skipped init

The MCP tools alone change nothing — an agent with a verifier it never reaches for still grades its own work. init writes this for you; if you configured by hand, put it in .cursorrules or an always-applied rule under .cursor/rules/:

Before reporting a task complete, verify each outcome you claim
with did_verify. Report done only on VERIFIED. Surface FAILED and
UNKNOWN instead of retrying silently.

Troubleshooting

No did_verify tool. Reload the Cursor window — MCP servers start with it. Check the MCP panel in settings for a startup error.

Everything is UNKNOWN. The claim named a system DidWork has no read credential for. Connect it at didwork.sh/console/providers.

The agent verifies once and then stops bothering. The rule is advice, not enforcement. If you want something that refuses to let an unverified side effect pass, that is the Stop gate on Claude Code, or the merge gate in CI.

Next

Make it a merge requirement with DidWork for GitHub Actions, or wire another host yourself with the MCP server.

DidWork: verification infrastructure for software outcomes