Skip to main content
Glama

search_hadith

Search Islamic Hadith collections by keywords or phrases to find teachings on specific topics like prayer, charity, or manners across major collections including Bukhari and Muslim.

Instructions

Search Hadith collections by keywords or phrases. Find hadiths about specific topics without knowing exact hadith numbers. Search across all major collections (Bukhari, Muslim, Abu Dawud, Tirmidhi, Nasai, Ibn Majah) or specific ones. Perfect for queries like "prayer times", "charity", "manners", etc.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
queryYesSearch query - keywords or phrases to search for in hadiths. Minimum 2 characters.
collectionsNoSpecific collections to search in (optional). If not specified, searches all collections. Options: bukhari, muslim, abudawud, tirmidhi, nasai, ibnmajah
max_resultsNoMaximum number of results to return (default: 20, max: 50)

Implementation Reference

  • The primary handler function implementing the search_hadith tool. Performs advanced fuzzy keyword search across specified Hadith collections using relevance scoring, Levenshtein distance for similarity, Arabic normalization, parallel API calls, and intelligent sampling for efficient results.
    export async function searchHadith(
      query: string,
      collections?: string[],
      maxResults: number = 20
    ): Promise<HadithSearchResult[]> {
      if (!query || query.trim().length < 2) {
        throw new QuranMCPError(
          'Search query must be at least 2 characters long',
          'INVALID_SEARCH_QUERY'
        );
      }
    
      // Validate collections
      const searchCollections = collections && collections.length > 0
        ? collections
        : HADITH_COLLECTIONS.map(c => c.slug);
    
      for (const col of searchCollections) {
        if (!HADITH_COLLECTIONS.find(c => c.slug === col)) {
          throw new QuranMCPError(
            `Unknown collection: ${col}. Available: ${HADITH_COLLECTIONS.map(c => c.slug).join(', ')}`,
            'INVALID_COLLECTION'
          );
        }
      }
    
      const cacheKey = `search:hadith:${searchCollections.join(',')}:${query}:${maxResults}`;
    
      return searchCacheService.getOrSet(cacheKey, async () => {
        const results: HadithSearchResult[] = [];
        const keywords = query.toLowerCase().split(/\s+/).filter(k => k.length > 1);
        const isArabicQuery = isArabic(query);
    
        // AGGRESSIVE optimization: Much smaller sample size, more parallel requests
        const SAMPLE_SIZE = 50; // Reduced from 200 to 50 for speed
        const PARALLEL_REQUESTS = 10; // Increased from 5 to 10 for speed
        const MAX_COLLECTIONS = 2; // Only search first 2 collections by default
    
        // Limit collections for performance
        const limitedCollections = searchCollections.slice(0, MAX_COLLECTIONS);
    
        // Process all collections in parallel
        const collectionPromises = limitedCollections.map(async (collectionSlug) => {
          const collectionInfo = HADITH_COLLECTIONS.find(c => c.slug === collectionSlug);
          if (!collectionInfo) return [];
    
          const collectionResults: HadithSearchResult[] = [];
          const sampleSize = Math.min(SAMPLE_SIZE, collectionInfo.totalHadiths);
          const step = Math.max(1, Math.floor(collectionInfo.totalHadiths / sampleSize));
    
          // Create array of hadith numbers to search
          const hadithNumbers: number[] = [];
          for (let i = 1; i <= collectionInfo.totalHadiths && hadithNumbers.length < sampleSize; i += step) {
            hadithNumbers.push(i);
          }
    
          // Process ALL hadiths in parallel batches
          for (let i = 0; i < hadithNumbers.length; i += PARALLEL_REQUESTS) {
            const batch = hadithNumbers.slice(i, i + PARALLEL_REQUESTS);
    
            const batchPromises = batch.map(async (hadithNum) => {
              try {
                // Fetch Arabic if query is Arabic, otherwise fetch English
                const lang = isArabicQuery ? 'ara' : 'eng';
                const url = `${API_ENDPOINTS.HADITH}/${lang}-${collectionSlug}/${hadithNum}.json`;
    
                const data = await fetchJSON<any>(url);
                if (!data) return null;
    
                // Handle the actual API structure: { metadata: {...}, hadiths: [{text: "..."}] }
                let text = '';
                let book = undefined;
                let chapter = undefined;
    
                if (data.hadiths && Array.isArray(data.hadiths) && data.hadiths.length > 0) {
                  // Find the hadith with matching number
                  const hadith = data.hadiths.find((h: any) => h.hadithnumber === hadithNum) || data.hadiths[0];
                  text = hadith.text || '';
                  book = hadith.reference?.book;
                  chapter = data.metadata?.section?.[book];
                } else {
                  // Fallback to old structure
                  text = data.text || data.hadith || '';
                  book = data.book;
                  chapter = data.chapter || data.chapterName;
                }
    
                if (!text || text.trim().length === 0) return null;
    
                const { score, matchType } = calculateRelevance(text, keywords);
    
                if (score > 0) {
                  return {
                    hadithNumber: hadithNum,
                    collection: collectionSlug,
                    collectionName: collectionInfo.name,
                    text,
                    book,
                    chapter,
                    relevanceScore: score,
                    matchType,
                  };
                }
              } catch {
                // Silently fail and continue
              }
              return null;
            });
    
            const batchResults = await Promise.all(batchPromises);
            collectionResults.push(...batchResults.filter(r => r !== null) as HadithSearchResult[]);
    
            // Early exit if we have enough high-quality results for this collection
            if (collectionResults.filter(r => r.matchType === 'exact').length >= maxResults) {
              break;
            }
          }
    
          return collectionResults;
        });
    
        // Wait for all collections to finish
        const allCollectionResults = await Promise.all(collectionPromises);
        results.push(...allCollectionResults.flat());
    
        // Sort by relevance and match type
        const sorted = results.sort((a, b) => {
          // Prioritize exact matches
          if (a.matchType === 'exact' && b.matchType !== 'exact') return -1;
          if (b.matchType === 'exact' && a.matchType !== 'exact') return 1;
          // Then by score
          return b.relevanceScore - a.relevanceScore;
        });
    
        const finalResults = sorted.slice(0, maxResults);
    
        // Add helpful message if no results or only fuzzy matches
        if (finalResults.length === 0) {
          console.log(`No hadiths found for "${query}". Try different keywords or check spelling.`);
        } else if (finalResults.every(r => r.matchType !== 'exact')) {
          console.log(`No exact matches for "${query}". Showing ${finalResults.length} similar hadiths.`);
        }
    
        return finalResults;
      });
  • JSON Schema definition for the search_hadith tool input validation, including parameters query, collections (array of strings), and max_results.
    {
      name: 'search_hadith',
      description: 'Search Hadith collections by keywords or phrases. Find hadiths about specific topics without knowing exact hadith numbers. Search across all major collections (Bukhari, Muslim, Abu Dawud, Tirmidhi, Nasai, Ibn Majah) or specific ones. Perfect for queries like "prayer times", "charity", "manners", etc.',
      inputSchema: {
        type: 'object',
        properties: {
          query: {
            type: 'string',
            description: 'Search query - keywords or phrases to search for in hadiths. Minimum 2 characters.',
          },
          collections: {
            type: 'array',
            description: 'Specific collections to search in (optional). If not specified, searches all collections. Options: bukhari, muslim, abudawud, tirmidhi, nasai, ibnmajah',
            items: {
              type: 'string',
              enum: ['bukhari', 'muslim', 'abudawud', 'tirmidhi', 'nasai', 'ibnmajah'],
            },
          },
          max_results: {
            type: 'number',
            description: 'Maximum number of results to return (default: 20, max: 50)',
            default: 20,
            maximum: 50,
          },
        },
        required: ['query'],
      },
    },
  • Registration and dispatch logic in the shared tool executor. Handles tool calls for 'search_hadith' by extracting arguments and invoking the searchHadith handler.
    case 'search_hadith': {
      const { query, collections, max_results = 20 } = args;
      result = await searchHadith(query, collections, max_results);
      break;
  • MCP tools/list handler in HTTP server that returns the full tools list including search_hadith.
    function handleMcpToolsList(id: string | number | null): JsonRpcResponse {
      return createJsonRpcResponse(id, {
        tools: tools,
      });
    }
  • src/index.ts:38-40 (registration)
    MCP tools/list handler in stdio server that registers all tools including search_hadith.
    server.setRequestHandler(ListToolsRequestSchema, async () => {
      return { tools };
    });

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/Prince77-7/quranMCP'

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