Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
a610ecc
add manual flag parsing for username, password, uri and database env …
keremgocen Nov 18, 2025
290a165
test edge cases for flags
keremgocen Nov 18, 2025
87bfb18
test coverage for loadConfig with env vars
keremgocen Nov 18, 2025
f6304fe
add config integration tests with real env variables
keremgocen Nov 18, 2025
a528603
fix the error message order
keremgocen Nov 18, 2025
429942f
clean up, minor improvements
keremgocen Nov 18, 2025
f450bdd
add AURA_USERNAME for CI
keremgocen Nov 18, 2025
d0a86ad
Update internal/cli/args.go
keremgocen Nov 18, 2025
1a0a176
Update internal/cli/args.go
keremgocen Nov 18, 2025
6d5750f
Update args.go
keremgocen Nov 18, 2025
5496aa6
Update internal/cli/args.go
keremgocen Nov 20, 2025
7bb4f1a
remove args walkthrough
keremgocen Nov 20, 2025
028c79d
Merge remote-tracking branch 'refs/remotes/origin/keremgocen/remove-d…
keremgocen Nov 20, 2025
8753520
remove the integration test, add cli overrides
keremgocen Nov 20, 2025
6da66be
add ParseConfigFlags
keremgocen Nov 20, 2025
c793930
add test coverage
keremgocen Nov 20, 2025
ead32ca
convert Telemetry and ReadOnly to booleans
keremgocen Nov 20, 2025
bfc4ccb
remove config integration test
keremgocen Nov 20, 2025
c8f30d6
Merge branch 'main' into keremgocen/remove-default-env-variables
keremgocen Nov 20, 2025
7a7a96b
Update internal/cli/args.go
keremgocen Nov 20, 2025
c2fe6f8
fix merge confict issue
keremgocen Nov 20, 2025
8c5cdd8
enable telemetry only when ALL requirements are met, reverted the log…
keremgocen Nov 20, 2025
1537961
Merge branch 'main' into keremgocen/remove-default-env-variables
keremgocen Nov 20, 2025
b4dc003
update docs
keremgocen Nov 20, 2025
b516560
add release notes for a minor bump
keremgocen Nov 20, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changes/unreleased/Minor-20251120-144649.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
kind: Minor
body: Added CLI flags for configuration (--neo4j-uri, --neo4j-username, --neo4j-password, --neo4j-database, --neo4j-read-only, --neo4j-telemetry) that override environment variables. NEO4J_URI, NEO4J_USERNAME, and NEO4J_PASSWORD are now required environment variables (no defaults provided).
time: 2025-11-20T14:46:49.67341Z
3 changes: 2 additions & 1 deletion .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,5 +67,6 @@ jobs:
run: go test -v -tags=integration ./test/integration/...
env:
USE_CONTAINER: "false"
NEO4J_PASSWORD: ${{ secrets.AURA_PASSWORD }}
NEO4J_URI: ${{ secrets.AURA_URL }}
NEO4J_USERNAME: ${{ secrets.AURA_USERNAME }}
NEO4J_PASSWORD: ${{ secrets.AURA_PASSWORD }}
14 changes: 10 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,23 @@ export PATH="$PATH:$(go env GOPATH)/bin"
## Environment Variables

The MCP server requires certain environment variables to connect to a Neo4j instance.
Defaults are provided for local development.
For local testing, make sure to set these environment variables (your local Neo4j instance must be running and it might require different credentials):

**Required variables** (server will not start without these):
```bash
export NEO4J_URI="bolt://localhost:7687"
export NEO4J_USERNAME="neo4j"
export NEO4J_PASSWORD="password"
export NEO4J_DATABASE="neo4j"
export NEO4J_READ_ONLY="true" // Optional: disables write tools
```

**Optional variables** (with defaults):
```bash
export NEO4J_DATABASE="neo4j" # Default: neo4j
export NEO4J_READ_ONLY="false" # Default: false (set to "true" to disable write tools)
export NEO4J_TELEMETRY="true" # Default: true
```

**Note:** Make sure your local Neo4j instance is running with the correct credentials before testing.

## Build / Test / Run

```bash
Expand Down
94 changes: 65 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,37 @@ neo4j-mcp -v

Should print the installed version.

## Configuration Options

The `neo4j-mcp` server can be configured using environment variables or CLI flags. CLI flags take precedence over environment variables.

### Environment Variables

See the configuration examples below for VSCode and Claude Desktop.

### CLI Flags

You can override any environment variable using CLI flags:

```bash
neo4j-mcp --neo4j-uri "bolt://localhost:7687" \
--neo4j-username "neo4j" \
--neo4j-password "password" \
--neo4j-database "neo4j" \
--neo4j-read-only false \
--neo4j-telemetry true
```

Available flags:
- `--neo4j-uri` - Neo4j connection URI (overrides NEO4J_URI)
- `--neo4j-username` - Database username (overrides NEO4J_USERNAME)
- `--neo4j-password` - Database password (overrides NEO4J_PASSWORD)
- `--neo4j-database` - Database name (overrides NEO4J_DATABASE)
- `--neo4j-read-only` - Enable read-only mode: `true` or `false` (overrides NEO4J_READ_ONLY)
- `--neo4j-telemetry` - Enable telemetry: `true` or `false` (overrides NEO4J_TELEMETRY)

Use `neo4j-mcp --help` to see all available options.

## Configure VSCode (MCP)

Create / edit `mcp.json` (docs: https://code.visualstudio.com/docs/copilot/customization/mcp-servers):
Expand All @@ -65,20 +96,22 @@ Create / edit `mcp.json` (docs: https://code.visualstudio.com/docs/copilot/custo
"type": "stdio",
"command": "neo4j-mcp",
"env": {
"NEO4J_URI": "bolt://localhost:7687",
"NEO4J_USERNAME": "neo4j",
"NEO4J_PASSWORD": "password",
"NEO4J_DATABASE": "neo4j",
"NEO4J_READ_ONLY": "true", // Optional: disables write tools
"NEO4J_TELEMETRY": "false", // Optional: disables telemetry
"NEO4J_LOG_LEVEL": "info", // Optional: log level (debug, info, notice, warning, error, critical, alert, emergency)
"NEO4J_LOG_FORMAT": "text" // Optional: log format (text or json)
"NEO4J_URI": "bolt://localhost:7687", // Required: Neo4j connection URI
"NEO4J_USERNAME": "neo4j", // Required: Database username
"NEO4J_PASSWORD": "password", // Required: Database password
"NEO4J_DATABASE": "neo4j", // Optional: Database name (default: neo4j)
"NEO4J_READ_ONLY": "true", // Optional: Disables write tools (default: false)
"NEO4J_TELEMETRY": "false", // Optional: Disables telemetry (default: true)
"NEO4J_LOG_LEVEL": "info", // Optional: Log level (default: info)
"NEO4J_LOG_FORMAT": "text" // Optional: Log format (default: text)
}
}
}
}
```

**Note:** The first three environment variables (NEO4J_URI, NEO4J_USERNAME, NEO4J_PASSWORD) are **required**. The server will fail to start if any of these are missing.

Restart VSCode; open Copilot Chat and ask: "List Neo4j MCP tools" to confirm.

## Configure Claude Desktop
Expand All @@ -92,7 +125,7 @@ We’ll need to configure Claude for Desktop for whichever MCP servers you want

in a text editor. Make sure to create the file if it doesn’t exist.

Youll then add the `neo4j-mcp` MCP in the mcpServers key:
You'll then add the `neo4j-mcp` MCP in the mcpServers key:

```json
{
Expand All @@ -102,30 +135,25 @@ You’ll then add the `neo4j-mcp` MCP in the mcpServers key:
"command": "neo4j-mcp",
"args": [],
"env": {
"NEO4J_URI": "bolt://localhost:7687",
"NEO4J_USERNAME": "neo4j",
"NEO4J_PASSWORD": "password",
"NEO4J_DATABASE": "neo4j",
"NEO4J_READ_ONLY": "true", // Optional: disables write tools
"NEO4J_TELEMETRY": "false", // Optional: disables telemetry
"NEO4J_LOG_LEVEL": "info", // Optional: log level (debug, info, notice, warning, error, critical, alert, emergency)
"NEO4J_LOG_FORMAT": "text" // Optional: log format (text or json)
"NEO4J_URI": "bolt://localhost:7687", // Required: Neo4j connection URI
"NEO4J_USERNAME": "neo4j", // Required: Database username
"NEO4J_PASSWORD": "password", // Required: Database password
"NEO4J_DATABASE": "neo4j", // Optional: Database name (default: neo4j)
"NEO4J_READ_ONLY": "true", // Optional: Disables write tools (default: false)
"NEO4J_TELEMETRY": "false", // Optional: Disables telemetry (default: true)
"NEO4J_LOG_LEVEL": "info", // Optional: Log level (default: info)
"NEO4J_LOG_FORMAT": "text" // Optional: Log format (default: text)
}
}
}
}
```

Notes:

- Adjust env vars for your setup (defaults shown above).
- Set `NEO4J_READ_ONLY=true` to disable all write tools (e.g., `write-cypher`).
- Set `NEO4J_TELEMETRY=false` to disable telemetry.
- Set `NEO4J_LOG_LEVEL` to control logging verbosity (see Logging section below).
- Set `NEO4J_LOG_FORMAT` to `json` for structured JSON logs (default is `text`).
- When enabled, only read operations are available; write tools are not exposed to clients.
- Neo4j Desktop default URI: `bolt://localhost:7687`.
- Aura: use the connection string from the Aura console.
**Important Notes:**
- The first three environment variables (NEO4J_URI, NEO4J_USERNAME, NEO4J_PASSWORD) are **required**. The server will fail to start if any are missing.
- Neo4j Desktop default URI: `bolt://localhost:7687`
- Aura: use the connection string from the Aura console
- See the [Readonly Mode](#readonly-mode-flag), [Logging](#logging), and [Telemetry](#telemetry) sections below for more details on optional configuration.

## Tools & Usage

Expand All @@ -140,7 +168,13 @@ Provided tools:

### Readonly mode flag

Enable readonly mode by setting the `NEO4J_READ_ONLY` environment variable to `true` (for example, `"NEO4J_READ_ONLY": "true"`).
Enable readonly mode by setting the `NEO4J_READ_ONLY` environment variable to `true` (for example, `"NEO4J_READ_ONLY": "true"`). Accepted values are `true` or `false` (default: `false`).

You can also override this setting using the `--neo4j-read-only` CLI flag:
```bash
neo4j-mcp --neo4j-uri "bolt://localhost:7687" --neo4j-username "neo4j" --neo4j-password "password" --neo4j-read-only true
```

When enabled, write tools (for example, `write-cypher`) are not exposed to clients.

### Query Classification
Expand Down Expand Up @@ -189,7 +223,9 @@ By default, `neo4j-mcp` collects anonymous usage data to help us improve the pro
This includes information like the tools being used, the operating system, and CPU architecture.
We do not collect any personal or sensitive information.

To disable telemetry, set the `NEO4J_TELEMETRY` environment variable to `"false"`.
To disable telemetry, set the `NEO4J_TELEMETRY` environment variable to `"false"`. Accepted values are `true` or `false` (default: `true`).

You can also use the `--neo4j-telemetry` CLI flag to override this setting.

## Documentation

Expand Down
29 changes: 20 additions & 9 deletions cmd/neo4j-mcp/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"context"
"fmt"
"log"
"log/slog"
"os"

Expand All @@ -24,8 +25,18 @@ func main() {
// Handle CLI arguments (version, help, etc.)
cli.HandleArgs(Version)

// get config from environment variables
cfg, err := config.LoadConfig()
// Parse CLI flags for configuration
cliArgs := cli.ParseConfigFlags()

// Load and validate configuration (env vars + CLI overrides)
cfg, err := config.LoadConfig(&config.CLIOverrides{
URI: cliArgs.URI,
Username: cliArgs.Username,
Password: cliArgs.Password,
Database: cliArgs.Database,
ReadOnly: cliArgs.ReadOnly,
Telemetry: cliArgs.Telemetry,
})
if err != nil {
// Can't use logger here yet, so just print to stderr
fmt.Fprintln(os.Stderr, "Failed to load configuration: "+err.Error())
Expand Down Expand Up @@ -59,14 +70,14 @@ func main() {

anService := analytics.NewAnalytics(MixPanelToken, MixPanelEndpoint, cfg.URI)

if cfg.Telemetry == "false" || MixPanelEndpoint == "" || MixPanelToken == "" {
slog.Info("Telemetry disabled.")
anService.Disable()
} else if cfg.Telemetry == "true" {
// Enable telemetry only when user has opted in AND the required tokens are present
if cfg.Telemetry && MixPanelEndpoint != "" && MixPanelToken != "" {
anService.Enable()
slog.Info("Telemetry is enabled to help us improve the product by collecting anonymous usage data " +
"such as: tools being used, the operating system, and CPU architecture.\n" +
"To disable telemetry, set the NEO4J_TELEMETRY environment variable to \"false\".")
log.Println("Telemetry is enabled to help us improve the product by collecting anonymous usage data such as: tools being used, the operating system, and CPU architecture.")
log.Println("To disable telemetry, set the NEO4J_TELEMETRY environment variable to \"false\".")
} else {
log.Println("Telemetry disabled.")
anService.Disable()
}

// Create and configure the MCP server
Expand Down
100 changes: 90 additions & 10 deletions internal/cli/args.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package cli

import (
"flag"
"fmt"
"os"
"strings"
)

// osExit is a variable that can be mocked in tests
Expand All @@ -14,38 +16,116 @@ Usage:
neo4j-mcp [OPTIONS]

Options:
-h, --help Show this help message
-v, --version Show version information
-h, --help Show this help message
-v, --version Show version information
--neo4j-uri <URI> Neo4j connection URI (overrides environment variable NEO4J_URI)
--neo4j-username <USERNAME> Database username (overrides environment variable NEO4J_USERNAME)
--neo4j-password <PASSWORD> Database password (overrides environment variable NEO4J_PASSWORD)
--neo4j-database <DATABASE> Database name (overrides environment variable NEO4J_DATABASE)
--neo4j-read-only <BOOLEAN> Enable read-only mode: true or false (overrides environment variable NEO4J_READ_ONLY)
--neo4j-telemetry <BOOLEAN> Enable telemetry: true or false (overrides environment variable NEO4J_TELEMETRY)

Environment Variables:
NEO4J_URI Neo4j database URI (default: bolt://localhost:7687)
NEO4J_USERNAME Database username (default: neo4j)
NEO4J_PASSWORD Database password (required)
Required Environment Variables:
NEO4J_URI Neo4j database URI
NEO4J_USERNAME Database username
NEO4J_PASSWORD Database password

Optional Environment Variables:
NEO4J_DATABASE Database name (default: neo4j)
NEO4J_TELEMETRY Enable/disable telemetry (default: true)
NEO4J_READ_ONLY Enable read-only mode (default: false)

Examples:
NEO4J_PASSWORD=mypassword neo4j-mcp
NEO4J_URI=bolt://db.example.com:7687 NEO4J_USERNAME=admin NEO4J_PASSWORD=secret neo4j-mcp
# Using environment variables
NEO4J_URI=bolt://localhost:7687 NEO4J_USERNAME=neo4j NEO4J_PASSWORD=password neo4j-mcp

# Using CLI flags (takes precedence over environment variables)
neo4j-mcp --neo4j-uri bolt://localhost:7687 --neo4j-username neo4j --neo4j-password password

For more information, visit: https://github.com/neo4j/mcp
`

// Args holds configuration values parsed from command-line flags
type Args struct {
URI string
Username string
Password string
Database string
ReadOnly string
Telemetry string
}

// ParseConfigFlags parses CLI flags and returns configuration values.
// It should be called after HandleArgs to ensure help/version flags are processed first.
func ParseConfigFlags() *Args {
neo4jURI := flag.String("neo4j-uri", "", "Neo4j connection URI (overrides NEO4J_URI env var)")
neo4jUsername := flag.String("neo4j-username", "", "Neo4j username (overrides NEO4J_USERNAME env var)")
neo4jPassword := flag.String("neo4j-password", "", "Neo4j password (overrides NEO4J_PASSWORD env var)")
neo4jDatabase := flag.String("neo4j-database", "", "Neo4j database name (overrides NEO4J_DATABASE env var)")
neo4jReadOnly := flag.String("neo4j-read-only", "", "Enable read-only mode: true or false (overrides NEO4J_READ_ONLY env var)")
neo4jTelemetry := flag.String("neo4j-telemetry", "", "Enable telemetry: true or false (overrides NEO4J_TELEMETRY env var)")

flag.Parse()

return &Args{
URI: *neo4jURI,
Username: *neo4jUsername,
Password: *neo4jPassword,
Database: *neo4jDatabase,
ReadOnly: *neo4jReadOnly,
Telemetry: *neo4jTelemetry,
}
}

// HandleArgs processes command-line arguments for version and help flags.
// It exits the program after displaying the requested information.
// If unknown flags are encountered, it prints an error message and exits.
// Known configuration flags are skipped here so that the flag package in main.go can handle them properly.
func HandleArgs(version string) {
if len(os.Args) <= 1 {
return
}

flags := make(map[string]bool)
var err error
i := 1 // we start from 1 because os.Args[0] is the program name ("neo4j-mcp") - not a flag

for _, arg := range os.Args[1:] {
for i < len(os.Args) {
arg := os.Args[i]
switch arg {
case "-h", "--help":
flags["help"] = true
i++
case "-v", "--version":
flags["version"] = true
i++
// Allow configuration flags to be parsed by the flag package
case "--neo4j-uri", "--neo4j-username", "--neo4j-password", "--neo4j-database", "--neo4j-read-only", "--neo4j-telemetry":
// Check if there's a value following the flag
if i+1 >= len(os.Args) {
err = fmt.Errorf("%s requires a value", arg)
break
}
// Check if next argument is another flag (starts with --)
nextArg := os.Args[i+1]
if strings.HasPrefix(nextArg, "-") {
err = fmt.Errorf("%s requires a value (got flag %s instead)", arg, nextArg)
break
}
// Safe to skip flag and value - let flag package handle them
i += 2
default:
err = fmt.Errorf("unknown flag or argument: %s", arg)
if arg == "--" {
// Stop processing our flags, let flag package handle the rest
i = len(os.Args)
} else {
err = fmt.Errorf("unknown flag or argument: %s", arg)
i++
}
}
// Exit loop if an error occurred
if err != nil {
break
}
}

Expand Down
Loading