|
| 1 | +# OB-Graph: Knowledge Graph Layer for Open Brain |
| 2 | + |
| 3 | +A knowledge graph you can build and query through your AI — powered by PostgreSQL, deployed as a Supabase Edge Function, and accessed via MCP. |
| 4 | + |
| 5 | +## Why This Matters |
| 6 | + |
| 7 | +Your Open Brain stores thoughts, but thoughts don't exist in isolation. Projects depend on tools. People work at companies. Concepts connect to other concepts. Without explicit relationships, your AI has to re-derive connections every time you ask "how is X related to Y?" OB-Graph gives your thoughts a relationship layer — nodes for entities, edges for connections, and graph traversal to surface paths you didn't know existed. |
| 8 | + |
| 9 | +## What It Does |
| 10 | + |
| 11 | +Adds two tables (`graph_nodes`, `graph_edges`) and an MCP server with 10 tools for: |
| 12 | + |
| 13 | +- **Building** a knowledge graph — create nodes (people, projects, concepts, tools) and connect them with typed edges (works_on, depends_on, related_to) |
| 14 | +- **Querying** relationships — get direct neighbors, multi-hop traversal, and shortest-path between any two nodes |
| 15 | +- **Linking** to existing thoughts — nodes can optionally reference a thought via `thought_id` |
| 16 | + |
| 17 | +All graph traversal runs in PostgreSQL using recursive CTEs — no external graph database needed. |
| 18 | + |
| 19 | +## Prerequisites |
| 20 | + |
| 21 | +- Working Open Brain setup ([Getting Started guide](../../docs/01-getting-started.md)) |
| 22 | +- Supabase project configured |
| 23 | +- Supabase CLI installed and linked to your project |
| 24 | + |
| 25 | +## Credential Tracker |
| 26 | + |
| 27 | +```text |
| 28 | +OB-GRAPH -- CREDENTIAL TRACKER |
| 29 | +-------------------------------------- |
| 30 | +
|
| 31 | +SUPABASE (from your Open Brain setup) |
| 32 | + Project URL: ____________ |
| 33 | + Secret key: ____________ |
| 34 | + Project ref: ____________ |
| 35 | +
|
| 36 | +GENERATED DURING SETUP |
| 37 | + Default User ID: ____________ |
| 38 | + MCP Access Key: ____________ |
| 39 | + MCP Server URL: ____________ |
| 40 | + MCP Connection URL: ____________ |
| 41 | +
|
| 42 | +-------------------------------------- |
| 43 | +``` |
| 44 | + |
| 45 | +## Setup Instructions |
| 46 | + |
| 47 | + |
| 48 | + |
| 49 | +<details> |
| 50 | +<summary><strong>SQL: Create tables, indexes, RLS, and graph functions</strong> (click to expand)</summary> |
| 51 | + |
| 52 | +Run the contents of `schema.sql` in your Supabase SQL Editor (Dashboard → SQL Editor → New Query → paste → Run). |
| 53 | + |
| 54 | +The schema creates: |
| 55 | + |
| 56 | +| Object | Purpose | |
| 57 | +|--------|---------| |
| 58 | +| `graph_nodes` | Entities in your knowledge graph | |
| 59 | +| `graph_edges` | Directed relationships between nodes | |
| 60 | +| `traverse_graph()` | Recursive CTE function for multi-hop traversal | |
| 61 | +| `find_shortest_path()` | BFS shortest path between two nodes | |
| 62 | +| RLS policies | User-scoped data isolation on both tables | |
| 63 | +| Indexes | Fast lookups by user, type, label, source/target | |
| 64 | + |
| 65 | +</details> |
| 66 | + |
| 67 | +> [!IMPORTANT] |
| 68 | +> The schema includes `GRANT` statements for `service_role`. These are required on newer Supabase projects — don't skip them. |
| 69 | +
|
| 70 | +Done when: You can see `graph_nodes` and `graph_edges` in the Supabase Table Editor, and both functions appear under Database → Functions. |
| 71 | + |
| 72 | +--- |
| 73 | + |
| 74 | + |
| 75 | + |
| 76 | +Follow the [Deploy an Edge Function](../../primitives/deploy-edge-function/) guide using these values: |
| 77 | + |
| 78 | +| Setting | Value | |
| 79 | +|---------|-------| |
| 80 | +| Function name | `ob-graph-mcp` | |
| 81 | +| Download path | `recipes/ob-graph` | |
| 82 | + |
| 83 | +Before you deploy, generate an MCP access key and decide which Open Brain user this graph belongs to. Then set the function secrets from `.env.example`: |
| 84 | + |
| 85 | +```bash |
| 86 | +openssl rand -hex 32 |
| 87 | +supabase secrets set \ |
| 88 | + MCP_ACCESS_KEY=your-generated-key \ |
| 89 | + DEFAULT_USER_ID=your-user-uuid |
| 90 | +``` |
| 91 | + |
| 92 | +`SUPABASE_URL` and `SUPABASE_SERVICE_ROLE_KEY` are provided automatically by Supabase for Edge Functions. You only need to set `MCP_ACCESS_KEY` and `DEFAULT_USER_ID` manually. |
| 93 | + |
| 94 | +Done when: The `ob-graph-mcp` function is deployed successfully and its secrets include `MCP_ACCESS_KEY` and `DEFAULT_USER_ID`. |
| 95 | + |
| 96 | +--- |
| 97 | + |
| 98 | + |
| 99 | + |
| 100 | +Follow the [Remote MCP Connection](../../primitives/remote-mcp/) guide to connect this recipe to Claude Desktop, ChatGPT, Claude Code, or any other MCP client. |
| 101 | + |
| 102 | +| Setting | Value | |
| 103 | +|---------|-------| |
| 104 | +| Connector name | `OB-Graph` | |
| 105 | +| URL | Your **MCP Connection URL** from the credential tracker | |
| 106 | + |
| 107 | +Done when: Your AI client can connect to the OB-Graph MCP server without authentication errors and the tools appear in its MCP tool list. |
| 108 | + |
| 109 | +--- |
| 110 | + |
| 111 | + |
| 112 | + |
| 113 | +Try these commands with your AI: |
| 114 | + |
| 115 | +**Build a small graph:** |
| 116 | +``` |
| 117 | +Create these graph nodes: |
| 118 | +- "Supabase" (type: tool) |
| 119 | +- "Open Brain" (type: project) |
| 120 | +- "PostgreSQL" (type: tool) |
| 121 | +
|
| 122 | +Then connect them: |
| 123 | +- Open Brain --depends_on--> Supabase |
| 124 | +- Open Brain --depends_on--> PostgreSQL |
| 125 | +- Supabase --built_with--> PostgreSQL |
| 126 | +``` |
| 127 | + |
| 128 | +**Query relationships:** |
| 129 | +``` |
| 130 | +What are all the neighbors of "Open Brain" in my graph? |
| 131 | +``` |
| 132 | + |
| 133 | +``` |
| 134 | +Traverse my graph starting from "Supabase" up to 3 hops deep |
| 135 | +``` |
| 136 | + |
| 137 | +``` |
| 138 | +Find the shortest path between "PostgreSQL" and "Open Brain" |
| 139 | +``` |
| 140 | + |
| 141 | +Done when: Your AI can create nodes, connect them with edges, and traverse the graph to answer relationship questions. |
| 142 | + |
| 143 | +## MCP Tools Reference |
| 144 | + |
| 145 | +| Tool | Description | |
| 146 | +|------|-------------| |
| 147 | +| `create_node` | Add an entity node (person, project, concept, tool, place) | |
| 148 | +| `create_edge` | Create a directed relationship between two nodes | |
| 149 | +| `search_nodes` | Find nodes by label or type | |
| 150 | +| `get_neighbors` | Get direct connections (incoming, outgoing, or both) | |
| 151 | +| `traverse_graph` | Multi-hop walk from a starting node (recursive CTE) | |
| 152 | +| `find_path` | Shortest path between two nodes (bidirectional BFS) | |
| 153 | +| `update_node` | Update label, type, or merge new properties | |
| 154 | +| `delete_node` | Remove a node and all its edges (CASCADE) | |
| 155 | +| `delete_edge` | Remove a specific relationship | |
| 156 | +| `list_edge_types` | List all relationship types in use with counts | |
| 157 | + |
| 158 | +> [!TIP] |
| 159 | +> As your graph grows, see the [MCP Tool Audit & Optimization Guide](../../docs/05-tool-audit.md) for strategies on managing tool context. |
| 160 | +
|
| 161 | +## How the Graph Traversal Works |
| 162 | + |
| 163 | +OB-Graph uses PostgreSQL recursive CTEs instead of a dedicated graph database. Here's the pattern: |
| 164 | + |
| 165 | +```sql |
| 166 | +WITH RECURSIVE graph_walk AS ( |
| 167 | + -- Base case: start node |
| 168 | + SELECT id, label, 0 AS depth, ARRAY[id] AS path |
| 169 | + FROM graph_nodes WHERE id = start_id |
| 170 | + |
| 171 | + UNION ALL |
| 172 | + |
| 173 | + -- Recursive case: follow edges, prevent cycles via path check |
| 174 | + SELECT n.id, n.label, gw.depth + 1, gw.path || n.id |
| 175 | + FROM graph_walk gw |
| 176 | + JOIN graph_edges e ON e.source_node_id = gw.id |
| 177 | + JOIN graph_nodes n ON n.id = e.target_node_id |
| 178 | + WHERE gw.depth < max_depth |
| 179 | + AND NOT n.id = ANY(gw.path) -- cycle prevention |
| 180 | +) |
| 181 | +SELECT * FROM graph_walk; |
| 182 | +``` |
| 183 | + |
| 184 | +This approach works well for knowledge graphs with thousands of nodes. It stays within Supabase's free tier limits (no extra services or extensions required) and gives you traversal, pathfinding, and neighbor queries — the core operations you need for relationship exploration. |
| 185 | + |
| 186 | +> [!NOTE] |
| 187 | +> Recursive CTEs are bounded by `max_depth` and cycle prevention. For very large graphs (100k+ edges), consider adding a `weight` threshold filter to prune low-confidence edges during traversal. |
| 188 | +
|
| 189 | +## Expected Outcome |
| 190 | + |
| 191 | +After setup, your AI can: |
| 192 | + |
| 193 | +1. Build a knowledge graph of entities and relationships as you talk about them |
| 194 | +2. Answer "how is X related to Y?" with concrete graph paths |
| 195 | +3. Explore multi-hop connections ("what's 3 degrees from this project?") |
| 196 | +4. List all relationship types to understand your graph's structure |
| 197 | +5. Link graph nodes back to existing thoughts for full context |
| 198 | + |
| 199 | +## Troubleshooting |
| 200 | + |
| 201 | +### "relation 'graph_nodes' does not exist" |
| 202 | + |
| 203 | +You haven't run the SQL from Step 1 yet. Copy `schema.sql` into your Supabase SQL Editor and run it. |
| 204 | + |
| 205 | +### "function traverse_graph does not exist" |
| 206 | + |
| 207 | +The SQL functions at the bottom of `schema.sql` didn't run. Make sure you execute the **entire** file, not just the `CREATE TABLE` statements. The functions (`traverse_graph`, `find_shortest_path`) are defined after the tables. |
| 208 | + |
| 209 | +> [!WARNING] |
| 210 | +> If you ran only part of the SQL, run the full file again — all statements use `IF NOT EXISTS` or `CREATE OR REPLACE`, so re-running is safe. |
| 211 | +
|
| 212 | +### "duplicate key value violates constraint unique_edge" |
| 213 | + |
| 214 | +You're trying to create an edge that already exists (same source, target, and relationship type). This is by design — the `unique_edge` constraint prevents duplicate relationships. If you want to update an existing edge's weight or properties, delete the old edge first and create a new one. |
0 commit comments