Graph Bridges
localnest_graph_bridgesIdentify cross-nest or cross-branch bridges within a nest. Use mode and nest parameters to find entities that link different nests or branches.
Instructions
Discover cross-nest bridges (default) or cross-branch bridges within a nest. Use mode="cross-branch" with a nest to find entities that span different branches within that nest.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| nest | No | ||
| mode | No | cross-nest | |
| response_format | No | json |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| data | Yes | ||
| meta | Yes |
Implementation Reference
- Core handler function that discovers bridges in the knowledge graph. Runs a SQL query to find triples whose subject and object belong to different nests (cross-nest mode) or different branches within the same nest (cross-branch mode). Returns bridge entries, insights about shared entities, and a summary.
export async function discoverBridges(adapter: Adapter, { nest, mode }: DiscoverBridgesOpts = {}): Promise<DiscoverBridgesResult> { const cleanNest = nest ? cleanString(nest, 200) : null; const isCrossBranch = mode === 'cross-branch' && !!cleanNest; // Use LEFT JOINs so entities with NULL memory_id still participate. // Derive nest/branch via COALESCE: entity's own memory_id -> triple's source_memory_id. let sql = ` SELECT * FROM ( SELECT DISTINCT t.id AS triple_id, t.subject_id, s.name AS subject_name, s.entity_type AS subject_type, t.predicate, t.object_id, o.name AS object_name, o.entity_type AS object_type, COALESCE(NULLIF(ms1.nest,''), NULLIF(mt.nest,'')) AS subject_nest, COALESCE(NULLIF(mo1.nest,''), NULLIF(mt.nest,'')) AS object_nest, COALESCE(NULLIF(ms1.branch,''), NULLIF(mt.branch,'')) AS subject_branch, COALESCE(NULLIF(mo1.branch,''), NULLIF(mt.branch,'')) AS object_branch FROM kg_triples t JOIN kg_entities s ON s.id = t.subject_id JOIN kg_entities o ON o.id = t.object_id LEFT JOIN memory_entries ms1 ON ms1.id = s.memory_id LEFT JOIN memory_entries mo1 ON mo1.id = o.memory_id LEFT JOIN memory_entries mt ON mt.id = t.source_memory_id WHERE t.valid_to IS NULL ) AS bridges`; const params: unknown[] = []; if (isCrossBranch) { // Cross-branch mode: find bridges across branches within the specified nest sql += ` WHERE subject_nest = ? AND object_nest = ? AND subject_branch != '' AND subject_branch IS NOT NULL AND object_branch != '' AND object_branch IS NOT NULL AND subject_branch != object_branch`; params.push(cleanNest, cleanNest); } else { // Default cross-nest mode sql += ` WHERE subject_nest != '' AND subject_nest IS NOT NULL AND object_nest != '' AND object_nest IS NOT NULL AND subject_nest != object_nest`; if (cleanNest) { sql += `\n AND (subject_nest = ? OR object_nest = ?)`; params.push(cleanNest, cleanNest); } } sql += `\n ORDER BY subject_nest, object_nest, subject_name`; const rows = await adapter.all<BridgeRow>(sql, params); // --- Build insights from entity -> scope mapping --- const scopeLabel = isCrossBranch ? 'branch' : 'nest'; const entityScopes = new Map<string, { name: string; type: string; scopes: Set<string>; predicates: Set<string> }>(); for (const row of rows) { const bothScopes = isCrossBranch ? [row.subject_branch, row.object_branch] : [row.subject_nest, row.object_nest]; for (const [id, name, type] of [ [row.subject_id, row.subject_name, row.subject_type], [row.object_id, row.object_name, row.object_type] ] as [string, string, string][]) { let entry = entityScopes.get(id); if (!entry) { entry = { name, type, scopes: new Set(), predicates: new Set() }; entityScopes.set(id, entry); } for (const s of bothScopes) entry.scopes.add(s); entry.predicates.add(row.predicate); } } const insights: string[] = []; for (const [, info] of entityScopes) { if (info.scopes.size >= 2) { const scopeList = [...info.scopes].sort().join(', '); const predList = [...info.predicates].sort().join(', '); insights.push(`'${info.name}' (${info.type}) bridges ${scopeLabel}es ${scopeList} via '${predList}'`); } } // --- Summary --- const scopePairs = new Set<string>(); for (const row of rows) { const a = isCrossBranch ? row.subject_branch : row.subject_nest; const b = isCrossBranch ? row.object_branch : row.object_nest; scopePairs.add([a, b].sort().join('<->')); } const sharedCount = [...entityScopes.values()].filter(e => e.scopes.size >= 2).length; const noResultsMsg = isCrossBranch ? `No cross-branch bridges found within nest '${cleanNest}'` : 'No cross-nest bridges found'; const foundMsg = isCrossBranch ? `Found ${rows.length} bridge(s) across ${scopePairs.size} branch pair(s) in nest '${cleanNest}' involving ${sharedCount} shared entity/entities` : `Found ${rows.length} bridge(s) across ${scopePairs.size} nest pair(s) involving ${sharedCount} shared entity/entities`; const summary = rows.length === 0 ? noResultsMsg : foundMsg; return { filter_nest: cleanNest, bridge_count: rows.length, bridges: rows.map((row): BridgeEntry => ({ triple_id: row.triple_id, subject_id: row.subject_id, subject_name: row.subject_name, predicate: row.predicate, object_id: row.object_id, object_name: row.object_name, subject_nest: row.subject_nest, object_nest: row.object_nest, subject_type: row.subject_type, object_type: row.object_type, subject_branch: row.subject_branch, object_branch: row.object_branch })), insights, summary }; } - src/services/memory/store.ts:432-439 (handler)Store-level wrapper for discoverBridges that initializes, emits before/after hooks, and delegates to the core graph function.
async discoverBridges(args: DiscoverBridgesOpts) { await this.init(); const hookResult = await this.hooks.emit('before:graph:bridges', args); if (hookResult.cancelled) return { cancelled: true, reason: hookResult.reason }; const result = await discoverBridgesFn(this.adapter!, hookResult.payload as DiscoverBridgesOpts); await this.hooks.emit('after:graph:bridges', result); return result; } - src/services/memory/service.ts:313-316 (handler)Service-level wrapper that asserts the memory service is enabled and delegates to store.discoverBridges.
async discoverBridges(args: DiscoverBridgesOpts) { this.assertEnabled(); return this.store.discoverBridges(args); } - src/mcp/tools/graph-tools.ts:320-333 (registration)Registration of the 'localnest_graph_bridges' tool via registerJsonTool, with input schema (nest, mode), read-only annotations, and handler that calls memory.discoverBridges.
registerJsonTool( ['localnest_graph_bridges'], { title: 'Graph Bridges', description: 'Discover cross-nest bridges (default) or cross-branch bridges within a nest. Use mode="cross-branch" with a nest to find entities that span different branches within that nest.', inputSchema: { nest: z.string().max(200).optional(), mode: z.enum(['cross-nest', 'cross-branch']).default('cross-nest') }, annotations: READ_ONLY_ANNOTATIONS, outputSchema: schemas.OUTPUT_BUNDLE_RESULT_SCHEMA }, async ({ nest, mode }: Record<string, unknown>) => memory.discoverBridges({ nest, mode: mode as 'cross-nest' | 'cross-branch' | undefined }) ); - Type definitions for DiscoverBridgesOpts (nest, mode), BridgeEntry (triple_id, subject/object details with nest/branch info), and DiscoverBridgesResult (filter_nest, bridge_count, bridges, insights, summary).
export interface DiscoverBridgesOpts { nest?: string; /** When 'cross-branch', find bridges across branches within the given nest instead of across nests. */ mode?: 'cross-nest' | 'cross-branch'; } export interface BridgeEntry { triple_id: string; subject_id: string; subject_name: string; predicate: string; object_id: string; object_name: string; subject_nest: string; object_nest: string; subject_type?: string; object_type?: string; subject_branch?: string; object_branch?: string; } export interface DiscoverBridgesResult { filter_nest: string | null; bridge_count: number; bridges: BridgeEntry[]; insights: string[]; summary: string; }