Skip to main content
Glama

searchNips

Search through Nostr Implementation Possibilities (NIPs) to find protocol specifications and standards for the Nostr network.

Instructions

Search through Nostr Implementation Possibilities (NIPs)

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
queryYesSearch query to find relevant NIPs
limitNoMaximum number of results to return
includeContentNoWhether to include the full content of each NIP in the results

Implementation Reference

  • Core handler function that implements the NIP search logic: loads/caches NIPs from GitHub, builds search indexes, handles direct NIP lookups, performs term-based relevance scoring, and returns top results.
    export async function searchNips(query: string, limit: number = 10): Promise<NipSearchResult[]> { // Ensure we have NIPs data and the search index is built const nips = await getNips(); if (nips.length === 0) { console.error("No NIPs available for search"); console.error('Completed searchNips with no results'); return []; } // Handle direct NIP number search as a special case (fastest path) const nipNumberMatch = query.match(/^(?:NIP-?)?(\d+)$/i); if (nipNumberMatch) { const nipNumber = parseInt(nipNumberMatch[1], 10); const directNip = nips.find(nip => nip.number === nipNumber); if (directNip) { console.error('Completed searchNips with direct match'); return [{ nip: directNip, relevance: 100, matchedTerms: [nipNumber.toString()] }]; } } // Split query into terms and filter out empty strings const searchTerms = query.split(/\s+/).filter(term => term.length > 0); // If the search terms are too short or common, warn about potential slow search if (searchTerms.some(term => term.length < 3)) { console.error('Search includes very short terms which may slow down the search'); } // Search through all NIPs efficiently const results: NipSearchResult[] = []; // Pre-filter NIPs that might be relevant based on fast checks // This avoids scoring every NIP for performance const potentialMatches = new Set<number>(); // First do a quick scan to find potential matches for (const term of searchTerms) { const lowerTerm = term.toLowerCase(); // Number match if (searchIndex.numberIndex.has(lowerTerm)) { potentialMatches.add(searchIndex.numberIndex.get(lowerTerm)!); } // Title matches const titleMatches = searchIndex.titleIndex.get(lowerTerm); if (titleMatches) { titleMatches.forEach(num => potentialMatches.add(num)); } // Description matches const descMatches = searchIndex.descriptionIndex.get(lowerTerm); if (descMatches) { descMatches.forEach(num => potentialMatches.add(num)); } // Content matches only if we have few potential matches so far if (potentialMatches.size < 50) { const contentMatches = searchIndex.contentIndex.get(lowerTerm); if (contentMatches) { contentMatches.forEach(num => potentialMatches.add(num)); } } // If we have too many potential matches, don't add more from content if (potentialMatches.size > 100) { break; } } // If no potential matches through indexing, do a linear scan if (potentialMatches.size === 0) { // Fallback: check titles directly for (const nip of nips) { for (const term of searchTerms) { if (nip.title.toLowerCase().includes(term.toLowerCase())) { potentialMatches.add(nip.number); break; } } } } // Score only the potential matches for (const nipNumber of potentialMatches) { const nip = nips.find(n => n.number === nipNumber); if (!nip) continue; const { score, matchedTerms } = calculateRelevance(nip, searchTerms); if (score > 0) { results.push({ nip, relevance: score, matchedTerms }); } } // Sort by relevance and limit results results.sort((a, b) => b.relevance - a.relevance); const limitedResults = results.slice(0, limit); return limitedResults; }
  • index.ts:891-940 (registration)
    MCP tool registration using server.tool(): defines name, description, input schema (query, limit, includeContent), and wrapper handler that delegates to core searchNips and formats output.
    server.tool( "searchNips", "Search through Nostr Implementation Possibilities (NIPs)", { query: z.string().describe("Search query to find relevant NIPs"), limit: z.number().min(1).max(50).default(10).describe("Maximum number of results to return"), includeContent: z.boolean().default(false).describe("Whether to include the full content of each NIP in the results"), }, async ({ query, limit, includeContent }) => { try { console.error(`Searching NIPs for: "${query}"`); const results = await searchNips(query, limit); if (results.length === 0) { return { content: [ { type: "text", text: `No NIPs found matching "${query}". Try different search terms or check the NIPs repository for the latest updates.`, }, ], }; } // Format results using the new formatter const formattedResults = results.map(result => formatNipResult(result, includeContent)).join("\n\n"); return { content: [ { type: "text", text: `Found ${results.length} matching NIPs:\n\n${formattedResults}`, }, ], }; } catch (error) { console.error("Error searching NIPs:", error); return { content: [ { type: "text", text: `Error searching NIPs: ${error instanceof Error ? error.message : "Unknown error"}`, }, ], }; } }, );
  • Zod input schema validation for the searchNips tool parameters.
    { query: z.string().describe("Search query to find relevant NIPs"), limit: z.number().min(1).max(50).default(10).describe("Maximum number of results to return"), includeContent: z.boolean().default(false).describe("Whether to include the full content of each NIP in the results"), },
  • Helper function to format individual NIP search results into readable output strings.
    export function formatNipResult(result: NipSearchResult, includeContent: boolean = false): string { const { nip, relevance, matchedTerms } = result; const lines = [ `NIP-${nip.number}: ${nip.title}`, `Status: ${nip.status}`, nip.kind ? `Kind: ${nip.kind}` : null, `Description: ${nip.description}`, `Relevance Score: ${relevance}`, matchedTerms.length > 0 ? `Matched Terms: ${matchedTerms.join(", ")}` : null, ].filter(Boolean); if (includeContent) { lines.push("", "Content:", nip.content); } lines.push("---"); return lines.join("\n"); }
  • Type definition for NIP search results used throughout the implementation.
    export interface NipSearchResult { nip: NipData; relevance: number; matchedTerms: string[]; }

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/AustinKelsay/nostr-mcp-server'

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