handbook-external
Learn techniques for integrating external APIs and resources, including AJAX requests, third-party libraries, and asynchronous operations, for efficient development workflows.
Instructions
Techniques for integrating external APIs and resources - covers AJAX requests, working with third-party libraries, and asynchronous operations
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Implementation Reference
- src/index.ts:16-45 (registration)Dynamically registers the 'handbook-external' tool (and others) by iterating over docFiles config and calling server.tool() with name, description, and an async handler that fetches the corresponding markdown file content using readMarkdownFile and returns it as MCP 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/config.ts:52-56 (schema)Configuration entry defining the metadata for the 'handbook-external' tool: folder, file path, name, and description used for registration.folder: 'handbook', file: '06_working_with_external_resources.md', name: 'handbook-external', description: 'Techniques for integrating external APIs and resources - covers AJAX requests, working with third-party libraries, and asynchronous operations' },
- src/documentReader.ts:14-61 (handler)Core handler function that implements the tool logic: reads the markdown file for 'handbook-external' from cache, fetches from GitHub raw content, or falls back to local file, invoked by the registered tool handler.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/utils.ts:23-42 (helper)Helper function to fetch the current main branch commit SHA and timestamp from GitHub API, used for caching in readMarkdownFile.export async function fetchMainBranchInformation(): Promise<{sha: string, timestamp: number}> { try { const response = await fetch(`${GITHUB_API_BASE_URL}/commits/${MAIN_BRANCH}`); if (!response.ok) { throw new Error(`GitHub API failed: ${response.status} ${response.statusText}`); } const data = await response.json(); // Get the commit timestamp (author date) const commitTimestamp = new Date(data.commit.author.date).getTime(); return { sha: data.sha, timestamp: commitTimestamp }; } catch (error) { console.error('Failed to get GitHub SHA:', error); throw error; } }
- src/utils.ts:50-74 (helper)Helper function to fetch markdown content from GitHub raw URL and cache it, primary source for the tool content.export async function fetchFromGitHub(filename: string, cacheKey?: string): Promise<string> { const githubUrl = `${GITHUB_RAW_BASE_URL}/${filename}`; 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; }