@kassol/mcp-searxng 1.0.3-custom.0 → 1.1.0
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.
- package/README.md +44 -24
- package/dist/headers.d.ts +3 -0
- package/dist/headers.js +26 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/resources.js +4 -2
- package/dist/search.js +2 -2
- package/dist/url-reader.js +2 -2
- package/package.json +7 -1
package/README.md
CHANGED
|
@@ -2,13 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
An [MCP server](https://modelcontextprotocol.io/introduction) that integrates the [SearXNG](https://docs.searxng.org) API, giving AI assistants web search capabilities.
|
|
4
4
|
|
|
5
|
-
This fork is published as `@kassol/mcp-searxng` and includes support for custom outgoing headers via `SEARXNG_HEADERS` and
|
|
5
|
+
This fork is published as `@kassol/mcp-searxng` and includes support for custom outgoing headers via `SEARXNG_HEADERS`, `URL_READER_HEADERS`, and their base64 variants.
|
|
6
6
|
|
|
7
|
-
[](https://www.npmjs.com/package/@kassol/mcp-searxng)
|
|
8
|
+
[](https://www.npmjs.com/package/@kassol/mcp-searxng)
|
|
9
|
+
[](LICENSE)
|
|
10
|
+
[](https://github.com/kassol/mcp-searxng)
|
|
12
11
|
|
|
13
12
|
## Quick Start
|
|
14
13
|
|
|
@@ -102,12 +101,10 @@ npm install -g @kassol/mcp-searxng
|
|
|
102
101
|
</details>
|
|
103
102
|
|
|
104
103
|
<details>
|
|
105
|
-
<summary>Docker</summary>
|
|
106
|
-
|
|
107
|
-
**Pre-built image:**
|
|
104
|
+
<summary>Docker (local build)</summary>
|
|
108
105
|
|
|
109
106
|
```bash
|
|
110
|
-
docker
|
|
107
|
+
docker build -t mcp-searxng:latest -f Dockerfile .
|
|
111
108
|
```
|
|
112
109
|
|
|
113
110
|
```json
|
|
@@ -118,7 +115,7 @@ docker pull isokoliuk/mcp-searxng:latest
|
|
|
118
115
|
"args": [
|
|
119
116
|
"run", "-i", "--rm",
|
|
120
117
|
"-e", "SEARXNG_URL",
|
|
121
|
-
"
|
|
118
|
+
"mcp-searxng:latest"
|
|
122
119
|
],
|
|
123
120
|
"env": {
|
|
124
121
|
"SEARXNG_URL": "YOUR_SEARXNG_INSTANCE_URL"
|
|
@@ -130,14 +127,6 @@ docker pull isokoliuk/mcp-searxng:latest
|
|
|
130
127
|
|
|
131
128
|
To pass additional env vars, add `-e VAR_NAME` to `args` and the variable to `env`.
|
|
132
129
|
|
|
133
|
-
**Build locally:**
|
|
134
|
-
|
|
135
|
-
```bash
|
|
136
|
-
docker build -t mcp-searxng:latest -f Dockerfile .
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
Use the same config above, replacing `isokoliuk/mcp-searxng:latest` with `mcp-searxng:latest`.
|
|
140
|
-
|
|
141
130
|
</details>
|
|
142
131
|
|
|
143
132
|
<details>
|
|
@@ -148,7 +137,10 @@ Use the same config above, replacing `isokoliuk/mcp-searxng:latest` with `mcp-se
|
|
|
148
137
|
```yaml
|
|
149
138
|
services:
|
|
150
139
|
mcp-searxng:
|
|
151
|
-
|
|
140
|
+
build:
|
|
141
|
+
context: .
|
|
142
|
+
dockerfile: Dockerfile
|
|
143
|
+
image: mcp-searxng:latest
|
|
152
144
|
stdin_open: true
|
|
153
145
|
environment:
|
|
154
146
|
- SEARXNG_URL=YOUR_SEARXNG_INSTANCE_URL
|
|
@@ -161,8 +153,8 @@ MCP client config:
|
|
|
161
153
|
{
|
|
162
154
|
"mcpServers": {
|
|
163
155
|
"searxng": {
|
|
164
|
-
"command": "docker
|
|
165
|
-
"args": ["run", "--rm", "mcp-searxng"]
|
|
156
|
+
"command": "docker",
|
|
157
|
+
"args": ["compose", "run", "--rm", "mcp-searxng"]
|
|
166
158
|
}
|
|
167
159
|
}
|
|
168
160
|
}
|
|
@@ -204,7 +196,25 @@ curl http://localhost:3000/health
|
|
|
204
196
|
|
|
205
197
|
Set `SEARXNG_URL` to your SearXNG instance URL. All other variables are optional.
|
|
206
198
|
|
|
207
|
-
Protected SearXNG instances can receive extra search request headers through `
|
|
199
|
+
Protected SearXNG instances can receive extra search request headers through `SEARXNG_HEADERS_BASE64`. This is the recommended format for ChatWise and other MCP clients that treat environment variables as plain key-value fields.
|
|
200
|
+
|
|
201
|
+
Generate the value directly from existing Cloudflare Access environment variables:
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
export CF_ACCESS_CLIENT_ID='your-client-id.access'
|
|
205
|
+
export CF_ACCESS_CLIENT_SECRET='your-client-secret'
|
|
206
|
+
|
|
207
|
+
node -e 'console.log(Buffer.from(JSON.stringify({"CF-Access-Client-Id":process.env.CF_ACCESS_CLIENT_ID,"CF-Access-Client-Secret":process.env.CF_ACCESS_CLIENT_SECRET})).toString("base64"))'
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Verify the generated value:
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
export SEARXNG_HEADERS_BASE64='paste-generated-value-here'
|
|
214
|
+
node -e 'console.log(Buffer.from(process.env.SEARXNG_HEADERS_BASE64,"base64").toString("utf8"))'
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
MCP client configuration:
|
|
208
218
|
|
|
209
219
|
```json
|
|
210
220
|
{
|
|
@@ -214,13 +224,23 @@ Protected SearXNG instances can receive extra search request headers through `SE
|
|
|
214
224
|
"args": ["-y", "@kassol/mcp-searxng"],
|
|
215
225
|
"env": {
|
|
216
226
|
"SEARXNG_URL": "https://search.example.com",
|
|
217
|
-
"
|
|
227
|
+
"SEARXNG_HEADERS_BASE64": "eyJDRi1BY2Nlc3MtQ2xpZW50LUlkIjoieW91ci1jbGllbnQtaWQuYWNjZXNzIiwiQ0YtQWNjZXNzLUNsaWVudC1TZWNyZXQiOiJ5b3VyLWNsaWVudC1zZWNyZXQifQ=="
|
|
218
228
|
}
|
|
219
229
|
}
|
|
220
230
|
}
|
|
221
231
|
}
|
|
222
232
|
```
|
|
223
233
|
|
|
234
|
+
ChatWise environment variables:
|
|
235
|
+
|
|
236
|
+
```text
|
|
237
|
+
SEARXNG_URL=https://search.example.com
|
|
238
|
+
USER_AGENT=Mozilla/5.0
|
|
239
|
+
SEARXNG_HEADERS_BASE64=paste-generated-value-here
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
`SEARXNG_HEADERS_BASE64` applies only to `searxng_web_search` requests. Use `URL_READER_HEADERS_BASE64` for headers that should be sent by `web_url_read`.
|
|
243
|
+
|
|
224
244
|
Full environment variable reference: [CONFIGURATION.md](CONFIGURATION.md)
|
|
225
245
|
|
|
226
246
|
## Troubleshooting
|
package/dist/headers.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
export type HeaderRecord = Record<string, string>;
|
|
2
2
|
export declare function parseHeadersFromEnv(envVarName: string): HeaderRecord;
|
|
3
|
+
export declare function parseHeadersFromBase64Env(envVarName: string): HeaderRecord;
|
|
4
|
+
export declare function getSearxngHeadersFromEnv(): HeaderRecord;
|
|
5
|
+
export declare function getUrlReaderHeadersFromEnv(): HeaderRecord;
|
|
3
6
|
export declare function mergeHeaders(headers: HeadersInit | undefined, additionalHeaders: HeaderRecord): HeaderRecord;
|
package/dist/headers.js
CHANGED
|
@@ -47,6 +47,23 @@ export function parseHeadersFromEnv(envVarName) {
|
|
|
47
47
|
if (!rawHeaders) {
|
|
48
48
|
return createHeaderRecord();
|
|
49
49
|
}
|
|
50
|
+
return parseHeadersJson(rawHeaders, envVarName);
|
|
51
|
+
}
|
|
52
|
+
export function parseHeadersFromBase64Env(envVarName) {
|
|
53
|
+
const rawHeaders = process.env[envVarName];
|
|
54
|
+
if (!rawHeaders) {
|
|
55
|
+
return createHeaderRecord();
|
|
56
|
+
}
|
|
57
|
+
let decodedHeaders;
|
|
58
|
+
try {
|
|
59
|
+
decodedHeaders = Buffer.from(rawHeaders, "base64").toString("utf8");
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
throw createConfigurationError(`${envVarName} must be valid base64`);
|
|
63
|
+
}
|
|
64
|
+
return parseHeadersJson(decodedHeaders, envVarName);
|
|
65
|
+
}
|
|
66
|
+
function parseHeadersJson(rawHeaders, envVarName) {
|
|
50
67
|
let parsedHeaders;
|
|
51
68
|
try {
|
|
52
69
|
parsedHeaders = JSON.parse(rawHeaders);
|
|
@@ -68,6 +85,15 @@ export function parseHeadersFromEnv(envVarName) {
|
|
|
68
85
|
}
|
|
69
86
|
return headers;
|
|
70
87
|
}
|
|
88
|
+
export function getSearxngHeadersFromEnv() {
|
|
89
|
+
return getHeadersFromEnv("SEARXNG_HEADERS", "SEARXNG_HEADERS_BASE64");
|
|
90
|
+
}
|
|
91
|
+
export function getUrlReaderHeadersFromEnv() {
|
|
92
|
+
return getHeadersFromEnv("URL_READER_HEADERS", "URL_READER_HEADERS_BASE64");
|
|
93
|
+
}
|
|
94
|
+
function getHeadersFromEnv(jsonEnvVarName, base64EnvVarName) {
|
|
95
|
+
return mergeHeaders(parseHeadersFromEnv(jsonEnvVarName), parseHeadersFromBase64Env(base64EnvVarName));
|
|
96
|
+
}
|
|
71
97
|
export function mergeHeaders(headers, additionalHeaders) {
|
|
72
98
|
const mergedHeaders = normalizeHeaders(headers);
|
|
73
99
|
for (const [name, value] of Object.entries(additionalHeaders)) {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
-
declare const packageVersion = "1.
|
|
3
|
+
declare const packageVersion = "1.1.0";
|
|
4
4
|
export { packageVersion };
|
|
5
5
|
export declare function isWebUrlReadArgs(args: unknown): args is {
|
|
6
6
|
url: string;
|
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@ import { fetchAndConvertToMarkdown } from "./url-reader.js";
|
|
|
10
10
|
import { createConfigResource, createHelpResource } from "./resources.js";
|
|
11
11
|
import { createHttpServer } from "./http-server.js";
|
|
12
12
|
// Use a static version string that will be updated by the version script
|
|
13
|
-
const packageVersion = "1.
|
|
13
|
+
const packageVersion = "1.1.0";
|
|
14
14
|
// Export the version for use in other modules
|
|
15
15
|
export { packageVersion };
|
|
16
16
|
// Type guard for URL reading args
|
package/dist/resources.js
CHANGED
|
@@ -15,8 +15,8 @@ export function createConfigResource() {
|
|
|
15
15
|
? { searxngUrl: process.env.SEARXNG_URL || "(not configured)" }
|
|
16
16
|
: { searxngUrlConfigured: !!process.env.SEARXNG_URL }),
|
|
17
17
|
hasAuth: !!(process.env.AUTH_USERNAME && process.env.AUTH_PASSWORD),
|
|
18
|
-
hasSearxngHeaders: !!process.env.SEARXNG_HEADERS,
|
|
19
|
-
hasUrlReaderHeaders: !!process.env.URL_READER_HEADERS,
|
|
18
|
+
hasSearxngHeaders: !!(process.env.SEARXNG_HEADERS || process.env.SEARXNG_HEADERS_BASE64),
|
|
19
|
+
hasUrlReaderHeaders: !!(process.env.URL_READER_HEADERS || process.env.URL_READER_HEADERS_BASE64),
|
|
20
20
|
hasProxy: !!(process.env.HTTP_PROXY || process.env.HTTPS_PROXY || process.env.http_proxy || process.env.https_proxy),
|
|
21
21
|
hasNoProxy: !!(process.env.NO_PROXY || process.env.no_proxy),
|
|
22
22
|
nodeVersion: process.version,
|
|
@@ -65,7 +65,9 @@ Reads and converts web page content to Markdown format.
|
|
|
65
65
|
- \`USER_AGENT\`: Global User-Agent header for outgoing requests
|
|
66
66
|
- \`URL_READER_USER_AGENT\`: User-Agent for \`web_url_read\`, overrides \`USER_AGENT\`
|
|
67
67
|
- \`SEARXNG_HEADERS\`: Extra JSON headers for SearXNG search requests
|
|
68
|
+
- \`SEARXNG_HEADERS_BASE64\`: Base64-encoded JSON headers for SearXNG search requests
|
|
68
69
|
- \`URL_READER_HEADERS\`: Extra JSON headers for URL read requests
|
|
70
|
+
- \`URL_READER_HEADERS_BASE64\`: Base64-encoded JSON headers for URL read requests
|
|
69
71
|
- \`HTTP_PROXY\` / \`HTTPS_PROXY\`: Proxy server configuration
|
|
70
72
|
- \`NO_PROXY\` / \`no_proxy\`: Comma-separated list of hosts to bypass proxy
|
|
71
73
|
- \`MCP_HTTP_PORT\`: Enable HTTP transport on specified port
|
package/dist/search.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createProxyAgent, createDefaultAgent, ProxyType } from "./proxy.js";
|
|
2
2
|
import { logMessage } from "./logging.js";
|
|
3
|
-
import {
|
|
3
|
+
import { getSearxngHeadersFromEnv, mergeHeaders } from "./headers.js";
|
|
4
4
|
import { MCPSearXNGError, validateEnvironment, createNetworkError, createServerError, createJSONError, createDataError, createNoResultsMessage } from "./error-handler.js";
|
|
5
5
|
export async function performWebSearch(mcpServer, query, pageno = 1, time_range, language = "all", safesearch) {
|
|
6
6
|
const startTime = Date.now();
|
|
@@ -61,7 +61,7 @@ export async function performWebSearch(mcpServer, query, pageno = 1, time_range,
|
|
|
61
61
|
'User-Agent': userAgent
|
|
62
62
|
};
|
|
63
63
|
}
|
|
64
|
-
const additionalHeaders =
|
|
64
|
+
const additionalHeaders = getSearxngHeadersFromEnv();
|
|
65
65
|
if (Object.keys(additionalHeaders).length > 0) {
|
|
66
66
|
requestOptions.headers = mergeHeaders(requestOptions.headers, additionalHeaders);
|
|
67
67
|
}
|
package/dist/url-reader.js
CHANGED
|
@@ -4,7 +4,7 @@ import { createProxyAgent, createDefaultAgent, ProxyType } from "./proxy.js";
|
|
|
4
4
|
import { logMessage } from "./logging.js";
|
|
5
5
|
import { urlCache } from "./cache.js";
|
|
6
6
|
import { getHttpSecurityConfig } from "./http-security.js";
|
|
7
|
-
import {
|
|
7
|
+
import { getUrlReaderHeadersFromEnv, mergeHeaders } from "./headers.js";
|
|
8
8
|
import { createURLFormatError, createURLSecurityPolicyError, createNetworkError, createServerError, createContentError, createConversionError, createTimeoutError, createEmptyContentWarning, createUnexpectedError } from "./error-handler.js";
|
|
9
9
|
function isPrivateHostname(hostname) {
|
|
10
10
|
const lower = hostname.toLowerCase().replace(/\.+$/, "");
|
|
@@ -194,7 +194,7 @@ export async function fetchAndConvertToMarkdown(mcpServer, url, timeoutMs = 1000
|
|
|
194
194
|
'User-Agent': userAgent
|
|
195
195
|
};
|
|
196
196
|
}
|
|
197
|
-
const additionalHeaders =
|
|
197
|
+
const additionalHeaders = getUrlReaderHeadersFromEnv();
|
|
198
198
|
if (Object.keys(additionalHeaders).length > 0) {
|
|
199
199
|
requestOptions.headers = mergeHeaders(requestOptions.headers, additionalHeaders);
|
|
200
200
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kassol/mcp-searxng",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"mcpName": "io.github.kassol/mcp-searxng",
|
|
5
5
|
"description": "MCP server for SearXNG integration",
|
|
6
6
|
"license": "MIT",
|
|
@@ -45,6 +45,12 @@
|
|
|
45
45
|
"bootstrap": "npm install && npm run build",
|
|
46
46
|
"inspector": "DANGEROUSLY_OMIT_AUTH=true npx @modelcontextprotocol/inspector node dist/index.js",
|
|
47
47
|
"lint": "eslint src __tests__",
|
|
48
|
+
"sync-version": "node scripts/update-version.js",
|
|
49
|
+
"release:check": "npm run build && npm run lint && npm test && npm pack --dry-run",
|
|
50
|
+
"release:dry-run": "npm publish --dry-run --access public --tag latest",
|
|
51
|
+
"release:patch": "npm version patch",
|
|
52
|
+
"release:minor": "npm version minor",
|
|
53
|
+
"release:major": "npm version major",
|
|
48
54
|
"postversion": "TAG=$(node scripts/update-version.js | tail -1) && git add src/index.ts .mcp/server.json && git commit --amend --no-edit && git tag -f $TAG"
|
|
49
55
|
},
|
|
50
56
|
"dependencies": {
|