Skip to main content
Glama

Umami MCP Server

MIT License
2
  • Apple
  • Linux
umami.go5.54 kB
package main import ( "bytes" "context" "encoding/json" "fmt" "io" "net/http" "strings" "time" ) type UmamiClient struct { baseURL string username string password string token string httpClient *http.Client } func NewUmamiClient(baseURL, username, password string) *UmamiClient { baseURL = strings.TrimSuffix(baseURL, "/") return &UmamiClient{ baseURL: baseURL, username: username, password: password, httpClient: &http.Client{Timeout: 30 * time.Second}, } } func (c *UmamiClient) Authenticate() error { payload := map[string]string{ "username": c.username, "password": c.password, } data, _ := json.Marshal(payload) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.baseURL+"/api/auth/login", bytes.NewReader(data)) if err != nil { return fmt.Errorf("failed to create request: %w", err) } req.Header.Set("Content-Type", "application/json") resp, err := c.httpClient.Do(req) if err != nil { return fmt.Errorf("authentication request failed: %w", err) } defer func() { _ = resp.Body.Close() }() if resp.StatusCode != http.StatusOK { return fmt.Errorf("authentication failed with status %d", resp.StatusCode) } var result struct { Token string `json:"token"` } if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { return fmt.Errorf("failed to decode auth response: %w", err) } c.token = result.Token return nil } func (c *UmamiClient) doRequest(path string, params map[string]string) ([]byte, error) { ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() req, err := http.NewRequestWithContext(ctx, http.MethodGet, c.baseURL+path, http.NoBody) if err != nil { return nil, err } if params != nil { q := req.URL.Query() for k, v := range params { q.Add(k, v) } req.URL.RawQuery = q.Encode() } req.Header.Set("Authorization", "Bearer "+c.token) req.Header.Set("Content-Type", "application/json") resp, err := c.httpClient.Do(req) if err != nil { return nil, err } defer func() { _ = resp.Body.Close() }() body, err := io.ReadAll(resp.Body) if err != nil { return nil, err } if resp.StatusCode >= 400 { return nil, fmt.Errorf("API error %d: %s", resp.StatusCode, string(body)) } return body, nil } type Website struct { ID string `json:"id"` Name string `json:"name"` Domain string `json:"domain"` CreatedAt time.Time `json:"createdAt"` } func (c *UmamiClient) GetWebsites() ([]Website, error) { data, err := c.doRequest("/api/websites", nil) if err != nil { return nil, err } var result struct { Data []Website `json:"data"` } if err := json.Unmarshal(data, &result); err != nil { return nil, err } return result.Data, nil } type Stats struct { PageViews ValueChange `json:"pageviews"` Visitors ValueChange `json:"visitors"` Bounces ValueChange `json:"bounces"` TotalTime ValueChange `json:"totaltime"` } type ValueChange struct { Value int `json:"value"` Change int `json:"change"` } func (c *UmamiClient) GetStats(websiteID, startDate, endDate string) (*Stats, error) { params := map[string]string{ "startAt": startDate, "endAt": endDate, } data, err := c.doRequest(fmt.Sprintf("/api/websites/%s/stats", websiteID), params) if err != nil { return nil, err } var stats Stats if err := json.Unmarshal(data, &stats); err != nil { return nil, err } return &stats, nil } type PageView struct { T string `json:"t"` Y int `json:"y"` } func (c *UmamiClient) GetPageViews(websiteID, startDate, endDate, unit string) ([]PageView, error) { params := map[string]string{ "startAt": startDate, "endAt": endDate, "unit": unit, } data, err := c.doRequest(fmt.Sprintf("/api/websites/%s/pageviews", websiteID), params) if err != nil { return nil, err } var response struct { PageViews []PageView `json:"pageviews"` Sessions []PageView `json:"sessions"` } if err := json.Unmarshal(data, &response); err != nil { var pageviews []PageView if err2 := json.Unmarshal(data, &pageviews); err2 != nil { return nil, err } return pageviews, nil } return response.PageViews, nil } type Metric struct { X string `json:"x"` Y int `json:"y"` } func (c *UmamiClient) GetMetrics(websiteID, startDate, endDate, metricType string, limit int) ([]Metric, error) { params := map[string]string{ "startAt": startDate, "endAt": endDate, "type": metricType, "limit": fmt.Sprintf("%d", limit), } data, err := c.doRequest(fmt.Sprintf("/api/websites/%s/metrics", websiteID), params) if err != nil { return nil, err } var metrics []Metric if err := json.Unmarshal(data, &metrics); err != nil { return nil, err } return metrics, nil } func (c *UmamiClient) GetActive(websiteID string) ([]Metric, error) { data, err := c.doRequest(fmt.Sprintf("/api/websites/%s/active", websiteID), nil) if err != nil { return nil, err } var response []struct { X int `json:"x"` Y int `json:"y"` } if err := json.Unmarshal(data, &response); err != nil { var singleResponse struct { X int `json:"x"` } if err2 := json.Unmarshal(data, &singleResponse); err2 != nil { return nil, err } return []Metric{{X: fmt.Sprintf("%d", singleResponse.X), Y: singleResponse.X}}, nil } metrics := make([]Metric, len(response)) for i, r := range response { metrics[i] = Metric{X: fmt.Sprintf("%d", r.X), Y: r.Y} } return metrics, nil }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Macawls/umami-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server