DEV Community

Cover image for My AI assistant kept guessing about data breaches. So I gave it one free URL (MCP, no API key)
Devanand Premkumar
Devanand Premkumar

Posted on

My AI assistant kept guessing about data breaches. So I gave it one free URL (MCP, no API key)

Ask an AI assistant if your email has been in a data breach. You'll get a neat, confident answer. And it's a guess.

It's not really the model's fault. It can't look anything up, so it fills the gap from old training data. But a guess is a bad answer when the question is about your own account.

I run XposedOrNot, a free and open-source breach index. I kept copying API results into chat windows by hand, and it felt silly. So I added an MCP endpoint to the API.

(New to MCP? It's a standard way to give an AI assistant tools it can call. You point it at a server and it works out when to use what.)

Here's what I wanted from it. A lot of breach lookup servers ask for a paid API key or a local install before they do anything. This one asks for nothing.

No key. No signup. Nothing to install.

Just a URL, and it's free.

Set it up (about two minutes)

Using Claude Code? It's one line:

claude mcp add --transport http xposedornot https://api.xposedornot.com/mcp
Enter fullscreen mode Exit fullscreen mode

Cursor users, put this in ~/.cursor/mcp.json:

{
  "mcpServers": {
    "xposedornot": { "url": "https://api.xposedornot.com/mcp" }
  }
}
Enter fullscreen mode Exit fullscreen mode

And for VS Code, it goes in .vscode/mcp.json:

{
  "servers": {
    "xposedornot": { "type": "http", "url": "https://api.xposedornot.com/mcp" }
  }
}
Enter fullscreen mode Exit fullscreen mode

Now ask a normal question. Something like "has my email been in any breaches, and what leaked?"

You don't have to name a tool. The assistant reads the tool list and picks one.

Want to see what the assistant sees? Use curl

I like this part the most. There's no magic in MCP. It's JSON over HTTP, one request per POST, so you can call the server yourself:

curl -s -X POST https://api.xposedornot.com/mcp \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' \
  | jq -r '.result.tools[].name'
Enter fullscreen mode Exit fullscreen mode
check_email_breaches
get_breach_analytics
list_breaches
domain_breach_summary
get_breach_metrics
get_recent_breaches
Enter fullscreen mode Exit fullscreen mode

You get six tools back. Here's what each one is for:

You ask Tool You get
Has this email been breached? check_email_breaches Breach names
How bad is it? get_breach_analytics Dates, data types, a risk score
Which breaches hit this company? list_breaches Catalog entries by domain or breach ID
How exposed is my domain? domain_breach_summary Counts only
How big is the index? get_breach_metrics Totals per year and industry
What's new? get_recent_breaches Latest additions

You can call a tool by hand too:

curl -s -X POST https://api.xposedornot.com/mcp \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/call",
       "params":{"name":"domain_breach_summary","arguments":{"domain":"example.com"}}}' \
  | jq -r '.result.content[0].text'
Enter fullscreen mode Exit fullscreen mode
{"sendDomains": {"breaches_details": [{"domain": "example.com", "breach_pastes": 50, "breach_emails": 10000, "breach_total": 10474, "breach_count": 172, "breach_last_seen": "01-Aug-2026"}]}, "SearchStatus": "Success"}
Enter fullscreen mode Exit fullscreen mode

See that? Only counts. It won't list the email addresses behind a domain, and that's on purpose.

Four things I learned building it

Keep it stateless. No sessions and no SSE stream. Each request stands on its own (a GET just gets a 405).

Our early docs told people to add "type": "sse", and their connections broke because of it. That one's on me, so if you still have that line in your config, take it out.

Tell the client your tools are read-only. All six carry readOnlyHint: true and idempotentHint: true.

Why does that matter? The client can ask you for permission once, not on every single call. Nobody likes a tool that nags.

Write error messages for the model. If the assistant sends x as the email, it doesn't get a stack trace. It gets this:

'x' is not a valid email address. Provide a full address such as user@example.com.
Enter fullscreen mode Exit fullscreen mode

That's a sentence the model can read. So it can fix the call and try again.

Leave some tools out. There's no password tool here. Nothing returns a password, a hash, or a person's address from a domain lookup. We started with three tools in October 2025 and we're at six now, but that one stays out.

Free has a limit (and what to do past it)

Free doesn't mean endless. The limits are per IP: 25 email checks an hour and 100 a day. Give it fifty addresses and it'll stop at 25.

That's fine for checking your own accounts, one address at a time. But I'll be honest, people hit it fast.

Need more than that? Maybe you're putting breach checks into your own product, or checking a lot of users.

That's what xonPlus is for. Same breach data, but with an API key and much higher limits (up to 25,000 requests a minute).

And one ask from me. Please only check emails and domains that are yours to check. This is for looking at your own exposure, not someone else's.

It's all open

The code is MIT licensed. You'll find the MCP handler in main.py in the XposedOrNot-API repo. It's in the official MCP registry too, as com.xposedornot/xposedornot, if your client installs from there.

The MCP page lists every tool, and our blog post has the Claude Desktop steps as well.

Have you built an MCP server? I'm curious about one thing.

Could you keep it stateless, or did you end up needing sessions? Tell me what pushed you there.

Top comments (0)