MCP security · Free tool

MCP Server Self-Check

How exposed is that MCP server? Work through a short guided check of one server or an agent's whole tool set: reachability, the Rule of Two, and drift. You get two independent verdicts and a plain-text report to paste into a ticket. Free, no sign-up, and your answers never leave your browser.

CyberDesserts tools

MCP server self-check

A short guided check, two independent verdicts, one copyable report. No single number: reachability and the Rule of Two are different findings and stay separate.

This check runs entirely in your browser. Nothing you enter is sent anywhere, stored, or logged. The check probes nothing: you run the commands, the page only does arithmetic on your answers.

Verdict · Reachability awaiting answers
Verdict · Rule of Two awaiting answers
Properties held A B C A · untrustworthy input B · sensitive systems C · changes state or sends out

Faint outline: assumed worst until answered

How this works

Questions appear as you answer, in four short parts. Nothing is computed until you have answered everything that applies.

  1. Scope. Say whether you are checking one server or an agent's whole tool set. This changes what the verdict means, so it comes first.
  2. Part 1, reachability. Who can reach this server, and what do they get without a credential? Each question comes with the command that answers it, and a tag saying where to run it: your own machine, the server host, or outside the network. You run the commands, then report what came back. This page never probes anything itself.
  3. Part 2, the Rule of Two. Could a poisoned input make the agent misuse its tools? No commands and nothing to look up: you tick what your tools can do from plain lists, and the page works out the risk properties for you. The idea gets a one-minute explanation before any ticking starts.
  4. Part 3, drift, then the report. Whether you would notice the server changing under you. Then two verdicts, computed separately and never combined into one number, and a plain-text report to paste into a ticket.
Scope

Are you checking one MCP server, or everything one agent is connected to?

The Rule of Two applies to a session, not to a server. An agent connected to three servers can hold all three properties across them while no single server holds more than one.

Part 1 of 3

Reachability

Who can reach this server, and what do they get without a credential? Five checks, each with a command. The tag above each command says where to run it, and that placement is the point: a check run from the wrong place returns an answer that means nothing.

Step 1.0 · Endpoint resolution

Confirm you have the right address

What this step does: establishes the exact address this server answers on, before anything is tested against it. MCP endpoints are fussy about the path: /mcp and /mcp/ are different addresses, and a server that answers one may redirect the other, or return nothing at all. Probe the wrong form and every later check reads as a false negative. Every later command inherits this answer, so it comes first.

Run from your own machine

# Try the address from your client config, then the trailing-slash variant
curl -sS -o /dev/null -w '%{http_code} -> %{redirect_url}\n' https://your-host/mcp
curl -sS -o /dev/null -w '%{http_code} -> %{redirect_url}\n' https://your-host/mcp/
What you might see
200 ->
405 ->
401 ->

A status with nothing after the arrow: that address answers directly. Answer: no redirect. Use that exact form in every command below. The number itself does not matter yet, later steps read it.

307 -> https://your-host/mcp/

Any status starting with 3 and an address after the arrow: the server wants the other form. Answer: a redirect. Write the target down, it replaces your-host/mcp in every command below.

404 ->
404 ->   (both forms)

Neither form is the endpoint. Check the client configuration or the server's documentation for the real path: some servers serve /sse, /api/mcp or a custom route. Re-run this step with that path before continuing; every answer below depends on probing an address the server actually serves.

Did the working address come via a redirect?
Step 1.1 · Transport

How does the client reach this server?

What this step does: establishes whether there is a network listener at all. No command needed, the answer is already written in your client configuration.

Check on the machine running your MCP client

Open the client configuration (claude_desktop_config.json, mcp.json or equivalent). A command key means stdio. A url key means HTTP.

What you might see
"github": {
  "command": "npx",
  "args": ["-y", "@modelcontextprotocol/server-github"]
}

A command key: the client launches the server as a local subprocess. Answer: stdio.

"github": {
  "url": "https://mcp.example.com/mcp"
}

A url key: the client talks to the server over the network. Answer: Streamable HTTP.

"github": {
  "url": "https://mcp.example.com/sse"
}

A url ending in /sse, or docs that describe separate SSE and message endpoints. Answer: legacy HTTP+SSE.

Step 1.2 · Binding

What is the listener bound to?

What this step does: shows which network interfaces the listener accepts connections from. Loopback means only this host can talk to it; every interface means anything that can route to the host can.

Run on the server host itself

# Linux
ss -tlnp | grep -E ':(3000|8000|8080|8443)'
# macOS
lsof -nP -iTCP -sTCP:LISTEN | grep -iE 'node|python|mcp'
What you might see
LISTEN 0 4096  127.0.0.1:8000  users:(("python",pid=812,fd=6))

The address starts 127.0.0.1. Only programs on this machine can connect. Answer: loopback only.

LISTEN 0 4096  0.0.0.0:8000  users:(("node",pid=291,fd=22))

The address starts 0.0.0.0 or [::]. Anything that can route to this machine can connect. Answer: every interface.

(the listener is loopback, but nginx, Caddy or a
 cloud load balancer forwards public traffic to it)

The binding looks contained but the internet still gets through the front door. Answer: behind a reverse proxy on a public host.

(no output at all)

The server may use a different port: re-run without the grep to see every listener, and look for your server's process name. Still unclear? Answer: not sure, and the tool assumes the worst on your behalf.

Step 1.3 · Unauthenticated enumeration

Can an outsider list the tools?

What this step does: asks the server for its tool list while carrying no credential, which is exactly what an attacker's first request looks like. A refusal here is a pass.

Run from outside the boundary, not from the host

Placement matters. Run this from outside the boundary the server is meant to sit inside: another machine on the network, or off the network entirely if the server is meant to face the internet. From the host itself it returns a loopback answer that tells you nothing.
curl -sS -D - -X POST https://your-host/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
What you might see
HTTP/2 401
www-authenticate: Bearer error="invalid_token", ...

The first line carries 401 or 403. The server said no to a stranger, which is what you want. Answer: refused. Keep this output, step 1.3b reads its headers.

HTTP/2 200
content-type: application/json

{"jsonrpc":"2.0","id":1,"result":{"tools":[{"name":"read_file", ...

A 200 and a "tools" list. The server handed its capabilities to an unauthenticated stranger. Answer: 200 with a tools array.

curl: (7) Failed to connect to your-host port 443
curl: (28) Connection timed out

Nothing answered from where you stood. Answer: connection refused or timeout. Only meaningful if you really were outside the boundary.

Step 1.3b · Authorisation discoverability

Is the refusal telling clients how to authenticate?

What this step does: checks whether that 401 came from a real authorisation implementation. A 401 on its own does not show the server implements the authorisation specification, it shows something said no. Both answers are already in front of you: the first in the headers step 1.3 just printed, the second from one more command.

Same position as step 1.3, outside the boundary

What you might see
www-authenticate: Bearer error="invalid_token",
  resource_metadata="https://your-host/.well-known/oauth-protected-resource/mcp"

The header names a resource_metadata address. The server is telling clients exactly how to authenticate. First question: yes.

www-authenticate: Bearer realm="mcp"

(or no www-authenticate line at all)

A refusal with no pointer. First question: no, then the second command below decides what that means.

Did the 401 response carry a WWW-Authenticate header containing resource_metadata?
And the fallback a client probes when the header omits it:
curl -isS https://your-host/.well-known/oauth-protected-resource
What you might see
HTTP/2 200
{"resource":"https://your-host/mcp",
 "authorization_servers":["https://auth.example.com"], ...}

A JSON document describing how to authenticate. Answer: resolves.

HTTP/2 404

Nothing published. Answer: 404.

Step 1.4 · Protocol revision Unvalidated

Which specification era is the server in?

This test is not yet validated against a running server. Treat the result as indicative.

What this step does: establishes which protocol revision the server actually speaks, because later verdicts depend on it. The 2025-03-26 revision shipped OAuth 2.1 authorisation, and 2026-07-28 changed how a gate can be built.

Run from outside; add your bearer token if step 1.3 was refused

A correctly configured server rejects these at the authorisation layer before method routing, so both probes return 401. If that happens, re-run with -H 'Authorization: Bearer YOUR_TOKEN', or answer "authorisation blocked both probes" and the report will record the version as unknown rather than as a finding.

# Probe 1, legacy handshake. Propose the NEWEST version you know, not an old one.
curl -sS -X POST https://your-host/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{
        "protocolVersion":"2025-11-25","capabilities":{},
        "clientInfo":{"name":"self-check","version":"1.0"}}}'

# Probe 2, modern discovery
curl -sS -X POST https://your-host/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -H 'MCP-Protocol-Version: 2026-07-28' \
  -d '{"jsonrpc":"2.0","id":1,"method":"server/discover"}'
What you might see, and how to read the pair
{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2025-06-18", ...}}

A probe that answers with a protocolVersion. Note the value: that is the negotiated revision for the next question.

{"jsonrpc":"2.0","id":1,"error":{"code":-32601,"message":"Method not found"}}

A probe the server does not recognise. This is a signal, not a failure: which probe got it decides the era.

Probe 1 answers, probe 2 method not found  = Legacy
Probe 1 and probe 2 both answer            = Dual era
Probe 1 method not found, probe 2 answers  = Modern

The reading rule for the pair.

HTTP/2 401  (both probes)

Authorisation answered before the protocol could. Answer: authorisation blocked both probes, or re-run both with your bearer token.

The version in an initialize response is negotiated, not declared: it reflects what the client proposed. Propose old and you measure old. Propose the newest revision you know of and read down: if the server answers with something older, that is its actual ceiling.
What did the pair of probes show?
Which revision did the probes report?

Part 2 of 3

The Rule of Two

Reachability asked who can talk to the server. This part asks a different question: if poisoned content reaches the agent, what damage can its tools do? No commands from here on, because no command can answer it.

The idea, in one minute

An agent should hold at most two of these three properties at once

Published by Meta on 31 October 2025 as the Agents Rule of Two. The three properties are the ingredients of a prompt injection attack. Hold any two and the attack is missing a step: content can be read but nothing sensitive is in reach, or sensitive things are in reach but nothing gets out. Hold all three in one session and a single poisoned input can read your systems and send what it finds out of the building.

[A]

Untrustworthy input

A tool reads content an outsider can influence: the attacker's way in.

Not yet answered
[B]

Sensitive systems

A tool can reach something where unintended access would matter: what the attacker is after.

Not yet answered
[C]

State change or outward communication

A tool can alter something or send something out: the attacker's way to act or exfiltrate.

Not yet answered

You do not need to work out the properties yourself. The tick lists below ask what your tools can do in plain terms, and the page derives the properties from your ticks. The cards above fill in as you go.

Step 2.1 · What can these tools do?

Tick anything any tool in scope can do

Capability, not intent: tick it if a tool could do it, even if you never ask it to. The page works out the three properties from your ticks, and the cards above fill in as you go. If nothing in a group applies, tick "none of these" so the check knows the group is answered.

[A] Ways inContent an outsider can influence. Fills card [A] above. Not yet answered
[B] Worth protectingSystems where unintended access would matter. Fills card [B] above. Not yet answered
[C] Ways to act or leakChanging state or sending outwards. Fills card [C] above. Not yet answered
Step 2.2 · Controls

What stands between a poisoned input and those tools?

One hard line here. The first four are mechanisms that work whether or not the model is fooled, and each one takes a property out of the attacker's reach. The fifth is software or instructions trying to spot the attack, which fails exactly when the attack is good. Tick what is actually in place.

These move the verdict

This does not move the verdict

Or

Step 2.3 · Autonomy

Do these calls run without a person watching?

This step checks whether a person is part of the loop when these calls run. Counts as unattended: scheduled jobs, CI triggers, background agents, or a client where you have selected approve-all or "don't ask again" for these tools. That last one is why this is a separate question: a permanently dismissed approval prompt is not a gate, and nothing in the server config records that it was dismissed.

Part 3 of 3

Drift

Two last questions about time. Whatever the verdicts say today, an already-approved server can change its tools or its access tomorrow without asking anyone. This part establishes whether you would notice.

Step 3.1 · Provenance

Where did this server come from?

What this step does: establishes who controls when this server changes, and how much warning you get. A server you approved once can ship new tools, new descriptions or new scopes in its next release. Where it comes from decides whether anyone tells you first, and whether you could audit or rebuild what you are actually running.

Step 3.2 · Baseline

Do you have a recorded tools/list response you could diff against today's?

What this step does: checks whether you could tell that this server changed. tools/list is the server's own inventory of its tools, their descriptions and their parameters; it is exactly what your agent sees and trusts. An already-approved server can change that inventory without prompting anyone for re-approval. A saved copy from the day you approved it is the baseline, and a diff against today's response is how drift shows up.

Verdict · Reachability
Answer the reachability questions to compute this verdict.
    Verdict · Rule of Two
    Answer the Rule of Two questions to compute this verdict.

      Report and hardening plan

      
        
      MCP self-check v1.2 · Computed against MCP specification 2026-07-28 · Encodes the Agents Rule of Two (Meta, 31 October 2025).
      This tool asks, it does not probe. Every command shown here is run by you, from where you choose to run it. Nothing on this page is sent anywhere, stored, or logged.

      Want the reasoning behind each question?

      The companion article covers MCP security best practices in depth: transport and authentication, tool poisoning, why the Rule of Two matters, and how to catch a server drifting under you.

      Read MCP security best practices on the blog →