feat: add Go server implementation in go-server/
Full Go port of local-mcp with all core features. Copied from local-mcp-go worktree to consolidate into single-branch repo (easier maintenance). Architecture: - internal/config: Environment variable configuration - internal/models: Shared types (Instruction, Settings, AgentActivity, etc.) - internal/db: SQLite init with modernc.org/sqlite (pure Go, no CGo) - internal/store: Database operations + WakeupSignal + AgentTracker - internal/events: SSE broker for browser /api/events endpoint - internal/mcp: get_user_request MCP tool with 5s keepalive progress bars - internal/api: chi HTTP router with Bearer auth middleware - main.go: Entry point with auto port switching and Windows interactive banner Dependencies: - github.com/mark3labs/mcp-go@v0.46.0 - github.com/go-chi/chi/v5@v5.2.5 - modernc.org/sqlite@v1.47.0 (pure Go SQLite) - github.com/google/uuid@v1.6.0 Static assets embedded via //go:embed static Features matching Python: - Same wait strategy: 50s with 5s progress keepalives - Same hardcoded constants (DEFAULT_WAIT_SECONDS, DEFAULT_EMPTY_RESPONSE) - Auto port switching (tries 8000-8009) - Windows interactive mode (formatted banner on double-click launch) Build: cd go-server && go build -o local-mcp.exe . Run: ./local-mcp.exe Binary size: ~18 MB (vs Python ~60+ MB memory footprint) Startup: ~10 ms (vs Python ~1-2s)
This commit is contained in:
69
go-server/internal/store/settings.go
Normal file
69
go-server/internal/store/settings.go
Normal file
@@ -0,0 +1,69 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/local-mcp/local-mcp-go/internal/models"
|
||||
)
|
||||
|
||||
// SettingsStore reads and writes the settings table.
|
||||
type SettingsStore struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
// NewSettingsStore creates a SettingsStore backed by db.
|
||||
func NewSettingsStore(db *sql.DB) *SettingsStore {
|
||||
return &SettingsStore{db: db}
|
||||
}
|
||||
|
||||
// Get returns the current settings.
|
||||
func (s *SettingsStore) Get() (models.Settings, error) {
|
||||
rows, err := s.db.Query(`SELECT key, value FROM settings`)
|
||||
if err != nil {
|
||||
return models.Settings{}, fmt.Errorf("get settings: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
cfg := models.Settings{
|
||||
DefaultWaitSeconds: 10,
|
||||
AgentStaleAfterSeconds: 30,
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
var key, value string
|
||||
if err := rows.Scan(&key, &value); err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
switch key {
|
||||
case "default_wait_seconds":
|
||||
if n, err := strconv.Atoi(value); err == nil {
|
||||
cfg.DefaultWaitSeconds = n
|
||||
}
|
||||
case "default_empty_response":
|
||||
cfg.DefaultEmptyResponse = value
|
||||
case "agent_stale_after_seconds":
|
||||
if n, err := strconv.Atoi(value); err == nil {
|
||||
cfg.AgentStaleAfterSeconds = n
|
||||
}
|
||||
}
|
||||
}
|
||||
return cfg, rows.Err()
|
||||
}
|
||||
|
||||
// Update saves settings. Only non-nil fields are updated; pass a partial
|
||||
// struct pointer using the Patch helper below.
|
||||
func (s *SettingsStore) Update(patch models.Settings) error {
|
||||
_, err := s.db.Exec(`
|
||||
INSERT OR REPLACE INTO settings (key, value) VALUES
|
||||
('default_wait_seconds', ?),
|
||||
('default_empty_response', ?),
|
||||
('agent_stale_after_seconds', ?)`,
|
||||
strconv.Itoa(patch.DefaultWaitSeconds),
|
||||
patch.DefaultEmptyResponse,
|
||||
strconv.Itoa(patch.AgentStaleAfterSeconds),
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user