deepwiki_fetch
Fetch and convert Deepwiki documentation into Markdown for individual or multiple sites. Specify URL, depth, and output mode to retrieve structured content from repositories.
Instructions
Fetch a deepwiki.com repo and return Markdown
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| maxDepth | No | Can fetch a single site => maxDepth 0 or multiple/all sites => maxDepth 1 | |
| mode | No | aggregate | |
| url | Yes | should be a URL, owner/repo name (e.g. "vercel/ai"), a two-word "owner repo" form (e.g. "vercel ai"), or a single library keyword | |
| verbose | No |
Implementation Reference
- src/tools/deepwiki.ts:19-131 (handler)The core handler logic for the 'deepwiki_fetch' tool: normalizes and validates input, crawls the deepwiki.com repository, converts HTML to Markdown, and structures the response as content array.async (input) => { // Normalize the URL to support short forms const normalizedInput = { ...input } if (typeof normalizedInput.url === 'string') { let url = normalizedInput.url.trim() // Only transform when it is not already an explicit HTTP(S) URL if (!/^https?:\/\//.test(url)) { // Check if the URL is already in the owner/repo format if (/^[^/]+\/[^/]+$/.test(url)) { // Already in owner/repo format, keep it as is // Just prefix with deepwiki.com } // Single word/term with no slash - process differently else if (/^[^/]+$/.test(url)) { // For single words, first try to extract a meaningful keyword if the input has spaces if (url.includes(' ')) { const extracted = extractKeyword(url) if (extracted) { url = extracted } } // Try to resolve the single term against GitHub try { const repo = await resolveRepo(url) // "owner/repo" url = repo } catch { // Fallback to previous behaviour for backward compatibility url = `defaultuser/${url}` // TODO: replace defaultuser logic } } // Other formats (phrases with slashes that don't match owner/repo) else { // Try to extract a library keyword from a free form phrase const extracted = extractKeyword(url) if (extracted) { // Resolve the extracted keyword try { const repo = await resolveRepo(extracted) url = repo } catch { url = `defaultuser/${extracted}` } } } // At this point url should be "owner/repo" url = `https://deepwiki.com/${url}` } normalizedInput.url = url } const parse = FetchRequest.safeParse(normalizedInput) if (!parse.success) { const err: z.infer<typeof ErrorEnvelope> = { status: 'error', code: 'VALIDATION', message: 'Request failed schema validation', details: parse.error.flatten(), } return err } const req = parse.data const root = new URL(req.url) if (req.maxDepth > 1) { const err: z.infer<typeof ErrorEnvelope> = { status: 'error', code: 'VALIDATION', message: 'maxDepth > 1 is not allowed', } return err } if (root.hostname !== 'deepwiki.com') { const err: z.infer<typeof ErrorEnvelope> = { status: 'error', code: 'DOMAIN_NOT_ALLOWED', message: 'Only deepwiki.com domains are allowed', } return err } // Progress emitter function emitProgress(e: any) { // Progress reporting is not supported in this context because McpServer does not have a sendEvent method. } const crawlResult = await crawl({ root, maxDepth: req.maxDepth, emit: emitProgress, verbose: req.verbose, }) // Convert each page const pages = await Promise.all( Object.entries(crawlResult.html).map(async ([path, html]) => ({ path, markdown: await htmlToMarkdown(html, req.mode), })), ) return { content: pages.map(page => ({ type: 'text', text: `# ${page.path}\n\n${page.markdown}`, })), } },
- src/schemas/deepwiki.ts:9-18 (schema)Input schema (FetchRequest) used for validating the tool parameters: url, maxDepth, mode, verbose.export const FetchRequest = z.object({ /** Deepwiki repo URL, eg https://deepwiki.com/user/repo */ url: z.string().describe('should be a URL, owner/repo name (e.g. "vercel/ai"), a two-word "owner repo" form (e.g. "vercel ai"), or a single library keyword'), /** Crawl depth limit: 0 means only the root page */ maxDepth: z.number().int().min(0).max(1).default(1).describe('Can fetch a single site => maxDepth 0 or multiple/all sites => maxDepth 1'), /** Conversion mode */ mode: ModeEnum.default('aggregate'), /** Verbose logging flag */ verbose: z.boolean().default(false), })
- src/tools/deepwiki.ts:14-133 (registration)Registration of the 'deepwiki_fetch' tool via mcp.tool(), specifying name, description, input schema, and inline handler. This function is called from src/index.ts.export function deepwikiTool({ mcp }: McpToolContext) { mcp.tool( 'deepwiki_fetch', 'Fetch a deepwiki.com repo and return Markdown', FetchRequest.shape, async (input) => { // Normalize the URL to support short forms const normalizedInput = { ...input } if (typeof normalizedInput.url === 'string') { let url = normalizedInput.url.trim() // Only transform when it is not already an explicit HTTP(S) URL if (!/^https?:\/\//.test(url)) { // Check if the URL is already in the owner/repo format if (/^[^/]+\/[^/]+$/.test(url)) { // Already in owner/repo format, keep it as is // Just prefix with deepwiki.com } // Single word/term with no slash - process differently else if (/^[^/]+$/.test(url)) { // For single words, first try to extract a meaningful keyword if the input has spaces if (url.includes(' ')) { const extracted = extractKeyword(url) if (extracted) { url = extracted } } // Try to resolve the single term against GitHub try { const repo = await resolveRepo(url) // "owner/repo" url = repo } catch { // Fallback to previous behaviour for backward compatibility url = `defaultuser/${url}` // TODO: replace defaultuser logic } } // Other formats (phrases with slashes that don't match owner/repo) else { // Try to extract a library keyword from a free form phrase const extracted = extractKeyword(url) if (extracted) { // Resolve the extracted keyword try { const repo = await resolveRepo(extracted) url = repo } catch { url = `defaultuser/${extracted}` } } } // At this point url should be "owner/repo" url = `https://deepwiki.com/${url}` } normalizedInput.url = url } const parse = FetchRequest.safeParse(normalizedInput) if (!parse.success) { const err: z.infer<typeof ErrorEnvelope> = { status: 'error', code: 'VALIDATION', message: 'Request failed schema validation', details: parse.error.flatten(), } return err } const req = parse.data const root = new URL(req.url) if (req.maxDepth > 1) { const err: z.infer<typeof ErrorEnvelope> = { status: 'error', code: 'VALIDATION', message: 'maxDepth > 1 is not allowed', } return err } if (root.hostname !== 'deepwiki.com') { const err: z.infer<typeof ErrorEnvelope> = { status: 'error', code: 'DOMAIN_NOT_ALLOWED', message: 'Only deepwiki.com domains are allowed', } return err } // Progress emitter function emitProgress(e: any) { // Progress reporting is not supported in this context because McpServer does not have a sendEvent method. } const crawlResult = await crawl({ root, maxDepth: req.maxDepth, emit: emitProgress, verbose: req.verbose, }) // Convert each page const pages = await Promise.all( Object.entries(crawlResult.html).map(async ([path, html]) => ({ path, markdown: await htmlToMarkdown(html, req.mode), })), ) return { content: pages.map(page => ({ type: 'text', text: `# ${page.path}\n\n${page.markdown}`, })), } }, ) }
- src/index.ts:30-30 (registration)Invocation of deepwikiTool to register the tool in the main server setup.deepwikiTool({ mcp } as McpToolContext)