better-web-search-mcp 0.3.0 → 0.4.1

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 CHANGED
@@ -72,15 +72,85 @@ Only escalates when needed. Results carry confidence scores so you know what you
72
72
 
73
73
  **Self-learning cache.** The first visit to a domain takes the full path. The second visit skips straight to what worked. Domain profiles and API patterns are remembered.
74
74
 
75
+ ## Token efficiency
76
+
77
+ Doing the web work inside the server, rather than handing pages to the model,
78
+ is the point. The repository ships the benchmark that checks it.
79
+
80
+ Same questions, same pages, two workflows:
81
+
82
+ - **baseline** — the agent drives the tools: one `web_search`, then
83
+ `web_extract` on each of the top 5 results. Every page lands in its context.
84
+ - **research** — one `web_research` call, returning cited passages.
85
+
86
+ 12 questions across definition, technical, comparison, troubleshooting and
87
+ research categories, run 2026-09-02 against the live web with the browser tier
88
+ disabled:
89
+
90
+ | | Baseline | `web_research` |
91
+ |---|---|---|
92
+ | Payload | 820,229 chars | **110,973 chars** |
93
+ | Tokens (est. at 4 chars) | ~205,000 | **~28,000** |
94
+ | Wall clock | 137.3s | **49.6s** |
95
+
96
+ **86.5% less text overall, 83.8% median**, per-question range 56.8% to 93.4%.
97
+
98
+ Reproduce it yourself:
99
+
100
+ ```bash
101
+ npm run build && npm run bench
102
+ ```
103
+
104
+ Honest caveats, because these are the ones that matter:
105
+
106
+ - Payload is counted in **characters** — exact and tokenizer-independent. The
107
+ token column is an estimate at 4 chars/token for orientation; the *ratio* is
108
+ the real result and barely moves between tokenizers.
109
+ - **This measures payload, not answer quality.** Deciding whether the retained
110
+ passages still support a correct answer needs a judge model, which this
111
+ harness does not have. A smaller payload is only a win if the right text
112
+ survived; `query_term_coverage` is reported per question as a weak proxy and
113
+ is deliberately not folded into the headline number.
114
+ - Reduction tracks how much page text there was. The 56.8% case had a small
115
+ baseline (~20k chars), where the fixed cost of the response's source list
116
+ dominates.
117
+ - Live-web runs are not bit-reproducible: pages change and DuckDuckGo
118
+ rate-limits scrapers.
119
+ - No comparison against Exa, Tavily's answer endpoint, or hosted web search —
120
+ they cannot be run under identical conditions on the same pages.
121
+
122
+ Method and raw per-question results: [`benchmarks/`](benchmarks/).
123
+
124
+ ## Security
125
+
126
+ Page content is attacker-controlled and URLs arrive from the calling agent, so
127
+ both are treated as untrusted.
128
+
129
+ - **SSRF guard.** Non-HTTP schemes are refused; hostnames are resolved and
130
+ private, loopback, link-local, CGNAT, multicast and reserved addresses
131
+ rejected — including IPv4-mapped IPv6 forms like `::ffff:127.0.0.1`.
132
+ Redirects are followed manually so *every hop* is re-checked, capped at 5.
133
+ - **Prompt-injection screening.** Every `web_extract` result carries a
134
+ `security` block marking content untrusted and flagging text that tries to
135
+ issue instructions to an agent, with the matched pattern and its offset.
136
+ Content is never rewritten, so extraction stays faithful to the source.
137
+ - **Source independence.** Pages are clustered by content before citing, so a
138
+ wire story republished by five outlets counts as one account rather than five
139
+ corroborating sources.
140
+
141
+ See [SECURITY.md](.github/SECURITY.md) for the full threat model.
142
+
75
143
  ## Project structure
76
144
 
77
145
  ```
78
146
  src/
79
147
  providers/ Search backends (DuckDuckGo, Brave, Tavily; SerpApi stubbed)
80
148
  extraction/ 3-tier content pipeline (fetch, structured, browser)
81
- ranking/ Deduplication, domain scoring, reranking
149
+ ranking/ Deduplication, domain scoring, reranking, passage selection,
150
+ source-independence clustering
82
151
  tools/ MCP tool definitions (search, research, extract, find, news)
83
- utils/ Config, caching, retry, query rewriting
152
+ utils/ Config, caching, retry, query rewriting, SSRF guard
153
+ benchmarks/ Token-efficiency harness and results
84
154
  ```
85
155
 
86
156
  ## Configuration
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Source independence via near-duplicate detection.
3
+ *
4
+ * URL deduplication only catches the same page twice. It does nothing about
5
+ * syndication: a wire story republished by five outlets, a vendor press
6
+ * release quoted verbatim, or an article and its own AMP variant all arrive as
7
+ * distinct URLs. Counting those as five sources is how a research tool reports
8
+ * strong agreement for what is really one claim from one author.
9
+ *
10
+ * Pages are compared by content shingles here, and near-duplicates are grouped
11
+ * into clusters with one primary per cluster. Callers can then say "three
12
+ * independent sources, two derivative" instead of "five sources", and citation
13
+ * selection can spread across clusters rather than across URLs.
14
+ *
15
+ * The comparison is Jaccard similarity over word shingles: deterministic,
16
+ * language-agnostic, and cheap at the page counts involved (research opens at
17
+ * most ten, so the quadratic comparison is a few dozen set intersections).
18
+ */
19
+ /** Words per shingle. Long enough to be distinctive, short enough to survive edits. */
20
+ export declare const SHINGLE_SIZE = 5;
21
+ /** Jaccard similarity at or above which two documents are near-duplicates. */
22
+ export declare const DUPLICATE_THRESHOLD = 0.4;
23
+ /** A document participating in independence analysis. */
24
+ export interface IndependenceInput {
25
+ /** The source URL, used to derive the registrable-ish host. */
26
+ url: string;
27
+ /** The extracted page content. */
28
+ content: string;
29
+ }
30
+ /** Where a document sits among its near-duplicates. */
31
+ export interface IndependenceResult {
32
+ /** Index of the cluster this document belongs to. */
33
+ cluster: number;
34
+ /**
35
+ * Whether this is the representative of its cluster. Exactly one document
36
+ * per cluster is primary; the rest are derivative copies of the same text.
37
+ */
38
+ primary: boolean;
39
+ /** How many other documents share this cluster. */
40
+ duplicates: number;
41
+ }
42
+ /**
43
+ * Build the set of word shingles for a document.
44
+ *
45
+ * Exported for testing; callers normally use {@link analyzeIndependence}.
46
+ */
47
+ export declare function shingles(text: string, size?: number): Set<string>;
48
+ /** Jaccard similarity of two sets: |intersection| / |union|. */
49
+ export declare function jaccard(a: ReadonlySet<string>, b: ReadonlySet<string>): number;
50
+ /** The host of a URL, lowercased and stripped of a leading `www.`. */
51
+ export declare function hostOf(url: string): string;
52
+ /**
53
+ * Group documents into clusters of near-duplicates.
54
+ *
55
+ * Two documents join the same cluster when their shingle sets reach
56
+ * {@link DUPLICATE_THRESHOLD}, or when they share a host — the same site
57
+ * publishing two pages on a topic is not two independent confirmations.
58
+ * Clustering is transitive via union-find, so A~B and B~C puts all three
59
+ * together even when A and C alone fall below the threshold.
60
+ *
61
+ * The first document in each cluster is its primary, which keeps the incoming
62
+ * relevance order meaningful: callers rank before calling this.
63
+ *
64
+ * @param docs Documents in the caller's preferred (usually ranked) order.
65
+ * @returns One result per input document, in the same order.
66
+ */
67
+ export declare function analyzeIndependence(docs: readonly IndependenceInput[]): IndependenceResult[];
68
+ /** How many distinct clusters a set of independence results spans. */
69
+ export declare function countIndependent(results: readonly IndependenceResult[]): number;
70
+ //# sourceMappingURL=independence.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"independence.d.ts","sourceRoot":"","sources":["../../src/ranking/independence.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,uFAAuF;AACvF,eAAO,MAAM,YAAY,IAAI,CAAC;AAE9B,8EAA8E;AAC9E,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAKvC,yDAAyD;AACzD,MAAM,WAAW,iBAAiB;IAChC,+DAA+D;IAC/D,GAAG,EAAE,MAAM,CAAC;IACZ,kCAAkC;IAClC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,uDAAuD;AACvD,MAAM,WAAW,kBAAkB;IACjC,qDAAqD;IACrD,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB,mDAAmD;IACnD,UAAU,EAAE,MAAM,CAAC;CACpB;AAOD;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,SAAe,GAAG,GAAG,CAAC,MAAM,CAAC,CAevE;AAED,gEAAgE;AAChE,wBAAgB,OAAO,CAAC,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,MAAM,CAa9E;AAED,sEAAsE;AACtE,wBAAgB,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAM1C;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,SAAS,iBAAiB,EAAE,GACjC,kBAAkB,EAAE,CA0EtB;AAED,sEAAsE;AACtE,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,SAAS,kBAAkB,EAAE,GACrC,MAAM,CAER"}
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Source independence via near-duplicate detection.
3
+ *
4
+ * URL deduplication only catches the same page twice. It does nothing about
5
+ * syndication: a wire story republished by five outlets, a vendor press
6
+ * release quoted verbatim, or an article and its own AMP variant all arrive as
7
+ * distinct URLs. Counting those as five sources is how a research tool reports
8
+ * strong agreement for what is really one claim from one author.
9
+ *
10
+ * Pages are compared by content shingles here, and near-duplicates are grouped
11
+ * into clusters with one primary per cluster. Callers can then say "three
12
+ * independent sources, two derivative" instead of "five sources", and citation
13
+ * selection can spread across clusters rather than across URLs.
14
+ *
15
+ * The comparison is Jaccard similarity over word shingles: deterministic,
16
+ * language-agnostic, and cheap at the page counts involved (research opens at
17
+ * most ten, so the quadratic comparison is a few dozen set intersections).
18
+ */
19
+ /** Words per shingle. Long enough to be distinctive, short enough to survive edits. */
20
+ export const SHINGLE_SIZE = 5;
21
+ /** Jaccard similarity at or above which two documents are near-duplicates. */
22
+ export const DUPLICATE_THRESHOLD = 0.4;
23
+ /** Minimum tokens a document needs before similarity means anything. */
24
+ const MIN_TOKENS = SHINGLE_SIZE * 4;
25
+ /** Lowercase word tokens used to build shingles. */
26
+ function tokens(text) {
27
+ return text.toLowerCase().match(/[a-z0-9]+/g) ?? [];
28
+ }
29
+ /**
30
+ * Build the set of word shingles for a document.
31
+ *
32
+ * Exported for testing; callers normally use {@link analyzeIndependence}.
33
+ */
34
+ export function shingles(text, size = SHINGLE_SIZE) {
35
+ const words = tokens(text);
36
+ const set = new Set();
37
+ if (words.length < size) {
38
+ // Too short to shingle: fall back to the whole token string so identical
39
+ // short documents still match each other.
40
+ if (words.length > 0) {
41
+ set.add(words.join(" "));
42
+ }
43
+ return set;
44
+ }
45
+ for (let i = 0; i + size <= words.length; i += 1) {
46
+ set.add(words.slice(i, i + size).join(" "));
47
+ }
48
+ return set;
49
+ }
50
+ /** Jaccard similarity of two sets: |intersection| / |union|. */
51
+ export function jaccard(a, b) {
52
+ if (a.size === 0 || b.size === 0) {
53
+ return 0;
54
+ }
55
+ const [small, large] = a.size <= b.size ? [a, b] : [b, a];
56
+ let intersection = 0;
57
+ for (const value of small) {
58
+ if (large.has(value)) {
59
+ intersection += 1;
60
+ }
61
+ }
62
+ const union = a.size + b.size - intersection;
63
+ return union === 0 ? 0 : intersection / union;
64
+ }
65
+ /** The host of a URL, lowercased and stripped of a leading `www.`. */
66
+ export function hostOf(url) {
67
+ try {
68
+ return new URL(url).hostname.toLowerCase().replace(/^www\./, "");
69
+ }
70
+ catch {
71
+ return "";
72
+ }
73
+ }
74
+ /**
75
+ * Group documents into clusters of near-duplicates.
76
+ *
77
+ * Two documents join the same cluster when their shingle sets reach
78
+ * {@link DUPLICATE_THRESHOLD}, or when they share a host — the same site
79
+ * publishing two pages on a topic is not two independent confirmations.
80
+ * Clustering is transitive via union-find, so A~B and B~C puts all three
81
+ * together even when A and C alone fall below the threshold.
82
+ *
83
+ * The first document in each cluster is its primary, which keeps the incoming
84
+ * relevance order meaningful: callers rank before calling this.
85
+ *
86
+ * @param docs Documents in the caller's preferred (usually ranked) order.
87
+ * @returns One result per input document, in the same order.
88
+ */
89
+ export function analyzeIndependence(docs) {
90
+ const parent = docs.map((_, index) => index);
91
+ const find = (index) => {
92
+ let root = index;
93
+ while (parent[root] !== root) {
94
+ root = parent[root] ?? root;
95
+ }
96
+ // Path compression keeps repeated lookups cheap.
97
+ let cursor = index;
98
+ while (parent[cursor] !== root) {
99
+ const next = parent[cursor] ?? root;
100
+ parent[cursor] = root;
101
+ cursor = next;
102
+ }
103
+ return root;
104
+ };
105
+ const union = (a, b) => {
106
+ const rootA = find(a);
107
+ const rootB = find(b);
108
+ if (rootA !== rootB) {
109
+ // Always attach to the lower index so the earliest document wins.
110
+ const [keep, drop] = rootA < rootB ? [rootA, rootB] : [rootB, rootA];
111
+ parent[drop] = keep;
112
+ }
113
+ };
114
+ const fingerprints = docs.map((doc) => shingles(doc.content));
115
+ const hosts = docs.map((doc) => hostOf(doc.url));
116
+ const lengths = docs.map((doc) => tokens(doc.content).length);
117
+ for (let i = 0; i < docs.length; i += 1) {
118
+ for (let j = i + 1; j < docs.length; j += 1) {
119
+ const sameHost = hosts[i] !== "" && hosts[i] === hosts[j];
120
+ if (sameHost) {
121
+ union(i, j);
122
+ continue;
123
+ }
124
+ // Documents too short to fingerprint are left independent rather than
125
+ // collapsed on a coincidental match.
126
+ if ((lengths[i] ?? 0) < MIN_TOKENS || (lengths[j] ?? 0) < MIN_TOKENS) {
127
+ continue;
128
+ }
129
+ const similarity = jaccard(fingerprints[i] ?? new Set(), fingerprints[j] ?? new Set());
130
+ if (similarity >= DUPLICATE_THRESHOLD) {
131
+ union(i, j);
132
+ }
133
+ }
134
+ }
135
+ // Number clusters by first appearance so output is stable and readable.
136
+ const clusterIds = new Map();
137
+ const members = new Map();
138
+ const roots = docs.map((_, index) => find(index));
139
+ for (const root of roots) {
140
+ if (!clusterIds.has(root)) {
141
+ clusterIds.set(root, clusterIds.size);
142
+ }
143
+ members.set(root, (members.get(root) ?? 0) + 1);
144
+ }
145
+ return docs.map((_, index) => {
146
+ const root = roots[index] ?? index;
147
+ return {
148
+ cluster: clusterIds.get(root) ?? 0,
149
+ primary: root === index,
150
+ duplicates: (members.get(root) ?? 1) - 1,
151
+ };
152
+ });
153
+ }
154
+ /** How many distinct clusters a set of independence results spans. */
155
+ export function countIndependent(results) {
156
+ return new Set(results.map((result) => result.cluster)).size;
157
+ }
158
+ //# sourceMappingURL=independence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"independence.js","sourceRoot":"","sources":["../../src/ranking/independence.ts"],"names":[],"mappings":"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"}
@@ -28,6 +28,16 @@ export interface Passage {
28
28
  }
29
29
  /** Target passage size in characters; paragraphs are packed up to this. */
30
30
  export declare const TARGET_PASSAGE_CHARS = 500;
31
+ /**
32
+ * Hard ceiling on a single passage, in characters.
33
+ *
34
+ * Paragraph splitting alone is not enough: plenty of extracted pages contain
35
+ * no blank lines at all, which made the whole page one passage. A cited
36
+ * "passage" was then the entire document, and `web_research` returned more
37
+ * text than reading the pages directly would have. Anything longer than this
38
+ * is windowed on sentence boundaries.
39
+ */
40
+ export declare const MAX_PASSAGE_CHARS = 1200;
31
41
  /** Lowercase alphanumeric tokens, stop words removed. */
32
42
  export declare function tokenize(text: string): string[];
33
43
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"passages.d.ts","sourceRoot":"","sources":["../../src/ranking/passages.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,+CAA+C;AAC/C,MAAM,WAAW,OAAO;IACtB,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAC;IACd,qEAAqE;IACrE,GAAG,EAAE,MAAM,CAAC;IACZ,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAC;CACf;AAMD,2EAA2E;AAC3E,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAUxC,yDAAyD;AACzD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAG/C;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,EAAE,CAuCxD;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,SAAS,OAAO,EAAE,EAC5B,KAAK,EAAE,MAAM,GACZ,OAAO,EAAE,CAqDX;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,KAAK,SAAI,GACR,OAAO,EAAE,CAEX"}
1
+ {"version":3,"file":"passages.d.ts","sourceRoot":"","sources":["../../src/ranking/passages.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,+CAA+C;AAC/C,MAAM,WAAW,OAAO;IACtB,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAC;IACd,qEAAqE;IACrE,GAAG,EAAE,MAAM,CAAC;IACZ,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAC;CACf;AAMD,2EAA2E;AAC3E,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAExC;;;;;;;;GAQG;AACH,eAAO,MAAM,iBAAiB,OAAQ,CAAC;AAUvC,yDAAyD;AACzD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAG/C;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,EAAE,CAyExD;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,SAAS,OAAO,EAAE,EAC5B,KAAK,EAAE,MAAM,GACZ,OAAO,EAAE,CAqDX;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,KAAK,SAAI,GACR,OAAO,EAAE,CAEX"}
@@ -20,6 +20,16 @@ const K1 = 1.5;
20
20
  const B = 0.75;
21
21
  /** Target passage size in characters; paragraphs are packed up to this. */
22
22
  export const TARGET_PASSAGE_CHARS = 500;
23
+ /**
24
+ * Hard ceiling on a single passage, in characters.
25
+ *
26
+ * Paragraph splitting alone is not enough: plenty of extracted pages contain
27
+ * no blank lines at all, which made the whole page one passage. A cited
28
+ * "passage" was then the entire document, and `web_research` returned more
29
+ * text than reading the pages directly would have. Anything longer than this
30
+ * is windowed on sentence boundaries.
31
+ */
32
+ export const MAX_PASSAGE_CHARS = 1_200;
23
33
  /** Words carrying no retrieval signal, skipped when scoring. */
24
34
  const STOP_WORDS = new Set([
25
35
  "a", "an", "and", "are", "as", "at", "be", "but", "by", "for", "from",
@@ -46,11 +56,44 @@ export function splitPassages(content) {
46
56
  let bufferStart = -1;
47
57
  let bufferEnd = -1;
48
58
  let buffer = "";
49
- const flush = () => {
50
- const text = buffer.trim();
51
- if (text !== "") {
52
- passages.push({ text, start: bufferStart, end: bufferEnd, score: 0 });
59
+ const emit = (text, start, end) => {
60
+ if (text.trim() === "") {
61
+ return;
62
+ }
63
+ if (text.length <= MAX_PASSAGE_CHARS) {
64
+ passages.push({ text: text.trim(), start, end, score: 0 });
65
+ return;
66
+ }
67
+ // Window an over-long block on sentence boundaries, keeping offsets
68
+ // accurate by tracking position in the original string.
69
+ let windowStart = 0;
70
+ while (windowStart < text.length) {
71
+ let windowEnd = Math.min(windowStart + MAX_PASSAGE_CHARS, text.length);
72
+ if (windowEnd < text.length) {
73
+ // Prefer the last sentence end inside the window; fall back to a
74
+ // space so words are never cut in half.
75
+ const slice = text.slice(windowStart, windowEnd);
76
+ const sentence = slice.lastIndexOf(". ");
77
+ const space = slice.lastIndexOf(" ");
78
+ const cut = sentence > MAX_PASSAGE_CHARS / 2 ? sentence + 1 : space;
79
+ if (cut > 0) {
80
+ windowEnd = windowStart + cut;
81
+ }
82
+ }
83
+ const chunk = text.slice(windowStart, windowEnd);
84
+ if (chunk.trim() !== "") {
85
+ passages.push({
86
+ text: chunk.trim(),
87
+ start: start + windowStart,
88
+ end: start + windowEnd,
89
+ score: 0,
90
+ });
91
+ }
92
+ windowStart = windowEnd;
53
93
  }
94
+ };
95
+ const flush = () => {
96
+ emit(buffer, bufferStart, bufferEnd);
54
97
  buffer = "";
55
98
  bufferStart = -1;
56
99
  bufferEnd = -1;
@@ -1 +1 @@
1
- {"version":3,"file":"passages.js","sourceRoot":"","sources":["../../src/ranking/passages.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAcH,oDAAoD;AACpD,MAAM,EAAE,GAAG,GAAG,CAAC;AACf,MAAM,CAAC,GAAG,IAAI,CAAC;AAEf,2EAA2E;AAC3E,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAG,CAAC;AAExC,gEAAgE;AAChE,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC;IACzB,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM;IACrE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG;IACzE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO;IAC5E,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;CAC5C,CAAC,CAAC;AAEH,yDAAyD;AACzD,MAAM,UAAU,QAAQ,CAAC,IAAY;IACnC,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,aAAa,CAAC,IAAI,EAAE,CAAC;IAC7D,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;AAC9E,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAAC,OAAe;IAC3C,MAAM,QAAQ,GAAc,EAAE,CAAC;IAC/B,MAAM,WAAW,GAAG,+BAA+B,CAAC;IAEpD,IAAI,WAAW,GAAG,CAAC,CAAC,CAAC;IACrB,IAAI,SAAS,GAAG,CAAC,CAAC,CAAC;IACnB,IAAI,MAAM,GAAG,EAAE,CAAC;IAEhB,MAAM,KAAK,GAAG,GAAS,EAAE;QACvB,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;QAC3B,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;YAChB,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;QACxE,CAAC;QACD,MAAM,GAAG,EAAE,CAAC;QACZ,WAAW,GAAG,CAAC,CAAC,CAAC;QACjB,SAAS,GAAG,CAAC,CAAC,CAAC;IACjB,CAAC,CAAC;IAEF,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;QAClD,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QAC3B,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC;QAC/B,IAAI,SAAS,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;YAC5B,SAAS;QACX,CAAC;QACD,IAAI,MAAM,KAAK,EAAE,EAAE,CAAC;YAClB,WAAW,GAAG,KAAK,CAAC;QACtB,CAAC;aAAM,IAAI,MAAM,CAAC,MAAM,GAAG,SAAS,CAAC,MAAM,GAAG,oBAAoB,EAAE,CAAC;YACnE,KAAK,EAAE,CAAC;YACR,WAAW,GAAG,KAAK,CAAC;QACtB,CAAC;QACD,MAAM,GAAG,MAAM,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,MAAM,OAAO,SAAS,EAAE,CAAC;QACjE,SAAS,GAAG,KAAK,GAAG,SAAS,CAAC,MAAM,CAAC;QACrC,IAAI,MAAM,CAAC,MAAM,IAAI,oBAAoB,EAAE,CAAC;YAC1C,KAAK,EAAE,CAAC;QACV,CAAC;IACH,CAAC;IACD,KAAK,EAAE,CAAC;IAER,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAC1B,QAA4B,EAC5B,KAAa;IAEb,MAAM,UAAU,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IACnC,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrD,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IAC/D,MAAM,SAAS,GACb,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC;IAEtE,mEAAmE;IACnE,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC1C,KAAK,MAAM,IAAI,IAAI,IAAI,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;QACvC,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;gBACvB,KAAK,IAAI,CAAC,CAAC;YACb,CAAC;QACH,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC3B,CAAC;IAED,MAAM,MAAM,GAAc,EAAE,CAAC;IAC7B,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE;QAClC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QAC9B,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACrB,OAAO;QACT,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;QACzC,KAAK,MAAM,KAAK,IAAI,GAAG,EAAE,CAAC;YACxB,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAClD,CAAC;QAED,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,KAAK,MAAM,IAAI,IAAI,IAAI,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;YACvC,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC9B,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;gBACvB,SAAS;YACX,CAAC;YACD,MAAM,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACjC,mEAAmE;YACnE,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;YAC9D,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;YAC7B,MAAM,KAAK,GAAG,IAAI,GAAG,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,IAAI,CAAC,CAAC,CAAC,CAAC;YACxE,KAAK,IAAI,GAAG,GAAG,CAAC,IAAI,GAAG,KAAK,CAAC,CAAC;QAChC,CAAC;QAED,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;YACd,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;QACrC,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;AAClD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAC5B,OAAe,EACf,KAAa,EACb,KAAK,GAAG,CAAC;IAET,OAAO,YAAY,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;AACrE,CAAC"}
1
+ {"version":3,"file":"passages.js","sourceRoot":"","sources":["../../src/ranking/passages.ts"],"names":[],"mappings":"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"}
@@ -12,6 +12,7 @@ import { z } from "zod";
12
12
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
13
13
  import { type RoutedPage } from "../extraction/router.js";
14
14
  import { Cache } from "../utils/cache.js";
15
+ import { type IndependenceResult } from "../ranking/independence.js";
15
16
  import type { SearchSource } from "./search.js";
16
17
  /** Input schema for `web_research`. */
17
18
  export declare const researchInputSchema: {
@@ -21,6 +22,15 @@ export declare const researchInputSchema: {
21
22
  count_per_query: z.ZodDefault<z.ZodNumber>;
22
23
  };
23
24
  /** The spec-shaped `web_research` response. */
25
+ /** Whether a source is an independent account or a copy of another. */
26
+ export interface SourceIndependence {
27
+ /** Index of the content cluster this source belongs to. */
28
+ cluster: number;
29
+ /** Whether this source is the representative of its cluster. */
30
+ primary: boolean;
31
+ /** How many other opened sources share this cluster. */
32
+ duplicates: number;
33
+ }
24
34
  /** A span of a source page that supports the answer. */
25
35
  export interface Citation {
26
36
  /** 1-based index into `sources`. */
@@ -47,6 +57,34 @@ export interface ResearchResponse {
47
57
  * statement to a span rather than to a whole page.
48
58
  */
49
59
  citations: Citation[];
60
+ /**
61
+ * Countable facts about the evidence behind the answer.
62
+ *
63
+ * Every field is something the server measured, not a judgement: there is no
64
+ * language model here to score truth or agreement, and an invented
65
+ * confidence number would be worse than none. An agent can use these to
66
+ * decide whether to research further.
67
+ */
68
+ evidence: {
69
+ /** Pages actually opened and extracted. */
70
+ sources_opened: number;
71
+ /**
72
+ * Distinct content clusters among those pages. Syndicated reprints and
73
+ * multiple pages from one host collapse into a single cluster, so this is
74
+ * the number of genuinely separate accounts.
75
+ */
76
+ independent_sources: number;
77
+ /** Pages that duplicate another page's content or host. */
78
+ derivative_sources: number;
79
+ /**
80
+ * Fraction of the question's content words that appear in the cited
81
+ * passages, 0..1. Low values mean the citations may not address the
82
+ * question, not that the answer is wrong.
83
+ */
84
+ query_term_coverage: number;
85
+ /** Number of cited spans in `citations`. */
86
+ cited_spans: number;
87
+ };
50
88
  extraction_stats: {
51
89
  method_counts: Record<string, number>;
52
90
  avgConfidence: number;
@@ -62,7 +100,15 @@ export interface ResearchResponse {
62
100
  * best ones returned with their offsets, which raises the signal and cuts the
63
101
  * token count, since only matching paragraphs are included.
64
102
  */
65
- export declare function collectCitations(pages: readonly RoutedPage[], sources: readonly SearchSource[], question: string, limit?: number): Citation[];
103
+ export declare function collectCitations(pages: readonly RoutedPage[], sources: readonly SearchSource[], question: string, limit?: number, independence?: readonly IndependenceResult[]): Citation[];
104
+ /**
105
+ * Fraction of the question's content words that appear in the cited passages.
106
+ *
107
+ * A blunt but honest measure of whether the citations engage with the question
108
+ * at all. It says nothing about correctness, which is not something this
109
+ * server can determine.
110
+ */
111
+ export declare function queryTermCoverage(question: string, citations: readonly Citation[]): number;
66
112
  /**
67
113
  * Run a deep research query and return the spec-shaped response.
68
114
  *
@@ -1 +1 @@
1
- {"version":3,"file":"research.d.ts","sourceRoot":"","sources":["../../src/tools/research.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAOzE,OAAO,EAAW,KAAK,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE1C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,uCAAuC;AACvC,eAAO,MAAM,mBAAmB;;;;;CAK/B,CAAC;AAEF,+CAA+C;AAC/C,wDAAwD;AACxD,MAAM,WAAW,QAAQ;IACvB,oCAAoC;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,8CAA8C;IAC9C,GAAG,EAAE,MAAM,CAAC;IACZ,wBAAwB;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,gEAAgE;IAChE,KAAK,EAAE,MAAM,CAAC;IACd,0EAA0E;IAC1E,KAAK,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,GAAG,EAAE,MAAM,CAAC;IACZ,iEAAiE;IACjE,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB;;;OAGG;IACH,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,gBAAgB,EAAE;QAChB,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACtC,aAAa,EAAE,MAAM,CAAC;KACvB,CAAC;CACH;AAmGD,6EAA6E;AAC7E;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,SAAS,UAAU,EAAE,EAC5B,OAAO,EAAE,SAAS,YAAY,EAAE,EAChC,QAAQ,EAAE,MAAM,EAChB,KAAK,SAAe,GACnB,QAAQ,EAAE,CAqDZ;AAkCD;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,KAAK,CAAC;CACf,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAgE5B;AAED,sFAAsF;AACtF,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,CAgCvE"}
1
+ {"version":3,"file":"research.d.ts","sourceRoot":"","sources":["../../src/tools/research.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAOzE,OAAO,EAAW,KAAK,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE1C,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,uCAAuC;AACvC,eAAO,MAAM,mBAAmB;;;;;CAK/B,CAAC;AAEF,+CAA+C;AAC/C,uEAAuE;AACvE,MAAM,WAAW,kBAAkB;IACjC,2DAA2D;IAC3D,OAAO,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,OAAO,EAAE,OAAO,CAAC;IACjB,wDAAwD;IACxD,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wDAAwD;AACxD,MAAM,WAAW,QAAQ;IACvB,oCAAoC;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,8CAA8C;IAC9C,GAAG,EAAE,MAAM,CAAC;IACZ,wBAAwB;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,gEAAgE;IAChE,KAAK,EAAE,MAAM,CAAC;IACd,0EAA0E;IAC1E,KAAK,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,GAAG,EAAE,MAAM,CAAC;IACZ,iEAAiE;IACjE,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB;;;OAGG;IACH,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB;;;;;;;OAOG;IACH,QAAQ,EAAE;QACR,2CAA2C;QAC3C,cAAc,EAAE,MAAM,CAAC;QACvB;;;;WAIG;QACH,mBAAmB,EAAE,MAAM,CAAC;QAC5B,2DAA2D;QAC3D,kBAAkB,EAAE,MAAM,CAAC;QAC3B;;;;WAIG;QACH,mBAAmB,EAAE,MAAM,CAAC;QAC5B,4CAA4C;QAC5C,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,gBAAgB,EAAE;QAChB,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACtC,aAAa,EAAE,MAAM,CAAC;KACvB,CAAC;CACH;AAmGD,6EAA6E;AAC7E;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,SAAS,UAAU,EAAE,EAC5B,OAAO,EAAE,SAAS,YAAY,EAAE,EAChC,QAAQ,EAAE,MAAM,EAChB,KAAK,SAAe,EACpB,YAAY,GAAE,SAAS,kBAAkB,EAAO,GAC/C,QAAQ,EAAE,CAkEZ;AAgBD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,SAAS,QAAQ,EAAE,GAC7B,MAAM,CAaR;AAoBD;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,KAAK,CAAC;CACf,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAuF5B;AAED,sFAAsF;AACtF,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,CAgCvE"}
@@ -16,7 +16,8 @@ import { expandQueries } from "../utils/queries.js";
16
16
  import { freshnessFromRecencyDays } from "../providers/brave.js";
17
17
  import { getPage } from "../extraction/router.js";
18
18
  import { Cache } from "../utils/cache.js";
19
- import { selectPassages } from "../ranking/passages.js";
19
+ import { selectPassages, tokenize } from "../ranking/passages.js";
20
+ import { analyzeIndependence, countIndependent, } from "../ranking/independence.js";
20
21
  /** Input schema for `web_research`. */
21
22
  export const researchInputSchema = {
22
23
  question: z.string().min(1, "question must not be empty"),
@@ -105,13 +106,19 @@ async function openPages(urls, cache) {
105
106
  * best ones returned with their offsets, which raises the signal and cuts the
106
107
  * token count, since only matching paragraphs are included.
107
108
  */
108
- export function collectCitations(pages, sources, question, limit = MAX_EXCERPTS) {
109
+ export function collectCitations(pages, sources, question, limit = MAX_EXCERPTS, independence = []) {
109
110
  const candidates = [];
110
111
  pages.forEach((page, index) => {
111
112
  const content = page.content.trim();
112
113
  if (content === "") {
113
114
  return;
114
115
  }
116
+ // A derivative source republishes another source's text. Quoting it adds
117
+ // no evidence and, once the breadth pass has run, the remaining slots
118
+ // would otherwise be filled with the same story under a second byline.
119
+ if (independence[index]?.primary === false) {
120
+ return;
121
+ }
115
122
  const source = sources[index];
116
123
  for (const passage of selectPassages(content, question, PASSAGES_PER_PAGE)) {
117
124
  candidates.push({
@@ -127,19 +134,24 @@ export function collectCitations(pages, sources, question, limit = MAX_EXCERPTS)
127
134
  }
128
135
  });
129
136
  candidates.sort((a, b) => b.score - a.score);
130
- // Prefer breadth: take the best passage from each distinct source before a
131
- // second from any one of them, so the answer is not a single page's view.
137
+ // Prefer breadth across independent accounts. Spreading over distinct URLs
138
+ // is not enough: five outlets running one wire story would fill every slot
139
+ // with the same text and present it as five corroborating sources.
140
+ const groupOf = (citation) => independence[citation - 1]?.cluster ?? citation;
132
141
  const chosen = [];
133
- const used = new Set();
142
+ const usedGroups = new Set();
134
143
  for (const candidate of candidates) {
135
144
  if (chosen.length >= limit) {
136
145
  break;
137
146
  }
138
- if (!used.has(candidate.citation)) {
139
- used.add(candidate.citation);
147
+ const group = groupOf(candidate.citation);
148
+ if (!usedGroups.has(group)) {
149
+ usedGroups.add(group);
140
150
  chosen.push(candidate);
141
151
  }
142
152
  }
153
+ // Remaining slots go to further passages from those same independent
154
+ // accounts, which adds depth without re-reporting one story twice.
143
155
  for (const candidate of candidates) {
144
156
  if (chosen.length >= limit) {
145
157
  break;
@@ -165,6 +177,27 @@ function buildAnswer(pages, citations) {
165
177
  .join("\n\n");
166
178
  return `Based on ${pages.length} sources:\n\n${body}`;
167
179
  }
180
+ /**
181
+ * Fraction of the question's content words that appear in the cited passages.
182
+ *
183
+ * A blunt but honest measure of whether the citations engage with the question
184
+ * at all. It says nothing about correctness, which is not something this
185
+ * server can determine.
186
+ */
187
+ export function queryTermCoverage(question, citations) {
188
+ const terms = new Set(tokenize(question));
189
+ if (terms.size === 0) {
190
+ return 0;
191
+ }
192
+ const cited = new Set(tokenize(citations.map((c) => c.quote).join(" ")));
193
+ let hits = 0;
194
+ for (const term of terms) {
195
+ if (cited.has(term)) {
196
+ hits += 1;
197
+ }
198
+ }
199
+ return Number((hits / terms.size).toFixed(4));
200
+ }
168
201
  /** Aggregate extraction stats across the opened pages. */
169
202
  function extractionStats(pages) {
170
203
  const methodCounts = {};
@@ -219,13 +252,27 @@ export async function runResearch(args) {
219
252
  const sources = top.map(toSource);
220
253
  // 4) Open the top pages in parallel (bounded concurrency, per-page timeout).
221
254
  const pages = await openPages(top.map((r) => r.url), cache);
222
- // 5) Select the spans that address the question, and cite them.
223
- const citations = collectCitations(pages, sources, question);
255
+ // 5) Work out which pages are genuinely separate accounts before citing, so
256
+ // syndicated copies do not masquerade as corroboration.
257
+ const independence = analyzeIndependence(pages.map((page, index) => ({
258
+ url: sources[index]?.url ?? page.url,
259
+ content: page.content,
260
+ })));
261
+ // 6) Select the spans that address the question, and cite them.
262
+ const citations = collectCitations(pages, sources, question, MAX_EXCERPTS, independence);
263
+ const independentSources = countIndependent(independence);
224
264
  const response = {
225
265
  answer: buildAnswer(pages, citations),
226
266
  sources,
227
267
  queries_used: queries,
228
268
  citations,
269
+ evidence: {
270
+ sources_opened: pages.length,
271
+ independent_sources: independentSources,
272
+ derivative_sources: Math.max(0, pages.length - independentSources),
273
+ query_term_coverage: queryTermCoverage(question, citations),
274
+ cited_spans: citations.length,
275
+ },
229
276
  extraction_stats: extractionStats(pages),
230
277
  };
231
278
  cache?.setSearch(cacheKey, question, response);
@@ -1 +1 @@
1
- {"version":3,"file":"research.js","sourceRoot":"","sources":["../../src/tools/research.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"research.js","sourceRoot":"","sources":["../../src/tools/research.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.3.0",
3
+ "version": "0.4.1",
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.3.0",
3
+ "version": "0.4.1",
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": {
@@ -51,6 +51,7 @@
51
51
  "release:minor": "npm run version:minor && git push --follow-tags",
52
52
  "release:major": "npm run version:major && git push --follow-tags",
53
53
  "check:publish": "npm pack --dry-run",
54
+ "bench": "node benchmarks/run.mjs",
54
55
  "docs:api": "typedoc --options typedoc.json && node scripts/typedoc-postprocess.mjs",
55
56
  "docs:changelog": "node scripts/sync-docs-changelog.mjs",
56
57
  "docs:build": "npm run docs:api && npm run docs:changelog && hugo --source site --gc --minify",