Skip to main content
Glama

upsert_entities

Insert new entities or update existing ones in the knowledge graph, ensuring data consistency and accuracy for structured reasoning and problem-solving.

Instructions

Create new entities or update existing ones in the knowledge graph using an upsert pattern

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
entitiesYesArray of entities to create or update

Implementation Reference

  • The execute function implementing the core logic of the upsert_entities tool. It processes a list of entities, creating new ones by adding observations, updating existing ones if specified (replacing observations), with timeout protection and detailed results reporting.
    execute: async (args, context) => {
      const total = args.entities.length;
      const log = context && context.log ? context.log : { info() {}, error() {}, warn() {}, debug() {} };
      
      const results = {
        created: [] as string[],
        updated: [] as string[],
        failed: [] as {name: string, reason: string}[],
        incomplete: false
      };
    
      // Process with timeout protection
      const startTime = Date.now();
      const timeoutMs = 25000; // 25 second timeout
      
      for (const entity of args.entities) {
        try {
          // Check if we've exceeded our time budget
          if (Date.now() - startTime > timeoutMs) {
            results.incomplete = true;
            break;
          }
          
          // Check if entity exists
          const existingEntity = graph.entities.get(entity.name);
          const doesExist = !!existingEntity;
          
          // If entity doesn't exist, create it
          if (!doesExist) {
            // Add observations for the new entity
            for (const observation of entity.observations) {
              await memoryStore.add(entity.name, observation, {
                version: '1.0'
              });
            }
            results.created.push(entity.name);
          } 
          // If entity exists and update flag is true, update it
          else if (entity.update) {
            // Update entity type
            existingEntity.entityType = entity.entityType;
            
            // Remove all existing observations and add new ones
            graph.deleteObservations(entity.name, existingEntity.observations);
            
            // Add new observations
            for (const observation of entity.observations) {
              await memoryStore.add(entity.name, observation, {
                version: '1.0'
              });
            }
            results.updated.push(entity.name);
          }
          // Entity exists but update flag is false, skip
          else {
            results.failed.push({
              name: entity.name,
              reason: "Entity already exists and update flag is false"
            });
          }
        } catch (error) {
          results.failed.push({
            name: entity.name,
            reason: `Error processing entity: ${error}`
          });
        }
      }
      
      // Save final changes
      await memoryStore.save();
      
      // Return detailed results
      return JSON.stringify({
        created: results.created.length > 0 ? results.created : null,
        updated: results.updated.length > 0 ? results.updated : null,
        failed: results.failed.length > 0 ? results.failed : null,
        incomplete: results.incomplete,
        message: `Created ${results.created.length} new entities. Updated ${results.updated.length} existing entities. Failed for ${results.failed.length} entities.${
          results.incomplete ? ` Operation incomplete due to timeout - ${results.created.length + results.updated.length + results.failed.length} of ${total} entities processed.` : ''
        }`
      });
    }
  • Zod schema defining the input structure for upsert_entities: array of entities with name, entityType, observations, optional context and update flag.
    export const UpsertEntitiesSchema = z.object({
      entities: z.array(z.object({
        name: z.string().min(1).describe('Unique identifier for the entity'),
        entityType: z.string().min(1).describe('Type classification of the entity'),
        observations: z.array(z.string()).describe('Facts or observations about the entity'),
        context: z.string().optional().describe('Optional context or situation relevant to this entity (e.g., project, meeting, or scenario)'),
        update: z.boolean().optional().default(false).describe('If true, will fully replace an existing entity; if false, will only create if entity doesn\'t exist')
      })).describe('Array of entities to create or update')
    });
  • Tool registration within registerMemoryTools function using FastMCP server.addTool, specifying name, description, schema reference, and inline execute handler.
    server.addTool({
      name: 'upsert_entities',
      description: 'Create new entities or update existing ones in the knowledge graph using an upsert pattern',
      parameters: Schemas.UpsertEntitiesSchema,
      execute: async (args, context) => {
        const total = args.entities.length;
        const log = context && context.log ? context.log : { info() {}, error() {}, warn() {}, debug() {} };
        
        const results = {
          created: [] as string[],
          updated: [] as string[],
          failed: [] as {name: string, reason: string}[],
          incomplete: false
        };
    
        // Process with timeout protection
        const startTime = Date.now();
        const timeoutMs = 25000; // 25 second timeout
        
        for (const entity of args.entities) {
          try {
            // Check if we've exceeded our time budget
            if (Date.now() - startTime > timeoutMs) {
              results.incomplete = true;
              break;
            }
            
            // Check if entity exists
            const existingEntity = graph.entities.get(entity.name);
            const doesExist = !!existingEntity;
            
            // If entity doesn't exist, create it
            if (!doesExist) {
              // Add observations for the new entity
              for (const observation of entity.observations) {
                await memoryStore.add(entity.name, observation, {
                  version: '1.0'
                });
              }
              results.created.push(entity.name);
            } 
            // If entity exists and update flag is true, update it
            else if (entity.update) {
              // Update entity type
              existingEntity.entityType = entity.entityType;
              
              // Remove all existing observations and add new ones
              graph.deleteObservations(entity.name, existingEntity.observations);
              
              // Add new observations
              for (const observation of entity.observations) {
                await memoryStore.add(entity.name, observation, {
                  version: '1.0'
                });
              }
              results.updated.push(entity.name);
            }
            // Entity exists but update flag is false, skip
            else {
              results.failed.push({
                name: entity.name,
                reason: "Entity already exists and update flag is false"
              });
            }
          } catch (error) {
            results.failed.push({
              name: entity.name,
              reason: `Error processing entity: ${error}`
            });
          }
        }
        
        // Save final changes
        await memoryStore.save();
        
        // Return detailed results
        return JSON.stringify({
          created: results.created.length > 0 ? results.created : null,
          updated: results.updated.length > 0 ? results.updated : null,
          failed: results.failed.length > 0 ? results.failed : null,
          incomplete: results.incomplete,
          message: `Created ${results.created.length} new entities. Updated ${results.updated.length} existing entities. Failed for ${results.failed.length} entities.${
            results.incomplete ? ` Operation incomplete due to timeout - ${results.created.length + results.updated.length + results.failed.length} of ${total} entities processed.` : ''
          }`
        });
      }
    });
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden. It mentions 'upsert pattern' but doesn't disclose key behavioral traits: it doesn't specify what happens on conflicts (e.g., whether updates merge or replace data), authentication needs, rate limits, or error handling. The description is minimal and lacks operational details critical for a mutation tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, efficient sentence that front-loads the core purpose. It avoids redundancy and waste, though it could be slightly more informative without losing conciseness. Every word earns its place, making it appropriately sized for the tool's complexity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (a mutation operation with no annotations and no output schema), the description is incomplete. It doesn't cover behavioral aspects like side effects, response format, or error conditions. For an upsert tool that modifies a knowledge graph, more context is needed to guide safe and effective use.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema fully documents the single parameter 'entities' and its nested properties. The description adds no additional meaning beyond the schema, such as explaining the 'upsert' logic in more detail or providing examples. Baseline 3 is appropriate as the schema does the heavy lifting.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('Create new entities or update existing ones') and resource ('in the knowledge graph'), and specifies the pattern ('using an upsert pattern'). It distinguishes from siblings like 'create_relations' or 'delete_entities' by focusing on entity creation/updates, though it doesn't explicitly contrast with 'add_observations' or 'update_relations' which might handle related but different operations.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. It doesn't mention when to prefer 'upsert_entities' over 'add_observations' for adding data, or 'delete_entities' for removal, nor does it specify prerequisites or contextual cues for upsert operations. Usage is implied by the term 'upsert' but not explicitly defined.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

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/flight505/mcp-think-tank'

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