Skip to content

Commit 8cccab0

Browse files
authored
docs: fundamental knowledge (#30)
1 parent 029064a commit 8cccab0

File tree

5 files changed

+723
-7
lines changed

5 files changed

+723
-7
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
],
1717
"scripts": {
1818
"dev": "tsc --watch",
19-
"build": "tsc && cp -r src/mcp-prompts/*.md dist/mcp-prompts/ && cp -r src/mcp-resources/nextjs-16-knowledge dist/mcp-resources/",
19+
"copy-resources": "node scripts/copy-resources.js",
20+
"build": "tsc && npm run copy-resources",
2021
"prepublishOnly": "pnpm run clean && pnpm run build",
2122
"clean": "rm -rf dist",
2223
"test": "vitest run",

scripts/copy-resources.js

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
#!/usr/bin/env node
2+
/**
3+
* Copy resources script
4+
* Copies all MCP resource files and directories from src to dist
5+
*
6+
* Usage: node scripts/copy-resources.js
7+
*/
8+
9+
const fs = require('fs')
10+
const path = require('path')
11+
12+
// Define resources to copy
13+
const resources = [
14+
{
15+
type: 'files',
16+
src: 'src/mcp-prompts/*.md',
17+
dest: 'dist/mcp-prompts/'
18+
},
19+
{
20+
type: 'dir',
21+
src: 'src/mcp-resources/nextjs-16-knowledge',
22+
dest: 'dist/mcp-resources/nextjs-16-knowledge'
23+
},
24+
{
25+
type: 'dir',
26+
src: 'src/mcp-resources/nextjs-fundamentals-knowledge',
27+
dest: 'dist/mcp-resources/nextjs-fundamentals-knowledge'
28+
}
29+
]
30+
31+
/**
32+
* Copy a file
33+
*/
34+
function copyFile(src, dest) {
35+
const destDir = path.dirname(dest)
36+
if (!fs.existsSync(destDir)) {
37+
fs.mkdirSync(destDir, { recursive: true })
38+
}
39+
fs.copyFileSync(src, dest)
40+
console.log(` ✓ ${path.relative(process.cwd(), dest)}`)
41+
}
42+
43+
/**
44+
* Copy files matching a glob pattern
45+
*/
46+
function copyFiles(pattern, destDir) {
47+
const srcDir = path.dirname(pattern)
48+
const filename = path.basename(pattern)
49+
50+
if (!fs.existsSync(srcDir)) {
51+
console.warn(` ⚠ Source directory not found: ${srcDir}`)
52+
return
53+
}
54+
55+
const files = fs.readdirSync(srcDir).filter(file => {
56+
if (filename === '*.md') return file.endsWith('.md')
57+
return file === filename
58+
})
59+
60+
if (files.length === 0) {
61+
console.warn(` ⚠ No files matching ${pattern}`)
62+
return
63+
}
64+
65+
if (!fs.existsSync(destDir)) {
66+
fs.mkdirSync(destDir, { recursive: true })
67+
}
68+
69+
files.forEach(file => {
70+
const src = path.join(srcDir, file)
71+
const dest = path.join(destDir, file)
72+
copyFile(src, dest)
73+
})
74+
}
75+
76+
/**
77+
* Copy a directory recursively
78+
*/
79+
function copyDir(src, dest) {
80+
if (!fs.existsSync(src)) {
81+
console.warn(` ⚠ Source directory not found: ${src}`)
82+
return
83+
}
84+
85+
if (!fs.existsSync(dest)) {
86+
fs.mkdirSync(dest, { recursive: true })
87+
}
88+
89+
const items = fs.readdirSync(src)
90+
91+
items.forEach(item => {
92+
const srcPath = path.join(src, item)
93+
const destPath = path.join(dest, item)
94+
const stat = fs.statSync(srcPath)
95+
96+
if (stat.isDirectory()) {
97+
copyDir(srcPath, destPath)
98+
} else {
99+
copyFile(srcPath, destPath)
100+
}
101+
})
102+
}
103+
104+
/**
105+
* Main function
106+
*/
107+
function main() {
108+
console.log('📦 Copying MCP resources...\n')
109+
110+
resources.forEach(resource => {
111+
if (resource.type === 'files') {
112+
console.log(`Copying markdown files: ${resource.src}`)
113+
copyFiles(resource.src, resource.dest)
114+
} else if (resource.type === 'dir') {
115+
console.log(`Copying directory: ${resource.src}`)
116+
copyDir(resource.src, resource.dest)
117+
}
118+
})
119+
120+
console.log('\n✅ Resources copied successfully!')
121+
}
122+
123+
main()

src/index.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ import {
2222
readKnowledgeSection,
2323
isNextjs16KnowledgeUri,
2424
} from "./mcp-resources/nextjs-16-knowledge.js"
25+
import {
26+
getNextjsFundamentalsResources,
27+
readFundamentalsSection,
28+
isNextjsFundamentalsUri,
29+
} from "./mcp-resources/nextjs-fundamentals-knowledge.js"
2530

2631
async function main() {
2732
// Create MCP server instance
@@ -123,7 +128,9 @@ async function main() {
123128

124129
// Register resources/list handler
125130
server.setRequestHandler(ListResourcesRequestSchema, async () => {
126-
const resources = getNextjs16KnowledgeResources()
131+
const nextjs16Resources = getNextjs16KnowledgeResources()
132+
const fundamentalsResources = getNextjsFundamentalsResources()
133+
const resources = [...nextjs16Resources, ...fundamentalsResources]
127134

128135
return { resources }
129136
})
@@ -132,12 +139,16 @@ async function main() {
132139
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
133140
const { uri } = request.params
134141

135-
if (!isNextjs16KnowledgeUri(uri)) {
136-
throw new Error(`Unknown resource URI: ${uri}`)
137-
}
138-
139142
try {
140-
const content = readKnowledgeSection(uri)
143+
let content: string
144+
145+
if (isNextjs16KnowledgeUri(uri)) {
146+
content = readKnowledgeSection(uri)
147+
} else if (isNextjsFundamentalsUri(uri)) {
148+
content = readFundamentalsSection(uri)
149+
} else {
150+
throw new Error(`Unknown resource URI: ${uri}`)
151+
}
141152

142153
return {
143154
contents: [
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import type { Resource } from "@modelcontextprotocol/sdk/types.js"
2+
import { readFileSync } from "node:fs"
3+
import { join } from "node:path"
4+
5+
/**
6+
* Next.js Fundamentals Knowledge Base Resources
7+
*
8+
* These resources provide comprehensive documentation about fundamental Next.js
9+
* concepts that are often confusing for developers, split into logical sections
10+
* for efficient context management.
11+
*/
12+
13+
export interface FundamentalsSection {
14+
file: string
15+
uri: string
16+
name: string
17+
description: string
18+
mimeType: string
19+
}
20+
21+
export const NEXTJS_FUNDAMENTALS_SECTIONS: FundamentalsSection[] = [
22+
{
23+
file: "01-use-client.md",
24+
uri: "nextjs-fundamentals://knowledge/use-client",
25+
name: "Understanding 'use client' Directive",
26+
description:
27+
"Learn when and why to use 'use client' in Server Components. Includes: when to mark components as client-only, why props must be serializable (functions/classes can't cross server-client boundary), composition patterns (moving 'use client' down tree, passing Server Components as children, context providers), common anti-patterns and fixes (wrapping entire app, passing functions from server), Server Actions as alternative to callbacks, React hooks that require 'use client', browser APIs, optimization checklist, and decision tree for Server vs Client components. Use this when confused about server/client boundaries, serialization errors, hydration issues, or deciding where to add 'use client'",
28+
mimeType: "text/markdown",
29+
},
30+
]
31+
32+
/**
33+
* Convert a fundamentals section definition to an MCP Resource
34+
*/
35+
export function fundamentalsSectionToResource(section: FundamentalsSection): Resource {
36+
return {
37+
uri: section.uri,
38+
name: section.name,
39+
description: section.description,
40+
mimeType: section.mimeType,
41+
}
42+
}
43+
44+
/**
45+
* Get all available Next.js fundamentals resources
46+
*/
47+
export function getNextjsFundamentalsResources(): Resource[] {
48+
return NEXTJS_FUNDAMENTALS_SECTIONS.map(fundamentalsSectionToResource)
49+
}
50+
51+
/**
52+
* Read the content of a specific fundamentals section
53+
*/
54+
export function readFundamentalsSection(uri: string): string {
55+
const section = NEXTJS_FUNDAMENTALS_SECTIONS.find((s) => s.uri === uri)
56+
57+
if (!section) {
58+
throw new Error(`Unknown fundamentals section: ${uri}`)
59+
}
60+
61+
const filePath = join(__dirname, "nextjs-fundamentals-knowledge", section.file)
62+
return readFileSync(filePath, "utf-8")
63+
}
64+
65+
/**
66+
* Check if a URI is a Next.js fundamentals knowledge resource
67+
*/
68+
export function isNextjsFundamentalsUri(uri: string): boolean {
69+
return uri.startsWith("nextjs-fundamentals://knowledge/")
70+
}

0 commit comments

Comments
 (0)