handbook-frames
Decompose web pages into independent segments for scoped navigation, lazy loading, and cache optimization using Turbo Frames.
Instructions
Turbo Frames for page decomposition - covers independent page segments, scoped navigation, eager/lazy loading, cache benefits, targeting navigation, frame promotion to visits, and custom rendering
Tool call template
This tool declares no required parameters. Add optional parameters from the input schema as needed.
{
"id": 1,
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"arguments": {},
"name": "handbook-frames"
}
}Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Implementation Reference
- src/index.ts:16-45 (registration)Registers all documentation tools, including 'handbook-frames', using server.tool() with an inline handler that reads the specific markdown file and returns it as text content block.
// Register a tool for each documentation file docFiles.forEach(({ folder, file, name, description }) => { server.tool( name, description, async () => { try { const content = await readMarkdownFile(path.join(folder, file)); return { content: [ { type: "text", text: content } ] }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { content: [ { type: "text", text: `Error reading ${file}: ${errorMessage}` } ] }; } } ); }); - src/documentReader.ts:14-61 (handler)Core handler logic invoked by the tool: reads the markdown file 'handbook/04_frames.md' from cache, GitHub, or local fallback, returning the content as string.
export async function readMarkdownFile(filename: string): Promise<string> { const filePath = path.join(docsFolder, filename); if (!filePath.startsWith(docsFolder)) { throw new Error("Invalid file path"); } // Get current commit info if we don't have it yet if (!mainBranchInfo) { try { const commitInfo = await fetchMainBranchInformation(); const cacheKey = `${commitInfo.sha.substring(0, 7)}-${commitInfo.timestamp}`; mainBranchInfo = { ...commitInfo, cacheKey }; } catch (shaError) { console.error('Failed to get GitHub commit info, falling back to direct fetch'); } } // Try to read from cache first if we have commit info if (mainBranchInfo) { const cachedFilePath = path.join(cacheFolder, mainBranchInfo.cacheKey, filename); try { const content = await fs.promises.readFile(cachedFilePath, "utf-8"); console.error(`Using cached content for ${mainBranchInfo.cacheKey}: ${filename}`); return content; } catch (cacheError) { // Cache miss, continue to fetch from GitHub } } // Fetch from GitHub try { return await fetchFromGitHub(filename, mainBranchInfo?.cacheKey); } catch (githubError) { console.error(`GitHub fetch failed: ${githubError}, attempting to read from local files...`); // Fallback: read from local files try { return await fs.promises.readFile(filePath, "utf-8"); } catch (localError) { const githubErrorMessage = githubError instanceof Error ? githubError.message : String(githubError); const localErrorMessage = localError instanceof Error ? localError.message : String(localError); throw new Error(`Failed to read file from GitHub (${githubErrorMessage}) and locally (${localErrorMessage})`); } } } - src/config.ts:32-37 (registration)Specific configuration for the 'handbook-frames' tool, providing name, description, folder, and file used in the dynamic registration.
{ folder: 'handbook', file: '04_frames.md', name: 'handbook-frames', description: 'Turbo Frames for page decomposition - covers independent page segments, scoped navigation, eager/lazy loading, cache benefits, targeting navigation, frame promotion to visits, and custom rendering' }, - src/utils.ts:50-76 (helper)Helper function to fetch markdown content from GitHub raw URL and cache it, used by readMarkdownFile for 'handbook/04_frames.md'.
export async function fetchFromGitHub(filename: string, cacheKey?: string): Promise<string> { const githubUrl = `${GITHUB_RAW_BASE_URL}/${filename}`; console.error(`Fetching ${filename} from GitHub: ${githubUrl}`); const response = await fetch(githubUrl); if (!response.ok) { throw new Error(`GitHub fetch failed: ${response.status} ${response.statusText}`); } const content = await response.text(); // Cache the content with cache key if available if (cacheKey) { try { const cacheFolder = path.resolve(__dirname, "../cache"); const cachedFilePath = path.join(cacheFolder, cacheKey, filename); await fs.promises.mkdir(path.dirname(cachedFilePath), { recursive: true }); await fs.promises.writeFile(cachedFilePath, content, "utf-8"); console.error(`Cached GitHub content for ${cacheKey}: ${filename}`); } catch (cacheError) { console.error(`Failed to cache content: ${cacheError}`); } } return content; }