Skip to main content
Glama

twining_neighbors

Explore connections in a knowledge graph by traversing from an entity to related nodes, with configurable depth and relation filtering to understand entity relationships.

Instructions

Traverse the knowledge graph from an entity, returning neighbors up to a given depth (max 3). Supports filtering by relation type. Useful for understanding how entities connect.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
entityYesEntity ID or name to start traversal from
depthNoTraversal depth (1-3, default: 1)
relation_typesNoFilter to only these relation types

Implementation Reference

  • Registration and handler definition for twining_neighbors tool. It calls the engine.neighbors method.
    // twining_neighbors — Traverse neighbors from an entity
    server.registerTool(
      "twining_neighbors",
      {
        description:
          "Traverse the knowledge graph from an entity, returning neighbors up to a given depth (max 3). Supports filtering by relation type. Useful for understanding how entities connect.",
        inputSchema: {
          entity: z
            .string()
            .describe("Entity ID or name to start traversal from"),
          depth: z
            .number()
            .optional()
            .describe("Traversal depth (1-3, default: 1)"),
          relation_types: z
            .array(z.string())
            .optional()
            .describe("Filter to only these relation types"),
        },
      },
      async (args) => {
        try {
          const result = await engine.neighbors(
            args.entity,
            args.depth,
            args.relation_types,
          );
          return toolResult(result);
        } catch (e) {
          if (e instanceof TwiningError) {
            return toolError(e.message, e.code);
          }
          return toolError(
            e instanceof Error ? e.message : "Unknown error",
            "INTERNAL_ERROR",
          );
        }
      },
    );
  • Implementation of the neighbor traversal logic (GraphEngine.neighbors).
    async neighbors(
      entityIdOrName: string,
      depth?: number,
      relationTypes?: string[],
    ): Promise<NeighborsResult> {
      // Resolve center entity
      const center = await this.resolveEntity(entityIdOrName);
      if (!center) {
        throw new TwiningError(
          `Entity not found: "${entityIdOrName}"`,
          "NOT_FOUND",
        );
      }
    
      const maxDepth = Math.min(Math.max(depth ?? 1, 1), 3);
      const relations = await this.graphStore.getRelations();
      const entities = await this.graphStore.getEntities();
    
      // Build entity lookup map
      const entityMap = new Map<string, Entity>();
      for (const e of entities) {
        entityMap.set(e.id, e);
      }
    
      // Filter relations by type if specified
      const filteredRelations = relationTypes
        ? relations.filter((r) => relationTypes.includes(r.type))
        : relations;
    
      // Build adjacency list: entityId -> [{neighborId, relation, direction}]
      const adjacency = new Map<
        string,
        { neighborId: string; relation: Relation; direction: RelationDirection }[]
      >();
    
      for (const rel of filteredRelations) {
        // Outgoing: source -> target
        if (!adjacency.has(rel.source)) adjacency.set(rel.source, []);
        adjacency.get(rel.source)!.push({
          neighborId: rel.target,
          relation: rel,
          direction: "outgoing",
        });
    
        // Incoming: target -> source
        if (!adjacency.has(rel.target)) adjacency.set(rel.target, []);
        adjacency.get(rel.target)!.push({
          neighborId: rel.source,
          relation: rel,
          direction: "incoming",
        });
      }
    
      // BFS
      const visited = new Set<string>();
      visited.add(center.id);
    
      const result: NeighborEntry[] = [];
      let frontier = [center.id];
    
      for (let d = 0; d < maxDepth; d++) {
        const nextFrontier: string[] = [];
    
        for (const nodeId of frontier) {
          const neighbors = adjacency.get(nodeId) ?? [];
          for (const { neighborId, relation, direction } of neighbors) {
            if (!visited.has(neighborId)) {
              visited.add(neighborId);
              const entity = entityMap.get(neighborId);
              if (entity) {
                result.push({ entity, relation, direction });
                nextFrontier.push(neighborId);
              }
            }
          }
        }
    
        frontier = nextFrontier;
        if (frontier.length === 0) break;
      }
    
      return { center, neighbors: result };
    }

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/daveangulo/twining-mcp'

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