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)
70 lines
1.6 KiB
Go
70 lines
1.6 KiB
Go
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
|
|
}
|
|
|