Skip to main content
Glama
Jakedismo

CodeGraph CLI MCP Server

by Jakedismo
graph_visualization.surql22.9 kB
-- ABOUTME: Graph visualization schema for CodeGraph -- ABOUTME: Creates graph relation tables and export functions for DOT, D3.js, Cytoscape formats -- ------------------------------ -- GRAPH EDGE RELATION TABLES -- These use SurrealDB's native RELATE syntax for graph traversal -- ------------------------------ -- Calls relationship: function/method calls another function/method DEFINE TABLE calls TYPE RELATION IN nodes OUT nodes SCHEMAFULL COMMENT 'Function/method call relationships for call graph visualization' PERMISSIONS FULL; DEFINE FIELD weight ON calls TYPE float DEFAULT 1f PERMISSIONS FULL; DEFINE FIELD metadata ON calls FLEXIBLE TYPE option<object> PERMISSIONS FULL; DEFINE FIELD created_at ON calls TYPE datetime DEFAULT time::now() READONLY PERMISSIONS FULL; -- Imports relationship: module/file imports another module/file DEFINE TABLE imports TYPE RELATION IN nodes OUT nodes SCHEMAFULL COMMENT 'Import/require relationships for dependency graph visualization' PERMISSIONS FULL; DEFINE FIELD weight ON imports TYPE float DEFAULT 1f PERMISSIONS FULL; DEFINE FIELD metadata ON imports FLEXIBLE TYPE option<object> PERMISSIONS FULL; DEFINE FIELD created_at ON imports TYPE datetime DEFAULT time::now() READONLY PERMISSIONS FULL; -- Uses relationship: code uses a type/variable/constant DEFINE TABLE uses TYPE RELATION IN nodes OUT nodes SCHEMAFULL COMMENT 'Usage relationships (types, variables, constants)' PERMISSIONS FULL; DEFINE FIELD weight ON uses TYPE float DEFAULT 1f PERMISSIONS FULL; DEFINE FIELD metadata ON uses FLEXIBLE TYPE option<object> PERMISSIONS FULL; DEFINE FIELD created_at ON uses TYPE datetime DEFAULT time::now() READONLY PERMISSIONS FULL; -- Extends relationship: class/interface extends another DEFINE TABLE extends TYPE RELATION IN nodes OUT nodes SCHEMAFULL COMMENT 'Inheritance relationships for class hierarchy visualization' PERMISSIONS FULL; DEFINE FIELD weight ON extends TYPE float DEFAULT 1f PERMISSIONS FULL; DEFINE FIELD metadata ON extends FLEXIBLE TYPE option<object> PERMISSIONS FULL; DEFINE FIELD created_at ON extends TYPE datetime DEFAULT time::now() READONLY PERMISSIONS FULL; -- Implements relationship: class implements interface/protocol DEFINE TABLE implements TYPE RELATION IN nodes OUT nodes SCHEMAFULL COMMENT 'Interface implementation relationships' PERMISSIONS FULL; DEFINE FIELD weight ON implements TYPE float DEFAULT 1f PERMISSIONS FULL; DEFINE FIELD metadata ON implements FLEXIBLE TYPE option<object> PERMISSIONS FULL; DEFINE FIELD created_at ON implements TYPE datetime DEFAULT time::now() READONLY PERMISSIONS FULL; -- References relationship: code references another element DEFINE TABLE references TYPE RELATION IN nodes OUT nodes SCHEMAFULL COMMENT 'General reference relationships' PERMISSIONS FULL; DEFINE FIELD weight ON references TYPE float DEFAULT 1f PERMISSIONS FULL; DEFINE FIELD metadata ON references FLEXIBLE TYPE option<object> PERMISSIONS FULL; DEFINE FIELD created_at ON references TYPE datetime DEFAULT time::now() READONLY PERMISSIONS FULL; -- ------------------------------ -- GRAPH POPULATION FUNCTIONS -- Populate relation tables from the relational edges table -- ------------------------------ -- Populate all graph edges from relational edges table DEFINE FUNCTION fn::populate_graph_edges($project_id: string) { -- Clear existing graph edges for this project DELETE calls WHERE in.project_id = $project_id OR out.project_id = $project_id; DELETE imports WHERE in.project_id = $project_id OR out.project_id = $project_id; DELETE uses WHERE in.project_id = $project_id OR out.project_id = $project_id; DELETE extends WHERE in.project_id = $project_id OR out.project_id = $project_id; DELETE implements WHERE in.project_id = $project_id OR out.project_id = $project_id; DELETE references WHERE in.project_id = $project_id OR out.project_id = $project_id; ------------------------------------------------------------------ -- calls ------------------------------------------------------------------ LET $calls_edges = ( SELECT from, to, weight, metadata FROM edges WHERE edge_type IN ['calls', 'Calls'] AND (from.project_id = $project_id OR from.project_id = NONE) AND (to.project_id = $project_id OR to.project_id = NONE) ); FOR $e IN $calls_edges { LET $from = $e.from; LET $to = $e.to; RELATE $from->calls->$to SET weight = $e.weight, metadata = $e.metadata; }; ------------------------------------------------------------------ -- imports ------------------------------------------------------------------ LET $imports_edges = ( SELECT from, to, weight, metadata FROM edges WHERE edge_type IN ['imports', 'Imports'] AND (from.project_id = $project_id OR from.project_id = NONE) AND (to.project_id = $project_id OR to.project_id = NONE) ); FOR $e IN $imports_edges { LET $from = $e.from; LET $to = $e.to; RELATE $from->imports->$to SET weight = $e.weight, metadata = $e.metadata; }; ------------------------------------------------------------------ -- uses ------------------------------------------------------------------ LET $uses_edges = ( SELECT from, to, weight, metadata FROM edges WHERE edge_type IN ['uses', 'Uses'] AND (from.project_id = $project_id OR from.project_id = NONE) AND (to.project_id = $project_id OR to.project_id = NONE) ); FOR $e IN $uses_edges { LET $from = $e.from; LET $to = $e.to; RELATE $from->uses->$to SET weight = $e.weight, metadata = $e.metadata; }; ------------------------------------------------------------------ -- extends ------------------------------------------------------------------ LET $extends_edges = ( SELECT from, to, weight, metadata FROM edges WHERE edge_type IN ['extends', 'Extends'] AND (from.project_id = $project_id OR from.project_id = NONE) AND (to.project_id = $project_id OR to.project_id = NONE) ); FOR $e IN $extends_edges { LET $from = $e.from; LET $to = $e.to; RELATE $from->extends->$to SET weight = $e.weight, metadata = $e.metadata; }; ------------------------------------------------------------------ -- implements ------------------------------------------------------------------ LET $implements_edges = ( SELECT from, to, weight, metadata FROM edges WHERE edge_type IN ['implements', 'Implements'] AND (from.project_id = $project_id OR from.project_id = NONE) AND (to.project_id = $project_id OR to.project_id = NONE) ); FOR $e IN $implements_edges { LET $from = $e.from; LET $to = $e.to; RELATE $from->implements->$to SET weight = $e.weight, metadata = $e.metadata; }; ------------------------------------------------------------------ -- references ------------------------------------------------------------------ LET $references_edges = ( SELECT from, to, weight, metadata FROM edges WHERE edge_type IN ['references', 'References'] AND (from.project_id = $project_id OR from.project_id = NONE) AND (to.project_id = $project_id OR to.project_id = NONE) ); FOR $e IN $references_edges { LET $from = $e.from; LET $to = $e.to; RELATE $from->references->$to SET weight = $e.weight, metadata = $e.metadata; }; RETURN { status: 'completed', project_id: $project_id, calls: array::len($calls_edges), imports: array::len($imports_edges), uses: array::len($uses_edges), extends: array::len($extends_edges), implements: array::len($implements_edges), references: array::len($references_edges) }; } PERMISSIONS FULL; -- ------------------------------ -- GRAPH STATISTICS FUNCTION -- ------------------------------ DEFINE FUNCTION fn::graph_stats($project_id: string) { LET $nodes = (SELECT count() AS cnt FROM nodes WHERE project_id = $project_id)[0].cnt ?? 0; LET $calls = (SELECT count() AS cnt FROM calls WHERE in.project_id = $project_id)[0].cnt ?? 0; LET $imports = (SELECT count() AS cnt FROM imports WHERE in.project_id = $project_id)[0].cnt ?? 0; LET $uses = (SELECT count() AS cnt FROM uses WHERE in.project_id = $project_id)[0].cnt ?? 0; LET $extends = (SELECT count() AS cnt FROM extends WHERE in.project_id = $project_id)[0].cnt ?? 0; LET $implements = (SELECT count() AS cnt FROM implements WHERE in.project_id = $project_id)[0].cnt ?? 0; LET $references = (SELECT count() AS cnt FROM references WHERE in.project_id = $project_id)[0].cnt ?? 0; RETURN { project_id: $project_id, node_count: $nodes, edge_counts: { calls: $calls, imports: $imports, uses: $uses, extends: $extends, implements: $implements, references: $references, total: $calls + $imports + $uses + $extends + $implements + $references } }; } PERMISSIONS FULL; -- ------------------------------ -- GRAPHVIZ DOT EXPORT FUNCTION -- ------------------------------ DEFINE FUNCTION fn::export_graphviz($project_id: string, $edge_types: array<string>) { LET $selected_types = IF array::len($edge_types) > 0 THEN $edge_types ELSE ['calls', 'imports', 'uses', 'extends', 'implements', 'references'] END; -- Get all nodes for the project LET $project_nodes = (SELECT id, name, node_type, file_path FROM nodes WHERE project_id = $project_id); -- Build node definitions LET $node_defs = (SELECT string::concat(' "', <string>id, '" [label="', name, '\\n(', node_type ?? 'unknown', ')"];') AS def FROM $project_nodes); -- Collect edges based on selected types LET $edge_list = []; IF 'calls' INSIDE $selected_types { LET $calls_list = (SELECT string::concat(' "', <string>in, '" -> "', <string>out, '" [color="red" label="calls"];') AS def FROM calls WHERE in.project_id = $project_id); LET $edge_list = array::concat($edge_list, $calls_list); }; IF 'imports' INSIDE $selected_types { LET $imports_list = (SELECT string::concat(' "', <string>in, '" -> "', <string>out, '" [color="blue" label="imports"];') AS def FROM imports WHERE in.project_id = $project_id); LET $edge_list = array::concat($edge_list, $imports_list); }; IF 'uses' INSIDE $selected_types { LET $uses_list = (SELECT string::concat(' "', <string>in, '" -> "', <string>out, '" [color="green" label="uses"];') AS def FROM uses WHERE in.project_id = $project_id); LET $edge_list = array::concat($edge_list, $uses_list); }; IF 'extends' INSIDE $selected_types { LET $extends_list = (SELECT string::concat(' "', <string>in, '" -> "', <string>out, '" [color="purple" style="bold" label="extends"];') AS def FROM extends WHERE in.project_id = $project_id); LET $edge_list = array::concat($edge_list, $extends_list); }; IF 'implements' INSIDE $selected_types { LET $implements_list = (SELECT string::concat(' "', <string>in, '" -> "', <string>out, '" [color="orange" style="dashed" label="implements"];') AS def FROM implements WHERE in.project_id = $project_id); LET $edge_list = array::concat($edge_list, $implements_list); }; IF 'references' INSIDE $selected_types { LET $references_list = (SELECT string::concat(' "', <string>in, '" -> "', <string>out, '" [color="gray" label="references"];') AS def FROM references WHERE in.project_id = $project_id); LET $edge_list = array::concat($edge_list, $references_list); }; LET $nodes_str = string::join('\n', (SELECT VALUE def FROM $node_defs)); LET $edges_str = string::join('\n', (SELECT VALUE def FROM $edge_list)); RETURN string::concat( 'digraph codegraph {\n', ' rankdir=LR;\n', ' node [shape=box fontname="Helvetica"];\n', ' edge [fontname="Helvetica" fontsize=10];\n\n', ' // Nodes\n', $nodes_str, '\n\n // Edges\n', $edges_str, '\n}\n' ); } PERMISSIONS FULL; -- ------------------------------ -- D3.JS JSON EXPORT FUNCTION -- ------------------------------ DEFINE FUNCTION fn::export_d3_json($project_id: string, $edge_types: array<string>) { LET $selected_types = IF array::len($edge_types) > 0 THEN $edge_types ELSE ['calls', 'imports', 'uses', 'extends', 'implements', 'references'] END; -- Get all nodes for the project LET $project_nodes = (SELECT <string>id AS id, name, node_type AS type, language, file_path FROM nodes WHERE project_id = $project_id); -- Collect links based on selected types LET $links = []; IF 'calls' INSIDE $selected_types { LET $calls_links = (SELECT <string>in AS source, <string>out AS target, 'calls' AS type, weight AS value FROM calls WHERE in.project_id = $project_id); LET $links = array::concat($links, $calls_links); }; IF 'imports' INSIDE $selected_types { LET $imports_links = (SELECT <string>in AS source, <string>out AS target, 'imports' AS type, weight AS value FROM imports WHERE in.project_id = $project_id); LET $links = array::concat($links, $imports_links); }; IF 'uses' INSIDE $selected_types { LET $uses_links = (SELECT <string>in AS source, <string>out AS target, 'uses' AS type, weight AS value FROM uses WHERE in.project_id = $project_id); LET $links = array::concat($links, $uses_links); }; IF 'extends' INSIDE $selected_types { LET $extends_links = (SELECT <string>in AS source, <string>out AS target, 'extends' AS type, weight AS value FROM extends WHERE in.project_id = $project_id); LET $links = array::concat($links, $extends_links); }; IF 'implements' INSIDE $selected_types { LET $implements_links = (SELECT <string>in AS source, <string>out AS target, 'implements' AS type, weight AS value FROM implements WHERE in.project_id = $project_id); LET $links = array::concat($links, $implements_links); }; IF 'references' INSIDE $selected_types { LET $references_links = (SELECT <string>in AS source, <string>out AS target, 'references' AS type, weight AS value FROM references WHERE in.project_id = $project_id); LET $links = array::concat($links, $references_links); }; RETURN { nodes: $project_nodes, links: $links }; } PERMISSIONS FULL; -- ------------------------------ -- CYTOSCAPE.JS JSON EXPORT FUNCTION -- ------------------------------ DEFINE FUNCTION fn::export_cytoscape($project_id: string, $edge_types: array<string>) { LET $selected_types = IF array::len($edge_types) > 0 THEN $edge_types ELSE ['calls', 'imports', 'uses', 'extends', 'implements', 'references'] END; -- Get all nodes for the project (Cytoscape format) LET $project_nodes = (SELECT { data: { id: <string>id, label: name, type: node_type, language: language, file_path: file_path } } AS element FROM nodes WHERE project_id = $project_id); -- Collect edges based on selected types (Cytoscape format) LET $edges = []; IF 'calls' INSIDE $selected_types { LET $calls_edges = (SELECT { data: { id: <string>id, source: <string>in, target: <string>out, type: 'calls', weight: weight } } AS element FROM calls WHERE in.project_id = $project_id); LET $edges = array::concat($edges, $calls_edges); }; IF 'imports' INSIDE $selected_types { LET $imports_edges = (SELECT { data: { id: <string>id, source: <string>in, target: <string>out, type: 'imports', weight: weight } } AS element FROM imports WHERE in.project_id = $project_id); LET $edges = array::concat($edges, $imports_edges); }; IF 'uses' INSIDE $selected_types { LET $uses_edges = (SELECT { data: { id: <string>id, source: <string>in, target: <string>out, type: 'uses', weight: weight } } AS element FROM uses WHERE in.project_id = $project_id); LET $edges = array::concat($edges, $uses_edges); }; IF 'extends' INSIDE $selected_types { LET $extends_edges = (SELECT { data: { id: <string>id, source: <string>in, target: <string>out, type: 'extends', weight: weight } } AS element FROM extends WHERE in.project_id = $project_id); LET $edges = array::concat($edges, $extends_edges); }; IF 'implements' INSIDE $selected_types { LET $implements_edges = (SELECT { data: { id: <string>id, source: <string>in, target: <string>out, type: 'implements', weight: weight } } AS element FROM implements WHERE in.project_id = $project_id); LET $edges = array::concat($edges, $implements_edges); }; IF 'references' INSIDE $selected_types { LET $references_edges = (SELECT { data: { id: <string>id, source: <string>in, target: <string>out, type: 'references', weight: weight } } AS element FROM references WHERE in.project_id = $project_id); LET $edges = array::concat($edges, $references_edges); }; LET $node_elements = (SELECT VALUE element FROM $project_nodes); LET $edge_elements = (SELECT VALUE element FROM $edges); RETURN { elements: array::concat($node_elements, $edge_elements) }; } PERMISSIONS FULL; -- ------------------------------ -- FILE SUBGRAPH EXPORT FUNCTION -- Export graph for a specific file and its dependencies -- ------------------------------ DEFINE FUNCTION fn::export_file_subgraph($project_id: string, $file_path: string, $depth: int) { LET $safe_depth = IF $depth > 0 AND $depth <= 3 THEN $depth ELSE 1 END; -- Get nodes in the target file LET $file_nodes = (SELECT id FROM nodes WHERE project_id = $project_id AND file_path = $file_path); LET $file_node_ids = (SELECT VALUE id FROM $file_nodes); -- Get depth-1 connected nodes LET $depth1_out = (SELECT VALUE out FROM calls, imports, uses, extends, implements, references WHERE in INSIDE $file_node_ids); LET $depth1_in = (SELECT VALUE in FROM calls, imports, uses, extends, implements, references WHERE out INSIDE $file_node_ids); LET $depth1_nodes = array::distinct(array::concat($depth1_out, $depth1_in)); -- Get depth-2 if requested LET $depth2_nodes = IF $safe_depth >= 2 THEN ( LET $d2_out = (SELECT VALUE out FROM calls, imports, uses, extends, implements, references WHERE in INSIDE $depth1_nodes AND out NOTINSIDE $file_node_ids); LET $d2_in = (SELECT VALUE in FROM calls, imports, uses, extends, implements, references WHERE out INSIDE $depth1_nodes AND in NOTINSIDE $file_node_ids); array::distinct(array::concat($d2_out, $d2_in)) ) ELSE [] END; -- Get depth-3 if requested LET $depth3_nodes = IF $safe_depth >= 3 THEN ( LET $all_previous = array::concat($file_node_ids, $depth1_nodes, $depth2_nodes); LET $d3_out = (SELECT VALUE out FROM calls, imports, uses, extends, implements, references WHERE in INSIDE $depth2_nodes AND out NOTINSIDE $all_previous); LET $d3_in = (SELECT VALUE in FROM calls, imports, uses, extends, implements, references WHERE out INSIDE $depth2_nodes AND in NOTINSIDE $all_previous); array::distinct(array::concat($d3_out, $d3_in)) ) ELSE [] END; -- Combine all relevant nodes LET $all_relevant = array::distinct(array::concat( $file_node_ids, $depth1_nodes, $depth2_nodes, $depth3_nodes )); -- Get node details LET $subgraph_nodes = (SELECT <string>id AS id, name, node_type AS type, language, file_path, IF id INSIDE $file_node_ids THEN 0 ELSE IF id INSIDE $depth1_nodes THEN 1 ELSE IF id INSIDE $depth2_nodes THEN 2 ELSE 3 END AS depth FROM nodes WHERE id INSIDE $all_relevant); -- Get edges between relevant nodes LET $subgraph_edges = (SELECT <string>in AS source, <string>out AS target, 'calls' AS type, weight FROM calls WHERE in INSIDE $all_relevant AND out INSIDE $all_relevant); LET $subgraph_edges = array::concat($subgraph_edges, (SELECT <string>in AS source, <string>out AS target, 'imports' AS type, weight FROM imports WHERE in INSIDE $all_relevant AND out INSIDE $all_relevant)); LET $subgraph_edges = array::concat($subgraph_edges, (SELECT <string>in AS source, <string>out AS target, 'uses' AS type, weight FROM uses WHERE in INSIDE $all_relevant AND out INSIDE $all_relevant)); LET $subgraph_edges = array::concat($subgraph_edges, (SELECT <string>in AS source, <string>out AS target, 'extends' AS type, weight FROM extends WHERE in INSIDE $all_relevant AND out INSIDE $all_relevant)); LET $subgraph_edges = array::concat($subgraph_edges, (SELECT <string>in AS source, <string>out AS target, 'implements' AS type, weight FROM implements WHERE in INSIDE $all_relevant AND out INSIDE $all_relevant)); LET $subgraph_edges = array::concat($subgraph_edges, (SELECT <string>in AS source, <string>out AS target, 'references' AS type, weight FROM references WHERE in INSIDE $all_relevant AND out INSIDE $all_relevant)); RETURN { file_path: $file_path, depth: $safe_depth, nodes: $subgraph_nodes, links: $subgraph_edges }; } PERMISSIONS FULL; -- ------------------------------ -- USAGE EXAMPLES -- ------------------------------ -- -- 1. First, populate the graph edges from relational edges: -- fn::populate_graph_edges('your-project-id'); -- -- 2. Get graph statistics: -- fn::graph_stats('your-project-id'); -- -- 3. Export to Graphviz DOT format (for Graphviz, OmniGraffle): -- fn::export_graphviz('your-project-id', ['calls', 'imports']); -- -- 4. Export to D3.js JSON format (for web visualization): -- fn::export_d3_json('your-project-id', ['calls', 'extends', 'implements']); -- -- 5. Export to Cytoscape.js format (for advanced web graphs): -- fn::export_cytoscape('your-project-id', []); -- all edge types -- -- 6. Export subgraph for a specific file: -- fn::export_file_subgraph('your-project-id', 'src/main.rs', 2); -- -- 7. Use SurrealDB graph traversal directly: -- SELECT ->calls->out AS called_functions FROM nodes:some_function; -- SELECT <-calls<-in AS callers FROM nodes:some_function; -- SELECT ->calls->out->calls->out AS transitive_calls FROM nodes:entry_point; -- -- 8. Get all info about relations: -- SELECT ->calls.* FROM nodes:some_function; -- SELECT <-calls.* FROM nodes:some_function;

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/Jakedismo/codegraph-rust'

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