search_nodes
Find knowledge graph nodes by searching entity names, types, subdomains, and content using keywords with OR matching.
Instructions
Search for nodes in the knowledge graph based on one or more keywords. The search covers entity names, types, subdomains, and observation content. Multiple keywords are treated as OR conditions, where any keyword must match somewhere in the entity's fields.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | Space-separated keywords to match against entity fields. Any keyword must match (OR condition). Example: 'budget management' will find entities where either 'budget' or 'management' appears in any field. |
Implementation Reference
- index.ts:221-318 (handler)The main handler function `searchNodes` in the `KnowledgeGraphManager` class. It implements the core logic for searching the knowledge graph: normalizes query to lowercase keywords, filters entities where any keyword matches (OR) in name, entityType, subdomain, or observations, includes related relations, with extensive debug logging.async searchNodes(query: string): Promise<KnowledgeGraph> { const graph = await this.loadGraph(); // Normalize query by converting to lowercase first const normalizedQuery = query.toLowerCase(); console.error(`[Debug] Original query: "${query}"`); console.error(`[Debug] Normalized query: "${normalizedQuery}"`); // Split into keywords and filter out empty strings const keywords = normalizedQuery .split(/[\s,&+]+/) // Split on whitespace and common separators .filter(k => k.length > 0); if (keywords.length === 0) { console.error(`[Debug] No valid keywords found in query: "${query}"`); return { entities: [], relations: [] }; } console.error(`[Debug] Keywords (${keywords.length}): ${JSON.stringify(keywords)}`); console.error(`[Debug] Total entities before filter: ${graph.entities.length}`); const filteredEntities = graph.entities.filter(e => { // Prepare searchable fields const searchableFields = { name: e.name.toLowerCase(), type: e.entityType.toLowerCase(), subdomain: e.subdomain?.toLowerCase() || '', observations: e.observations.map(o => o.toLowerCase()) }; console.error(`[Debug] Checking entity: ${e.name}`); console.error(`[Debug] Searchable fields:`, searchableFields); // Check each keyword against all fields (OR condition) const keywordMatches = keywords.map(keyword => { const nameMatch = searchableFields.name.includes(keyword); const typeMatch = searchableFields.type.includes(keyword); const subdomainMatch = searchableFields.subdomain.includes(keyword); const observationMatch = searchableFields.observations.some(o => o.includes(keyword)); const matches = { keyword, nameMatch, typeMatch, subdomainMatch, observationMatch, anyMatch: nameMatch || typeMatch || subdomainMatch || observationMatch }; if (matches.anyMatch) { console.error(`[Debug] Keyword "${keyword}" matched:`, { name: nameMatch ? searchableFields.name : false, type: typeMatch ? searchableFields.type : false, subdomain: subdomainMatch ? searchableFields.subdomain : false, observations: observationMatch ? searchableFields.observations.filter(o => o.includes(keyword)) : false }); } else { console.error(`[Debug] Keyword "${keyword}" did not match any fields`); } return matches.anyMatch; }); // Entity matches if ANY keyword matches (OR condition) const hasMatch = keywordMatches.some(match => match); console.error(`[Debug] Entity "${e.name}" final result: ${hasMatch} (matched ${keywordMatches.filter(m => m).length}/${keywords.length} keywords)`); return hasMatch; }); console.error(`[Debug] Total entities after filter: ${filteredEntities.length}`); if (filteredEntities.length > 0) { console.error(`[Debug] Matched entities:`, filteredEntities.map(e => ({ name: e.name, type: e.entityType, subdomain: e.subdomain, observations: e.observations }))); } else { console.error(`[Debug] No entities matched the search criteria`); console.error(`[Debug] Available entities:`, graph.entities.map(e => ({ name: e.name, type: e.entityType, subdomain: e.subdomain, observations: e.observations }))); } const filteredEntityNames = new Set(filteredEntities.map(e => e.name)); const filteredRelations = graph.relations.filter(r => filteredEntityNames.has(r.from) && filteredEntityNames.has(r.to) ); return { entities: filteredEntities, relations: filteredRelations }; }
- index.ts:511-520 (schema)The input schema for the 'search_nodes' tool, defining a required 'query' string parameter with description explaining the OR keyword matching behavior.inputSchema: { type: "object", properties: { query: { type: "string", description: "Space-separated keywords to match against entity fields. Any keyword must match (OR condition). Example: 'budget management' will find entities where either 'budget' or 'management' appears in any field." }, }, required: ["query"], },
- index.ts:508-521 (registration)Registration of the 'search_nodes' tool in the ListToolsRequestSchema handler, including name, description, and input schema.{ name: "search_nodes", description: "Search for nodes in the knowledge graph based on one or more keywords. The search covers entity names, types, subdomains, and observation content. Multiple keywords are treated as OR conditions, where any keyword must match somewhere in the entity's fields.", inputSchema: { type: "object", properties: { query: { type: "string", description: "Space-separated keywords to match against entity fields. Any keyword must match (OR condition). Example: 'budget management' will find entities where either 'budget' or 'management' appears in any field." }, }, required: ["query"], }, },
- index.ts:570-572 (registration)Dispatch case in the CallToolRequestSchema handler that invokes the searchNodes handler with the query argument and returns JSON response.case "search_nodes": return createResponse(JSON.stringify(await knowledgeGraphManager.searchNodes(args.query as string), null, 2)); case "open_nodes":
- index.ts:50-52 (schema)Output type definition for the search_nodes tool, representing the returned knowledge graph structure with entities and relations.interface KnowledgeGraph { entities: Entity[]; relations: Relation[];