@@ -29,7 +29,10 @@ import (
2929 "github.com/malbeclabs/doublezero/tools/dz-ai/internal/mcp/duck"
3030 "github.com/malbeclabs/doublezero/tools/dz-ai/internal/mcp/metrics"
3131 "github.com/malbeclabs/doublezero/tools/dz-ai/internal/mcp/server"
32+ "github.com/malbeclabs/doublezero/tools/maxmind/pkg/geoip"
33+ "github.com/malbeclabs/doublezero/tools/maxmind/pkg/metrodb"
3234 "github.com/malbeclabs/doublezero/tools/solana/pkg/rpc"
35+ "github.com/oschwald/geoip2-golang"
3336)
3437
3538var (
@@ -45,10 +48,13 @@ const (
4548 defaultMaxConcurrency = 64
4649 defaultDZEnv = config .EnvMainnetBeta
4750 defaultMetricsAddr = "0.0.0.0:8080"
48- // defaultDBPath = ".tmp/mcp.duckdb"
4951 defaultDBPath = ":memory:"
5052 defaultDuckDBSpillPath = ".tmp/duckdb-spill-tmp"
5153 defaultDBPathEnvVar = "MCP_DB_PATH"
54+ defaultGeoipCityDBPath = "/usr/share/GeoIP/GeoLite2-City.mmdb"
55+ defaultGeoipASNDBPath = "/usr/share/GeoIP/GeoLite2-ASN.mmdb"
56+ geoipCityDBPathEnvVar = "MCP_GEOIP_CITY_DB_PATH"
57+ geoipASNDBPathEnvVar = "MCP_GEOIP_ASN_DB_PATH"
5258)
5359
5460func main () {
@@ -69,6 +75,8 @@ func run() error {
6975 metricsAddrFlag := flag .String ("metrics-addr" , defaultMetricsAddr , "Address to listen on for prometheus metrics" )
7076 dbPathFlag := flag .String ("db-path" , defaultDBPath , "Path to DuckDB database file (empty for in-memory, or set MCP_DB_PATH env var)" )
7177 dbSpillDirFlag := flag .String ("db-spill-dir" , defaultDuckDBSpillPath , "Path to DuckDB temporary spill directory" )
78+ geoipCityDBPathFlag := flag .String ("geoip-city-db-path" , defaultGeoipCityDBPath , "Path to MaxMind GeoIP2 City database file (or set MCP_GEOIP_CITY_DB_PATH env var)" )
79+ geoipASNDBPathFlag := flag .String ("geoip-asn-db-path" , defaultGeoipASNDBPath , "Path to MaxMind GeoIP2 ASN database file (or set MCP_GEOIP_ASN_DB_PATH env var)" )
7280 flag .Parse ()
7381
7482 networkConfig , err := config .NetworkConfigForEnv (* envFlag )
@@ -137,8 +145,10 @@ func run() error {
137145
138146 // Determine database path: flag takes precedence, then env var, then default
139147 dbPath := * dbPathFlag
140- if dbPath == "" {
141- dbPath = os .Getenv (defaultDBPathEnvVar )
148+ if dbPath == "" || dbPath == defaultDBPath {
149+ if envPath := os .Getenv (defaultDBPathEnvVar ); envPath != "" {
150+ dbPath = envPath
151+ }
142152 }
143153 if dbPath == "" {
144154 dbPath = defaultDBPath
@@ -151,6 +161,28 @@ func run() error {
151161 }
152162 defer dbCloseFn ()
153163
164+ // Determine GeoIP database paths: flag takes precedence, then env var, then default
165+ geoipCityDBPath := * geoipCityDBPathFlag
166+ if geoipCityDBPath == defaultGeoipCityDBPath {
167+ if envPath := os .Getenv (geoipCityDBPathEnvVar ); envPath != "" {
168+ geoipCityDBPath = envPath
169+ }
170+ }
171+
172+ geoipASNDBPath := * geoipASNDBPathFlag
173+ if geoipASNDBPath == defaultGeoipASNDBPath {
174+ if envPath := os .Getenv (geoipASNDBPathEnvVar ); envPath != "" {
175+ geoipASNDBPath = envPath
176+ }
177+ }
178+
179+ // Initialize GeoIP resolver
180+ geoIPResolver , geoIPCloseFn , err := initializeGeoIP (geoipCityDBPath , geoipASNDBPath , log )
181+ if err != nil {
182+ return fmt .Errorf ("failed to initialize GeoIP: %w" , err )
183+ }
184+ defer geoIPCloseFn ()
185+
154186 // Parse allowed tokens from environment variable (comma-separated)
155187 // Auth can be explicitly disabled with MCP_AUTH_DISABLED=true
156188 var allowedTokens []string
@@ -188,6 +220,7 @@ func run() error {
188220 InternetLatencyAgentPK : networkConfig .InternetLatencyCollectorPK ,
189221 InternetDataProviders : telemetryconfig .InternetTelemetryDataProviders ,
190222 AllowedTokens : allowedTokens ,
223+ GeoIPResolver : geoIPResolver ,
191224 })
192225 if err != nil {
193226 return fmt .Errorf ("failed to create server: %w" , err )
@@ -273,3 +306,40 @@ func initializeDuckDB(dbPath string, spillDir string, log *slog.Logger) (duck.DB
273306 return nil
274307 }, nil
275308}
309+
310+ func initializeGeoIP (cityDBPath , asnDBPath string , log * slog.Logger ) (geoip.Resolver , func () error , error ) {
311+ cityDB , err := geoip2 .Open (cityDBPath )
312+ if err != nil {
313+ return nil , nil , fmt .Errorf ("failed to open GeoIP city database: %w" , err )
314+ }
315+
316+ asnDB , err := geoip2 .Open (asnDBPath )
317+ if err != nil {
318+ cityDB .Close ()
319+ return nil , nil , fmt .Errorf ("failed to open GeoIP ASN database: %w" , err )
320+ }
321+
322+ metroDB , err := metrodb .New ()
323+ if err != nil {
324+ cityDB .Close ()
325+ asnDB .Close ()
326+ return nil , nil , fmt .Errorf ("failed to create metro database: %w" , err )
327+ }
328+
329+ resolver , err := geoip .NewResolver (log , cityDB , asnDB , metroDB )
330+ if err != nil {
331+ cityDB .Close ()
332+ asnDB .Close ()
333+ return nil , nil , fmt .Errorf ("failed to create GeoIP resolver: %w" , err )
334+ }
335+
336+ return resolver , func () error {
337+ if err := cityDB .Close (); err != nil {
338+ return fmt .Errorf ("failed to close city database: %w" , err )
339+ }
340+ if err := asnDB .Close (); err != nil {
341+ return fmt .Errorf ("failed to close ASN database: %w" , err )
342+ }
343+ return nil
344+ }, nil
345+ }
0 commit comments