mcp-web-engine 1.0.3 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +84 -107
  2. package/bin/cli.js +71 -36
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,132 +1,87 @@
1
1
  # 🌐 MCP Web Engine & Search Gateway
2
2
 
3
- > **Public HTTPS Specification 2026-07-28 Pure Stateless Core MCP Server for AI Agents**
3
+ > **Privacy-First, Self-Hostable & SSRF-Hardened MCP Server & Web Engine for AI Agents**
4
+ > **MCP Specification 2026-07-28 Pure Stateless Core & Stdio / HTTP Dual Mode**
5
+
6
+ [![PyPI Version](https://img.shields.io/badge/pypi-v1.0.5-blue.svg)](https://pypi.org/project/mcp-web-engine/)
7
+ [![npm Version](https://img.shields.io/badge/npm-v1.0.5-red.svg)](https://www.npmjs.com/package/mcp-web-engine)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
4
9
 
5
10
  ---
6
11
 
7
- ## 🌐 Public Production HTTPS Endpoint (Cloudflare Managed Tunnel)
12
+ ## ⚡ Instant Quickstart with `uvx` (Recommended for AI Agents)
13
+
14
+ AI agents (Claude Desktop, Cursor, Windsurf, TurboLLM, Hermes Agent) can run `mcp-web-engine` in an isolated ephemeral environment with **zero configuration**:
8
15
 
9
- - **Public MCP 2026-07-28 Endpoint:** `https://ultimate-ignored-over-light.trycloudflare.com/v1/mcp`
10
- - **Health Check:** `https://ultimate-ignored-over-light.trycloudflare.com/health`
16
+ ```bash
17
+ # Run stdio MCP server in an isolated environment (10ms startup, 0% system contamination)
18
+ uvx mcp-web-engine
19
+ ```
20
+
21
+ Or run via Git repository:
22
+ ```bash
23
+ uvx --from git+https://github.com/Arbolencio/mcp-web-engine mcp-web-engine
24
+ ```
11
25
 
12
26
  ---
13
27
 
14
28
  ## 📌 What it does & Why it exists
15
29
 
16
- `MCP Web Engine` is a high-performance Model Context Protocol (MCP) server implementing the **MCP Specification 2026-07-28 Pure Stateless Core** (JSON-RPC 2.0 over HTTPS). It provides AI Agents (Claude Desktop, Cursor, Windsurf, MCP Inspector, Hermes Agent) with secure, private access to web search, raw content fetching, and clean HTML-to-Markdown extraction.
30
+ `mcp-web-engine` is a high-performance Model Context Protocol (MCP) server implementing the **MCP Specification 2026-07-28 Pure Stateless Core**. It provides AI Agents with secure, private, and SSRF-hardened access to multi-engine web search, raw content fetching, and clean HTML-to-Markdown extraction.
17
31
 
18
32
  ### Key Capabilities & Architecture
33
+ - **Dual Execution Mode:**
34
+ - **Stdio Mode (Default):** Runs JSON-RPC over `stdin`/`stdout` for direct LLM client integration (`uvx mcp-web-engine`).
35
+ - **HTTP/SSE Server Mode:** Runs FastAPI/Uvicorn server (`mcp-web-engine --serve`).
19
36
  - **Pure Stateless Core (Spec 2026-07-28):** Zero sessions, zero state, zero initialize handshake required. Every request is completely independent.
20
- - **Protocol Methods:** Implements `server/discover`, `tools/list`, and `tools/call` with standard JSON-RPC 2.0 payloads.
37
+ - **Protocol Methods:** Implements `server/discover`, `tools/list`, and `tools/call` with standard JSON-RPC 2.0.
21
38
  - **Zero Third-Party Tracking:** Powered by an internal SearXNG meta-search engine aggregating 70+ sources with DuckDuckGo fallback.
39
+ - **Automatic URL Normalization:** Handled transparently by `normalize_searxng_url()` — any provided `SEARXNG_URL` variation (`http://127.0.0.1:8082`, `http://127.0.0.1:8082/`, `http://127.0.0.1:8082/search`) automatically appends `/search` if missing.
22
40
  - **SSRF Hardened:** Pre-request DNS resolution checks prevent agents from accessing internal networks (`localhost`, `127.0.0.1`, `192.168.x.x`, `169.254.169.254`). Step-by-step HTTP 301/302 redirect re-validation.
23
- - **TLS Fingerprinting:** Powered by `curl_cffi` (Chrome 120+ TLS impersonation) for clean research scraping without heavy browser overhead.
24
-
25
- ---
26
-
27
- ## 🏗️ Architecture
28
-
29
- ```
30
- [ MCP Client / Claude / Cursor / Windsurf / Inspector ]
31
- │
32
- (HTTPS Header: Authorization: Bearer sk_mcp_...)
33
- (HTTPS Header: MCP-Protocol-Version: 2026-07-28)
34
- ▼
35
- [ Cloudflare Managed HTTPS Tunnel ]
36
- │
37
- ▼
38
- [ FastAPI Gateway (Port 5050) ]
39
- ├── Endpoint: POST /v1/mcp (Pure Stateless Core)
40
- ├── Security & Auth (Bearer Token + Sliding Window Rate Limiter)
41
- ├── SSRF Validator (Pre-request DNS + IP Subnet Filtering)
42
- ├── Method 1: server/discover (Server Metadata Discovery)
43
- ├── Method 2: tools/list (Tools Discovery)
44
- └── Method 3: tools/call (Tool Execution Engine)
45
- ```
41
+ - **Zero `--break-system-packages`:** Executed 100% in sandboxed virtual environments created on-the-fly by `uv` or `venv`.
46
42
 
47
43
  ---
48
44
 
49
- ## 🚀 Quickstart & Docker Installation (< 2 Minutes)
45
+ ## 🔌 Integration Guides
50
46
 
51
- ```bash
52
- # 1. Clone Repository
53
- git clone https://github.com/Arbolencio/mcp-web-engine.git
54
- cd mcp-web-engine
55
-
56
- # 2. Copy Environment Example
57
- cp .env.example .env
58
-
59
- # 3. Launch with Docker Compose
60
- docker compose up -d
61
-
62
- # 4. Verify Health Check
63
- curl -s https://ultimate-ignored-over-light.trycloudflare.com/health
64
- # {"status":"ok","environment":"production","searxng_url":"http://host.docker.internal:8082/search","mcp_version":"2026-07-28"}
47
+ ### 1. TurboLLM / Local MCP Catalog
48
+ ```typescript
49
+ {
50
+ id: 'mcp-web-engine',
51
+ name: 'MCP Web Engine',
52
+ cat: 'Search',
53
+ desc: 'Privacy-first, self-hostable & SSRF-hardened web search engine and scraper for AI agents.',
54
+ cmd: 'uvx mcp-web-engine',
55
+ uvx: true,
56
+ argNote: 'Requires a running SearXNG instance.',
57
+ envs: [
58
+ { key: 'SEARXNG_URL', desc: 'SearXNG meta-search endpoint (default: http://127.0.0.1:8082/search)', required: true },
59
+ ],
60
+ }
65
61
  ```
66
62
 
67
- ---
68
-
69
- ## 🔌 Connecting from MCP Clients (Cursor, Windsurf, Inspector, Claude Desktop)
70
-
71
- ### 1. Cursor / Windsurf / MCP Inspector (Direct HTTPS):
72
- - **Server Endpoint:** `https://ultimate-ignored-over-light.trycloudflare.com/v1/mcp`
73
- - **Headers:**
74
- - `Authorization: Bearer YOUR_BETA_KEY_HERE`
75
- - `MCP-Protocol-Version: 2026-07-28`
76
-
77
- ### 2. Claude Desktop (via `mcp-remote` bridge):
63
+ ### 2. Claude Desktop (`claude_desktop_config.json`)
78
64
  ```json
79
65
  {
80
66
  "mcpServers": {
81
67
  "mcp-web-engine": {
82
- "command": "npx",
83
- "args": [
84
- "-y",
85
- "mcp-remote",
86
- "https://ultimate-ignored-over-light.trycloudflare.com/v1/mcp",
87
- "--header",
88
- "Authorization: Bearer YOUR_BETA_KEY_HERE"
89
- ]
68
+ "command": "uvx",
69
+ "args": ["mcp-web-engine"],
70
+ "env": {
71
+ "SEARXNG_URL": "http://127.0.0.1:8082/search"
72
+ }
90
73
  }
91
74
  }
92
75
  }
93
76
  ```
94
77
 
95
- ---
96
-
97
- ## 🛠️ MCP 2026-07-28 Stateless Protocol Lifecycle
98
-
99
- ### 1. `server/discover` Discovery
100
- ```json
101
- {
102
- "jsonrpc": "2.0",
103
- "id": 1,
104
- "method": "server/discover",
105
- "params": {}
106
- }
107
- ```
108
-
109
- ### 2. `tools/list` Discovery
110
- ```json
111
- {
112
- "jsonrpc": "2.0",
113
- "id": 2,
114
- "method": "tools/list",
115
- "params": {}
116
- }
117
- ```
118
-
119
- ### 3. `tools/call` Execution
78
+ ### 3. Cursor / Windsurf / Hermes Agent
120
79
  ```json
121
80
  {
122
- "jsonrpc": "2.0",
123
- "id": 3,
124
- "method": "tools/call",
125
- "params": {
126
- "name": "web_search",
127
- "arguments": {
128
- "query": "Model Context Protocol 2026 specification",
129
- "limit": 5
81
+ "mcpServers": {
82
+ "mcp-web-engine": {
83
+ "command": "uvx",
84
+ "args": ["mcp-web-engine"]
130
85
  }
131
86
  }
132
87
  }
@@ -134,28 +89,50 @@ curl -s https://ultimate-ignored-over-light.trycloudflare.com/health
134
89
 
135
90
  ---
136
91
 
137
- ## 🔑 Managing Beta Keys
92
+ ## 🛠️ Provided MCP Tools
93
+
94
+ | Tool Name | Parameters | Description |
95
+ |---|---|---|
96
+ | `web_search` | `query` (str), `limit` (int, default: 10) | Multi-engine meta-search via SearXNG returning structured JSON results |
97
+ | `fetch_url` | `url` (str), `max_bytes` (int, optional) | Fetches raw web content after strict SSRF security validation |
98
+ | `extract_markdown` | `url` (str), `max_bytes` (int, optional) | Scrapes a webpage and converts HTML into clean, LLM-optimized Markdown |
99
+
100
+ ---
101
+
102
+ ## ⚙️ Configuration & Environment Variables
138
103
 
139
- Manage API access keys using the CLI helper:
104
+ | Variable | Required | Default | Description |
105
+ |---|---|---|---|
106
+ | `SEARXNG_URL` | **Yes** | `http://127.0.0.1:8082/search` | SearXNG meta-search endpoint (Auto-normalizes trailing slashes and missing `/search`) |
107
+ | `PORT` | No | `5050` | HTTP/SSE server port (Used only when running in `--serve` mode) |
108
+ | `HOST` | No | `0.0.0.0` | HTTP/SSE server binding host |
109
+ | `API_KEY` | No | `""` | Optional authentication key for HTTP endpoints |
110
+
111
+ ---
112
+
113
+ ## 🛠️ Development & Building
140
114
 
141
115
  ```bash
142
- # Generate 20 Beta Keys
143
- python manage_beta_keys.py init20
116
+ # Clone Repository
117
+ git clone https://github.com/Arbolencio/mcp-web-engine.git
118
+ cd mcp-web-engine
119
+
120
+ # Build Wheel for PyPI
121
+ python3 -m build --wheel
144
122
 
145
- # View Beta User Telemetry
146
- python manage_beta_keys.py telemetry
123
+ # Run locally in stdio mode via uv
124
+ uv run python -m mcp_web_engine.main
147
125
 
148
- # Revoke a key by ID
149
- python manage_beta_keys.py revoke --id Beta_001
126
+ # Run locally in HTTP server mode via uv
127
+ uv run python -m mcp_web_engine.main --serve
150
128
  ```
151
129
 
152
130
  ---
153
131
 
154
- ## 🛡️ Security & Honest Technical Limits
132
+ ## 🛡️ Security & Environment Isolation
155
133
 
156
134
  - **SSRF Hardening:** Blocks `localhost`, loopbacks, private subnets (`10.0.0.0/8`, `192.168.0.0/16`, `172.16.0.0/12`), percent-encoding bypasses (`%31%32%37...`), and re-validates `Location` headers on HTTP redirects step-by-step.
157
- - **Rate Limiting:** Default limit of 120 req/min per key.
158
- - **Honest Positioning:** Designed for research, documentation retrieval, and web search. Auth-walled sites requiring login (e.g. private social feeds) are not supported.
135
+ - **Zero Package Pollution:** Execution via `uvx` or `bin/cli.js` never modifies global site-packages or uses dangerous flags like `--break-system-packages`.
159
136
 
160
137
  ---
161
138
 
package/bin/cli.js CHANGED
@@ -1,55 +1,90 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * MCP Web Engine CLI Entry Point (npm package executable)
5
- * Spawns the MCP Web Engine Python server (stdio/SSE)
6
- * Respects SEARXNG_URL, PORT, ALLOWED_NETWORKS, and environment settings.
4
+ * MCP Web Engine CLI Entry Point (npm / npx fallback)
5
+ * Spawns MCP Web Engine in an ISOLATED Python environment without polluting global system packages.
6
+ *
7
+ * Recommended execution for AI agents:
8
+ * uvx mcp-web-engine
7
9
  */
8
10
 
9
11
  const { spawn, execSync } = require('child_process');
10
12
  const path = require('path');
13
+ const fs = require('fs');
14
+ const os = require('os');
11
15
 
12
- const mainPy = path.join(__dirname, '..', 'main.py');
13
16
  const reqTxt = path.join(__dirname, '..', 'requirements.txt');
14
17
 
15
- function findPython() {
16
- const candidates = ['python3', 'python'];
17
- for (const cmd of candidates) {
18
- try {
19
- execSync(`${cmd} --version`, { stdio: 'ignore' });
20
- return cmd;
21
- } catch (e) {}
22
- }
23
- return 'python3';
18
+ function forwardChild(child) {
19
+ child.on('error', (err) => {
20
+ console.error('[mcp-web-engine] Failed to start process:', err.message);
21
+ process.exit(1);
22
+ });
23
+ child.on('exit', (code, signal) => {
24
+ if (signal) {
25
+ process.exit(128 + (signal === 'SIGINT' ? 2 : 15));
26
+ }
27
+ process.exit(code ?? 0);
28
+ });
24
29
  }
25
30
 
26
- const pythonCmd = findPython();
27
-
28
- // Auto-check and install python dependencies if missing (pip logs sent to stderr to preserve stdio JSON-RPC)
29
- try {
30
- execSync(`${pythonCmd} -c "import fastapi, uvicorn, requests, bs4"`, { stdio: 'ignore' });
31
- } catch (e) {
31
+ // 1. Try running with 'uv' if available (fastest, cleanest, isolated)
32
+ function tryUv() {
32
33
  try {
33
- execSync(`${pythonCmd} -m pip install -r "${reqTxt}" --break-system-packages`, { stdio: [0, 2, 2] });
34
- } catch (err) {
35
- // Fallback gracefully
34
+ execSync('uv --version', { stdio: 'ignore' });
35
+ const repoRoot = path.join(__dirname, '..');
36
+ const child = spawn('uv', ['run', '--directory', repoRoot, 'python', '-m', 'mcp_web_engine.main', ...process.argv.slice(2)], {
37
+ stdio: 'inherit',
38
+ env: process.env
39
+ });
40
+ forwardChild(child);
41
+ return true;
42
+ } catch (e) {
43
+ return false;
36
44
  }
37
45
  }
38
46
 
39
- // Spawn MCP Web Engine
40
- const child = spawn(pythonCmd, [mainPy, ...process.argv.slice(2)], {
41
- stdio: 'inherit',
42
- env: process.env
43
- });
47
+ // 2. Fallback to dedicated isolated virtualenv in user cache dir (~/.cache/mcp-web-engine/venv)
48
+ function runInIsolatedVenv() {
49
+ const cacheDir = process.env.XDG_CACHE_HOME || path.join(os.homedir(), '.cache');
50
+ const venvDir = path.join(cacheDir, 'mcp-web-engine', 'venv');
51
+ const venvPython = process.platform === 'win32'
52
+ ? path.join(venvDir, 'Scripts', 'python.exe')
53
+ : path.join(venvDir, 'bin', 'python');
54
+ const venvPip = process.platform === 'win32'
55
+ ? path.join(venvDir, 'Scripts', 'pip.exe')
56
+ : path.join(venvDir, 'bin', 'pip');
44
57
 
45
- child.on('error', (err) => {
46
- console.error('[mcp-web-engine] Failed to start Python process:', err.message);
47
- process.exit(1);
48
- });
58
+ function findSystemPython() {
59
+ for (const cmd of ['python3', 'python']) {
60
+ try {
61
+ execSync(`${cmd} --version`, { stdio: 'ignore' });
62
+ return cmd;
63
+ } catch (e) {}
64
+ }
65
+ throw new Error('Python 3 is required but was not found on PATH.');
66
+ }
49
67
 
50
- child.on('exit', (code, signal) => {
51
- if (signal) {
52
- process.exit(128 + (signal === 'SIGINT' ? 2 : 15));
68
+ // Ensure isolated venv exists and has requirements installed
69
+ if (!fs.existsSync(venvPython)) {
70
+ console.log('[mcp-web-engine] Creating isolated Python venv at:', venvDir);
71
+ const sysPython = findSystemPython();
72
+ fs.mkdirSync(path.dirname(venvDir), { recursive: true });
73
+ execSync(`${sysPython} -m venv "${venvDir}"`, { stdio: 'inherit' });
74
+ console.log('[mcp-web-engine] Installing dependencies into isolated venv...');
75
+ execSync(`"${venvPip}" install -r "${reqTxt}"`, { stdio: 'inherit' });
53
76
  }
54
- process.exit(code ?? 0);
55
- });
77
+
78
+ const repoRoot = path.join(__dirname, '..');
79
+ const env = { ...process.env, PYTHONPATH: path.join(repoRoot, 'src') };
80
+
81
+ const child = spawn(venvPython, ['-m', 'mcp_web_engine.main', ...process.argv.slice(2)], {
82
+ stdio: 'inherit',
83
+ env: env
84
+ });
85
+ forwardChild(child);
86
+ }
87
+
88
+ if (!tryUv()) {
89
+ runInIsolatedVenv();
90
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-web-engine",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "Privacy-First, Self-Hostable & SSRF-Hardened MCP Server & Web Engine for AI Agents",
5
5
  "main": "bin/cli.js",
6
6
  "bin": {