fix(mcp): reduce Exa API context size sent to LLM (#2026)
Browse filesUse highlights instead of full page text to minimize context size.
The previous implementation requested `text: true` which returned
entire page contents (5-20K+ chars per result), overwhelming LLM context.
Now uses:
- highlights: 3 sentences, 2 per URL (concise, query-relevant)
- text fallback: max 500 chars (only if highlights empty)
This matches what the MCP server at mcp.exa.ai likely returns.
src/lib/server/mcp/exaDirect.ts
CHANGED
|
@@ -140,6 +140,7 @@ interface ExaSearchResponse {
|
|
| 140 |
|
| 141 |
/**
|
| 142 |
* Format Exa search results as human-readable text
|
|
|
|
| 143 |
*/
|
| 144 |
function formatSearchResultsAsText(results: ExaSearchResult[]): string {
|
| 145 |
if (results.length === 0) {
|
|
@@ -154,10 +155,11 @@ function formatSearchResultsAsText(results: ExaSearchResult[]): string {
|
|
| 154 |
parts.push(` Published: ${result.publishedDate}`);
|
| 155 |
}
|
| 156 |
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
} else if (result.highlights && result.highlights.length > 0) {
|
| 160 |
parts.push(` ${result.highlights.join(" ... ")}`);
|
|
|
|
|
|
|
| 161 |
}
|
| 162 |
|
| 163 |
return parts.join("\n");
|
|
@@ -186,13 +188,22 @@ export async function callExaDirectApi(
|
|
| 186 |
throw new Error("Missing required parameter: query");
|
| 187 |
}
|
| 188 |
|
| 189 |
-
// Build request body - pass through all args, ensure query exists
|
|
|
|
|
|
|
| 190 |
const requestBody: Record<string, unknown> = {
|
| 191 |
...args,
|
| 192 |
query,
|
| 193 |
-
// Required to get page text content, not just metadata
|
| 194 |
contents: {
|
| 195 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 196 |
},
|
| 197 |
};
|
| 198 |
|
|
|
|
| 140 |
|
| 141 |
/**
|
| 142 |
* Format Exa search results as human-readable text
|
| 143 |
+
* Prioritizes highlights (concise snippets) over full text to minimize LLM context
|
| 144 |
*/
|
| 145 |
function formatSearchResultsAsText(results: ExaSearchResult[]): string {
|
| 146 |
if (results.length === 0) {
|
|
|
|
| 155 |
parts.push(` Published: ${result.publishedDate}`);
|
| 156 |
}
|
| 157 |
|
| 158 |
+
// Prefer highlights (concise, query-relevant) over full text
|
| 159 |
+
if (result.highlights && result.highlights.length > 0) {
|
|
|
|
| 160 |
parts.push(` ${result.highlights.join(" ... ")}`);
|
| 161 |
+
} else if (result.text) {
|
| 162 |
+
parts.push(` ${result.text}`);
|
| 163 |
}
|
| 164 |
|
| 165 |
return parts.join("\n");
|
|
|
|
| 188 |
throw new Error("Missing required parameter: query");
|
| 189 |
}
|
| 190 |
|
| 191 |
+
// Build request body - pass through all args, ensure query exists
|
| 192 |
+
// Use highlights (not full text) to minimize context size sent to LLM
|
| 193 |
+
// This matches what the MCP server at mcp.exa.ai returns
|
| 194 |
const requestBody: Record<string, unknown> = {
|
| 195 |
...args,
|
| 196 |
query,
|
|
|
|
| 197 |
contents: {
|
| 198 |
+
// Use highlights for concise, query-relevant snippets (much smaller than full text)
|
| 199 |
+
highlights: {
|
| 200 |
+
numSentences: 3,
|
| 201 |
+
highlightsPerUrl: 2,
|
| 202 |
+
},
|
| 203 |
+
// Also get limited text as fallback if highlights are empty
|
| 204 |
+
text: {
|
| 205 |
+
maxCharacters: 500,
|
| 206 |
+
},
|
| 207 |
},
|
| 208 |
};
|
| 209 |
|