EdgeDefense
Scan your home network and local machine for security risks, open ports, weak Wi-Fi, unknown devices with a trust score and clear explanations.
Documentation
EdgeDefense MCP
Ask Claude about your home network. Runs on your machine — no account, no admin rights, and nothing sent anywhere except the one speed test you have to ask for.
Server-specific docs live in mcp-server/README.md.
What this is
You know something is connected to your wifi. You have no idea what it is. Most tools answer that with a dashboard full of numbers you have to interpret yourself.
This one lets you just ask. Point Claude (or Cursor, or any MCP client) at your network and talk to it in plain English: what's connected, what that unknown thing at 192.168.1.47 probably is, and whether anything looks wrong.
It answers the other half of the question too — why is this slow? — which is usually a weak Wi-Fi signal or a crowded channel rather than anything to do with your internet plan:
"Why is my wifi slow?" "Is the problem my wifi or my ISP?" "How fast is my internet actually?" "Why do my video calls break up when someone else is downloading?"
Everything happens on your computer, with one exception you invoke yourself: measuring download speed means transferring real data with a server on the internet, so edgedefense_speed_test does exactly that and nothing else does. Nothing is uploaded, nothing is logged to a server, and there is nothing to sign up for.
Why it's different
Most network scanners hand you a table of IP addresses, MAC addresses and open ports, and leave the interpretation to you. That works if you already know what port 23 means and why it matters. This gives you a conversation instead — you ask a normal question, you get a normal answer, and if something is flagged you can ask why and get an explanation in plain language, including what the check genuinely can't tell you.
Two promises make it safe to install on a whim:
It needs no special permissions. Everything above works as a regular user. No sudo, no "run as administrator", no driver to install.
It never phones home. Not for updates, not for analytics, not even to look up who made a device — that database ships inside the package. There is exactly one tool that talks to the internet, edgedefense_speed_test, and only because measuring download speed is impossible without transferring real data. It is the only one, it never runs on its own, and it is marked as reaching outside your machine so your client can tell you before it runs. You can verify all of this yourself in about ten minutes; the whole thing is a few thousand lines with zero runtime dependencies.
It is also read-only by design. There is deliberately no "block this device" button. A tool you installed sixty seconds ago should not be able to disconnect things from your network.
Tools
| Tool | What you get |
|---|---|
edgedefense_scan_network | Finds everything on your network and summarises it in plain English. Start here. |
edgedefense_whats_changed | Tells you what's new on your network, what vanished, and what ports opened since last scan |
edgedefense_list_devices | Lists what's connected — filter to just the unknown ones, or just the ones with problems |
edgedefense_get_device_detail | Everything known about one device: what it probably is, who made it, what it's running |
edgedefense_name_device | Assigns a friendly name to a device so you can track it |
edgedefense_local_security | Checks the Wi-Fi security, DNS, and open ports on the machine running the server |
edgedefense_get_trust_score | A single 0–100 score for your network, with the reasons behind it |
edgedefense_explain_finding | Turns any flagged issue into a plain-English explanation of what it means and what to do |
And for when the network is working, but badly:
| Tool | What you get |
|---|---|
edgedefense_network_stats | Live upload/download rate per adapter, packet errors, Wi-Fi signal strength, and how many neighbours are crowding your channel. Start here for "why is it slow". |
edgedefense_latency_check | Round trip to your router, jitter, packet loss, and how long each of your DNS servers takes to answer |
edgedefense_speed_test | Actual download and upload speed in Mbps, plus bufferbloat. ⚠️ The one tool that contacts the internet |
The first two are the interesting ones. "My internet is slow" is usually a weak
Wi-Fi signal or a crowded channel, and a speed test cannot tell you that — it
just gives you a smaller number. edgedefense_network_stats tells you the
signal is −78 dBm and four neighbours are sitting on your channel, which is an
answer you can act on.
edgedefense_speed_test also reports bufferbloat: how far latency climbs
while the connection is saturated. That, not bandwidth, is usually why a video
call falls apart the moment someone else starts a download — and it is fixed in
your router's settings rather than by paying for a faster plan.
Install
Step 1 — install the server. Requires Python 3.10 or newer.
pip install "edgedefense-core @ git+https://github.com/splashxmoon/edgemcp.git#subdirectory=core-engine" "edgedefense-mcp @ git+https://github.com/splashxmoon/edgemcp.git#subdirectory=mcp-server"
Step 2 — tell your client about it.
Claude Code
claude mcp add edgedefense -- edgedefense-mcp
Codex CLI
codex mcp add edgedefense -- edgedefense-mcp
Claude Desktop — claude_desktop_config.json
{
"mcpServers": {
"edgedefense": {
"command": "edgedefense-mcp"
}
}
}
Cursor — ~/.cursor/mcp.json
{
"mcpServers": {
"edgedefense": {
"command": "edgedefense-mcp"
}
}
}
Codex CLI (manual) — ~/.codex/config.toml
[mcp_servers.edgedefense]
command = "edgedefense-mcp"
args = []
VS Code / Copilot — .vscode/mcp.json
{
"servers": {
"edgedefense": {
"command": "edgedefense-mcp"
}
}
}
Restart your client afterwards. Any other MCP client works too — the command is
edgedefense-mcp, it takes no arguments, and it speaks stdio.
Remote connector (claude.ai "Add custom connector")
That dialog asks for a URL rather than a command, which means the server has to be reachable over HTTP.
The server must still run on a machine in your home. It identifies devices by reading the local address table and listening for the announcements devices broadcast on the local link — so it can only ever see the network it is sitting on. Hosting it on a VPS would work perfectly and tell you about the VPS's network, which is not what you want. The arrangement that makes sense is a tunnel from a public hostname back to a machine at home.
Keeping it running, and shipping updates without breaking the URL
Once other people are using the URL, two things about the simple setup start to hurt:
- A quick tunnel's hostname is random, and changes on every restart.
serve_remote.pystops the tunnel when it stops the server, so restarting to pick up new code silently hands you a different address.
Together those mean a routine deploy breaks every saved connector URL — and it breaks quietly, because the old address just stops answering.
scripts/serve_forever.py is the supervised version that avoids both:
python scripts/serve_forever.py --hostname mcp-tunnel.example.com
It starts the tunnel once and leaves it alone. Every five minutes it fetches
the tracked branch, and when new commits land it fast-forwards and restarts
only the server. The tunnel keeps running throughout, so the public address —
and every URL anyone has saved — survives the update. Push to main, and within
five minutes the remote server is serving the new code at the same link.
A few deliberate choices:
--ff-only. A supervisor that silently creates merge commits on an unattended machine is a way to lose work. A diverged branch is reported and the current code keeps serving.- The tunnel is restarted only if it actually dies. Restarting a healthy tunnel is the one thing guaranteed to break the URL.
--check-every 0disables auto-updating if you would rather deploy by hand.
Without --hostname it falls back to a quick tunnel and prints a warning: the
address is then only stable for as long as that tunnel process lives.
Getting a public URL
Start with a quick tunnel. It needs no DNS changes at all and proves the whole thing works in about a minute:
cloudflared tunnel --url http://127.0.0.1:8765
That prints a https://<random>.trycloudflare.com URL. Pass it as
--public-url and use it in your client. The URL changes each run, which is
fine for testing.
For a stable address on your own domain, note two things. A subdomain like
mcp.example.com is yours automatically — subdomains are not bought
separately, you just create a DNS record. But cloudflared tunnel route dns
only works if the domain's nameservers point at Cloudflare. If your domain is
still on your registrar's nameservers, either move the zone to Cloudflare
(re-creating every existing record first — MX records especially, or email
stops arriving) or use a tunnel provider that gives you a CNAME target you
can paste into your current DNS panel.
With browser sign-in (recommended)
Connecting opens a sign-in page on your own domain, and your client ends up holding a revocable token instead of a secret URL. The authorization server runs inside this process — there is no hosted login service and no account.
export EDGEDEFENSE_PASSPHRASE='choose-something-long'
edgedefense-mcp --http --oauth --public-url https://mcp.edgedefenseai.com --port 8765
cloudflared tunnel --url http://127.0.0.1:8765
Then paste just the endpoint into the connector dialog, leaving both OAuth fields empty:
https://mcp.edgedefenseai.com/mcp
The client registers itself, discovers the authorization server, and opens the sign-in page. Enter the passphrase and it connects. Restarting the server revokes every issued token.
--public-url must match exactly what the browser sees, since the OAuth
metadata and the redirect back to your client are built from it.
Sign in with Google instead of a passphrase
Rather than sharing one passphrase, you can sign in with Google and allow specific addresses.
Create an OAuth client in the Google Cloud Console (APIs & Services → Credentials → Create credentials → OAuth client ID → Web application), and add this exact redirect URI:
https://mcp.edgedefenseai.com/auth/google/callback
Then:
export EDGEDEFENSE_GOOGLE_CLIENT_ID='...apps.googleusercontent.com'
export EDGEDEFENSE_GOOGLE_CLIENT_SECRET='...'
edgedefense-mcp --http --oauth \
--public-url https://mcp.edgedefenseai.com \
--allow-email you@gmail.com \
--no-passphrase
--allow-email is required and repeatable. Signing in with Google proves who
someone is; it does not decide whether they may read your network, and without
an allowlist any Google account would satisfy the check. Omit --no-passphrase
to offer both methods on the sign-in page.
One honest caveat. With Google sign-in enabled, this server contacts Google while somebody is signing in — that is unavoidable for any identity provider. Scanning still makes no outbound request of any kind, and no network data is ever sent anywhere. If you would rather the server never talk to anything, use the passphrase — and leave
edgedefense_speed_testalone, which is the only other tool that reaches outside your machine.
With a token in the URL (simpler)
No browser step; the secret lives in the URL instead.
edgedefense-mcp --http --port 8765 --token "$(python -c 'import secrets;print(secrets.token_urlsafe(32))')" --allow-host mcp.edgedefenseai.com
Point a tunnel at it:
cloudflared tunnel --url http://127.0.0.1:8765
Then paste the printed endpoint into the connector dialog:
https://mcp.edgedefenseai.com/t/<your-token>/mcp
Leave the OAuth fields empty. The dialog has nowhere to put a header, so the token travels in the path instead.
The URL is a password. Anyone holding it gets a full inventory of your home network. Don't put it anywhere you wouldn't put a password, and rotate it by restarting with a new
--token.
Binding to anything other than loopback without a token is refused; the server
generates one and prints it rather than exposing your network anonymously.
--allow-host must name the hostname clients will actually use, or the
DNS-rebinding check rejects the request. edgedefense-mcp --help lists the rest.
Where is the Claude Desktop config file?
| Platform | Path |
|---|---|
| macOS | ~/Library/Application Support/Claude/claude_desktop_config.json |
| Windows | %APPDATA%\Claude\claude_desktop_config.json |
| Linux | ~/.config/Claude/claude_desktop_config.json |
Client says "command not found" or the server won't start
Desktop apps often don't see the same PATH your terminal does, which matters
if you installed into a virtual environment. Find the real location:
# macOS / Linux
which edgedefense-mcp
# Windows
where edgedefense-mcp
Then use that full path in the config instead of the bare name:
{
"mcpServers": {
"edgedefense": {
"command": "/full/path/to/edgedefense-mcp"
}
}
}
Running it through Python directly also works, and avoids the PATH question
entirely:
{
"mcpServers": {
"edgedefense": {
"command": "/full/path/to/python",
"args": ["-m", "edgedefense_mcp"]
}
}
}
Try it
Paste any of these:
Scan my home network and tell me what's connected.
What's my network trust score, and why?
Is anything unusual connected right now?
Good follow-ups once you have results: "What is that device at 192.168.1.47?" · "Which devices couldn't you identify?" · "Why is that a problem?"
A first scan takes about ten seconds.
Architecture
Below this line is for people reading the code rather than using the tool.
This is a monorepo with a deliberate split between shared detection logic and the products built on top of it.
edgemcp/
├── core-engine/ Shared discovery, identification and scoring. Imported, never forked.
├── mcp-server/ The free, open-source MCP server. Ships independently.
└── core-app/ The paid Core/Home product. Not present yet.
Why the split exists
core-engine/ is the only place detection logic lives. Both mcp-server/ and
the future core-app/ import it the same way; neither reimplements any of it.
This is decided up front on purpose. Retrofitting a shared engine after two codebases have already diverged is expensive and entirely avoidable, and the cheapest moment to draw the line is before the second consumer exists.
mcp-server/ is a separate folder rather than a subfolder of the engine because
it ships independently: its own packaging, its own public repository, its own
README, its own MIT licence.
The hard boundary
mcp-server/ is public. Anything reachable from it is public.
core-engine/ therefore contains only generic, safe-to-open-source logic:
device discovery, ARP and mDNS scanning, vendor lookup, and scoring arithmetic.
The trained ML detection pipeline behind the paid product is not in this repository, is not imported by anything here, and lives in a separate private package. This is a standing architectural constraint, not a future TODO.
Two guarantees that constrain every change
-
Nothing phones home. No outbound network request is made by any shipped code path, for any purpose, including analytics. Vendor lookups read a bundled database on disk. There are exactly two exceptions, both deliberate and both narrow:
core-engine/scripts/update_oui.py, a maintainer-run script that is not part of the distributed package and is documented as such in its own docstring.core-engine/src/edgedefense_core/perf/speedtest.py, which transfers data to and from a public speed test service. Throughput cannot be measured any other way. It is confined to that one module, is imported by nothing else, never runs as part of a scan, and its tool is the only one carryingopenWorldHint: true. A test asserts that it stays the only one — if you add a second outbound tool,test_only_the_speed_test_reaches_outside_this_machinefails, and that failure is the point.
-
Needs no elevated privileges. Device discovery, identification, port fingerprinting, the trust score and every performance check all run as an ordinary user on Windows, macOS and Linux. Latency measurement shells out to the system
pingrather than opening a raw socket, precisely so it does not need root.
Both are load-bearing for the product's premise. A privacy tool that quietly calls an external API loses the argument permanently the first time a technical user runs a packet capture on it.
Current scope
In: Zero-permission discovery, identification, and performance measurement.
Out, by decision rather than by omission: the ML pipeline, packet-capture
traffic analysis, destructive tools such as block_device, and Core/Home
product features. A free tool installed sixty seconds ago should not be able to
disconnect things from your network; enforcement belongs in a product you have
decided to trust.
Packet capture is out for a harder reason than the others: it needs elevated
privileges on every platform, which would break the "no admin rights" promise
that makes this safe to install on a whim. Anything requiring sudo or a
capture driver belongs in the paid product, not here.
Development
pip install -e ./core-engine
pip install -e ./mcp-server
Run the tests:
cd core-engine && python -m pytest -q
cd ../mcp-server && python -m pytest -q
Verify the evaluation questions are answerable from tool output:
cd mcp-server && python evaluation/run_evaluation.py
Then point an MCP client at it — see mcp-server/README.md for install and configuration.
Licence
MIT, across the repository.