sincewhen is a remote MCP server: streamable HTTP at https://sincewhen.dev/mcp, authenticated with an API key or, for clients that will only do OAuth, an OAuth connection you approve in the browser. Nothing to install, nothing running on your machine. Your agent gets eight tools — the headline one being changes_since, which answers "what changed across everything I watch?" as a single markdown document.
Skip this if you are connecting the Claude desktop, web or mobile app — those use OAuth and never see a key.
One call. No CAPTCHA, no email confirmation loop — an agent can do this itself.
curl -X POST https://sincewhen.dev/api/auth/signup \
-H "Content-Type: application/json" \
-d '{"email":"you@example.com","password":"at least 10 chars","create_api_key":true}'
The key comes back once, in api_key.key, and looks like sw_live_…. Store it; we only keep its hash. Already have an account? Mint one in the dashboard.
No key, and nothing to paste but the URL. In Claude's settings, add a custom connector pointing at:
https://sincewhen.dev/mcp
Claude discovers the rest on its own. You will land on a sincewhen page asking you to sign in and approve the connection, and it says exactly what the connection can do before you approve it. You can revoke it at any time from the dashboard, under Keys & apps; revoking is immediate and does not affect your API keys.
These apps authenticate remote MCP servers with OAuth and have no field for a bearer header, which is why an API key cannot connect them — the flow above is the supported path, not a workaround. On a Team or Enterprise plan, adding a connector is an admin action, so your workspace owner does the same thing once.
claude mcp add --transport http sincewhen https://sincewhen.dev/mcp \ --header "Authorization: Bearer sw_live_…"
Add --scope user to make it available in every project rather than just this one.
In ~/.cursor/mcp.json for every project, or .cursor/mcp.json for one:
{
"mcpServers": {
"sincewhen": {
"type": "http",
"url": "https://sincewhen.dev/mcp",
"headers": { "Authorization": "Bearer sw_live_…" }
}
}
}
In .mcp.json at your project root. Note the root key is servers here, not mcpServers — the one difference between these configs, and the usual reason a copy-paste silently does nothing:
{
"servers": {
"sincewhen": {
"type": "http",
"url": "https://sincewhen.dev/mcp",
"headers": { "Authorization": "Bearer ${input:sincewhen-key}" }
}
},
"inputs": [
{ "id": "sincewhen-key", "type": "password", "description": "sincewhen API key" }
]
}
The inputs block makes the editor prompt for the key instead of you committing it. Worth doing in any config that lands in git.
Before trusting a config, ask the server directly. This is the same handshake your client performs:
curl -s https://sincewhen.dev/mcp \
-H "Authorization: Bearer sw_live_…" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
You should see eight tools. A 401 means the key is wrong or revoked. If you prefer a UI, npx @modelcontextprotocol/inspector speaks the same protocol.
An approved application gets what an API key gets, and nothing more: it can read your watchlist and diffs, and add or remove watches. It cannot change your password, create or revoke API keys, generate recovery codes, or delete your account — those need you, signed in, with your password. A connection is also ended by anything that recovers your account: a password reset or a redeemed recovery code disconnects every app, on the reasoning that recovery exists to evict whoever should no longer be there.
| Tool | What it does |
|---|---|
| changes_since | Everything that changed across your watchlist, as one markdown document with diffs. Pass use_cursor: true and the server remembers your read position, so a stateless agent never has to track timestamps. |
| ack_changes | Advance that read position once you have acted. Until you do, the same batch is replayed — an agent that dies mid-work loses nothing. |
| add_watch | Watch a URL. Optional css_selector narrows it to one region, so nothing outside that region can produce a change. |
| get_diff | The full unified diff for one change. |
| get_current_snapshot | A page as we last read it, without fetching it yourself. |
| list_watches | Watches with status and last-changed times. |
| remove_watch | Stop watching. |
| account_status | Plan, limits, usage. |
Full reference, including the REST and RSS equivalents, is in the docs.
When changes_since reports nothing, it also tells you how many pages that claim actually covers, and names every page it could not check — failing fetches, pages that render in a browser and give us nothing to read, watches whose selector stopped matching. "No changes" is only worth something if it means we looked, so we never let it stand in for we could not look. Expect the gaps section; it is the feature, not an error.