Skip to main content
Glama

search_nodes

Search for entities and their relations via text or vector similarity on the MCP server, leveraging libSQL for efficient semantic knowledge storage and retrieval.

Instructions

Search for entities and their relations using text or vector similarity

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
queryYes

Implementation Reference

  • MCP server tool handler for 'search_nodes': validates input, calls db.search_nodes(query, limit), returns JSON result or error.
    server.tool<typeof SearchNodesSchema>(
    	{
    		name: 'search_nodes',
    		description:
    			'Search for entities and their relations using text search with relevance ranking',
    		schema: SearchNodesSchema,
    	},
    	async ({ query, limit }) => {
    		try {
    			const result = await db.search_nodes(query, limit);
    			return {
    				content: [
    					{
    						type: 'text' as const,
    						text: JSON.stringify(result, null, 2),
    					},
    				],
    			};
    		} catch (error) {
    			return {
    				content: [
    					{
    						type: 'text' as const,
    						text: JSON.stringify(
    							{
    								error: 'internal_error',
    								message:
    									error instanceof Error
    										? error.message
    										: 'Unknown error',
    							},
    							null,
    							2,
    						),
    					},
    				],
    				isError: true,
    			};
    		}
    	},
  • Valibot schema definition for search_nodes tool input: query (required string), limit (optional number).
    const SearchNodesSchema = v.object({
    	query: v.string(),
    	limit: v.optional(v.number()),
    });
  • src/index.ts:102-141 (registration)
    Registration of 'search_nodes' tool on the MCP server within setupTools function.
    server.tool<typeof SearchNodesSchema>(
    	{
    		name: 'search_nodes',
    		description:
    			'Search for entities and their relations using text search with relevance ranking',
    		schema: SearchNodesSchema,
    	},
    	async ({ query, limit }) => {
    		try {
    			const result = await db.search_nodes(query, limit);
    			return {
    				content: [
    					{
    						type: 'text' as const,
    						text: JSON.stringify(result, null, 2),
    					},
    				],
    			};
    		} catch (error) {
    			return {
    				content: [
    					{
    						type: 'text' as const,
    						text: JSON.stringify(
    							{
    								error: 'internal_error',
    								message:
    									error instanceof Error
    										? error.message
    										: 'Unknown error',
    							},
    							null,
    							2,
    						),
    					},
    				],
    				isError: true,
    			};
    		}
    	},
  • DatabaseManager.search_nodes: core logic searches entities using text query with relevance, fetches associated relations.
    async search_nodes(
    	query: string,
    	limit?: number,
    ): Promise<{ entities: Entity[]; relations: Relation[] }> {
    	try {
    		// Validate text query
    		if (typeof query !== 'string') {
    			throw new Error('Text query must be a string');
    		}
    		if (query.trim() === '') {
    			throw new Error('Text query cannot be empty');
    		}
    
    		// Text-based search with optional limit
    		const entities = await this.search_entities(query, limit);
    
    		// If no entities found, return empty result
    		if (entities.length === 0) {
    			return { entities: [], relations: [] };
    		}
    
    		const relations = await this.get_relations_for_entities(
    			entities,
    		);
    		return { entities, relations };
    	} catch (error) {
    		throw new Error(
    			`Node search failed: ${
    				error instanceof Error ? error.message : String(error)
    			}`,
    		);
    	}
    }
  • DatabaseManager.search_entities: performs fuzzy text search on entities, types, observations with relevance scoring.
    async search_entities(
    	query: string,
    	limit: number = 10,
    ): Promise<Entity[]> {
    	// Normalize query for fuzzy matching
    	const normalized_query = `%${query.replace(/[\s_-]+/g, '%')}%`;
    
    	const results = await this.client.execute({
    		sql: `
           SELECT DISTINCT
             e.name,
             e.entity_type,
             e.created_at,
             CASE
               WHEN e.name LIKE ? COLLATE NOCASE THEN 3
               WHEN e.entity_type LIKE ? COLLATE NOCASE THEN 2
               ELSE 1
             END as relevance_score
           FROM entities e
           LEFT JOIN observations o ON e.name = o.entity_name
           WHERE e.name LIKE ? COLLATE NOCASE
              OR e.entity_type LIKE ? COLLATE NOCASE
              OR o.content LIKE ? COLLATE NOCASE
           ORDER BY relevance_score DESC, e.created_at DESC
           LIMIT ?
         `,
    		args: [
    			normalized_query,
    			normalized_query,
    			normalized_query,
    			normalized_query,
    			normalized_query,
    			limit > 50 ? 50 : limit, // Cap at 50
    		],
    	});
    
    	const entities: Entity[] = [];
    	for (const row of results.rows) {
    		const name = row.name as string;
    		const observations = await this.client.execute({
    			sql: 'SELECT content FROM observations WHERE entity_name = ?',
    			args: [name],
    		});
    
    		entities.push({
    			name,
    			entityType: row.entity_type as string,
    			observations: observations.rows.map(
    				(obs) => obs.content as string,
    			),
    		});
    	}
    
    	return entities;
    }
Install Server

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/spences10/mcp-memory-libsql'

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