Is Your .cursor/mcp.json Exposed? The Obvious Check Lies (2026)

Run this against Vercel's own website right now:

The check everyone reaches for
$ curl -s -o /dev/null -w "%{http_code}\n" https://vercel.com/.cursor/mcp.json
200

Vercel is not leaking its Cursor config. That 200 is 2.5 MB of marketing HTML, served by a catch-all route that answers every unmatched path the same way. We checked it on 2026-08-26 and got the same result twice.

That's the problem with the advice going around. Scanners really did add AI assistant config paths to their wordlists, the check people reach for really is a one-line curl, and that one-line curl gives a false positive on a large share of the apps this audience ships.

TL;DR

Scanners now request /.cursor/mcp.json and friends, with real MCP protocol handshakes rather than blind guessing. On Vercel and Netlify with a normal framework preset your file almost certainly isn't reachable, for two separate reasons. There's a narrow set of deployment shapes where it is. Checking takes thirty seconds, but only if you look at the content type and the body instead of the status code.

What was actually observed

On 2026-07-13, SANS Internet Storm Center handler Manuel Humberto Santander Peláez published a diary built from 14 days of Apache and ModSecurity logs on one small web host. The host ran no AI infrastructure at all. In his words: "None of the AI-agent infrastructure the scanners were looking for existed on this host. That is the point."

The requested paths:

PathWhat it would be
/.claude/mcp.jsonClaude Code MCP server config
/.cursor/mcp.json, /.cursor/mcp_config.jsonCursor MCP server config
/.vscode/mcp.json, /.mcp/config.jsonVS Code and generic MCP config
/.claude/settings.local.jsonClaude Code local settings
/.claude/.credentials.json, /.config/claude/.credentials.jsonStored auth token
/v1/models, /api/tagsUnauthenticated local LLM APIs
POST /mcp, GET /sseA live MCP server

The genuinely interesting part isn't the path list, it's the method. The scanners send correctly formed JSON-RPC 2.0 handshakes with protocol version 2025-03-26, not blind GETs. The diary puts it plainly: "The scanner is not blindly requesting a URL. It is speaking the protocol." Credential files got HEAD requests rather than GET, which the author reads as checking existence before spending bandwidth, a sign of "a mature, wide campaign rather than a one-off curiosity probe."

Keep this in proportion. All four AI categories together came to roughly 200 requests over 14 days, about 14 a day, and the diary explicitly frames that as the small bucket: "Spring Boot Actuator scanning dominates by request volume." This is one handler's logs from one host, self-reported, with no published dataset. It's evidence that AI paths are now in commodity wordlists. It isn't evidence that anyone is hammering your site.

Why your file is probably not reachable

Two independent things have to fail before a repo-root .cursor/mcp.json answers over HTTP. Most people only know about the first.

Layer one: only the output directory is served. Both major hosts document this in almost identical language.

Vercel, in Configuring a Build: "Only the contents of this Output Directory will be served statically by Vercel."

Netlify, in Build configuration overview: "Only files in the publish directory are deployed. Files and assets located outside of the publish directory won't be included in site deploys."

A file being in your git repository has nothing to do with whether it's on the web. What matters is which directory the host serves. With Next.js, Astro, Vite or Nuxt on a standard preset, that's a build output folder, and .cursor/ at the repo root isn't in it.

Layer two: dotfiles get special treatment. Next.js serves public/ through the send library without enabling its dotfiles option, so dotfiles return 404 (open discussion since December 2020). Netlify staff have confirmed on their forum that files and directories beginning with a period are stripped, saying "I do not believe we are planning any changes here."

Don't treat layer two as a guarantee. Neither vendor documents dotfile handling in their official docs. The evidence is a GitHub discussion and a support thread, and Netlify's own suggested workaround ("deploy via git or use a redirect") implies the behaviour differs between git builds and zip or CLI deploys. It's real, staff-acknowledged, undocumented, and method-dependent. Verify rather than assume.

Where it genuinely can be reachable

Vercel documents the first case itself. For projects with no build step: "Choose 'Other' as the Framework Preset. This sets the output directory as public if it exists or . (root directory of the project) otherwise."

Output directory equals repository root is exactly the shape where this stops being theoretical. The other shapes:

  • vercel.json with "outputDirectory": "." set by hand
  • An nginx or Apache root pointed straight at a git checkout
  • A Dockerfile that does COPY . . and then serves its working directory
  • python -m http.server in a project folder, or a Vite dev server left exposed (vite, not vite preview), both of which serve from the project root

There's a real sighting of the general failure on Vercel's own forum: a thread where README.md and requirements.txt were served as public static assets on an "Other" preset project, because filesystem precedence beat the catch-all rewrite. Those aren't dotfiles, which is consistent with the two layers above behaving independently.

The check that actually works

Status code alone is useless. Look at what came back.

Content-aware check
curl -sS -L --max-time 20 -o /tmp/body -w 'status=%{http_code} type=%{content_type}\n' \
  https://YOURSITE.com/.cursor/mcp.json

head -c 400 /tmp/body

You are only exposed if all three are true:

1

Status is 200.

2

Content type is application/json, not text/html. An HTML body is your app's catch-all route answering, which is the false positive that fools nearly everyone.

3

The body actually contains config, meaning strings like mcpServers, command or args. Not a rendered page, not an error object.

Run it against the other paths too: /.claude/mcp.json, /.vscode/mcp.json, /.claude/.credentials.json, /.env, /.git/config. Same three tests each time.

404 isn't the only good answer. A 403 is common when nginx carries location ~ /\. { deny all; }, and a Cloudflare or WAF interstitial also means the file isn't being served. Anything that isn't JSON containing your actual config is fine.

One thing that's true for everyone

Whatever your hosting does, put these in .gitignore:

.gitignore
.cursor/
.claude/
.vscode/
.mcp/

This costs nothing and doesn't depend on a vendor's undocumented dotfile behaviour staying the way it is. If those directories are already committed, taking them out means no deployment shape you migrate to later can serve them.

A leaked mcp.json isn't automatically a leaked key. Cursor's MCP documentation uses ${env:NAME} indirection, so a well-formed config names servers and commands while the secrets sit in the environment. The diary is careful here too, describing the prize as "server endpoints and sometimes API keys." Server names and internal URLs still tell an attacker what your agent can reach, which is worth having.

The part that worries us more

Buried in the same diary is a finding that doesn't depend on an unusual deploy shape at all. If you wired an AI agent with a tool that fetches arbitrary URLs, you built an SSRF primitive. The diary's phrasing: "An MCP server or an agent tool that fetches arbitrary URLs is a ready-made SSRF primitive." The scanners rotate parameter names (url, uri, path, dest) and aim at cloud metadata endpoints.

That one is live for anyone whose agent can fetch a link, no misconfiguration required. If your app has a "summarise this URL" feature, it's the thing to look at first.

The author's summary of why an exposed MCP server is worse than a leaked config file, from the body of the diary: it is "a remote, machine-readable menu of everything an agent can touch, offered to anyone who completes the handshake."

Is my .cursor/mcp.json exposed on my deployed site?

On Vercel or Netlify with a normal framework preset, almost certainly not. Both only serve the build output directory, and a repo-root .cursor/ is not in it. The cases where it can be reachable are specific: a no-build static deploy whose output directory is the repository root, an nginx or Apache root pointed at a checkout, or a Docker image built with COPY . . that serves its working directory.

Why does curl return 200 for a file I do not have?

Because most single-page apps ship a catch-all rewrite that serves index.html for any unmatched path. The status code is 200 and the body is your app. As of 2026-08-26, https://vercel.com/.cursor/mcp.json returns 200 with about 2.5 MB of HTML, and Vercel is obviously not leaking a config file. Check the content type and the body, never the status code alone.

What paths are scanners actually requesting?

The SANS Internet Storm Center diary of 2026-07-13 lists /.claude/mcp.json, /.cursor/mcp.json, /.cursor/mcp_config.json, /.vscode/mcp.json, /.mcp/config.json, /.claude/settings.local.json, /.claude/.credentials.json and /.config/claude/.credentials.json, alongside probes to /v1/models and /api/tags. That is one handler's logs from one host over 14 days, so treat it as a wordlist sighting rather than a measure of how much traffic you should expect.

Does a leaked mcp.json contain my API keys?

Sometimes, but often not. Cursor's documented format uses ${env:NAME} indirection, so a well-formed file names servers and commands while the secrets live in the environment. The diary is careful about this too, describing the prize as server endpoints and sometimes API keys. Server names and internal URLs are still worth something to an attacker: they describe what your agent can reach.

What should I do regardless of how my site is deployed?

Put .cursor/, .claude/ and .vscode/ in .gitignore. That is free, correct for every reader, and does not depend on your hosting behaving a particular way. If those directories are already committed, removing them from the repo means they cannot be served by any deployment shape you might move to later.

Let something else run the checks

A scan requests the paths that should never answer, follows the redirects, and reads the bodies rather than the status codes. It also checks the things this post did not: exposed .env files, .git directories, and endpoints that should require auth.

How-To Guides

Is Your .cursor/mcp.json Exposed? The Obvious Check Lies (2026)