Skip to content

Commit 1c52bbd

Browse files
committed
feat: add project id
1 parent 1b2d6fa commit 1c52bbd

30 files changed

+2175
-393
lines changed

.cursor/mcp.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"mcpServers": {
3+
"gograph": {
4+
"command": "/Users/pedronauck/Dev/ai/gograph/bin/gograph",
5+
"args": ["serve-mcp"],
6+
"env": {
7+
"NEO4J_URI": "bolt://localhost:7687",
8+
"NEO4J_USERNAME": "neo4j",
9+
"NEO4J_PASSWORD": "password"
10+
}
11+
}
12+
}
13+
}

MCP_SETUP.md

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
# GoGraph MCP Server Setup for Cursor
2+
3+
This guide explains how to set up the GoGraph MCP (Model Context Protocol) server to work with Cursor IDE.
4+
5+
## Prerequisites
6+
7+
1. **Neo4j Database**: Ensure Neo4j is running on `bolt://localhost:7687`
8+
2. **Built GoGraph Binary**: Run `make build` to create the `bin/gograph` executable
9+
10+
## Setup Instructions
11+
12+
### 1. Build GoGraph
13+
14+
```bash
15+
make build
16+
```
17+
18+
### 2. Configure Cursor MCP Settings
19+
20+
1. Open Cursor IDE
21+
2. Go to **Settings** (Cmd/Ctrl + ,)
22+
3. Search for "mcp" or navigate to **Extensions****Model Context Protocol**
23+
4. Add the following configuration to your MCP settings:
24+
25+
```json
26+
{
27+
"mcpServers": {
28+
"gograph": {
29+
"command": "/Users/pedronauck/Dev/ai/gograph/bin/gograph",
30+
"args": ["serve-mcp"],
31+
"env": {
32+
"NEO4J_URI": "bolt://localhost:7687",
33+
"NEO4J_USERNAME": "neo4j",
34+
"NEO4J_PASSWORD": "password"
35+
}
36+
}
37+
}
38+
}
39+
```
40+
41+
**Important**: Update the paths and credentials:
42+
- Replace `/Users/pedronauck/Dev/ai/gograph/bin/gograph` with the absolute path to your built binary
43+
- Update Neo4j credentials if different from defaults
44+
45+
### 3. Alternative Configuration File
46+
47+
You can also create a separate MCP configuration file:
48+
49+
1. Create `~/.cursor/mcp_settings.json` with the configuration above
50+
2. Or use the provided `cursor-mcp-config.json` as a template
51+
52+
### 4. Restart Cursor
53+
54+
After adding the configuration, restart Cursor IDE for the changes to take effect.
55+
56+
## Available Tools
57+
58+
Once configured, Cursor will have access to 17 GoGraph tools:
59+
60+
### 📊 Analysis Tools
61+
- `analyze_project` - Analyze a Go project and build its dependency graph
62+
- `query_dependencies` - Query dependencies of a package or file
63+
64+
### 🧭 Navigation Tools
65+
- `find_implementations` - Find all implementations of an interface
66+
- `trace_call_chain` - Trace call chains between functions
67+
- `detect_circular_deps` - Detect circular dependencies in the project
68+
- `get_function_info` - Get detailed information about a function
69+
- `list_packages` - List all packages in the project
70+
- `get_package_structure` - Get detailed structure of a package
71+
72+
### 🔍 Query Tools
73+
- `execute_cypher` - Execute a Cypher query against the graph database
74+
- `natural_language_query` - Convert natural language to Cypher and execute
75+
76+
### ✅ Verification Tools
77+
- `verify_code_exists` - Verify if a code element exists in the project
78+
- `get_code_context` - Get context around a code element for LLM understanding
79+
- `validate_import_path` - Validate and resolve an import path
80+
81+
### 🎯 Pattern Tools
82+
- `detect_code_patterns` - Detect common code patterns and anti-patterns
83+
- `get_naming_conventions` - Analyze naming conventions used in the project
84+
85+
### 🧪 Test Tools
86+
- `find_tests_for_code` - Find tests for a specific code element
87+
- `check_test_coverage` - Check test coverage for packages or files
88+
89+
## Usage Examples
90+
91+
Once configured, you can use natural language in Cursor to interact with your Go codebase:
92+
93+
- *"Analyze this project and show me the main packages"*
94+
- *"Find all implementations of the ServiceAdapter interface"*
95+
- *"Show me the call chain from main() to database operations"*
96+
- *"What functions call the HandleRequest method?"*
97+
- *"Detect any circular dependencies in this codebase"*
98+
- *"Find tests for the ParseProject function"*
99+
100+
## Troubleshooting
101+
102+
### MCP Server Not Starting
103+
1. Verify Neo4j is running: `docker ps` or check your local Neo4j instance
104+
2. Check the binary path is correct and executable: `ls -la /path/to/bin/gograph`
105+
3. Test the server manually: `./bin/gograph serve-mcp --help`
106+
107+
### Connection Issues
108+
1. Verify Neo4j credentials in the configuration
109+
2. Check Neo4j is accessible: `nc -zv localhost 7687`
110+
3. Review Cursor's MCP logs in the developer console
111+
112+
### Tools Not Available
113+
1. Restart Cursor after configuration changes
114+
2. Check MCP configuration syntax is valid JSON
115+
3. Verify the `command` path points to the built binary
116+
117+
## Manual Testing
118+
119+
Test the MCP server manually:
120+
121+
```bash
122+
# Test tools list
123+
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' | ./bin/gograph serve-mcp
124+
125+
# Test project analysis (replace with your project path)
126+
echo '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"analyze_project","arguments":{"project_path":".","project_id":"test-project"}}}' | ./bin/gograph serve-mcp
127+
```
128+
129+
## Next Steps
130+
131+
Once set up, GoGraph will enhance Cursor's understanding of your Go codebase by providing:
132+
- Accurate code navigation and search
133+
- Dependency analysis and visualization
134+
- Interface implementation discovery
135+
- Call chain tracing
136+
- Anti-pattern detection
137+
- Test coverage analysis
138+
139+
The integration enables Cursor to provide more accurate suggestions and better understand your codebase structure.

Makefile

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,9 @@ ifeq ($(CI),true)
9393
done
9494
else
9595
@command -v docker-compose >/dev/null 2>&1 || { echo >&2 "docker-compose is required but not installed. Aborting."; exit 1; }
96-
@docker-compose -f docker-compose.test.yml up -d
96+
@docker-compose -f docker-compose.yml up -d
9797
@echo "Waiting for Neo4j to be ready..."
98-
@docker-compose -f docker-compose.test.yml exec -T neo4j-test wget -q --spider --tries=30 --waitretry=2 http://localhost:7474 || true
98+
@docker-compose -f docker-compose.yml exec -T neo4j-test wget -q --spider --tries=30 --waitretry=2 http://localhost:7474 || true
9999
endif
100100
@echo "Test dependencies ready"
101101

@@ -105,7 +105,7 @@ test-down:
105105
ifeq ($(CI),true)
106106
@echo "CI environment detected - skipping docker-compose down"
107107
else
108-
@docker-compose -f docker-compose.test.yml down
108+
@docker-compose -f docker-compose.yml down
109109
endif
110110

111111
# Clean test dependencies (including volumes)
@@ -114,15 +114,15 @@ test-clean:
114114
ifeq ($(CI),true)
115115
@echo "CI environment detected - skipping docker-compose cleanup"
116116
else
117-
@docker-compose -f docker-compose.test.yml down -v
117+
@docker-compose -f docker-compose.yml down -v
118118
endif
119119

120120
# Show test logs
121121
test-logs:
122122
ifeq ($(CI),true)
123123
@echo "CI environment detected - use GitHub Actions logs"
124124
else
125-
@docker-compose -f docker-compose.test.yml logs -f
125+
@docker-compose -f docker-compose.yml logs -f
126126
endif
127127

128128
# -----------------------------------------------------------------------------

bin/gograph

55.1 KB
Binary file not shown.

cmd/gograph/commands/analyze.go

Lines changed: 34 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ import (
1111
"github.com/compozy/gograph/engine/graph"
1212
"github.com/compozy/gograph/engine/infra"
1313
"github.com/compozy/gograph/engine/parser"
14+
"github.com/compozy/gograph/pkg/config"
1415
"github.com/compozy/gograph/pkg/errors"
1516
"github.com/compozy/gograph/pkg/logger"
1617
"github.com/compozy/gograph/pkg/progress"
1718
"github.com/spf13/cobra"
18-
"github.com/spf13/viper"
1919
)
2020

2121
var analyzeCmd = &cobra.Command{
@@ -58,74 +58,72 @@ The resulting graph allows you to:
5858

5959
// Wrap the entire command execution with panic recovery
6060
return errors.WithRecover("analyze_command", func() error {
61+
// Load configuration
62+
cfg, err := config.Load("")
63+
if err != nil {
64+
return fmt.Errorf("failed to load config: %w", err)
65+
}
66+
67+
// Get project ID from config or override flag
68+
projectID := core.ID(cfg.Project.ID)
69+
if projectIDFlag, err := cmd.Flags().GetString("project-id"); err == nil && projectIDFlag != "" {
70+
projectID = core.ID(projectIDFlag)
71+
}
72+
6173
// Check for --no-progress flag
6274
noProgress, err := cmd.Flags().GetBool("no-progress")
6375
if err != nil {
6476
return fmt.Errorf("failed to get no-progress flag: %w", err)
6577
}
6678

67-
// Initialize parser configuration from Viper
79+
// Initialize parser configuration from config
6880
parserConfig := &parser.Config{
69-
IgnoreDirs: viper.GetStringSlice("parser.ignore_dirs"),
70-
IgnoreFiles: viper.GetStringSlice("parser.ignore_files"),
71-
IncludeTests: viper.GetBool("parser.include_tests"),
72-
IncludeVendor: viper.GetBool("parser.include_vendor"),
73-
MaxConcurrency: viper.GetInt("parser.max_concurrency"),
81+
IgnoreDirs: cfg.Analysis.IgnoreDirs,
82+
IgnoreFiles: cfg.Analysis.IgnoreFiles,
83+
IncludeTests: cfg.Analysis.IncludeTests,
84+
IncludeVendor: cfg.Analysis.IncludeVendor,
85+
MaxConcurrency: cfg.Analysis.MaxConcurrency,
7486
}
7587

76-
// Initialize analyzer configuration from Viper
88+
// Initialize analyzer configuration with defaults
7789
analyzerConfig := analyzer.DefaultAnalyzerConfig()
78-
if maxDepth := viper.GetInt("analyzer.max_dependency_depth"); maxDepth > 0 {
79-
analyzerConfig.MaxDependencyDepth = maxDepth
80-
}
81-
if ignoreTest := viper.GetBool("analyzer.ignore_test_files"); ignoreTest {
82-
analyzerConfig.IgnoreTestFiles = ignoreTest
83-
}
84-
if ignoreVendor := viper.GetBool("analyzer.ignore_vendor"); ignoreVendor {
85-
analyzerConfig.IgnoreVendor = ignoreVendor
86-
}
87-
if includeMetrics := viper.GetBool("analyzer.include_metrics"); includeMetrics {
88-
analyzerConfig.IncludeMetrics = includeMetrics
89-
}
90-
if workers := viper.GetInt("analyzer.parallel_workers"); workers > 0 {
91-
analyzerConfig.ParallelWorkers = workers
92-
}
9390

94-
// Initialize Neo4j configuration from Viper with fallback to defaults
95-
neo4jURI := viper.GetString("neo4j.uri")
91+
// Initialize Neo4j configuration from config with fallback to defaults
92+
neo4jURI := cfg.Neo4j.URI
9693
if neo4jURI == "" {
97-
neo4jURI = DefaultNeo4jURI // Default only if not set via env vars
94+
neo4jURI = DefaultNeo4jURI
9895
}
99-
neo4jUsername := viper.GetString("neo4j.username")
96+
neo4jUsername := cfg.Neo4j.Username
10097
if neo4jUsername == "" {
101-
neo4jUsername = DefaultNeo4jUsername // Default only if not set via env vars
98+
neo4jUsername = DefaultNeo4jUsername
10299
}
103-
neo4jPassword := viper.GetString("neo4j.password")
100+
neo4jPassword := cfg.Neo4j.Password
104101
if neo4jPassword == "" {
105-
neo4jPassword = DefaultNeo4jPassword // Default only if not set via env vars
102+
neo4jPassword = DefaultNeo4jPassword
106103
}
107104

108105
neo4jConfig := &infra.Neo4jConfig{
109106
URI: neo4jURI,
110107
Username: neo4jUsername,
111108
Password: neo4jPassword,
112-
Database: viper.GetString("neo4j.database"),
109+
Database: cfg.Neo4j.Database,
113110
MaxRetries: 3,
114111
BatchSize: 1000,
115112
}
116113

117114
// Start the analysis
118115
if noProgress {
119-
return runAnalysisWithoutProgress(projectPath, parserConfig, analyzerConfig, neo4jConfig)
116+
return runAnalysisWithoutProgress(projectPath, projectID, parserConfig, analyzerConfig, neo4jConfig)
120117
}
121118

122-
return runAnalysisWithProgress(projectPath, parserConfig, analyzerConfig, neo4jConfig)
119+
return runAnalysisWithProgress(projectPath, projectID, parserConfig, analyzerConfig, neo4jConfig)
123120
})
124121
},
125122
}
126123

127124
func runAnalysisWithoutProgress(
128125
projectPath string,
126+
projectID core.ID,
129127
parserConfig *parser.Config,
130128
analyzerConfig *analyzer.Config,
131129
neo4jConfig *infra.Neo4jConfig,
@@ -151,7 +149,6 @@ func runAnalysisWithoutProgress(
151149
// -----
152150
logger.Info("analyzing project structure")
153151
analyzerService := analyzer.NewAnalyzer(analyzerConfig)
154-
projectID := core.NewID()
155152
analysisInput := &analyzer.AnalysisInput{
156153
ProjectID: projectID.String(),
157154
Files: parseResult.Files,
@@ -203,6 +200,7 @@ func runAnalysisWithoutProgress(
203200

204201
func runAnalysisWithProgress(
205202
projectPath string,
203+
projectID core.ID,
206204
parserConfig *parser.Config,
207205
analyzerConfig *analyzer.Config,
208206
neo4jConfig *infra.Neo4jConfig,
@@ -213,7 +211,6 @@ func runAnalysisWithProgress(
213211
var parseResult *parser.ParseResult
214212
var report *analyzer.AnalysisReport
215213
var graphResult *core.AnalysisResult
216-
var projectID core.ID
217214

218215
// -----
219216
// Parsing Phase
@@ -233,7 +230,6 @@ func runAnalysisWithProgress(
233230
// -----
234231
err = progress.WithProgress("Analyzing code structure", func() error {
235232
analyzerService := analyzer.NewAnalyzer(analyzerConfig)
236-
projectID = core.NewID()
237233
analysisInput := &analyzer.AnalysisInput{
238234
ProjectID: projectID.String(),
239235
Files: parseResult.Files,
@@ -298,7 +294,8 @@ func InitAnalyzeCommand() {
298294
initAnalyzeOnce.Do(func() {
299295
rootCmd.AddCommand(analyzeCmd)
300296

301-
// Add progress flag
297+
// Add flags
302298
analyzeCmd.Flags().Bool("no-progress", false, "Disable progress indicators")
299+
analyzeCmd.Flags().String("project-id", "", "Override project ID from config file")
303300
})
304301
}

0 commit comments

Comments
 (0)