@@ -23,7 +23,32 @@ const (
2323)
2424
2525func main () {
26- // Parse command line flags
26+ config := parseFlags ()
27+ if config .help {
28+ showHelp ()
29+ return
30+ }
31+
32+ ctx := setupContext ()
33+ db := initializeDatabase (config .dbPath , config .readWrite )
34+ defer closeDatabase (db )
35+
36+ mcpServer := createMCPServer ()
37+ registerToolsAndResources (mcpServer , db , config .readWrite )
38+
39+ runServer (ctx , mcpServer , config .addr , config .dbPath , config .readWrite )
40+ }
41+
42+ // Config holds the parsed command line configuration
43+ type Config struct {
44+ dbPath string
45+ addr string
46+ readWrite bool
47+ help bool
48+ }
49+
50+ // parseFlags parses command line flags and returns configuration
51+ func parseFlags () Config {
2752 dbPath := flag .String ("db" , defaultDB , "Path to SQLite database file" )
2853 addr := flag .String ("addr" , getDefaultAddress (), "Address to listen on" )
2954 readWrite := flag .Bool ("read-write" , false ,
@@ -32,24 +57,31 @@ func main() {
3257
3358 flag .Parse ()
3459
35- if * help {
36- fmt .Printf ("SQLite MCP Server - A Model Context Protocol server for SQLite databases\n \n " )
37- fmt .Printf ("Usage: %s [options]\n \n " , os .Args [0 ])
38- fmt .Printf ("Options:\n " )
39- flag .PrintDefaults ()
40- fmt .Printf ("\n Environment Variables:\n " )
41- fmt .Printf (" MCP_PORT Port to listen on (overrides -addr flag port)\n " )
42- fmt .Printf ("\n Example:\n " )
43- fmt .Printf (" %s -db ./mydata.db -addr :8080\n " , os .Args [0 ])
44- fmt .Printf (" MCP_PORT=9000 %s -db ./mydata.db\n " , os .Args [0 ])
45- return
60+ return Config {
61+ dbPath : * dbPath ,
62+ addr : * addr ,
63+ readWrite : * readWrite ,
64+ help : * help ,
4665 }
66+ }
4767
48- // Create a context that can be cancelled
68+ // showHelp displays the help message
69+ func showHelp () {
70+ fmt .Printf ("SQLite MCP Server - A Model Context Protocol server for SQLite databases\n \n " )
71+ fmt .Printf ("Usage: %s [options]\n \n " , os .Args [0 ])
72+ fmt .Printf ("Options:\n " )
73+ flag .PrintDefaults ()
74+ fmt .Printf ("\n Environment Variables:\n " )
75+ fmt .Printf (" MCP_PORT Port to listen on (overrides -addr flag port)\n " )
76+ fmt .Printf ("\n Example:\n " )
77+ fmt .Printf (" %s -db ./mydata.db -addr :8080\n " , os .Args [0 ])
78+ fmt .Printf (" MCP_PORT=9000 %s -db ./mydata.db\n " , os .Args [0 ])
79+ }
80+
81+ // setupContext creates a cancellable context with signal handling
82+ func setupContext () context.Context {
4983 ctx , cancel := context .WithCancel (context .Background ())
50- defer cancel ()
5184
52- // Set up signal handling
5385 sigCh := make (chan os.Signal , 1 )
5486 signal .Notify (sigCh , os .Interrupt , syscall .SIGTERM )
5587 go func () {
@@ -58,42 +90,56 @@ func main() {
5890 cancel ()
5991 }()
6092
93+ return ctx
94+ }
95+
96+ // initializeDatabase validates and initializes the database connection
97+ func initializeDatabase (dbPath string , readWrite bool ) * database.DB {
6198 // Validate database file exists (skip check for in-memory databases)
62- if * dbPath != ":memory:" {
63- if _ , err := os .Stat (* dbPath ); os .IsNotExist (err ) {
64- log .Fatalf ("Database file does not exist: %s" , * dbPath )
99+ if dbPath != ":memory:" {
100+ if _ , err := os .Stat (dbPath ); os .IsNotExist (err ) {
101+ log .Fatalf ("Database file does not exist: %s" , dbPath )
65102 }
66103 }
67104
68105 // Initialize database connection with read-only mode detection
69- db , err := database .New (* dbPath , ! * readWrite )
106+ db , err := database .New (dbPath , ! readWrite )
70107 if err != nil {
71108 log .Fatalf ("Failed to connect to database: %v" , err )
72109 }
73- defer func () {
74- if err := db .Close (); err != nil {
75- log .Printf ("Error closing database: %v" , err )
76- }
77- }()
78110
79- // Create MCP server with capabilities
80- mcpServer := server .NewMCPServer (
111+ return db
112+ }
113+
114+ // closeDatabase safely closes the database connection
115+ func closeDatabase (db * database.DB ) {
116+ if err := db .Close (); err != nil {
117+ log .Printf ("Error closing database: %v" , err )
118+ }
119+ }
120+
121+ // createMCPServer creates and configures the MCP server
122+ func createMCPServer () * server.MCPServer {
123+ return server .NewMCPServer (
81124 "sqlite-mcp" ,
82125 "1.0.0" ,
83126 server .WithToolCapabilities (false ), // No tool list change notifications
84127 server .WithResourceCapabilities (false , false ), // No resource subscriptions or change notifications
85128 server .WithLogging (), // Enable logging
86129 server .WithRecovery (), // Enable panic recovery
87130 )
131+ }
88132
133+ // registerToolsAndResources registers tools and resources with the MCP server
134+ func registerToolsAndResources (mcpServer * server.MCPServer , db * database.DB , readWrite bool ) {
89135 // Initialize tools and resources
90136 queryTools := tools .New (db )
91137 schemaResources := resources .New (db )
92138
93139 // Register tools based on read-write mode
94140 for _ , tool := range queryTools .GetTools () {
95141 // In read-only mode, skip write operations
96- if ! * readWrite && (tool .Name == "execute_statement" ) {
142+ if ! readWrite && (tool .Name == "execute_statement" ) {
97143 log .Printf ("Skipping write tool '%s' in read-only mode" , tool .Name )
98144 continue
99145 }
@@ -109,28 +155,17 @@ func main() {
109155 for _ , template := range schemaResources .GetResourceTemplates () {
110156 mcpServer .AddResourceTemplate (template , schemaResources .HandleResource )
111157 }
158+ }
112159
113- // Create SSE server with defaults (following OSV pattern)
160+ // runServer starts the server and handles shutdown
161+ func runServer (ctx context.Context , mcpServer * server.MCPServer , addr , dbPath string , readWrite bool ) {
114162 sseServer := server .NewSSEServer (mcpServer )
115163
116164 // Start server in a goroutine
117165 errChan := make (chan error , 1 )
118166 go func () {
119- mode := "read-write"
120- if ! * readWrite {
121- mode = "read-only"
122- }
123- log .Printf ("Starting SQLite MCP Server on %s (%s mode)" , * addr , mode )
124- log .Printf ("Database: %s" , * dbPath )
125-
126- if * readWrite {
127- log .Printf ("Available tools: execute_query, execute_statement, list_tables, describe_table" )
128- } else {
129- log .Printf ("Available tools: execute_query, list_tables, describe_table" )
130- }
131- log .Printf ("Available resources: schema://tables, schema://table/{name}" )
132-
133- errChan <- sseServer .Start (* addr )
167+ logServerStart (addr , dbPath , readWrite )
168+ errChan <- sseServer .Start (addr )
134169 }()
135170
136171 // Wait for signal or error
@@ -146,6 +181,24 @@ func main() {
146181 log .Println ("Server shutdown complete" )
147182}
148183
184+ // logServerStart logs server startup information
185+ func logServerStart (addr , dbPath string , readWrite bool ) {
186+ mode := "read-only"
187+ if readWrite {
188+ mode = "read-write"
189+ }
190+
191+ log .Printf ("Starting SQLite MCP Server on %s (%s mode)" , addr , mode )
192+ log .Printf ("Database: %s" , dbPath )
193+
194+ if readWrite {
195+ log .Printf ("Available tools: execute_query, execute_statement, list_tables, describe_table" )
196+ } else {
197+ log .Printf ("Available tools: execute_query, list_tables, describe_table" )
198+ }
199+ log .Printf ("Available resources: schema://tables, schema://table/{name}" )
200+ }
201+
149202// getDefaultAddress returns the address to listen on based on MCP_PORT environment variable.
150203// If the environment variable is not set, returns ":8080".
151204// If set, validates that the port is valid and returns ":<port>".
0 commit comments