docs_list
List all available documentation files from installed @particle-academy packages. Optionally filter by package name to narrow results.
Instructions
List doc file paths. Each entry is the path you pass to docs_read. Optionally filter to one package.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| package | No | Package name (e.g. '@particle-academy/react-fancy'). Omit to list all. |
Implementation Reference
- src/tools.ts:65-85 (handler)The handler function for the 'docs_list' tool. It filters the cached scan results by an optional 'package' argument, then returns a formatted list of doc file paths with package name, version, path, and byte size.
(args) => { const pkgFilter = typeof args.package === "string" ? args.package : undefined; const filtered = pkgFilter ? cache.filter((p) => p.name === pkgFilter) : cache; if (filtered.length === 0) { return errorResult( pkgFilter ? `No docs found for package '${pkgFilter}'. Call docs_list_packages to see available packages.` : "No docs found.", ); } const rows = filtered.flatMap((p) => p.files.map((f) => ({ package: p.name, version: p.version, path: f.path, bytes: f.bytes, })), ); const lines = rows.map((r) => `${r.package}@${r.version} ${r.path} (${r.bytes}B)`); return textResult(lines.join("\n"), rows as unknown as JsonObject[]); }, - src/tools.ts:55-63 (schema)The input schema for 'docs_list'. Defines a single optional 'package' string property that filters results to a specific package.
inputSchema: { type: "object", properties: { package: { type: "string", description: "Package name (e.g. '@particle-academy/react-fancy'). Omit to list all.", }, }, }, - src/tools.ts:50-86 (registration)Registration of the 'docs_list' tool via server.registerTool(), linking its name, description, inputSchema, and handler function.
server.registerTool( { name: "docs_list", description: "List doc file paths. Each entry is the path you pass to docs_read. Optionally filter to one package.", inputSchema: { type: "object", properties: { package: { type: "string", description: "Package name (e.g. '@particle-academy/react-fancy'). Omit to list all.", }, }, }, }, (args) => { const pkgFilter = typeof args.package === "string" ? args.package : undefined; const filtered = pkgFilter ? cache.filter((p) => p.name === pkgFilter) : cache; if (filtered.length === 0) { return errorResult( pkgFilter ? `No docs found for package '${pkgFilter}'. Call docs_list_packages to see available packages.` : "No docs found.", ); } const rows = filtered.flatMap((p) => p.files.map((f) => ({ package: p.name, version: p.version, path: f.path, bytes: f.bytes, })), ); const lines = rows.map((r) => `${r.package}@${r.version} ${r.path} (${r.bytes}B)`); return textResult(lines.join("\n"), rows as unknown as JsonObject[]); }, ); - src/scanner.ts:50-98 (helper)The scan() function in scanner.ts produces the PackageDocs[] cache that the docs_list handler reads from. It discovers packages with docs/ directories and README.md files.
export function scan(opts: ScanOptions = {}): PackageDocs[] { const cwd = resolve(opts.cwd ?? process.cwd()); const scopes = opts.scopes ?? DEFAULT_SCOPES; const allowName = (name: string) => { if (opts.packages && opts.packages.length > 0) { return opts.packages.includes(name); } if (scopes.length === 0) { return opts.includeUnscoped || name.startsWith("@"); } if (name.startsWith("@")) { const scope = name.split("/")[0]; return scopes.includes(scope); } return Boolean(opts.includeUnscoped); }; const found = new Map<string, PackageDocs>(); // Installed packages (node_modules) const nm = join(cwd, "node_modules"); if (existsSync(nm)) { for (const dir of listPackageDirs(nm)) { if (!allowName(dir.name)) continue; const pkg = readPackage(dir.absolutePath, "node_modules"); if (pkg) found.set(pkg.name, pkg); } } // Workspace packages (monorepo) — these win over node_modules with the // same name because in-tree docs are the source of truth during dev. if (opts.includeWorkspace !== false) { const pkgsRoot = join(cwd, "packages"); if (existsSync(pkgsRoot)) { for (const entry of readdirSafe(pkgsRoot)) { const abs = join(pkgsRoot, entry); if (!isDir(abs)) continue; const pkgJsonPath = join(abs, "package.json"); if (!existsSync(pkgJsonPath)) continue; const meta = readPackageJson(pkgJsonPath); if (!meta || !allowName(meta.name)) continue; const pkg = collectDocs(abs, meta.name, meta.version, "workspace"); if (pkg) found.set(pkg.name, pkg); } } } return [...found.values()].sort((a, b) => a.name.localeCompare(b.name)); }