EgressView exposes a Model Context Protocol (MCP) server that lets AI assistants β including AWS Kiro, Anthropic Claude, Anysphere Cursor, Zed, and any MCP-compatible agent β query your network data directly.
π―π΅ ζ₯ζ¬θͺηγ―γγ‘γ
The MCP SDK v2 server uses one tool definition for both protocol eras:
2025-11-25 clients use the legacy initialize flow, while 2026-07-28
clients use stateless server/discover and per-request metadata. The legacy
fallback is retained for compatibility; no sticky session is required.
Before choosing a transport, select one of the cloud-neutral
deployment profiles: local-stdio, private-http,
private-oauth, or public-oauth. Set EGRESSVIEW_DEPLOYMENT_PROFILE
explicitly in managed environments; a transport/authentication mismatch fails
before the endpoint starts.
Once connected, just ask in natural language:
"Show me a threat summary for the last 24 hours"
β 18,142 sessions total: 18,117 safe, 25 warn, 0 danger
"Which devices made the most connections today?"
β Lists top devices with session counts, MAC, vendor
"Any new devices on the network this week?"
β Reports first-seen devices and destinations in the last 7 days
"Are there any threat connections right now?"
β Lists destinations flagged by Feodo, ThreatFox, URLhaus, or Spamhaus DROP
"What is 192.168.1.50 connecting to?"
β Top destinations for that device with country, org, and threat level
"Show me all alerts from the last 6 hours"
β Detection log: threat hits, new device alerts, beacon candidates
"Show me all device notes"
β Lists every device that has a memo attached
"Add a note to 192.168.1.97: Roomba, connects to GitHub for OTA updates"
β Saves the memo to that device
The agent selects the appropriate tool automatically and combines multiple tool calls when needed.
| Tool | What it returns |
|---|---|
get_threat_summary |
safe / warn / danger session counts for a time period |
get_traffic_summary |
total sessions, unique destinations, unique devices |
get_top_destinations |
top contacted destinations (ranked by session count, with country, org, threat level) |
get_device_traffic |
per-device traffic; pass a src IP to get one deviceβs top destinations |
get_new_nodes |
devices and destinations first seen during the period |
get_threat_connections |
destinations flagged as threats (low/high confidence) |
get_alerts |
detection log entries (threats, new devices, beacons) |
get_devices |
all known LAN devices with MAC, vendor, status, last-seen |
query_connections |
connection log search with src/dst filters |
get_device_notes |
device memo notes; omit src for all devices with notes, pass src IP for one device |
set_device_note |
set or update a device memo by src IP; pass empty string to delete |
Time-window tools accept a period parameter: 1h, 6h, 24h (default), 7d, or 14d.
get_devices, get_device_notes, and set_device_note do not use period.
Run the MCP server as a local process on the same machine as Claude Desktop. It makes REST API calls to your EgressView instance β which can be running locally or on a remote server.
This is the recommended approach for Claude Desktop. The command-based stdio transport is universally supported and avoids any URL validation restrictions.
Prerequisites: Node.js 22+, a running EgressView instance, API/admin token.
# 1. Clone (if not already):
git clone https://github.com/yo1t/egressview.git
cd egressview
npm install
Claude Desktop config (~/Library/Application Support/Claude/claude_desktop_config.json on macOS):
{
"mcpServers": {
"egressview": {
"command": "node",
"args": ["/absolute/path/to/egressview/mcp-server.js"],
"env": {
"EGRESSVIEW_URL": "http://your-server-ip:3000",
"EGRESSVIEW_TOKEN": "your-admin-token"
}
}
}
}
/absolute/path/to/egressview with the actual path where you cloned the repo.EGRESSVIEW_URL is the base URL of your EgressView server. If EgressView is behind a reverse proxy at /egressview/, use that path (e.g. http://your-server-ip/egressview).EGRESSVIEW_TOKEN is the API/admin token shown in the EgressView console on first startup, not the browser login password.Restart Claude Desktop after editing. The egressview server appears in the MCP tools list.
Run mcp-server.js as an HTTP server on the same host as EgressView. A reverse proxy (Apache or nginx) exposes it externally.
Note for Claude Desktop users: Claude Desktop currently requires
https://URLs for remote MCP servers. If your reverse proxy does not terminate TLS, use Option A (stdio) instead β it works with both local and remote EgressView instances over plain HTTP.
This option is intended for MCP clients that natively support HTTP transport (Anysphere Cursor, Zed, AWS Kiro or Anthropic Claude with HTTP MCP config, custom agents).
To use EgressView from ChatGPT, configure it separately in ChatGPT as a remote MCP server / app. Settings added to Codex (~/.codex/config.toml) or Claude Desktop (claude_desktop_config.json) are not shared with ChatGPT automatically.
Remote MCP servers used directly from ChatGPT generally need an HTTPS URL that ChatGPT can reach. If EgressView runs inside a home LAN, SOHO network, corporate network, or private subnet, do not expose the MCP endpoint directly to the public Internet without a deliberate access-control design. Consider OpenAIβs Secure MCP Tunnel or an equivalent private connectivity pattern for private / behind-firewall MCP servers.
If you publish or share the integration as a ChatGPT App, also plan for per-user authentication and authorization. X-Admin-Token is enough for a personal proof of concept, but OAuth or another user-scoped authorization flow is more appropriate for multi-user or externally shared deployments. MCP tools can reveal network state and device notes, so only trusted clients and users should be allowed to call them.
# Copy and edit:
cp .env.mcp.example .env.mcp
# Set MCP_PORT=3010, MCP_TOKEN=<a dedicated random token>,
# MCP_SERVICE_TOKEN=<a scoped egv_... identity>,
# MCP_AUDIT_HMAC_KEY=<a dedicated 32+ character secret>,
# and EGRESSVIEW_URL=http://localhost:3000.
# If your EgressView service listens on a different local port behind a proxy,
# use that local URL instead (for example http://localhost:3002).
chmod 600 .env.mcp
# Test run:
set -a; source .env.mcp; set +a
node mcp-server.js
# β [egressview-mcp] HTTP transport listening on 127.0.0.1:3010/mcp
HTTP token mode requires three distinct credentials: MCP_TOKEN authenticates
the MCP client, MCP_SERVICE_TOKEN is an egv_... API identity with exactly
network.read and notes.write, and MCP_AUDIT_HMAC_KEY pseudonymises the
append-only audit trail. None may equal EGRESSVIEW_TOKEN or each other.
HTTP mode never uses the browser/admin token for runtime API calls. Stdio mode
is unchanged.
P2-60βs OAuth Resource Server boundary can be enabled for private integration
testing with MCP_AUTH_MODE=oauth,
MCP_OAUTH_ISSUER, MCP_OAUTH_RESOURCE, MCP_OAUTH_READ_SCOPE,
MCP_OAUTH_NOTES_WRITE_SCOPE, MCP_SERVICE_TOKEN, and
MCP_AUDIT_HMAC_KEY.
It publishes RFC 9728 Protected Resource Metadata, validates RS256 JWT
signatures through the issuerβs discovery/JWKS endpoints, and fails closed on
issuer, expiry, audience, or scope mismatches. The issuer must advertise PKCE
S256. HTTPS is required except for loopback-only testing.
The default MCP_OAUTH_COMPATIBILITY_PROFILE=strict keeps that metadata check.
For an exact AWS Cognito regional user-pool issuer only, setting the profile to
cognito permits an omitted code_challenge_methods_supported field. It does
not permit contradictory metadata and does not relax signature, issuer,
expiry, single-audience, or scope checks. Do not enable this profile until the
pre-publication evidence proves PKCE S256 on the wire, both RFC 8707 resource
parameters, the exact callback, refresh rotation, and revocation with each
supported client version.
External provider scopes map to EgressViewβs shared permissions:
MCP_OAUTH_READ_SCOPE grants network.read, while
MCP_OAUTH_NOTES_WRITE_SCOPE grants notes.write. A read-only access token
does not list set_device_note; a direct call is rejected with 403
insufficient_scope. The write challenge includes both scopes so a step-up
authorization does not discard the already granted read scope.
Both HTTP token and OAuth modes require a dedicated API identity with exactly
network.read and notes.write. Place its one-time plaintext token in
MCP_SERVICE_TOKEN:
curl -sS -X POST http://127.0.0.1:3002/api/auth/api-identities \
-H "X-Admin-Token: $EGRESSVIEW_TOKEN" \
-H 'Content-Type: application/json' \
--data '{"label":"Remote MCP service","permissions":["network.read","notes.write"],"expiresInMs":31536000000}'
OAuth mode accepts only an egv_... scoped identity and never falls back to
EGRESSVIEW_TOKEN. Store .env.mcp with mode 0600, rotate the identity
before expiry, and revoke the previous identity after validation. Generate the
audit key once with openssl rand -hex 32 and keep it unchanged when rotating
the service identity. The Internet publication gate is delivered by the next
P2-60 phase. Do not publish this endpoint to the Internet yet.
Add inside your existing <VirtualHost> or server config. The MCP block must come before the general /egressview/ ProxyPass rule.
# βββ EgressView MCP Server ββββββββββββββββββββββββββββββββββββββββββββββββββββ
<Location /egressview/mcp>
ProxyPass http://127.0.0.1:3010/mcp flushpackets=on
ProxyPassReverse http://127.0.0.1:3010/mcp
# MCP Streamable HTTP requires both content types in Accept
RequestHeader set Accept "application/json, text/event-stream"
</Location>
# OAuth mode only: expose RFC 9728 metadata while preserving its path.
ProxyPass /.well-known/oauth-protected-resource http://127.0.0.1:3010/.well-known/oauth-protected-resource
ProxyPassReverse /.well-known/oauth-protected-resource http://127.0.0.1:3010/.well-known/oauth-protected-resource
# βββ EgressView Web UI (existing rule β keep below) ββββββββββββββββββββββββββ
ProxyPass /egressview/ http://127.0.0.1:3002/
ProxyPassReverse /egressview/ http://127.0.0.1:3002/
Required Apache modules: mod_proxy, mod_proxy_http, mod_headers (usually enabled by default).
sudo apachectl configtest && sudo systemctl reload httpd
Add inside your server {} block:
location /egressview/mcp {
proxy_pass http://127.0.0.1:3010/mcp;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
# Required for Server-Sent Events (streaming responses)
proxy_set_header Accept "application/json, text/event-stream";
proxy_set_header Connection '';
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 3600s;
}
# OAuth mode only: preserve root and resource-specific metadata paths.
location /.well-known/oauth-protected-resource {
proxy_pass http://127.0.0.1:3010;
}
sudo nginx -t && sudo systemctl reload nginx
# /etc/systemd/system/egressview-mcp.service
[Unit]
Description=EgressView MCP Server
After=network.target egressview.service
[Service]
Type=simple
User=ec2-user
WorkingDirectory=/home/ec2-user/egressview
EnvironmentFile=/home/ec2-user/egressview/.env.mcp
ExecStart=/usr/bin/node /home/ec2-user/egressview/mcp-server.js
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now egressview-mcp
For MCP clients that support HTTP transport (Anysphere Cursor, Zed, custom agents):
{
"mcpServers": {
"egressview": {
"url": "https://your-server/egressview/mcp",
"headers": {
"X-Admin-Token": "your-dedicated-mcp-token"
}
}
}
}
Use https:// if your reverse proxy terminates TLS (required for Claude Desktop). For plain http://, use Option A (stdio) from Claude Desktop instead.
| Variable | Required | Default | Description |
|---|---|---|---|
EGRESSVIEW_DEPLOYMENT_PROFILE |
Recommended | Inferred | local-stdio, private-http, private-oauth, or public-oauth; see the deployment-profile matrix. |
EGRESSVIEW_URL |
β | http://localhost:3000 |
Base URL of the EgressView server |
EGRESSVIEW_TOKEN |
stdio/setup | β | API/admin token used by stdio compatibility or to create the HTTP service identity; HTTP runtime calls use MCP_SERVICE_TOKEN. |
MCP_PORT |
HTTP mode | β | Local port for the MCP HTTP server (e.g. 3010). Omit for stdio mode. |
MCP_BIND_ADDRESS |
HTTP mode | 127.0.0.1 |
Literal IPv4/IPv6 bind address. Hostnames are rejected. |
MCP_ALLOW_NON_LOOPBACK |
Non-loopback bind | false |
Must be exactly true, together with an explicit deployment profile, before a LAN/container/all-interface bind is allowed. |
MCP_AUTH_MODE |
HTTP mode | token |
HTTP endpoint authentication mode: token or staged oauth. |
MCP_TOKEN |
HTTP token mode | β | Dedicated private HTTP endpoint token. It must be set explicitly and must differ from EGRESSVIEW_TOKEN. |
MCP_OAUTH_ISSUER |
HTTP OAuth mode | β | Exact HTTPS authorization-server issuer URL. Loopback HTTP is allowed only for testing. |
MCP_OAUTH_COMPATIBILITY_PROFILE |
HTTP OAuth mode | strict |
strict, or cognito for an exact AWS Cognito regional user-pool issuer with completed compatibility evidence. |
MCP_OAUTH_RESOURCE |
HTTP OAuth mode | β | Canonical public MCP resource URL used for exact JWT audience validation. |
MCP_OAUTH_READ_SCOPE |
HTTP OAuth mode | β | Provider scope mapped to the internal network.read permission. |
MCP_OAUTH_NOTES_WRITE_SCOPE |
HTTP OAuth mode | β | Provider scope mapped to the internal notes.write permission. |
MCP_SERVICE_TOKEN |
HTTP mode | β | Dedicated egv_... API identity token with exactly network.read and notes.write. |
MCP_AUDIT_HMAC_KEY |
HTTP mode | β | Stable secret of at least 32 characters used only to pseudonymise audit subjects and clients. |
127.0.0.1 by default. A non-loopback literal IP requires both an explicit profile and MCP_ALLOW_NON_LOOPBACK=true; protect that path with TLS and network policy.MCP_TOKEN through X-Admin-Token or Authorization: Bearer; it never falls back to the EgressView admin token.set_device_note can write device memo notes (stored in .egressview.notes.json, not the main database)..env.mcp permissions at chmod 600; it contains private API credentials.The runtime limits and audit controls apply to both private token and OAuth HTTP modes. Stdio mode is unaffected. OAuth provides per-user/per-client identity; private token mode records and limits the shared private credential.
Three independent buckets apply, and all must allow a request. A separate concurrency cap bounds in-flight work, because slow tool calls exhaust the process long before a per-minute limit trips.
| Setting | Default | Purpose |
|---|---|---|
MCP_RATE_LIMIT_GLOBAL |
60/min | Protects the host from any single burst |
MCP_RATE_LIMIT_SUBJECT |
30/min | One compromised user cannot consume the whole budget |
MCP_RATE_LIMIT_CLIENT |
30/min | One misbehaving client cannot either |
MCP_MAX_CONCURRENT |
4 | Bounds simultaneous tool calls |
MCP_MAX_BODY |
256kb |
Body is bounded before parsing or authentication |
MCP_TRUST_PROXY |
unset | Comma-separated exact IPs and IPv4 CIDRs of proxies allowed to set the client address |
MCP_REQUEST_TIMEOUT_MS |
30000 | Deadline for one MCP exchange |
MCP_API_TIMEOUT_MS |
15000 | Deadline for one internal EgressView API call |
Without a deadline, MCP_MAX_CONCURRENT stalled calls would hold every slot and wedge the endpoint closed. The request deadline aborts the internal API call and ends the response. Note that the MCP transport streams: once it has begun a response the status can no longer become 504, so a call that blows its deadline mid-stream is recorded as request_timeout in the audit rather than reported by status code. Timeout values must be whole milliseconds from 1 through 600000; invalid values fall back to the documented defaults.
The defaults are deliberately tight: there is no measured usage to size them from yet, and loosening one after a false positive is cheap, while discovering one was too loose only happens after abuse. Raise them if a legitimate workload trips a limit.
A rejected request returns 429 with Retry-After. Values that are not positive integers fall back to the default rather than disabling the limit β a typo cannot silently remove a bound.
Keep the reverse proxyβs own limits as well. These are the Node-side half; a bug or restart here must not leave the endpoint unbounded. Trust X-Forwarded-For only from the proxy addresses listed in EGRESSVIEW_TRUST_PROXY (see the authentication guide).
Every HTTP request is appended to a dedicated store (MCP_AUDIT_DB_PATH, default .egressview-mcp-audit.db). The HTTP endpoint fails to start if this store cannot be verified writable. It is separate from EgressViewβs own audit trail on purpose: the MCP process runs with a scoped API identity, and giving it write access to the main trail would let a compromised MCP forge or tamper with those records.
Recorded: pseudonymised OAuth subject and client id, tool name, granted scopes, outcome, a reason code, the request id, and duration.
Never recorded: tool arguments, MAC addresses, device note bodies, access tokens, raw JWTs, or provider error text. The client address is stored only as a keyed hash, never in the clear.
Joining the two trails. EgressView audits what the MCP service identity did (actor: api:<id>); this store records which OAuth subject asked for it. The MCP request id is forwarded to EgressView as X-Request-Id, so one incident can be followed across both. Keep the two retention windows equal, or the record of who asked will expire while the record of what happened remains.
Reason codes: unauthorized, invalid_token, insufficient_scope, bad_request, not_found, method_not_allowed, payload_too_large, client_error, tool_error, global_rate_limit, subject_rate_limit, client_rate_limit, concurrency_limit, request_timeout, server_error. Request-level rows also record the bounded MCP method and HTTP status. Tool calls are recorded when the handler completes rather than when a long-lived response stream closes. Tool arguments, response bodies, and provider error text are never stored. A run of failures is the signal to investigate.
Subjects are pseudonymised with the dedicated MCP_AUDIT_HMAC_KEY, so the same person correlates across requests without the identifier being stored. Keep this key stable when rotating MCP_SERVICE_TOKEN; changing it intentionally starts a new pseudonym namespace. Entries older than 180 days are pruned at startup and every 24 hours thereafter, matching EgressViewβs own audit retention, so the window keeps applying however long the process stays up.
MCP_AUTH_MODE=token. Local collection, the browser UI, stdio and private HTTP keep running.MCP_SERVICE_TOKEN if the MCP host itself may be compromised. Issue a new scoped API identity in EgressView, update .env.mcp, restart, then revoke the old identity.The public MCP endpoint fails closed and returns 401. Everything else keeps running: router collection, the browser UI, stdio clients and private HTTP mode. Do not treat an IdP outage as an EgressView outage.
P2-60 provides a fail-closed gate that must pass before a public DNS record is
created. The gate does not create or modify DNS, certificates, load
balancers, security groups, Keycloak, or EgressView configuration. A passing
result is only ready_for_manual_dns_review; publishing DNS remains a separate
reviewed operation.
Run it against the DNS-unpublished ALB or reverse proxy. The canonical hostname
is retained for TLS SNI and the HTTP Host header, while
MCP_GATE_CONNECT_ADDRESS pins the staged target like curl --connect-to.
cp .env.mcp-gate.example .env.mcp-gate
chmod 600 .env.mcp-gate
cp docs/mcp-publication-evidence.example.json \
.egressview-mcp-publication-evidence.json
set -a
. ./.env.mcp-gate
set +a
npm run mcp:publication-gate
Both local files are gitignored. Keep bearer tokens only in the mode-0600
environment file and remove them immediately after the run. The evidence JSON
and generated report contain no token values, tool arguments, network
observations, IP/MAC addresses, or credentials.
The dual-era gate requires evidence schema v3. Replace an older template rather than editing its version number alone; the client protocol and compatibility fields are mandatory and must come from actual client runs.
Every evidence item must be successful, refer to the exact deployed 40-character Git commit, and be no more than 30 days old:
/healthz, /readyz, and current router collection remained
healthy;reject-replay), or replay detection revoked the complete family and the
current refresh token was then rejected (revoke-family);2025-11-25 and 2026-07-28 independently of product release timing;unsupported-random-loopback-port when the tested release
cannot use a fixed callback accepted by Cognito;2025-11-25.With MCP_GATE_OAUTH_COMPATIBILITY_PROFILE=cognito, the gate additionally
requires cognitoCompatibility evidence matching the configured issuer and
resource. It must record PKCE S256, resource in both authorization and
token requests, the access-token audience and refreshed audience, exact
callback matching for tested compatible clients, old-refresh-token and
post-revocation rejection, and the tested Inspector, Claude Code, and Copilot
CLI versions. A Cognito callback
limitation must be recorded explicitly and does not claim Copilot
compatibility. The Keycloak database restore entry is not required in this
profile.
The JWKS outage test must use a cold MCP process. A running process may legitimately continue validating signatures with a still-valid cached JWKS; that is not evidence that discovery fails closed.
The command then verifies:
401 challenges for unauthenticated calls;2025-11-25 initialize and 2026-07-28 server/discover;-32020 and unsupported-version error
-32022;403 for a read token attempting set_device_note, and visibility of that
tool with a write-scoped token;429 plus Retry-After after the configured staging burst;The rate probe intentionally fills the staging processβs global one-minute bucket. Do not run it against a live public endpoint.
Because EgressView validates JWTs offline, revoking a Keycloak session or
refresh family does not invalidate an already-issued access token before its
exp. MCP_GATE_REVOKED_EXPIRED_TOKEN is therefore checked after the short
access-token TTL expires. Evidence mode reject-replay requires the replay
request itself to fail while the current family remains usable. Mode
revoke-family, matching the observed Keycloak 26.7.0 behavior, requires the
current family to fail after replay detection; the replay request may have
minted one final short-lived access token. Both modes require an access-token
lifetime of at most 15 minutes. For immediate containment, remove the public
proxy route first; do not claim immediate access-token revocation.
A successful run writes a mode-0600
.egressview-mcp-publication-gate.json. It records the deployed commit,
timestamp, hostname, router count, and pass/fail categories, but no secrets.
Failure exits nonzero and must block DNS publication.
If any post-publication check fails, remove the Web/MCP DNS aliases, disable the MCP proxy route, and return to VPN/SSM access. Restore the last verified application release and Keycloak database only through their separately tested rollback procedures. Router collection and the local recovery administrator must remain available throughout.
AWS Kiro, Anthropic Claude, Anysphere Cursor, and other product names are trademarks or registered trademarks of their respective owners. EgressView is not affiliated with, endorsed by, or sponsored by those companies.