Skip to content

Commit 61d5862

Browse files
authored
search across mcp resources first (#17)
1 parent 62248b3 commit 61d5862

File tree

1 file changed

+103
-2
lines changed

1 file changed

+103
-2
lines changed

src/mcp-tools/nextjs-docs.ts

Lines changed: 103 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import { tool } from "ai"
22
import { z } from "zod"
3+
import {
4+
NEXTJS_16_KNOWLEDGE_SECTIONS,
5+
readKnowledgeSection,
6+
type KnowledgeSection,
7+
} from "../mcp-resources/nextjs-16-knowledge.js"
38

49
// Next.js Docs Tool
510
const nextjsDocsInputSchema = z.object({
@@ -12,6 +17,74 @@ const nextjsDocsInputSchema = z.object({
1217

1318
let cachedDocs: { url: string; title: string; category: string }[] | null = null
1419

20+
/**
21+
* Search through MCP resources (Next.js 16 knowledge sections)
22+
* Returns relevant sections based on query
23+
*/
24+
function searchMcpResources(query: string): KnowledgeSection[] {
25+
const queryLower = query.toLowerCase()
26+
const results: Array<{ section: KnowledgeSection; score: number }> = []
27+
28+
for (const section of NEXTJS_16_KNOWLEDGE_SECTIONS) {
29+
let score = 0
30+
31+
// Check if query matches section name
32+
if (section.name.toLowerCase().includes(queryLower)) {
33+
score += 10
34+
}
35+
36+
// Check if query matches section description
37+
if (section.description.toLowerCase().includes(queryLower)) {
38+
score += 5
39+
}
40+
41+
// Check for keyword matches
42+
const keywords = [
43+
"cache",
44+
"prefetch",
45+
"public",
46+
"private",
47+
"revalidate",
48+
"invalidation",
49+
"async",
50+
"params",
51+
"searchParams",
52+
"cookies",
53+
"headers",
54+
"connection",
55+
"build",
56+
"prerender",
57+
"metadata",
58+
"error",
59+
"test",
60+
"cacheLife",
61+
"cacheTag",
62+
"updateTag",
63+
]
64+
65+
for (const keyword of keywords) {
66+
if (queryLower.includes(keyword)) {
67+
if (
68+
section.name.toLowerCase().includes(keyword) ||
69+
section.description.toLowerCase().includes(keyword)
70+
) {
71+
score += 3
72+
}
73+
}
74+
}
75+
76+
if (score > 0) {
77+
results.push({ section, score })
78+
}
79+
}
80+
81+
// Sort by score and return top matches
82+
return results
83+
.sort((a, b) => b.score - a.score)
84+
.slice(0, 3)
85+
.map((r) => r.section)
86+
}
87+
1588
async function getNextJsDocs(): Promise<{ url: string; title: string; category: string }[]> {
1689
if (cachedDocs) {
1790
return cachedDocs
@@ -55,12 +128,40 @@ async function getNextJsDocs(): Promise<{ url: string; title: string; category:
55128

56129
export const nextjsDocsTool = tool({
57130
description: `Search and retrieve Next.js official documentation.
131+
First searches MCP resources (Next.js 16 knowledge base) for latest information, then falls back to official Next.js documentation if nothing is found.
58132
Provides access to comprehensive Next.js guides, API references, and best practices.`,
59133
inputSchema: nextjsDocsInputSchema,
60134
execute: async ({
61135
query,
62136
category = "all",
63137
}: z.infer<typeof nextjsDocsInputSchema>): Promise<string> => {
138+
// Step 1: Search MCP resources first (Next.js 16 knowledge base)
139+
const mcpMatches = searchMcpResources(query)
140+
141+
if (mcpMatches.length > 0) {
142+
// Found relevant content in MCP resources, return it
143+
let result = `Found ${mcpMatches.length} relevant section(s) in Next.js 16 knowledge base:\n\n`
144+
145+
for (const section of mcpMatches) {
146+
result += `## ${section.name}\n\n`
147+
result += `${section.description}\n\n`
148+
149+
try {
150+
const content = readKnowledgeSection(section.uri)
151+
// Limit content to first 3000 characters to avoid overwhelming the response
152+
const truncatedContent =
153+
content.length > 3000 ? content.substring(0, 3000) + "\n\n...(truncated)" : content
154+
result += `${truncatedContent}\n\n`
155+
result += `---\n\n`
156+
} catch (error) {
157+
result += `Error reading section: ${error instanceof Error ? error.message : String(error)}\n\n`
158+
}
159+
}
160+
161+
return result
162+
}
163+
164+
// Step 2: Fallback to official Next.js documentation
64165
const docs = await getNextJsDocs()
65166

66167
let filtered = docs
@@ -78,10 +179,10 @@ Provides access to comprehensive Next.js guides, API references, and best practi
78179
.slice(0, 10)
79180

80181
if (results.length === 0) {
81-
return `No documentation found for "${query}"${category !== "all" ? ` in category "${category}"` : ""}`
182+
return `No documentation found for "${query}"${category !== "all" ? ` in category "${category}"` : ""} in both MCP resources and official Next.js documentation.`
82183
}
83184

84-
return `Found ${results.length} documentation page(s):\n\n${results
185+
return `No matches in MCP resources. Found ${results.length} documentation page(s) from official Next.js docs:\n\n${results
85186
.map((doc) => `- [${doc.title}](${doc.url})`)
86187
.join("\n")}`
87188
},

0 commit comments

Comments
 (0)