I built a small MCP server that tells AI assistants what is for lunch at a nearby restaurant. It scrapes a public menu, normalizes the result, and returns it as JSON. Oh, by the way, you need an account in our corporate Entra tenant to access it. This is, by any reasonable measure, one of the least threatening applications ever deployed to the internet.
Google classified it as a deceptive site.
Chrome started throwing a full-page Safe Browsing warning specifically on the callback URL, and Search Console reported Deceptive pages. Excellent. Very actionable. So I did what any reasonable person would do: I assigned a small army of Codex agents to investigate why Google thought my lunch was dangerous.
Technically Correct?
The application uses OAuth because the MCP endpoint is private. Authentication happens at Microsoft, through a corporate Entra ID tenant. The application never sees a password, never displays a password field, and never asks the user to type credentials anywhere except Microsoft’s own login page.
The flow was also technically sound. The MCP client registered itself, the authorization server validated its callback, the user saw a consent screen, Microsoft authenticated the user, and the tokens went where they were supposed to go.
Google did not know it was a lunch menu, Google knew it had bad vibes.
The Fix Was a Website?
My first instinct was to look for a security bug. Open redirect? Leaked OAuth state? Some exciting new way to steal tokens through a soup recommendation? Nah, I know my way around this stuff.
After some research, it turns out Google doesn’t like random public API endpoints.
So I added a public landing page explaining what the MCP does, who runs it, how authentication works, and that the application never receives Microsoft passwords. No trackers, no remote widgets, no credential form, no mysterious button asking users to “continue.” Just enough information for a human—or a crawler—to understand what they were looking at.
Then I configured the MCP’s own name, website URL, and icon in the MCP server metadata. The consent page now identifies the application instead of falling back to generic framework branding. The icon is served from the same origin, because loading your identity from an unrelated domain is a strange way to establish that you are definitely not impersonating anyone.
Finally, I replaced the browser-visible errors on /auth/callback and /consent. They now use fixed, locally rendered the pages that explain the failure without echoing query parameters, authorization codes, state values, tokens, redirect URIs, or exception messages. Successful OAuth responses still pass through untouched; only errors get the safe, boring treatment.
So now everything works, right? Hell no. You can get blocklisted automatically, but to get removed, someone needs to decide that my lucnh menu MCP is legit. When or if that happens, God knows.
The Actual Point
- Security is not only about whether a protocol can be exploited. It is also about whether a user can tell what the hell is happening.
- A browser vendor can decide anytime that your internal, corporate systems will be blacklisted
- Google safe browsing can kiss my butt