Skip to main content
Glama
yodakeisuke

Knowledge Graph Memory Server

by yodakeisuke

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

  • 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 }; }
  • 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":
  • Output type definition for the search_nodes tool, representing the returned knowledge graph structure with entities and relations.
    interface KnowledgeGraph { entities: Entity[]; relations: Relation[];

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