Skip to main content
Glama
pathakkhhimanshu

AI Dev Assistant

doc_search

Search local documentation folders for keywords across markdown, text, JSON, YAML, and code files to find relevant information quickly.

Instructions

Searches all documents in a local docs/ folder (or any directory) for one or more keywords. Supports case-insensitive multi-keyword search across markdown, text, JSON, YAML, code, and other text files. Returns matching lines with surrounding context. Searchable extensions: .md, .txt, .rst, .html, .json, .yaml, .ts, .js, .py, .sh, .bat, .ps1, and more.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
docs_pathYesAbsolute path to the docs folder to search. Windows example: C:\Users\YourName\Projects\my-repo\docs
keywordsYesList of keywords to search for. All keywords must appear on the same line (AND logic).
case_sensitiveNoWhether the search is case-sensitive. Default: false.
file_extension_filterNoOptional: only search files with this extension (e.g., '.md', '.txt'). Leave blank to search all supported file types.

Implementation Reference

  • The handler implementation for the doc_search tool. It performs directory traversal, file filtering, and line-by-line keyword matching.
    async handler(args: {
      docs_path: string;
      keywords: string[];
      case_sensitive?: boolean;
      file_extension_filter?: string;
    }): Promise<string> {
      const { keywords, case_sensitive = false, file_extension_filter } = args;
      const docsPath = path.resolve(args.docs_path);
    
      if (!fs.existsSync(docsPath)) {
        return `ERROR: Docs path does not exist: ${docsPath}`;
      }
      if (!fs.statSync(docsPath).isDirectory()) {
        return `ERROR: Path is not a directory: ${docsPath}`;
      }
      if (!keywords.length || keywords.some((k) => !k.trim())) {
        return `ERROR: Please provide at least one non-empty keyword.`;
      }
    
      let files = walkDirectory(docsPath);
    
      if (file_extension_filter) {
        const ext = file_extension_filter.startsWith(".")
          ? file_extension_filter.toLowerCase()
          : `.${file_extension_filter.toLowerCase()}`;
        files = files.filter((f) => path.extname(f).toLowerCase() === ext);
      }
    
      if (files.length === 0) {
        return `No searchable files found in: ${docsPath}`;
      }
    
      const results: SearchResult[] = [];
      let totalMatches = 0;
    
      for (const file of files) {
        const matches = searchFileForKeywords(file, keywords, case_sensitive);
        if (matches.length > 0) {
          results.push({
            file: path.relative(docsPath, file).replace(/\\/g, "/"),
            matchCount: matches.length,
            matches,
          });
          totalMatches += matches.length;
          if (totalMatches >= MAX_RESULTS) break;
        }
      }
    
      if (results.length === 0) {
        return (
          `## Doc Search Results\n` +
          `**Query:** \`${keywords.join(" AND ")}\`  |  **Directory:** ${docsPath}\n\n` +
          `No matches found across ${files.length} files.`
        );
      }
    
      const sections: string[] = [
        `## Doc Search Results`,
        `**Query:** \`${keywords.join(" AND ")}\`  |  **Case Sensitive:** ${case_sensitive}`,
        `**Directory:** ${docsPath}`,
        `**Files searched:** ${files.length}  |  **Files with matches:** ${results.length}  |  **Total matches:** ${totalMatches}${totalMatches >= MAX_RESULTS ? " (capped)" : ""}`,
      ];
    
      for (const result of results) {
        sections.push(`\n### 📄 ${result.file}  *(${result.matchCount} match${result.matchCount !== 1 ? "es" : ""})*`);
    
        for (const match of result.matches) {
          const contextLines: string[] = [];
    
          if (match.contextBefore.length > 0) {
            match.contextBefore.forEach((line, i) => {
              const lineNum = match.lineNumber - match.contextBefore.length + i;
              contextLines.push(`  ${String(lineNum).padStart(4)} │ ${line}`);
            });
          }
    
          contextLines.push(`▶ ${String(match.lineNumber).padStart(4)} │ ${match.line}`);
    
          if (match.contextAfter.length > 0) {
            match.contextAfter.forEach((line, i) => {
              const lineNum = match.lineNumber + i + 1;
              contextLines.push(`  ${String(lineNum).padStart(4)} │ ${line}`);
            });
          }
    
          sections.push(`\`\`\`\n${contextLines.join("\n")}\n\`\`\``);
        }
      }
    
      return sections.join("\n");
    },
  • The JSON schema for the inputs required by the doc_search tool.
    inputSchema: {
      type: "object",
      properties: {
        docs_path: {
          type: "string",
          description:
            "Absolute path to the docs folder to search. " +
            "Windows example: C:\\Users\\YourName\\Projects\\my-repo\\docs",
        },
        keywords: {
          type: "array",
          items: { type: "string" },
          description:
            "List of keywords to search for. All keywords must appear on the same line (AND logic).",
        },
        case_sensitive: {
          type: "boolean",
          description: "Whether the search is case-sensitive. Default: false.",
        },
        file_extension_filter: {
          type: "string",
          description:
            "Optional: only search files with this extension (e.g., '.md', '.txt'). " +
            "Leave blank to search all supported file types.",
        },
      },
      required: ["docs_path", "keywords"],
    },
  • src/index.ts:34-34 (registration)
    Registration of the doc_search tool in the main index file.
    docSearchTool(),

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/pathakkhhimanshu/MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server