diff --git a/.changeset/mcp-search-best-section.md b/.changeset/mcp-search-best-section.md new file mode 100644 index 000000000..189c1799a --- /dev/null +++ b/.changeset/mcp-search-best-section.md @@ -0,0 +1,5 @@ +--- +"gitbook": patch +--- + +Reduce the size of `searchDocumentation` MCP responses by returning only the best-matching section per page instead of concatenating every section body. diff --git a/packages/gitbook/src/app/sites/dynamic/[mode]/[siteURL]/[siteData]/~gitbook/mcp/handler.ts b/packages/gitbook/src/app/sites/dynamic/[mode]/[siteURL]/[siteData]/~gitbook/mcp/handler.ts index 13c35b467..5b496a838 100644 --- a/packages/gitbook/src/app/sites/dynamic/[mode]/[siteURL]/[siteData]/~gitbook/mcp/handler.ts +++ b/packages/gitbook/src/app/sites/dynamic/[mode]/[siteURL]/[siteData]/~gitbook/mcp/handler.ts @@ -5,6 +5,7 @@ import { getExposableError, throwIfDataError } from '@/lib/data'; import { getMarkdownForPageInSpace } from '@/lib/markdownPage'; import { resolvePagePath } from '@/lib/pages'; import { joinPathWithBaseURL } from '@/lib/paths'; +import { getBestScoredResult } from '@/lib/search'; import { findSiteSpaceBy, findSiteSpaceByUrl } from '@/lib/sites'; import { trackServerInsightsEvents } from '@/lib/tracking'; import { waitUntil } from '@/lib/waitUntil'; @@ -128,9 +129,9 @@ export async function handleMcpRequest( ) ); - const body = pageResult.sections - ?.map((section) => section.body) - .join('\n'); + const body = getBestScoredResult( + (pageResult.sections ?? []).filter((section) => section.body) + )?.body; return { type: 'text', diff --git a/packages/gitbook/src/app/sites/dynamic/[mode]/[siteURL]/[siteData]/~gitbook/search/route.ts b/packages/gitbook/src/app/sites/dynamic/[mode]/[siteURL]/[siteData]/~gitbook/search/route.ts index f450acf0f..e55e729d7 100644 --- a/packages/gitbook/src/app/sites/dynamic/[mode]/[siteURL]/[siteData]/~gitbook/search/route.ts +++ b/packages/gitbook/src/app/sites/dynamic/[mode]/[siteURL]/[siteData]/~gitbook/search/route.ts @@ -8,6 +8,7 @@ import { throwIfDataError } from '@/lib/data'; import { toEmbeddableLinkForPublishedContent } from '@/lib/embeddable-linker'; import { getSiteURLDataFromMiddleware } from '@/lib/middleware'; import { joinPathWithBaseURL } from '@/lib/paths'; +import { getBestScoredResult } from '@/lib/search'; import { getServerActionBaseContext } from '@/lib/server-actions'; import { findSiteSpaceBy, getLocalizedTitle } from '@/lib/sites'; import type { @@ -187,10 +188,7 @@ function transformSitePageResult(args: { }) ?? []; // Find the best-scoring section to use as a body preview on the page result. - const bestSection = pageSections.reduce( - (best, section) => (!best || section.score > best.score ? section : best), - undefined - ); + const bestSection = getBestScoredResult(pageSections); if (bestSection) { page.bestSection = { href: bestSection.href, diff --git a/packages/gitbook/src/lib/search.ts b/packages/gitbook/src/lib/search.ts new file mode 100644 index 000000000..14acd8e24 --- /dev/null +++ b/packages/gitbook/src/lib/search.ts @@ -0,0 +1,11 @@ +/** + * Return the highest-scoring item in the list, or undefined when empty. + */ +export function getBestScoredResult( + items: readonly T[] +): T | undefined { + return items.reduce( + (best, item) => (best === undefined || item.score > best.score ? item : best), + undefined + ); +}