search_nodes
Locate nodes in a knowledge graph by matching keywords across entity names, types, subdomains, or observation content. Input space-separated terms for OR-based search to retrieve relevant entities.
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)Core implementation of the search_nodes tool logic within KnowledgeGraphManager. Filters entities matching any keyword in query across name, entityType, subdomain, or observations (OR condition). Includes related relations.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:570-572 (handler)MCP tool dispatch handler for 'search_nodes' that invokes the KnowledgeGraphManager.searchNodes method and formats the response.case "search_nodes": return createResponse(JSON.stringify(await knowledgeGraphManager.searchNodes(args.query as string), null, 2)); case "open_nodes":
- index.ts:511-520 (schema)Input schema definition for the search_nodes tool, specifying a required 'query' string parameter.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 ListTools response, 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"], }, },