better-web-search-mcp 0.8.0 → 0.9.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.
@@ -1 +1 @@
1
- {"version":3,"file":"brave.d.ts","sourceRoot":"","sources":["../../src/providers/brave.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAK9E;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAqBzE;AA4BD;;;;;;GAMG;AACH,qBAAa,aAAc,YAAW,cAAc;IAClD,QAAQ,CAAC,IAAI,WAAW;IAElB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;CAsE1E"}
1
+ {"version":3,"file":"brave.d.ts","sourceRoot":"","sources":["../../src/providers/brave.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAa9E;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAqBzE;AA4BD;;;;;;GAMG;AACH,qBAAa,aAAc,YAAW,cAAc;IAClD,QAAQ,CAAC,IAAI,WAAW;IAElB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;CAyE1E"}
@@ -1,6 +1,14 @@
1
1
  import { withRetry } from "../utils/retry.js";
2
+ import { CoolingDownError, limiterFor } from "../utils/rateLimit.js";
2
3
  const BRAVE_ENDPOINT = "https://api.search.brave.com/res/v1/web/search";
3
4
  const TIMEOUT_MS = 8_000;
5
+ /**
6
+ * Spacing between Brave requests, in milliseconds.
7
+ *
8
+ * The free tier allows one request per second, so an expanded research
9
+ * question would otherwise spend most of its queries on 429s.
10
+ */
11
+ const BRAVE_INTERVAL_MS = 1_100;
4
12
  /**
5
13
  * Map a number of days to a Brave `freshness` filter value.
6
14
  *
@@ -71,14 +79,15 @@ export class BraveProvider {
71
79
  }
72
80
  const controller = new AbortController();
73
81
  const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
82
+ const limiter = limiterFor("brave", BRAVE_INTERVAL_MS);
74
83
  try {
75
- const response = await withRetry(() => fetch(`${BRAVE_ENDPOINT}?${params.toString()}`, {
84
+ const response = await limiter.schedule(() => withRetry(() => fetch(`${BRAVE_ENDPOINT}?${params.toString()}`, {
76
85
  headers: {
77
86
  "X-Subscription-Token": apiKey,
78
87
  Accept: "application/json",
79
88
  },
80
89
  signal: controller.signal,
81
- }), { retryOn: [429, 503], retryNetworkErrors: true });
90
+ }), { retryOn: [429, 503], retryNetworkErrors: true }));
82
91
  if (response.status === 401) {
83
92
  console.warn(`[brave] API returned ${response.status}; returning no results.`);
84
93
  return [];
@@ -1 +1 @@
1
- {"version":3,"file":"brave.js","sourceRoot":"","sources":["../../src/providers/brave.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"brave.js","sourceRoot":"","sources":["../../src/providers/brave.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"duckduckgo.d.ts","sourceRoot":"","sources":["../../src/providers/duckduckgo.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAM9E;;;;;;GAMG;AACH,qBAAa,kBAAmB,YAAW,cAAc;IACvD,QAAQ,CAAC,IAAI,gBAAgB;IAEvB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;CAyD1E;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAKrD;AAED,kFAAkF;AAClF,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,YAAY,EAAE,CA6B5E"}
1
+ {"version":3,"file":"duckduckgo.d.ts","sourceRoot":"","sources":["../../src/providers/duckduckgo.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAO9E;;;;;;GAMG;AACH,qBAAa,kBAAmB,YAAW,cAAc;IACvD,QAAQ,CAAC,IAAI,gBAAgB;IAEvB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;CA0E1E;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAKrD;AAED,kFAAkF;AAClF,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,YAAY,EAAE,CA6B5E"}
@@ -1,6 +1,7 @@
1
1
  import * as cheerio from "cheerio";
2
- import { withRetry } from "../utils/retry.js";
2
+ import { parseRetryAfter, withRetry } from "../utils/retry.js";
3
3
  import { ProviderBlockedError } from "./types.js";
4
+ import { CoolingDownError, limiterFor } from "../utils/rateLimit.js";
4
5
  const DDG_ENDPOINT = "https://html.duckduckgo.com/html/";
5
6
  const TIMEOUT_MS = 8_000;
6
7
  /**
@@ -20,15 +21,20 @@ export class DuckDuckGoProvider {
20
21
  }
21
22
  const controller = new AbortController();
22
23
  const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
24
+ // Paced so an expanded research question does not fire several queries at
25
+ // the endpoint at once, which is what earns a challenge page.
26
+ const limiter = limiterFor("duckduckgo");
23
27
  try {
24
- const response = await withRetry(() => fetch(`${DDG_ENDPOINT}?${params.toString()}`, {
28
+ const response = await limiter.schedule(() => withRetry(() => fetch(`${DDG_ENDPOINT}?${params.toString()}`, {
25
29
  headers: {
26
30
  "User-Agent": "Mozilla/5.0 (compatible; BetterWebSearch-MCP/1.0; +https://github.com)",
27
31
  Accept: "text/html",
28
32
  },
29
33
  signal: controller.signal,
30
- }), { retryOn: [429, 503], retryNetworkErrors: true });
34
+ }), { retryOn: [429, 503], retryNetworkErrors: true }));
31
35
  if (!response.ok) {
36
+ limiter.startCooldown(parseRetryAfter(response.headers?.get("retry-after")) ??
37
+ undefined);
32
38
  throw new ProviderBlockedError("duckduckgo", `endpoint returned ${response.status}`);
33
39
  }
34
40
  const html = await response.text();
@@ -37,8 +43,12 @@ export class DuckDuckGoProvider {
37
43
  // "no results", which is indistinguishable from a genuinely empty search
38
44
  // and made rate limiting look like a broken query.
39
45
  if (isChallengePage(html)) {
46
+ // Back off rather than keep asking: further requests during the
47
+ // cooldown fail immediately instead of deepening the block.
48
+ limiter.startCooldown();
40
49
  throw new ProviderBlockedError("duckduckgo", `rate limited (HTTP ${response.status} challenge page)`);
41
50
  }
51
+ limiter.clearCooldown();
42
52
  return parseHtmlResults(html, opts.count ?? 10);
43
53
  }
44
54
  catch (error) {
@@ -47,6 +57,10 @@ export class DuckDuckGoProvider {
47
57
  if (error instanceof ProviderBlockedError) {
48
58
  throw error;
49
59
  }
60
+ // A cooldown is a refusal too, reported with the reason and the wait.
61
+ if (error instanceof CoolingDownError) {
62
+ throw new ProviderBlockedError("duckduckgo", error.message);
63
+ }
50
64
  console.warn(`[duckduckgo] search failed (${error instanceof Error ? error.message : String(error)}); returning no results.`);
51
65
  return [];
52
66
  }
@@ -1 +1 @@
1
- {"version":3,"file":"duckduckgo.js","sourceRoot":"","sources":["../../src/providers/duckduckgo.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"duckduckgo.js","sourceRoot":"","sources":["../../src/providers/duckduckgo.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"tavily.d.ts","sourceRoot":"","sources":["../../src/providers/tavily.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAiB9E;;;;;;GAMG;AACH,qBAAa,cAAe,YAAW,cAAc;IACnD,QAAQ,CAAC,IAAI,YAAY;IAEnB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;CA6D1E"}
1
+ {"version":3,"file":"tavily.d.ts","sourceRoot":"","sources":["../../src/providers/tavily.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAiB9E;;;;;;GAMG;AACH,qBAAa,cAAe,YAAW,cAAc;IACnD,QAAQ,CAAC,IAAI,YAAY;IAEnB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;CAgE1E"}
@@ -1,4 +1,5 @@
1
1
  import { withRetry } from "../utils/retry.js";
2
+ import { CoolingDownError, limiterFor } from "../utils/rateLimit.js";
2
3
  const TAVILY_ENDPOINT = "https://api.tavily.com/search";
3
4
  const TIMEOUT_MS = 8_000;
4
5
  /**
@@ -19,8 +20,9 @@ export class TavilyProvider {
19
20
  }
20
21
  const controller = new AbortController();
21
22
  const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
23
+ const limiter = limiterFor("tavily");
22
24
  try {
23
- const response = await withRetry(() => fetch(TAVILY_ENDPOINT, {
25
+ const response = await limiter.schedule(() => withRetry(() => fetch(TAVILY_ENDPOINT, {
24
26
  method: "POST",
25
27
  headers: {
26
28
  Authorization: `Bearer ${apiKey}`,
@@ -35,7 +37,7 @@ export class TavilyProvider {
35
37
  : {}),
36
38
  }),
37
39
  signal: controller.signal,
38
- }), { retryOn: [429, 503], retryNetworkErrors: true });
40
+ }), { retryOn: [429, 503], retryNetworkErrors: true }));
39
41
  if (!response.ok) {
40
42
  console.warn(`[tavily] API returned ${response.status}; returning no results.`);
41
43
  return [];
@@ -1 +1 @@
1
- {"version":3,"file":"tavily.js","sourceRoot":"","sources":["../../src/providers/tavily.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tavily.js","sourceRoot":"","sources":["../../src/providers/tavily.ts"],"names":[],"mappings":"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"}
@@ -14,6 +14,21 @@ export interface AppConfig {
14
14
  cachePath: string;
15
15
  browserEnabled: boolean;
16
16
  }
17
+ /**
18
+ * Where the cache lives when nothing overrides it.
19
+ *
20
+ * The default used to be the relative path `data/cache.db`, which resolves
21
+ * against the working directory. An MCP client spawns the server from
22
+ * wherever it happens to be, so that scattered `data/cache.db` files around
23
+ * the filesystem — two were sitting in the home and Downloads folders — and
24
+ * meant the domain profiles and page cache never accumulated, because every
25
+ * launch directory got its own empty database. A per-user location is stable
26
+ * across launches and keeps the writes inside a directory meant for them.
27
+ *
28
+ * `BETTER_WEB_SEARCH_CACHE_PATH` still overrides, and a relative override is
29
+ * still honoured for anyone who wants a project-local cache.
30
+ */
31
+ export declare function defaultCachePath(env?: NodeJS.ProcessEnv, platform?: string): string;
17
32
  export declare function loadConfig(): AppConfig;
18
33
  /** Shape for the provider banner (pure, testable). */
19
34
  export interface ProviderStatus {
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAuBH,MAAM,WAAW,SAAS;IACxB,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,YAAY,EAAE,OAAO,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,OAAO,CAAC;CACzB;AAED,wBAAgB,UAAU,IAAI,SAAS,CA0BtC;AAED,sDAAsD;AACtD,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,SAAS,GAAG,cAAc,EAAE,CAqBjE;AAED,wBAAgB,YAAY,CAC1B,GAAG,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EACtC,GAAG,EAAE,SAAS,GACb,MAAM,CAgBR"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAyBH,MAAM,WAAW,SAAS;IACxB,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,YAAY,EAAE,OAAO,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,OAAO,CAAC;CACzB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,GAAE,MAAM,CAAC,UAAwB,EACpC,QAAQ,GAAE,MAAyB,GAClC,MAAM,CAuBR;AAED,wBAAgB,UAAU,IAAI,SAAS,CA0BtC;AAED,sDAAsD;AACtD,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,SAAS,GAAG,cAAc,EAAE,CAqBjE;AAED,wBAAgB,YAAY,CAC1B,GAAG,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EACtC,GAAG,EAAE,SAAS,GACb,MAAM,CAgBR"}
@@ -6,6 +6,7 @@
6
6
  * to upgrade. Also supports the BETTER_WEB_SEARCH_* prefix for namespacing
7
7
  * (e.g. BETTER_WEB_SEARCH_BRAVE_API_KEY) as an alias for the bare name.
8
8
  */
9
+ import { join } from "node:path";
9
10
  const KEY_ALIASES = {
10
11
  BRAVE_API_KEY: ["BETTER_WEB_SEARCH_BRAVE_API_KEY", "BRAVE_SEARCH_API_KEY"],
11
12
  TAVILY_API_KEY: ["BETTER_WEB_SEARCH_TAVILY_API_KEY"],
@@ -24,6 +25,44 @@ function readKey(canonical) {
24
25
  const aliases = KEY_ALIASES[canonical] ?? [];
25
26
  return pickFirstEnv([canonical, ...aliases]);
26
27
  }
28
+ /**
29
+ * Where the cache lives when nothing overrides it.
30
+ *
31
+ * The default used to be the relative path `data/cache.db`, which resolves
32
+ * against the working directory. An MCP client spawns the server from
33
+ * wherever it happens to be, so that scattered `data/cache.db` files around
34
+ * the filesystem — two were sitting in the home and Downloads folders — and
35
+ * meant the domain profiles and page cache never accumulated, because every
36
+ * launch directory got its own empty database. A per-user location is stable
37
+ * across launches and keeps the writes inside a directory meant for them.
38
+ *
39
+ * `BETTER_WEB_SEARCH_CACHE_PATH` still overrides, and a relative override is
40
+ * still honoured for anyone who wants a project-local cache.
41
+ */
42
+ export function defaultCachePath(env = process.env, platform = process.platform) {
43
+ const home = env.HOME ?? env.USERPROFILE;
44
+ const app = "better-web-search-mcp";
45
+ if (platform === "win32") {
46
+ const base = env.LOCALAPPDATA ?? (home ? join(home, "AppData", "Local") : undefined);
47
+ if (base) {
48
+ return join(base, app, "cache.db");
49
+ }
50
+ }
51
+ else if (platform === "darwin") {
52
+ if (home) {
53
+ return join(home, "Library", "Caches", app, "cache.db");
54
+ }
55
+ }
56
+ else {
57
+ const base = env.XDG_CACHE_HOME ?? (home ? join(home, ".cache") : undefined);
58
+ if (base) {
59
+ return join(base, app, "cache.db");
60
+ }
61
+ }
62
+ // No home directory to write into: fall back to the old relative path
63
+ // rather than failing to start.
64
+ return join("data", "cache.db");
65
+ }
27
66
  export function loadConfig() {
28
67
  const braveApiKey = readKey("BRAVE_API_KEY");
29
68
  const tavilyApiKey = readKey("TAVILY_API_KEY");
@@ -32,7 +71,7 @@ export function loadConfig() {
32
71
  (process.env.DISABLE_CACHE ?? "").toLowerCase() !== "true";
33
72
  const cachePath = process.env.BETTER_WEB_SEARCH_CACHE_PATH ??
34
73
  process.env.CACHE_PATH ??
35
- "data/cache.db";
74
+ defaultCachePath();
36
75
  const browserEnabled = (process.env.BETTER_WEB_SEARCH_DISABLE_BROWSER ?? "").toLowerCase() !== "true" &&
37
76
  (process.env.DISABLE_BROWSER ?? "").toLowerCase() !== "true";
38
77
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,GAA6B;IAC5C,aAAa,EAAE,CAAC,iCAAiC,EAAE,sBAAsB,CAAC;IAC1E,cAAc,EAAE,CAAC,kCAAkC,CAAC;IACpD,WAAW,EAAE,CAAC,+BAA+B,EAAE,cAAc,CAAC;CAC/D,CAAC;AAEF,SAAS,YAAY,CAAC,KAAe;IACnC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAChC,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;YAC/C,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC;QACtB,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,OAAO,CAAC,SAAiB;IAChC,MAAM,OAAO,GAAG,WAAW,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;IAC7C,OAAO,YAAY,CAAC,CAAC,SAAS,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC;AAC/C,CAAC;AAWD,MAAM,UAAU,UAAU;IACxB,MAAM,WAAW,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IAC7C,MAAM,YAAY,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAC/C,MAAM,UAAU,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IAE1C,MAAM,YAAY,GAChB,CAAC,OAAO,CAAC,GAAG,CAAC,+BAA+B,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,KAAK,MAAM;QAC5E,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,KAAK,MAAM,CAAC;IAE7D,MAAM,SAAS,GACb,OAAO,CAAC,GAAG,CAAC,4BAA4B;QACxC,OAAO,CAAC,GAAG,CAAC,UAAU;QACtB,eAAe,CAAC;IAElB,MAAM,cAAc,GAClB,CAAC,OAAO,CAAC,GAAG,CAAC,iCAAiC,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,KAAK,MAAM;QAC9E,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,KAAK,MAAM,CAAC;IAE/D,OAAO;QACL,WAAW;QACX,YAAY;QACZ,UAAU;QACV,YAAY;QACZ,SAAS;QACT,cAAc;KACf,CAAC;AACJ,CAAC;AAUD,MAAM,UAAU,gBAAgB,CAAC,GAAc;IAC7C,OAAO;QACL;YACE,IAAI,EAAE,YAAY;YAClB,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,IAAI;YACb,IAAI,EAAE,WAAW;SAClB;QACD;YACE,IAAI,EAAE,OAAO;YACb,OAAO,EAAE,GAAG,CAAC,WAAW,KAAK,SAAS;YACtC,OAAO,EAAE,KAAK;YACd,IAAI,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,sCAAsC;SAC3E;QACD;YACE,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,GAAG,CAAC,YAAY,KAAK,SAAS;YACvC,OAAO,EAAE,KAAK;YACd,IAAI,EAAE,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,+BAA+B;SACrE;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,GAAsC,EACtC,GAAc;IAEd,MAAM,SAAS,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;IACxC,MAAM,IAAI,GAAG,CAAC,CAAiB,EAAU,EAAE;QACzC,MAAM,IAAI,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;QACnC,MAAM,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;QACtE,OAAO,KAAK,IAAI,IAAI,CAAC,CAAC,IAAI,KAAK,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;IACvE,CAAC,CAAC;IACF,OAAO;QACL,GAAG,GAAG,CAAC,IAAI,KAAK,GAAG,CAAC,OAAO,EAAE;QAC7B,4BAA4B;QAC5B,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC;QACtB,UAAU,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,4BAA4B,EAAE;QAC3E,YAAY,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,8BAA8B,CAAC,CAAC,CAAC,UAAU,EAAE;QAC9E,8EAA8E;QAC9E,0GAA0G;KAC3G,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Per-provider request pacing and cooldown.
3
+ *
4
+ * `web_research` expands a question into several queries and runs them in
5
+ * parallel across every provider, so a single call could fire three or more
6
+ * requests at one endpoint within milliseconds. DuckDuckGo answers that with a
7
+ * challenge page, and the tool then reports an empty search. Reporting it
8
+ * accurately was the previous fix; not provoking it is this one.
9
+ *
10
+ * Two mechanisms, both deterministic:
11
+ *
12
+ * pacing requests to one provider are spaced by a minimum interval, so
13
+ * parallel callers queue rather than burst
14
+ * cooldown once a provider refuses, further requests fail immediately for
15
+ * a while instead of hammering an endpoint that is already
16
+ * turning us away
17
+ *
18
+ * The first request through a fresh limiter never waits, so a single search is
19
+ * as fast as it ever was; only a burst pays the interval.
20
+ */
21
+ /** Default spacing between requests to one provider, in milliseconds. */
22
+ export declare const DEFAULT_INTERVAL_MS = 1000;
23
+ /** How long a provider is skipped after refusing, in milliseconds. */
24
+ export declare const DEFAULT_COOLDOWN_MS = 60000;
25
+ /** A provider was asked for something while it is cooling down. */
26
+ export declare class CoolingDownError extends Error {
27
+ /** Milliseconds remaining before the provider is tried again. */
28
+ readonly remainingMs: number;
29
+ constructor(name: string, remainingMs: number);
30
+ }
31
+ /**
32
+ * Serializes work for one provider, spacing it and honouring a cooldown.
33
+ *
34
+ * Scheduling is a chain rather than a timestamp check: concurrent callers each
35
+ * await the previous one, so N parallel requests are spread across N intervals
36
+ * instead of all seeing the same "last call" time and going at once.
37
+ */
38
+ export declare class RateLimiter {
39
+ /** Provider name, used in error messages. */
40
+ readonly name: string;
41
+ /** Minimum spacing between requests, in milliseconds. */
42
+ private intervalMs;
43
+ /** Tail of the queue: resolves when the previous caller may proceed. */
44
+ private chain;
45
+ /** Epoch milliseconds until which requests are refused outright. */
46
+ private cooldownUntil;
47
+ constructor(name: string, intervalMs?: number);
48
+ /** Change the spacing. Used by configuration and by tests. */
49
+ setInterval(intervalMs: number): void;
50
+ /** Whether this provider is currently refusing work. */
51
+ isCoolingDown(now?: number): boolean;
52
+ /** Milliseconds until the cooldown lifts, or 0 if it is not active. */
53
+ cooldownRemaining(now?: number): number;
54
+ /**
55
+ * Stop sending requests for a while.
56
+ *
57
+ * @param ms How long to wait. Callers pass a `Retry-After` value when the
58
+ * provider supplied one, so an explicit instruction is preferred over the
59
+ * default guess.
60
+ */
61
+ startCooldown(ms?: number, now?: number): void;
62
+ /** Clear the cooldown, used when a request succeeds again. */
63
+ clearCooldown(): void;
64
+ /**
65
+ * Run `fn` once the provider is due another request.
66
+ *
67
+ * Throws {@link CoolingDownError} without running `fn` if the provider is in
68
+ * a cooldown, so a blocked provider costs nothing rather than a timeout.
69
+ */
70
+ schedule<T>(fn: () => Promise<T>): Promise<T>;
71
+ }
72
+ /**
73
+ * Read the configured spacing.
74
+ *
75
+ * `BETTER_WEB_SEARCH_RATE_LIMIT_MS=0` disables pacing, which is what the test
76
+ * suite does so unit tests are not slowed by real waits.
77
+ */
78
+ export declare function configuredIntervalMs(env?: NodeJS.ProcessEnv): number;
79
+ /** The limiter for a provider, created on first use. */
80
+ export declare function limiterFor(name: string, intervalMs?: number): RateLimiter;
81
+ /** Drop every limiter. Tests use this to avoid state leaking between cases. */
82
+ export declare function resetRateLimiters(): void;
83
+ //# sourceMappingURL=rateLimit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rateLimit.d.ts","sourceRoot":"","sources":["../../src/utils/rateLimit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,yEAAyE;AACzE,eAAO,MAAM,mBAAmB,OAAQ,CAAC;AAEzC,sEAAsE;AACtE,eAAO,MAAM,mBAAmB,QAAS,CAAC;AAE1C,mEAAmE;AACnE,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,iEAAiE;IACjE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;gBAEjB,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM;CAO9C;AAOD;;;;;;GAMG;AACH,qBAAa,WAAW;IACtB,6CAA6C;IAC7C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,yDAAyD;IACzD,OAAO,CAAC,UAAU,CAAS;IAE3B,wEAAwE;IACxE,OAAO,CAAC,KAAK,CAAoC;IAEjD,oEAAoE;IACpE,OAAO,CAAC,aAAa,CAAK;gBAEd,IAAI,EAAE,MAAM,EAAE,UAAU,GAAE,MAA4B;IAKlE,8DAA8D;IAC9D,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAIrC,wDAAwD;IACxD,aAAa,CAAC,GAAG,GAAE,MAAmB,GAAG,OAAO;IAIhD,uEAAuE;IACvE,iBAAiB,CAAC,GAAG,GAAE,MAAmB,GAAG,MAAM;IAInD;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,GAAE,MAA4B,EAAE,GAAG,GAAE,MAAmB,GAAG,IAAI;IAO/E,8DAA8D;IAC9D,aAAa,IAAI,IAAI;IAIrB;;;;;OAKG;IACG,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;CA8BpD;AAKD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,CAOR;AAED,wDAAwD;AACxD,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,WAAW,CASzE;AAED,+EAA+E;AAC/E,wBAAgB,iBAAiB,IAAI,IAAI,CAExC"}
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Per-provider request pacing and cooldown.
3
+ *
4
+ * `web_research` expands a question into several queries and runs them in
5
+ * parallel across every provider, so a single call could fire three or more
6
+ * requests at one endpoint within milliseconds. DuckDuckGo answers that with a
7
+ * challenge page, and the tool then reports an empty search. Reporting it
8
+ * accurately was the previous fix; not provoking it is this one.
9
+ *
10
+ * Two mechanisms, both deterministic:
11
+ *
12
+ * pacing requests to one provider are spaced by a minimum interval, so
13
+ * parallel callers queue rather than burst
14
+ * cooldown once a provider refuses, further requests fail immediately for
15
+ * a while instead of hammering an endpoint that is already
16
+ * turning us away
17
+ *
18
+ * The first request through a fresh limiter never waits, so a single search is
19
+ * as fast as it ever was; only a burst pays the interval.
20
+ */
21
+ /** Default spacing between requests to one provider, in milliseconds. */
22
+ export const DEFAULT_INTERVAL_MS = 1_000;
23
+ /** How long a provider is skipped after refusing, in milliseconds. */
24
+ export const DEFAULT_COOLDOWN_MS = 60_000;
25
+ /** A provider was asked for something while it is cooling down. */
26
+ export class CoolingDownError extends Error {
27
+ /** Milliseconds remaining before the provider is tried again. */
28
+ remainingMs;
29
+ constructor(name, remainingMs) {
30
+ super(`${name} is cooling down for another ${Math.ceil(remainingMs / 1000)}s after refusing a request`);
31
+ this.name = "CoolingDownError";
32
+ this.remainingMs = remainingMs;
33
+ }
34
+ }
35
+ /** Promise-based sleep. */
36
+ function sleep(ms) {
37
+ return new Promise((resolve) => setTimeout(resolve, ms));
38
+ }
39
+ /**
40
+ * Serializes work for one provider, spacing it and honouring a cooldown.
41
+ *
42
+ * Scheduling is a chain rather than a timestamp check: concurrent callers each
43
+ * await the previous one, so N parallel requests are spread across N intervals
44
+ * instead of all seeing the same "last call" time and going at once.
45
+ */
46
+ export class RateLimiter {
47
+ /** Provider name, used in error messages. */
48
+ name;
49
+ /** Minimum spacing between requests, in milliseconds. */
50
+ intervalMs;
51
+ /** Tail of the queue: resolves when the previous caller may proceed. */
52
+ chain = Promise.resolve();
53
+ /** Epoch milliseconds until which requests are refused outright. */
54
+ cooldownUntil = 0;
55
+ constructor(name, intervalMs = DEFAULT_INTERVAL_MS) {
56
+ this.name = name;
57
+ this.intervalMs = intervalMs;
58
+ }
59
+ /** Change the spacing. Used by configuration and by tests. */
60
+ setInterval(intervalMs) {
61
+ this.intervalMs = Math.max(0, intervalMs);
62
+ }
63
+ /** Whether this provider is currently refusing work. */
64
+ isCoolingDown(now = Date.now()) {
65
+ return now < this.cooldownUntil;
66
+ }
67
+ /** Milliseconds until the cooldown lifts, or 0 if it is not active. */
68
+ cooldownRemaining(now = Date.now()) {
69
+ return Math.max(0, this.cooldownUntil - now);
70
+ }
71
+ /**
72
+ * Stop sending requests for a while.
73
+ *
74
+ * @param ms How long to wait. Callers pass a `Retry-After` value when the
75
+ * provider supplied one, so an explicit instruction is preferred over the
76
+ * default guess.
77
+ */
78
+ startCooldown(ms = DEFAULT_COOLDOWN_MS, now = Date.now()) {
79
+ const until = now + Math.max(0, ms);
80
+ // Never shorten an existing cooldown: a second refusal during one means
81
+ // the provider is still unhappy.
82
+ this.cooldownUntil = Math.max(this.cooldownUntil, until);
83
+ }
84
+ /** Clear the cooldown, used when a request succeeds again. */
85
+ clearCooldown() {
86
+ this.cooldownUntil = 0;
87
+ }
88
+ /**
89
+ * Run `fn` once the provider is due another request.
90
+ *
91
+ * Throws {@link CoolingDownError} without running `fn` if the provider is in
92
+ * a cooldown, so a blocked provider costs nothing rather than a timeout.
93
+ */
94
+ async schedule(fn) {
95
+ if (this.isCoolingDown()) {
96
+ throw new CoolingDownError(this.name, this.cooldownRemaining());
97
+ }
98
+ const previous = this.chain;
99
+ let release = () => { };
100
+ this.chain = new Promise((resolve) => {
101
+ release = resolve;
102
+ });
103
+ await previous;
104
+ // Re-check: the queue may have been waiting while a cooldown began.
105
+ if (this.isCoolingDown()) {
106
+ release();
107
+ throw new CoolingDownError(this.name, this.cooldownRemaining());
108
+ }
109
+ try {
110
+ return await fn();
111
+ }
112
+ finally {
113
+ // Hold the slot for the interval so the next caller is spaced out, then
114
+ // let it through. Detached so this call returns as soon as it is done.
115
+ if (this.intervalMs > 0) {
116
+ void sleep(this.intervalMs).then(release);
117
+ }
118
+ else {
119
+ release();
120
+ }
121
+ }
122
+ }
123
+ }
124
+ /** Limiters by provider name. */
125
+ const limiters = new Map();
126
+ /**
127
+ * Read the configured spacing.
128
+ *
129
+ * `BETTER_WEB_SEARCH_RATE_LIMIT_MS=0` disables pacing, which is what the test
130
+ * suite does so unit tests are not slowed by real waits.
131
+ */
132
+ export function configuredIntervalMs(env = process.env) {
133
+ const raw = env.BETTER_WEB_SEARCH_RATE_LIMIT_MS;
134
+ if (raw === undefined || raw.trim() === "") {
135
+ return DEFAULT_INTERVAL_MS;
136
+ }
137
+ const value = Number(raw);
138
+ return Number.isFinite(value) && value >= 0 ? value : DEFAULT_INTERVAL_MS;
139
+ }
140
+ /** The limiter for a provider, created on first use. */
141
+ export function limiterFor(name, intervalMs) {
142
+ let limiter = limiters.get(name);
143
+ if (limiter === undefined) {
144
+ limiter = new RateLimiter(name, intervalMs ?? configuredIntervalMs());
145
+ limiters.set(name, limiter);
146
+ }
147
+ else if (intervalMs !== undefined) {
148
+ limiter.setInterval(intervalMs);
149
+ }
150
+ return limiter;
151
+ }
152
+ /** Drop every limiter. Tests use this to avoid state leaking between cases. */
153
+ export function resetRateLimiters() {
154
+ limiters.clear();
155
+ }
156
+ //# sourceMappingURL=rateLimit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rateLimit.js","sourceRoot":"","sources":["../../src/utils/rateLimit.ts"],"names":[],"mappings":"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"}
package/mcp.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "better-web-search-mcp",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "description": "BetterWebSearch MCP — keyless-first web research (DuckDuckGo) with optional Brave/Tavily and 3-tier extraction",
5
5
  "mcpServers": {
6
6
  "better-web-search-mcp": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "better-web-search-mcp",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "description": "MCP server for genuinely better web research — keyless-first (DuckDuckGo), 3-tier extraction, deep research. Brave/Tavily optional.",
5
5
  "type": "module",
6
6
  "bin": {