
geo-shield
io.github.krishnaGauss/geo-shield
Detects hidden AI prompt injection, AI recommendation poisoning, sneaky redirects and hidden spam.
Documentation
GEO Shield MCP
A defensive, read-only MCP server that lets AI agents (Claude, Gemini CLI, Cursor, VS Code Copilot, Codex and others) inspect websites for manipulation aimed at search engines and AI assistants: sneaky and open redirects, hidden text and links, injected spam, hijacked metadata, hidden prompt-injection text, and "Summarise with AI" links whose prefilled prompts try to plant instructions in a visitor's assistant ("AI recommendation poisoning").
Every finding is produced by a deterministic rule and backed by evidence: location, visibility state, an excerpt, severity and confidence (kept separate), remediation, and false-positive notes. Scanned content is always treated as untrusted data.
Status: Milestone 1.
geo_scan_url,geo_trace_redirectsandgeo_get_reportare implemented and tested. See Pending for later phases.
Install
Requires Node.js ≥ 22.12 (it uses the built-in node:sqlite). No API keys are needed. Every client below runs the same command: npx -y geo-shield-mcp.
Claude Code
claude mcp add geo-shield -s user -- npx -y geo-shield-mcp
Gemini CLI
gemini mcp add geo-shield npx -y geo-shield-mcp
OpenAI Codex CLI
codex mcp add geo-shield -- npx -y geo-shield-mcp
Claude Desktop, Cursor, Windsurf, Antigravity
These clients use the same JSON shape:
| Client | Config file |
|---|---|
| Claude Desktop | ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows) |
| Cursor | ~/.cursor/mcp.json |
| Windsurf | ~/.codeium/windsurf/mcp_config.json |
| Antigravity | ~/.gemini/antigravity/mcp_config.json |
{
"mcpServers": {
"geo-shield": {
"command": "npx",
"args": ["-y", "geo-shield-mcp"]
}
}
}
VS Code (GitHub Copilot agent mode)
.vscode/mcp.json:
{
"servers": {
"geo-shield": { "type": "stdio", "command": "npx", "args": ["-y", "geo-shield-mcp"] }
}
}
ChatGPT and other web apps
Web apps such as ChatGPT can only connect to remote MCP servers over HTTPS. This release runs locally over stdio, so it works with the desktop, CLI and IDE agents above. A hosted HTTP version is planned.
From source
git clone https://github.com/krishnaGauss/MCP_WebSafe.git && cd MCP_WebSafe && npm install && npm run build
Then point your client at node /absolute/path/to/MCP_WebSafe/dist/index.js instead of npx -y geo-shield-mcp.
Usage
Ask your agent in plain language:
- "Scan https://example.com with geo_scan_url and explain anything suspicious."
- "Trace where this link really goes: https://…"
- "Check https://… for hidden AI prompt injection." (or run the
investigate_ai_injectionprompt) - "Scan it again as a mobile browser and as a search crawler. Does the content change?"
- "Give me the full markdown report for scan_…"
Scans are stored locally in ~/.geo-shield/geo-shield.db and purged after 30 days by default.
Tools
| Tool | What it does |
|---|---|
geo_scan_url | Fetches one URL (SSRF-protected, no JS execution), reconstructs redirects, runs all rules. Inputs: url, scan_profile (quick/standard/deep), check_redirects, check_hidden_content, check_ai_injection, render_javascript (not yet supported — returns a notice), request_context. |
geo_trace_redirects | Hop-by-hop redirect chain (3xx + Refresh), each hop re-validated; reports declared meta/JS redirects on the final page. Inputs: url, max_redirects, request_context. |
geo_get_report | Full stored report as markdown or json. |
Request contexts: desktop_browser, mobile_browser, search_referrer, crawler_ua_simulated. Non-browser contexts are labelled as simulations. A request from this machine is not proof of what a real crawler sees.
Resources: geo://taxonomy, geo://rules, geo://rules/{rule_id}, geo://scan/{scan_id}, geo://domain/{domain}/history
Prompts: investigate_redirect, investigate_ai_injection, review_scan_findings
Rules (Milestone 1)
| Category | Rules |
|---|---|
| Redirects / cloaking | RED-001 cross-domain redirect · RED-002 open-redirect observed · RED-003 on-page redirect endpoint with external target · RED-004 meta-refresh/JS navigation off-site · RED-005 loop · RED-006 link text shows a different domain · RED-007 HTTPS→HTTP · RED-008 referrer/UA-conditional JS redirect |
| Hidden content | HID-001 hidden text · HID-002 hidden external links · HID-003 off-screen · HID-004 same-colour text · HID-005 tiny/zero-size |
| Spam / integrity | SPM-001 spam-keyword outbound links · SPM-002 hidden/unexpected iframe · SPM-003 obfuscated inline script · MET-001 spam keywords in title/metadata · MET-002 cross-domain canonical |
| AI context | AI-001 hidden AI instructions · AI-002 instructions in comments/attributes/meta/JSON-LD · AI-003 instructions in visible text · AI-004 manipulative AI-assistant prefill link · AI-005 benign prefill link (info) · AI-006 unverifiable AI endorsement claims |
Full definitions (detection logic, severity mapping, remediation, false positives) are served at geo://rules.
How false positives are kept down:
- Hidden elements that match accessibility or UI patterns (sr-only,
<details>, menus, tabs, modals,aria-controlstargets, camelCase component classes) are counted but not flagged, unless they contain spam keywords, AI instructions or external links. - Text hidden by a UI toggle (
display:none,opacity:0,hidden) is informational unless its content is suspicious. - Same-colour checks use the colour the text actually inherits, and are marked "unconfirmed" when no background is declared or the text sits next to an image.
- AI-instruction patterns work in families (override, persistence, exfiltration, suppression, recommendation, AI-address). Visible text needs corroborating families before it is flagged.
Security design
- SSRF: only http/https on ports 80/443 (add more with
GEO_SHIELD_ALLOWED_PORTS). The scanner blocks:- credentials embedded in URLs
localhost,*.internal,*.localand single-label hostnames- IP literals in any encoding (decimal, hex, octal)
- private, loopback, link-local, CGNAT and metadata ranges, for both IPv4 and IPv6 (including IPv4-mapped addresses)
- DNS rebinding: a guarded lookup inside the HTTP connector validates every resolved address at connect time, so the address it checks is the address it connects to.
- Redirects: followed manually and re-validated on every hop, with loop detection, a timeout, and a body cap (oversized bodies are truncated and flagged).
- No execution: scripts are never run, and links and "Summarise with AI" buttons are inspected statically, never followed.
- Untrusted output:
- Excerpts are stripped of control, zero-width and bidi characters, then truncated.
- The markdown report wraps excerpts in code spans they can't break out of.
- Every tool result carries an untrusted-content notice.
- Privacy and logging:
Set-Cookievalues are never stored, and logs go to stderr only. - Fixture mode (
GEO_SHIELD_FIXTURE_ALLOW=host:port,...) is the only way to reach loopback. It's for tests only and logs a warning when set.
Configuration
See .env.example: GEO_SHIELD_DB_PATH, GEO_SHIELD_LOG_LEVEL, GEO_SHIELD_FETCH_TIMEOUT_MS, GEO_SHIELD_MAX_BODY_BYTES, GEO_SHIELD_ALLOWED_PORTS, GEO_SHIELD_RETENTION_DAYS (stored scans older than this are purged at startup), and GEO_SHIELD_FIXTURE_ALLOW.
Development
npm test
npm run lint && npm run typecheck
npm run build && npm run demo -- https://example.com
npm run demostarts the local fixture site, launches the built server over real stdio as Claude would, and calls every tool.npm run fixturesruns the fixture site on its own, for use withnpm run inspect(MCP Inspector).
The tests cover:
- 14+ fixture pages: clean, legitimate hidden UI, screen-reader text, white-on-white links, off-screen text, open redirect, UA- and referrer-conditional redirects, injected casino link, hidden iframe, hidden AI instructions, malicious "Summarise with AI" links, a hijacked spam page, a legitimate migration redirect, an article about prompt injection, and more
- SSRF bypass attempts
- unit tests for each rule
- an end-to-end MCP client/server test
Releasing
- Bump the version in
package.json,server.json(twice) andSERVER_INFOinsrc/mcp/server.ts. A test fails if they disagree. - Publish to npm.
prepublishOnlyruns type-check, lint, tests and build first:npm publish - Publish to the MCP Registry:
mcp-publisher login github && mcp-publisher publish
Architecture
src/
index.ts STDIO entry (serveStdio)
mcp/ tools, resources, prompts (transport layer only)
scanner/orchestrator.ts pipeline: validate → fetch → parse → detect → score → persist
scanner/safe-fetch.ts manual redirects, size/time limits, guarded DNS
security/ SSRF policy
analysis/ HTML parser, static visibility engine, keyword + AI-instruction patterns
detectors/ redirect-abuse, hidden-content, spam-injection, ai-prompt-injection
rules/catalog.ts rule definitions + taxonomy
risk/ evidence builder, scoring
reports/ markdown renderer
storage/ SQLite (scans, pages, findings, redirect_hops)
The scanner engine is independent of MCP: ScanOrchestrator can be reused by a CLI or CI job.
Limitations
- Static analysis only: no JavaScript rendering, and external stylesheets aren't fetched. Visibility is inferred from inline styles,
<style>blocks and attributes. - Simulated contexts: request contexts are simulations, not verified crawler behaviour.
- No baseline yet: without a stored baseline, a scan can't tell when content was added.
- Indicators, not verdicts: findings call for investigation. They aren't proof of intent or of a search-policy violation.
- No ingestion proof: a page scan can't show that any AI model ingested or remembered the content.
Pending
- Phase 3 ·
geo_compare_page_variants(cloaking diff across contexts), Playwright rendering - Phase 4 ·
geo_scan_site(sitemap + scoped crawl with authorisation), baselines,geo_diff_baseline - Phase 4/5 ·
geo_audit_ugc,geo_inspect_ai_contentas a focused tool, memory-poisoning consistency checks - Phase 6 · historical comparison, finding annotations,
geo://domain/{domain}/baseline
geo-shield-mcpnpm install geo-shield-mcp