Skip to main content
Glama
yodakeisuke

Knowledge Graph Memory Server

by yodakeisuke

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
NameRequiredDescriptionDefault
queryYesSpace-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

  • 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 }; }
  • 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":
  • 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"], }, },

Other Tools

Related 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/yodakeisuke/mcp-memory-domain-knowledge'

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