Skip to main content
Glama

open_nodes

Expand entities to retrieve full details, observations, and relations from Memento's offline memory storage.

Instructions

Expand specified entities: return their full details including observations and relations.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
namesYesNames of entities to expand.

Implementation Reference

  • src/server.js:202-219 (registration)
    MCP tool registration for 'open_nodes', including input schema using Zod, description, and handler function that calls knowledgeGraphManager.openNodes and returns JSON response.
    // Tool: open_nodes
    this.tool(
        'open_nodes',
        'Expand specified entities: return their full details including observations and relations.',
        {
            names: z.array(z.string()).describe('Names of entities to expand.')
        },
        async ({ names }) => ({
            content: [{
                type: 'text',
                text: JSON.stringify(
                    await this.#knowledgeGraphManager.openNodes(names),
                    null,
                    2
                )
            }]
        })
    );
  • Zod input schema for the open_nodes tool: array of entity names.
        names: z.array(z.string()).describe('Names of entities to expand.')
    },
  • Knowledge graph manager method that delegates openNodes call to the underlying repository implementation.
    async openNodes(names) {
        return this.#repository.openNodes(names);
    }
  • PostgreSQL repository implementation of openNodes: queries entities by names, their observations, and relations between them, formats into {entities, relations} structure.
    async openNodes(names) {
        if (!names.length) {
            return { entities: [], relations: [] };
        }
    
        /**
         * @type {[{entitytype, id, name}]}
         */
        const entities = await this.#query(
            `SELECT *
             FROM entities
             WHERE name = ANY ($1)`,
            [ names ]
        );
    
        if (!entities.length) {
            return { entities: [], relations: [] };
        }
    
        const ids = entities.map(e => e.id);
    
        const observations = await this.#query(
            `SELECT entity_id, content
             FROM observations
             WHERE entity_id = ANY ($1)`,
            [ ids ]
        );
    
        /**
         * @type {[{name, from_name, to_name, relationtype}]}
         */
        const relations = await this.#query(
            `SELECT r.from_id,
                    r.to_id,
                    r.relationtype,
                    ef.name AS from_name,
                    et.name AS to_name
             FROM relations r
                      JOIN entities ef ON ef.id = r.from_id
                      JOIN entities et ON et.id = r.to_id
             WHERE r.from_id = ANY ($1)
               AND r.to_id = ANY ($1)`,
            [ ids ]
        );
    
        return {
            entities:  entities.map(e => ({
                name:         e.name,
                entityType:   e.entitytype,
                observations: observations
                                  .filter(o => o.entity_id === e.id)
                                  .map(o => o.content)
            })),
            relations: relations.map(rel => ({
                from:         rel.from_name,
                to:           rel.to_name,
                relationType: rel.relationtype
            }))
        };
  • SQLite repository implementation of openNodes: similar to Postgres, queries entities by names, observations, and mutual relations using parameterized queries.
    async openNodes(names) {
        if (!names.length) {
            return { entities: [], relations: [] };
        }
    
        const placeholders = names.map(() => '?').join(',');
        const entities = await this.db.all(
            `SELECT * FROM entities WHERE name IN (${placeholders})`,
            names
        );
    
        if (!entities.length) {
            return { entities: [], relations: [] };
        }
    
        const ids = entities.map(e => e.id);
        const idPlaceholders = ids.map(() => '?').join(',');
        const observations = await this.db.all(
            `SELECT entity_id, content FROM observations WHERE entity_id IN (${idPlaceholders})`,
            ids
        );
        /**
         *
         * @type {[{from_name, to_name, relationType}]}
         */
        const relations = await this.db.all(
            `SELECT r.from_id, r.to_id, r.relationType, ef.name AS from_name, et.name AS to_name
             FROM relations r
                      JOIN entities ef ON ef.id = r.from_id
                      JOIN entities et ON et.id = r.to_id
             WHERE r.from_id IN (${idPlaceholders}) AND r.to_id IN (${idPlaceholders})`,
            [...ids, ...ids]
        );
    
        return {
            entities: entities.map(entity => ({
                name: entity.name,
                entityType: entity.entityType,
                observations: observations
                    .filter(obs => obs.entity_id === entity.id)
                    .map(obs => obs.content)
            })),
            relations: relations.map(relation => ({
                from: relation.from_name,
                to: relation.to_name,
                relationType: relation.relationType
            }))
        };
  • JSDoc type definition specifying the input (string[] names) and output structure for the openNodes method.
    * @property {(names: string[]) => Promise<{ entities: Array<{ name: string, entityType: string, observations: string[] }>, relations: Array<{ from: string, to: string, relationType: string }> }>} openNodes

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/iAchilles/memento'

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