@kassol/mcp-searxng 1.0.3-custom.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.
@@ -0,0 +1,52 @@
1
+ function isEnabled(value) {
2
+ return value === "true";
3
+ }
4
+ function parseCsv(value) {
5
+ return (value || "")
6
+ .split(",")
7
+ .map((item) => item.trim())
8
+ .filter(Boolean);
9
+ }
10
+ export function getHttpSecurityConfig() {
11
+ const harden = isEnabled(process.env.MCP_HTTP_HARDEN);
12
+ const authToken = process.env.MCP_HTTP_AUTH_TOKEN;
13
+ const allowedOrigins = parseCsv(process.env.MCP_HTTP_ALLOWED_ORIGINS);
14
+ const allowedHosts = parseCsv(process.env.MCP_HTTP_ALLOWED_HOSTS);
15
+ return {
16
+ harden,
17
+ requireAuth: harden,
18
+ authToken,
19
+ restrictOrigins: harden,
20
+ allowedOrigins,
21
+ enableDnsRebindingProtection: harden,
22
+ allowedHosts: allowedHosts.length > 0 ? allowedHosts : ["127.0.0.1", "localhost"],
23
+ exposeFullConfig: isEnabled(process.env.MCP_HTTP_EXPOSE_FULL_CONFIG),
24
+ allowPrivateUrls: isEnabled(process.env.MCP_HTTP_ALLOW_PRIVATE_URLS),
25
+ };
26
+ }
27
+ export function validateHttpSecurityConfig(config) {
28
+ if (!config.harden) {
29
+ return;
30
+ }
31
+ if (!config.authToken) {
32
+ throw new Error("MCP_HTTP_HARDEN=true requires MCP_HTTP_AUTH_TOKEN to be set.");
33
+ }
34
+ if (config.allowedOrigins.length === 0) {
35
+ throw new Error("MCP_HTTP_HARDEN=true requires MCP_HTTP_ALLOWED_ORIGINS to be set.");
36
+ }
37
+ }
38
+ export function isRequestAuthorized(headerValue, config) {
39
+ if (!config.requireAuth) {
40
+ return true;
41
+ }
42
+ return headerValue === `Bearer ${config.authToken}` || headerValue === config.authToken;
43
+ }
44
+ export function isOriginAllowed(origin, config) {
45
+ if (!config.restrictOrigins) {
46
+ return true;
47
+ }
48
+ if (!origin) {
49
+ return true;
50
+ }
51
+ return config.allowedOrigins.includes(origin);
52
+ }
@@ -0,0 +1,3 @@
1
+ import express from "express";
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ export declare function createHttpServer(createMcpServer: () => McpServer): Promise<express.Application>;
@@ -0,0 +1,185 @@
1
+ import express from "express";
2
+ import cors from "cors";
3
+ import { randomUUID } from "crypto";
4
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
5
+ import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
6
+ import { logMessage } from "./logging.js";
7
+ import { packageVersion } from "./index.js";
8
+ import { getHttpSecurityConfig, isOriginAllowed, isRequestAuthorized, validateHttpSecurityConfig, } from "./http-security.js";
9
+ export async function createHttpServer(createMcpServer) {
10
+ const app = express();
11
+ const security = getHttpSecurityConfig();
12
+ validateHttpSecurityConfig(security);
13
+ app.use(express.json());
14
+ // Add CORS support for web clients
15
+ app.use(cors({
16
+ origin: (origin, callback) => {
17
+ if (isOriginAllowed(origin || undefined, security)) {
18
+ callback(null, true);
19
+ return;
20
+ }
21
+ callback(null, false);
22
+ },
23
+ exposedHeaders: ["Mcp-Session-Id"],
24
+ allowedHeaders: ["Content-Type", "mcp-session-id", "authorization"],
25
+ }));
26
+ function rejectUnauthorized(res) {
27
+ res.status(401).json({
28
+ jsonrpc: "2.0",
29
+ error: {
30
+ code: -32001,
31
+ message: "Unauthorized: missing or invalid HTTP auth token",
32
+ },
33
+ id: null,
34
+ });
35
+ }
36
+ // Map to store sessions by session ID
37
+ const sessions = new Map();
38
+ // Handle POST requests for client-to-server communication
39
+ app.post('/mcp', async (req, res) => {
40
+ if (!isRequestAuthorized(req.headers.authorization, security)) {
41
+ rejectUnauthorized(res);
42
+ return;
43
+ }
44
+ const sessionId = req.headers['mcp-session-id'];
45
+ let transport;
46
+ let mcpServer;
47
+ if (sessionId && sessions.has(sessionId)) {
48
+ // Reuse existing session
49
+ const session = sessions.get(sessionId);
50
+ transport = session.transport;
51
+ mcpServer = session.mcpServer;
52
+ logMessage(mcpServer, "debug", `Reusing session: ${sessionId}`);
53
+ }
54
+ else if (!sessionId && isInitializeRequest(req.body)) {
55
+ // New initialization request — create fresh McpServer and transport
56
+ mcpServer = createMcpServer();
57
+ transport = new StreamableHTTPServerTransport({
58
+ sessionIdGenerator: () => randomUUID(),
59
+ onsessioninitialized: (sessionId) => {
60
+ sessions.set(sessionId, { transport, mcpServer });
61
+ logMessage(mcpServer, "debug", `Session initialized: ${sessionId}`);
62
+ },
63
+ enableDnsRebindingProtection: security.enableDnsRebindingProtection,
64
+ allowedHosts: security.allowedHosts,
65
+ allowedOrigins: security.allowedOrigins,
66
+ });
67
+ // Clean up session when transport closes
68
+ transport.onclose = () => {
69
+ if (transport.sessionId) {
70
+ sessions.delete(transport.sessionId);
71
+ }
72
+ };
73
+ // Connect this session's McpServer to its transport
74
+ await mcpServer.connect(transport);
75
+ }
76
+ else {
77
+ // Invalid request
78
+ console.warn(`⚠️ POST request rejected - invalid request:`, {
79
+ clientIP: req.ip || req.socket.remoteAddress,
80
+ sessionId: sessionId || 'undefined',
81
+ hasInitializeRequest: isInitializeRequest(req.body),
82
+ userAgent: req.headers['user-agent'],
83
+ contentType: req.headers['content-type'],
84
+ accept: req.headers['accept']
85
+ });
86
+ res.status(400).json({
87
+ jsonrpc: '2.0',
88
+ error: {
89
+ code: -32000,
90
+ message: 'Bad Request: No valid session ID provided',
91
+ },
92
+ id: null,
93
+ });
94
+ return;
95
+ }
96
+ // Handle the request
97
+ try {
98
+ await transport.handleRequest(req, res, req.body);
99
+ }
100
+ catch (error) {
101
+ // Log header-related rejections for debugging
102
+ if (error instanceof Error && error.message.includes('accept')) {
103
+ console.warn(`⚠️ Connection rejected due to missing headers:`, {
104
+ clientIP: req.ip || req.socket.remoteAddress,
105
+ userAgent: req.headers['user-agent'],
106
+ contentType: req.headers['content-type'],
107
+ accept: req.headers['accept'],
108
+ error: error.message
109
+ });
110
+ }
111
+ throw error;
112
+ }
113
+ });
114
+ // Handle GET requests for server-to-client notifications via SSE
115
+ app.get('/mcp', async (req, res) => {
116
+ if (!isRequestAuthorized(req.headers.authorization, security)) {
117
+ rejectUnauthorized(res);
118
+ return;
119
+ }
120
+ const sessionId = req.headers['mcp-session-id'];
121
+ if (!sessionId || !sessions.has(sessionId)) {
122
+ console.warn(`⚠️ GET request rejected - missing or invalid session ID:`, {
123
+ clientIP: req.ip || req.socket.remoteAddress,
124
+ sessionId: sessionId || 'undefined',
125
+ userAgent: req.headers['user-agent']
126
+ });
127
+ res.status(400).send('Invalid or missing session ID');
128
+ return;
129
+ }
130
+ const session = sessions.get(sessionId);
131
+ try {
132
+ await session.transport.handleRequest(req, res);
133
+ }
134
+ catch (error) {
135
+ console.warn(`⚠️ GET request failed:`, {
136
+ clientIP: req.ip || req.socket.remoteAddress,
137
+ sessionId,
138
+ error: error instanceof Error ? error.message : String(error)
139
+ });
140
+ throw error;
141
+ }
142
+ });
143
+ // Handle DELETE requests for session termination
144
+ app.delete('/mcp', async (req, res) => {
145
+ if (!isRequestAuthorized(req.headers.authorization, security)) {
146
+ rejectUnauthorized(res);
147
+ return;
148
+ }
149
+ const sessionId = req.headers['mcp-session-id'];
150
+ if (!sessionId || !sessions.has(sessionId)) {
151
+ console.warn(`⚠️ DELETE request rejected - missing or invalid session ID:`, {
152
+ clientIP: req.ip || req.socket.remoteAddress,
153
+ sessionId: sessionId || 'undefined',
154
+ userAgent: req.headers['user-agent']
155
+ });
156
+ res.status(400).send('Invalid or missing session ID');
157
+ return;
158
+ }
159
+ const session = sessions.get(sessionId);
160
+ try {
161
+ await session.transport.handleRequest(req, res);
162
+ }
163
+ catch (error) {
164
+ console.warn(`⚠️ DELETE request failed:`, {
165
+ clientIP: req.ip || req.socket.remoteAddress,
166
+ sessionId,
167
+ error: error instanceof Error ? error.message : String(error)
168
+ });
169
+ throw error;
170
+ }
171
+ finally {
172
+ sessions.delete(sessionId);
173
+ }
174
+ });
175
+ // Health check endpoint
176
+ app.get('/health', (_req, res) => {
177
+ res.json({
178
+ status: 'healthy',
179
+ server: 'kassol/mcp-searxng',
180
+ version: packageVersion,
181
+ transport: 'http'
182
+ });
183
+ });
184
+ return app;
185
+ }
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ declare const packageVersion = "1.0.3-custom.0";
4
+ export { packageVersion };
5
+ export declare function isWebUrlReadArgs(args: unknown): args is {
6
+ url: string;
7
+ startChar?: number;
8
+ maxLength?: number;
9
+ section?: string;
10
+ paragraphRange?: string;
11
+ readHeadings?: boolean;
12
+ };
13
+ /**
14
+ * Creates and configures a new McpServer with all handlers registered.
15
+ * Called once per HTTP session, or once for STDIO mode.
16
+ */
17
+ export declare function createMcpServer(): McpServer;
package/dist/index.js ADDED
@@ -0,0 +1,252 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { CallToolRequestSchema, ListToolsRequestSchema, SetLevelRequestSchema, ListResourcesRequestSchema, ListResourceTemplatesRequestSchema, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
5
+ // Import modularized functionality
6
+ import { WEB_SEARCH_TOOL, READ_URL_TOOL, isSearXNGWebSearchArgs } from "./types.js";
7
+ import { logMessage, setLogLevel, getCurrentLogLevel } from "./logging.js";
8
+ import { performWebSearch } from "./search.js";
9
+ import { fetchAndConvertToMarkdown } from "./url-reader.js";
10
+ import { createConfigResource, createHelpResource } from "./resources.js";
11
+ import { createHttpServer } from "./http-server.js";
12
+ // Use a static version string that will be updated by the version script
13
+ const packageVersion = "1.0.3-custom.0";
14
+ // Export the version for use in other modules
15
+ export { packageVersion };
16
+ // Type guard for URL reading args
17
+ export function isWebUrlReadArgs(args) {
18
+ if (typeof args !== "object" ||
19
+ args === null ||
20
+ !("url" in args) ||
21
+ typeof args.url !== "string") {
22
+ return false;
23
+ }
24
+ const urlArgs = args;
25
+ // Convert empty strings to undefined for optional string parameters
26
+ if (urlArgs.section === "")
27
+ urlArgs.section = undefined;
28
+ if (urlArgs.paragraphRange === "")
29
+ urlArgs.paragraphRange = undefined;
30
+ // Validate optional parameters
31
+ if (urlArgs.startChar !== undefined && (typeof urlArgs.startChar !== "number" || urlArgs.startChar < 0)) {
32
+ return false;
33
+ }
34
+ if (urlArgs.maxLength !== undefined && (typeof urlArgs.maxLength !== "number" || urlArgs.maxLength < 1)) {
35
+ return false;
36
+ }
37
+ if (urlArgs.section !== undefined && typeof urlArgs.section !== "string") {
38
+ return false;
39
+ }
40
+ if (urlArgs.paragraphRange !== undefined && typeof urlArgs.paragraphRange !== "string") {
41
+ return false;
42
+ }
43
+ if (urlArgs.readHeadings !== undefined && typeof urlArgs.readHeadings !== "boolean") {
44
+ return false;
45
+ }
46
+ return true;
47
+ }
48
+ /**
49
+ * Creates and configures a new McpServer with all handlers registered.
50
+ * Called once per HTTP session, or once for STDIO mode.
51
+ */
52
+ export function createMcpServer() {
53
+ const mcpServer = new McpServer({
54
+ name: "kassol/mcp-searxng",
55
+ version: packageVersion,
56
+ }, {
57
+ capabilities: {
58
+ logging: {},
59
+ resources: {},
60
+ tools: {},
61
+ },
62
+ });
63
+ const server = mcpServer.server;
64
+ // List tools handler
65
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
66
+ logMessage(mcpServer, "debug", "Handling list_tools request");
67
+ return {
68
+ tools: [WEB_SEARCH_TOOL, READ_URL_TOOL],
69
+ };
70
+ });
71
+ // Call tool handler
72
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
73
+ const { name, arguments: args } = request.params;
74
+ logMessage(mcpServer, "debug", `Handling call_tool request: ${name}`);
75
+ try {
76
+ if (name === "searxng_web_search") {
77
+ if (!isSearXNGWebSearchArgs(args)) {
78
+ throw new Error("Invalid arguments for web search");
79
+ }
80
+ const result = await performWebSearch(mcpServer, args.query, args.pageno, args.time_range, args.language, args.safesearch);
81
+ return {
82
+ content: [
83
+ {
84
+ type: "text",
85
+ text: result,
86
+ },
87
+ ],
88
+ };
89
+ }
90
+ else if (name === "web_url_read") {
91
+ if (!isWebUrlReadArgs(args)) {
92
+ throw new Error("Invalid arguments for URL reading");
93
+ }
94
+ const paginationOptions = {
95
+ startChar: args.startChar,
96
+ maxLength: args.maxLength,
97
+ section: args.section,
98
+ paragraphRange: args.paragraphRange,
99
+ readHeadings: args.readHeadings,
100
+ };
101
+ const result = await fetchAndConvertToMarkdown(mcpServer, args.url, 10000, paginationOptions);
102
+ return {
103
+ content: [
104
+ {
105
+ type: "text",
106
+ text: result,
107
+ },
108
+ ],
109
+ };
110
+ }
111
+ else {
112
+ throw new Error(`Unknown tool: ${name}`);
113
+ }
114
+ }
115
+ catch (error) {
116
+ logMessage(mcpServer, "error", `Tool execution error: ${error instanceof Error ? error.message : String(error)}`, {
117
+ tool: name,
118
+ args: args,
119
+ error: error instanceof Error ? error.stack : String(error)
120
+ });
121
+ throw error;
122
+ }
123
+ });
124
+ // Logging level handler
125
+ server.setRequestHandler(SetLevelRequestSchema, async (request) => {
126
+ const { level } = request.params;
127
+ logMessage(mcpServer, "info", `Setting log level to: ${level}`);
128
+ setLogLevel(level);
129
+ return {};
130
+ });
131
+ // List resources handler
132
+ server.setRequestHandler(ListResourcesRequestSchema, async () => {
133
+ logMessage(mcpServer, "debug", "Handling list_resources request");
134
+ return {
135
+ resources: [
136
+ {
137
+ uri: "config://server-config",
138
+ mimeType: "application/json",
139
+ name: "Server Configuration",
140
+ description: "Current server configuration and environment variables"
141
+ },
142
+ {
143
+ uri: "help://usage-guide",
144
+ mimeType: "text/markdown",
145
+ name: "Usage Guide",
146
+ description: "How to use the MCP SearXNG server effectively"
147
+ }
148
+ ]
149
+ };
150
+ });
151
+ // List resource templates handler
152
+ // Returns empty list — required by MCP spec even when no templates exist
153
+ server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => {
154
+ logMessage(mcpServer, "debug", "Handling list_resource_templates request");
155
+ return { resourceTemplates: [] };
156
+ });
157
+ // Read resource handler
158
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
159
+ const { uri } = request.params;
160
+ logMessage(mcpServer, "debug", `Handling read_resource request for: ${uri}`);
161
+ switch (uri) {
162
+ case "config://server-config":
163
+ return {
164
+ contents: [
165
+ {
166
+ uri: uri,
167
+ mimeType: "application/json",
168
+ text: createConfigResource()
169
+ }
170
+ ]
171
+ };
172
+ case "help://usage-guide":
173
+ return {
174
+ contents: [
175
+ {
176
+ uri: uri,
177
+ mimeType: "text/markdown",
178
+ text: createHelpResource()
179
+ }
180
+ ]
181
+ };
182
+ default:
183
+ throw new Error(`Unknown resource: ${uri}`);
184
+ }
185
+ });
186
+ return mcpServer;
187
+ }
188
+ // Main function
189
+ async function main() {
190
+ // Check for HTTP transport mode
191
+ const httpPort = process.env.MCP_HTTP_PORT;
192
+ if (httpPort) {
193
+ const port = parseInt(httpPort, 10);
194
+ if (isNaN(port) || port < 1 || port > 65535) {
195
+ console.error(`Invalid HTTP port: ${httpPort}. Must be between 1-65535.`);
196
+ process.exit(1);
197
+ }
198
+ console.log(`Starting HTTP transport on port ${port}`);
199
+ const app = await createHttpServer(createMcpServer);
200
+ const httpServer = app.listen(port, () => {
201
+ console.log(`HTTP server listening on port ${port}`);
202
+ console.log(`Health check: http://localhost:${port}/health`);
203
+ console.log(`MCP endpoint: http://localhost:${port}/mcp`);
204
+ });
205
+ // Handle graceful shutdown
206
+ const shutdown = (signal) => {
207
+ console.log(`Received ${signal}. Shutting down HTTP server...`);
208
+ httpServer.close(() => {
209
+ console.log("HTTP server closed");
210
+ process.exit(0);
211
+ });
212
+ };
213
+ process.on('SIGINT', () => shutdown('SIGINT'));
214
+ process.on('SIGTERM', () => shutdown('SIGTERM'));
215
+ }
216
+ else {
217
+ // Default STDIO transport — single session, single server
218
+ const mcpServer = createMcpServer();
219
+ // Show helpful message when running in terminal
220
+ if (process.stdin.isTTY) {
221
+ console.error(`🔍 MCP SearXNG Server v${packageVersion} - Ready`);
222
+ if (process.env.SEARXNG_URL) {
223
+ console.error(`🌐 SearXNG URL: ${process.env.SEARXNG_URL}`);
224
+ }
225
+ else {
226
+ console.error("⚠️ SEARXNG_URL not set — configure it before using search tools");
227
+ }
228
+ console.error("📡 Waiting for MCP client connection via STDIO...\n");
229
+ }
230
+ const transport = new StdioServerTransport();
231
+ await mcpServer.connect(transport);
232
+ // Log after connection is established
233
+ logMessage(mcpServer, "info", `MCP SearXNG Server v${packageVersion} connected via STDIO`);
234
+ logMessage(mcpServer, "info", `Log level: ${getCurrentLogLevel()}`);
235
+ logMessage(mcpServer, "info", `Environment: ${process.env.NODE_ENV || 'development'}`);
236
+ logMessage(mcpServer, "info", `SearXNG URL: ${process.env.SEARXNG_URL || 'not configured'}`);
237
+ }
238
+ }
239
+ // Handle uncaught errors
240
+ process.on('uncaughtException', (error) => {
241
+ console.error('Uncaught Exception:', error);
242
+ process.exit(1);
243
+ });
244
+ process.on('unhandledRejection', (reason, promise) => {
245
+ console.error('Unhandled Rejection at:', promise, 'reason:', reason);
246
+ process.exit(1);
247
+ });
248
+ // Start the server (CLI entrypoint)
249
+ main().catch((error) => {
250
+ console.error("Failed to start server:", error);
251
+ process.exit(1);
252
+ });
@@ -0,0 +1,6 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { LoggingLevel } from "@modelcontextprotocol/sdk/types.js";
3
+ export declare function logMessage(mcpServer: McpServer, level: LoggingLevel, message: string, data?: unknown): void;
4
+ export declare function shouldLog(level: LoggingLevel): boolean;
5
+ export declare function setLogLevel(level: LoggingLevel): void;
6
+ export declare function getCurrentLogLevel(): LoggingLevel;
@@ -0,0 +1,35 @@
1
+ // Logging state
2
+ let currentLogLevel = "info";
3
+ // Shared handler for sendLoggingMessage errors
4
+ function handleSendError(error) {
5
+ if (error instanceof Error && error.message !== "Not connected") {
6
+ console.error("Logging error:", error);
7
+ }
8
+ }
9
+ // Logging helper function
10
+ export function logMessage(mcpServer, level, message, data) {
11
+ if (shouldLog(level)) {
12
+ try {
13
+ const notificationData = data !== undefined
14
+ ? (typeof data === 'object' && data !== null ? { message, ...data } : { message, data })
15
+ : { message };
16
+ mcpServer.sendLoggingMessage({
17
+ level,
18
+ data: notificationData
19
+ }).catch(handleSendError);
20
+ }
21
+ catch (error) {
22
+ handleSendError(error);
23
+ }
24
+ }
25
+ }
26
+ export function shouldLog(level) {
27
+ const levels = ["debug", "info", "warning", "error"];
28
+ return levels.indexOf(level) >= levels.indexOf(currentLogLevel);
29
+ }
30
+ export function setLogLevel(level) {
31
+ currentLogLevel = level;
32
+ }
33
+ export function getCurrentLogLevel() {
34
+ return currentLogLevel;
35
+ }
@@ -0,0 +1,40 @@
1
+ import { Agent, ProxyAgent } from "undici";
2
+ /**
3
+ * Proxy configuration type for separating search and URL reader proxies.
4
+ */
5
+ export declare const ProxyType: {
6
+ readonly SEARCH: "search";
7
+ readonly URL_READER: "url_reader";
8
+ };
9
+ export type ProxyType = typeof ProxyType[keyof typeof ProxyType];
10
+ /**
11
+ * Creates a proxy agent dispatcher for Node.js fetch API.
12
+ *
13
+ * Node.js fetch uses Undici under the hood, which requires a 'dispatcher' option
14
+ * instead of 'agent'. This function creates a ProxyAgent compatible with fetch.
15
+ *
16
+ * Environment variables checked (in order, depending on URL protocol):
17
+ * - For type 'search' and HTTPS URLs:
18
+ * SEARCH_HTTPS_PROXY, SEARCH_HTTP_PROXY, search_https_proxy, search_http_proxy,
19
+ * then HTTPS_PROXY, HTTP_PROXY, https_proxy, http_proxy
20
+ * - For type 'search' and HTTP/unknown URLs:
21
+ * SEARCH_HTTP_PROXY, SEARCH_HTTPS_PROXY, search_http_proxy, search_https_proxy,
22
+ * then HTTP_PROXY, HTTPS_PROXY, http_proxy, https_proxy
23
+ * - For type 'url_reader' and HTTPS URLs:
24
+ * URL_READER_HTTPS_PROXY, URL_READER_HTTP_PROXY, url_reader_https_proxy, url_reader_http_proxy,
25
+ * then HTTPS_PROXY, HTTP_PROXY, https_proxy, http_proxy
26
+ * - For type 'url_reader' and HTTP/unknown URLs:
27
+ * URL_READER_HTTP_PROXY, URL_READER_HTTPS_PROXY, url_reader_http_proxy, url_reader_https_proxy,
28
+ * then HTTP_PROXY, HTTPS_PROXY, http_proxy, https_proxy
29
+ * - For no specific type and HTTPS URLs:
30
+ * HTTPS_PROXY, HTTP_PROXY, https_proxy, http_proxy
31
+ * - For no specific type and HTTP/unknown URLs:
32
+ * HTTP_PROXY, HTTPS_PROXY, http_proxy, https_proxy
33
+ * - NO_PROXY / no_proxy: Comma-separated list of hosts to bypass proxy
34
+ *
35
+ * @param targetUrl - Optional target URL to check against NO_PROXY rules
36
+ * @param type - Optional proxy type ('search' or 'url_reader') for separate proxy configs
37
+ * @returns ProxyAgent dispatcher for fetch, or undefined if no proxy configured or bypassed
38
+ */
39
+ export declare function createProxyAgent(targetUrl?: string, type?: ProxyType): ProxyAgent | undefined;
40
+ export declare function createDefaultAgent(): Agent | undefined;