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)
59 lines
1.6 KiB
Go
59 lines
1.6 KiB
Go
package store
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/local-mcp/local-mcp-go/internal/models"
|
|
)
|
|
|
|
// AgentStore records and retrieves agent connectivity data.
|
|
type AgentStore struct {
|
|
db *sql.DB
|
|
}
|
|
|
|
// NewAgentStore creates an AgentStore backed by db.
|
|
func NewAgentStore(db *sql.DB) *AgentStore {
|
|
return &AgentStore{db: db}
|
|
}
|
|
|
|
// Record upserts agent activity for agentID with the given result type.
|
|
func (s *AgentStore) Record(agentID, resultType string) error {
|
|
now := time.Now().UTC().Format(time.RFC3339Nano)
|
|
_, err := s.db.Exec(`
|
|
INSERT INTO agent_activity (agent_id, last_seen_at, last_fetch_at, last_result_type)
|
|
VALUES (?, ?, ?, ?)
|
|
ON CONFLICT(agent_id) DO UPDATE SET
|
|
last_seen_at = excluded.last_seen_at,
|
|
last_fetch_at = excluded.last_fetch_at,
|
|
last_result_type = excluded.last_result_type`,
|
|
agentID, now, now, resultType)
|
|
return err
|
|
}
|
|
|
|
// Latest returns the most recently active agent, or nil if no agent has ever
|
|
// called get_user_request.
|
|
func (s *AgentStore) Latest() (*models.AgentActivity, error) {
|
|
row := s.db.QueryRow(`
|
|
SELECT agent_id, last_seen_at, last_fetch_at, last_result_type
|
|
FROM agent_activity
|
|
ORDER BY last_seen_at DESC
|
|
LIMIT 1`)
|
|
|
|
var a models.AgentActivity
|
|
var seenStr, fetchStr string
|
|
err := row.Scan(&a.AgentID, &seenStr, &fetchStr, &a.LastResultType)
|
|
if err == sql.ErrNoRows {
|
|
return nil, nil
|
|
}
|
|
if err != nil {
|
|
return nil, fmt.Errorf("latest agent: %w", err)
|
|
}
|
|
|
|
a.LastSeenAt, _ = time.Parse(time.RFC3339Nano, seenStr)
|
|
a.LastFetchAt, _ = time.Parse(time.RFC3339Nano, fetchStr)
|
|
return &a, nil
|
|
}
|
|
|