@@ -23,7 +23,32 @@ const (
23
23
)
24
24
25
25
func 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 {
27
52
dbPath := flag .String ("db" , defaultDB , "Path to SQLite database file" )
28
53
addr := flag .String ("addr" , getDefaultAddress (), "Address to listen on" )
29
54
readWrite := flag .Bool ("read-write" , false ,
@@ -32,24 +57,31 @@ func main() {
32
57
33
58
flag .Parse ()
34
59
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 ,
46
65
}
66
+ }
47
67
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 {
49
83
ctx , cancel := context .WithCancel (context .Background ())
50
- defer cancel ()
51
84
52
- // Set up signal handling
53
85
sigCh := make (chan os.Signal , 1 )
54
86
signal .Notify (sigCh , os .Interrupt , syscall .SIGTERM )
55
87
go func () {
@@ -58,42 +90,56 @@ func main() {
58
90
cancel ()
59
91
}()
60
92
93
+ return ctx
94
+ }
95
+
96
+ // initializeDatabase validates and initializes the database connection
97
+ func initializeDatabase (dbPath string , readWrite bool ) * database.DB {
61
98
// 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 )
65
102
}
66
103
}
67
104
68
105
// Initialize database connection with read-only mode detection
69
- db , err := database .New (* dbPath , ! * readWrite )
106
+ db , err := database .New (dbPath , ! readWrite )
70
107
if err != nil {
71
108
log .Fatalf ("Failed to connect to database: %v" , err )
72
109
}
73
- defer func () {
74
- if err := db .Close (); err != nil {
75
- log .Printf ("Error closing database: %v" , err )
76
- }
77
- }()
78
110
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 (
81
124
"sqlite-mcp" ,
82
125
"1.0.0" ,
83
126
server .WithToolCapabilities (false ), // No tool list change notifications
84
127
server .WithResourceCapabilities (false , false ), // No resource subscriptions or change notifications
85
128
server .WithLogging (), // Enable logging
86
129
server .WithRecovery (), // Enable panic recovery
87
130
)
131
+ }
88
132
133
+ // registerToolsAndResources registers tools and resources with the MCP server
134
+ func registerToolsAndResources (mcpServer * server.MCPServer , db * database.DB , readWrite bool ) {
89
135
// Initialize tools and resources
90
136
queryTools := tools .New (db )
91
137
schemaResources := resources .New (db )
92
138
93
139
// Register tools based on read-write mode
94
140
for _ , tool := range queryTools .GetTools () {
95
141
// In read-only mode, skip write operations
96
- if ! * readWrite && (tool .Name == "execute_statement" ) {
142
+ if ! readWrite && (tool .Name == "execute_statement" ) {
97
143
log .Printf ("Skipping write tool '%s' in read-only mode" , tool .Name )
98
144
continue
99
145
}
@@ -109,28 +155,17 @@ func main() {
109
155
for _ , template := range schemaResources .GetResourceTemplates () {
110
156
mcpServer .AddResourceTemplate (template , schemaResources .HandleResource )
111
157
}
158
+ }
112
159
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 ) {
114
162
sseServer := server .NewSSEServer (mcpServer )
115
163
116
164
// Start server in a goroutine
117
165
errChan := make (chan error , 1 )
118
166
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 )
134
169
}()
135
170
136
171
// Wait for signal or error
@@ -146,6 +181,24 @@ func main() {
146
181
log .Println ("Server shutdown complete" )
147
182
}
148
183
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
+
149
202
// getDefaultAddress returns the address to listen on based on MCP_PORT environment variable.
150
203
// If the environment variable is not set, returns ":8080".
151
204
// If set, validates that the port is valid and returns ":<port>".
0 commit comments