@aj-2000-test/goodlogs-mcp 0.1.7 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +26 -35
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,46 +1,37 @@
1
1
  #!/usr/bin/env node
2
- import {McpServer}from'@modelcontextprotocol/sdk/server/mcp.js';import {StdioServerTransport}from'@modelcontextprotocol/sdk/server/stdio.js';import {z}from'zod';var g={eu:"https://goodlogs-api-eu.yellowmeadow-422296f6.japaneast.azurecontainerapps.io",ap:"https://goodlogs-api-ap.delightfulsand-90b72c09.southeastasia.azurecontainerapps.io"};function m(t,o){if(o)return o;let r=t.split("_");if(r.length>=4&&r[0]==="gl"){let s=r[2];if(g[s])return g[s]}return g.eu}var a=process.env.GOODLOGS_API_KEY??"",c=m(a,process.env.GOODLOGS_API_URL||void 0);function p(){if(a)return `Bearer ${a}`;throw new Error("Set GOODLOGS_API_KEY environment variable (create one at Dashboard \u2192 API Keys)")}async function l(t,o){let r=await fetch(`${c}${t}`,{method:o?"POST":"GET",headers:{"Content-Type":"application/json",Authorization:p(),"X-GoodLogs-SDK":"mcp/0.1.0"},body:o?JSON.stringify(o):void 0});if(!r.ok){let s=await r.text().catch(()=>"");throw new Error(`API error ${r.status}: ${s}`)}return r.json()}var n=new McpServer({name:"goodlogs",version:"0.1.0"});n.tool("query",`Query logs or events from GoodLogs. Two modes: search (individual rows) or aggregate (group by + metrics).
2
+ import {McpServer}from'@modelcontextprotocol/sdk/server/mcp.js';import {StdioServerTransport}from'@modelcontextprotocol/sdk/server/stdio.js';import {z}from'zod';var u={eu:"https://goodlogs-api-eu.yellowmeadow-422296f6.japaneast.azurecontainerapps.io",ap:"https://goodlogs-api-ap.delightfulsand-90b72c09.southeastasia.azurecontainerapps.io"};function p(e,r){if(r)return r;let t=e.split("_");if(t.length>=4&&t[0]==="gl"){let o=t[2];if(u[o])return u[o]}return u.eu}var a=process.env.GOODLOGS_API_KEY??"",c=p(a,process.env.GOODLOGS_API_URL||void 0);function f(){if(a)return `Bearer ${a}`;throw new Error("Set GOODLOGS_API_KEY environment variable (create one at Dashboard \u2192 API Keys)")}async function l(e,r){let t=await fetch(`${c}${e}`,{method:"GET",headers:{"Content-Type":"application/json",Authorization:f(),"X-GoodLogs-SDK":"mcp/0.1.0"},body:void 0});if(!t.ok){let o=await t.text().catch(()=>"");throw new Error(`API error ${t.status}: ${o}`)}return t.json()}var s=new McpServer({name:"goodlogs",version:"0.1.0"});s.tool("gql",`Execute a GQL (GoodLogs Query Language) query against logs and events.
3
+
4
+ IMPORTANT: Call get_schema first to discover available fields.
5
+
6
+ All fields are flat \u2014 no prefixes needed: severity, message, code_location, event, distinct_id, orderId, service, etc.
7
+
8
+ Syntax: [from:logs|events|all] filters [| pipeline]*
3
9
 
4
10
  SEARCH examples:
5
- - Recent errors: {"table":"logs","severity":"error","limit":10}
6
- - Search by text: {"table":"logs","query":"payment","hours":6}
7
- - Filter by metadata: {"table":"logs","metadata_filters":{"orderId":"ord_123"}}
8
- - Filter by context: {"table":"logs","context_filters":{"service":"billing"}}
9
- - Search events: {"table":"events","event_name":"purchase","limit":5}
10
- - Filter by user: {"table":"events","distinct_id":"user_42"}
11
- - Filter by properties: {"table":"events","properties_filters":{"plan":"pro"}}
11
+ severity:error last:24h
12
+ message:~timeout severity:error last:6h
13
+ orderId:"ord_123" last:7d
14
+ from:events event:purchase last:24h
15
+ from:all last:1h
12
16
 
13
17
  AGGREGATE examples:
14
- - Count by severity: {"table":"logs","mode":"aggregate","group_by":"severity"}
15
- - Error messages ranked: {"table":"logs","mode":"aggregate","group_by":"message","severity":"error","limit":5}
16
- - Errors per hour: {"table":"logs","mode":"aggregate","group_by":"hour","severity":"error","hours":24}
17
- - Events per day: {"table":"events","mode":"aggregate","group_by":"day"}
18
- - Top event names: {"table":"events","mode":"aggregate","group_by":"event_name","limit":10}
19
- - Unique users: {"table":"events","mode":"aggregate","group_by":"event_name","metric":"dcount"}
20
- - Sum revenue: {"table":"events","mode":"aggregate","group_by":"day","metric":"sum","metric_field":"properties.amount"}
21
- - Avg response time: {"table":"logs","mode":"aggregate","group_by":"hour","metric":"avg","metric_field":"metadata.duration_ms"}
22
- - Max latency by endpoint: {"table":"logs","mode":"aggregate","group_by":"code_location","metric":"max","metric_field":"metadata.duration_ms"}
23
- - Min price: {"table":"events","mode":"aggregate","group_by":"event_name","metric":"min","metric_field":"properties.price"}
24
-
25
- IMPORTANT \u2014 metric_field column reference:
26
- - logs table JSONB columns: metadata, context \u2192 use "metadata.KEY" or "context.KEY"
27
- - events table JSONB column: properties \u2192 use "properties.KEY"
28
- - WRONG: {"table":"events","metric_field":"$value_ms"} \u2014 missing prefix
29
- - RIGHT: {"table":"events","metric_field":"properties.$value_ms"}
30
- - WRONG: {"table":"events","metric_field":"metadata.duration_ms"} \u2014 events don't have metadata
31
- - RIGHT: {"table":"logs","metric_field":"metadata.duration_ms"}`,{table:z.enum(["logs","events"]).describe("Which table to query"),mode:z.enum(["search","aggregate"]).optional().describe("search=rows, aggregate=group by"),query:z.string().optional().describe("Text search on message (logs) or event_name (events)"),severity:z.enum(["debug","info","warn","error","fatal"]).optional(),code_location:z.string().optional().describe("Filter logs by code location"),metadata_filters:z.record(z.string(),z.string()).optional().describe("Filter logs metadata JSONB"),context_filters:z.record(z.string(),z.string()).optional().describe("Filter logs context JSONB"),event_name:z.string().optional().describe("Filter events by name"),distinct_id:z.string().optional().describe("Filter events by user ID"),properties_filters:z.record(z.string(),z.string()).optional().describe("Filter events properties JSONB"),group_by:z.enum(["severity","message","code_location","event_name","distinct_id","hour","day"]).optional(),metric:z.enum(["count","dcount","sum","avg","mean","min","max"]).optional(),metric_field:z.string().optional().describe("JSONB path for sum/avg/mean/min/max, e.g. metadata.duration_ms or properties.amount"),order:z.enum(["asc","desc"]).optional(),hours:z.number().optional().describe("Time range in hours (default 24)"),limit:z.number().optional().describe("Max results (default 20)")},async t=>{let o=await l("/v1/query",t);return {content:[{type:"text",text:JSON.stringify(o,null,2)}]}});n.tool("get_field_keys",`Discover available field keys and sample values in your GoodLogs data.
32
-
33
- Sources:
34
- - logs_metadata: business fields in log metadata
35
- - logs_context: infrastructure fields in log context
36
- - events_properties: fields in event properties
37
- - event_names: distinct event names with counts
38
- - logs_severities: severity levels with counts`,{source:z.enum(["logs_metadata","logs_context","events_properties","event_names","logs_severities"]).describe("Which field to discover keys for")},async t=>{let o=await l("/v1/query",_(t.source));return {content:[{type:"text",text:JSON.stringify(o,null,2)}]}});function _(t){switch(t){case "event_names":return {table:"events",mode:"aggregate",group_by:"event_name",limit:50};case "logs_severities":return {table:"logs",mode:"aggregate",group_by:"severity"};default:return {table:t.startsWith("events")?"events":"logs",limit:50}}}n.tool("get_alerts",`Get alert rules, their current status (ok/triggered), and recent incident timeline.
18
+ severity:error | count by severity | last:24h
19
+ severity:error | count by message | top 10
20
+ | count by severity | timeseries 1h | last:7d
21
+ from:events | count by event | last:24h
22
+ from:events | count_distinct(distinct_id) | last:7d
23
+ | avg(duration_ms) by code_location | top 10
24
+ | p95(duration_ms) | last:24h
25
+
26
+ Filters: field:value, field:!=val, field:>N, field:~pattern, field:=~regex, field:(a,b,c), has:field, not:field:value
27
+ Pipeline: count, sum, avg, min, max, p95, p99, count_distinct, by FIELD, top N, timeseries, select
28
+ Modifiers: last:24h, limit:50, order:asc, from:logs/events/all
29
+ Alias: | count as error_count`,{q:z.string().describe("The GQL query string")},async e=>{let r=await l(`/v1/gql?q=${encodeURIComponent(e.q)}`);return {content:[{type:"text",text:JSON.stringify(r,null,2)}]}});s.tool("get_schema","Discover the schema for this project \u2014 all known property keys, their types, sample values, and event names for both logs and events. Call this FIRST before writing GQL queries.",{},async()=>{let e=await l("/v1/schema");return {content:[{type:"text",text:JSON.stringify(e,null,2)}]}});s.tool("get_alerts",`Get alert rules, their current status (ok/triggered), and recent incident timeline.
39
30
 
40
31
  Examples:
41
32
  - All alerts: {}
42
33
  - Only firing: {"status": "triggered"}
43
34
  - Error alerts in last 6h: {"metric": "error_count", "hours": 6}
44
- - Rules only (no timeline): {"include_timeline": false}`,{status:z.enum(["ok","triggered","all"]).optional().describe("Filter by status (default: all)"),metric:z.enum(["error_count","event_count","log_volume"]).optional().describe("Filter by metric type"),hours:z.number().optional().describe("Timeline lookback in hours (default: 24)"),include_timeline:z.boolean().optional().describe("Include trigger/resolve events (default: true)")},async t=>{let r=await l("/v1/alerts");t.status&&t.status!=="all"&&(r=r.filter(i=>i.status===t.status)),t.metric&&(r=r.filter(i=>i.metric===t.metric));let s=[];return t.include_timeline!==false&&(s=await l("/v1/alerts/timeline").catch(()=>[])),{content:[{type:"text",text:JSON.stringify({alert_rules:r,total:r.length,triggered:r.filter(i=>i.status==="triggered").length,timeline:s},null,2)}]}});n.tool("get_project_info",`Get project details, organization info, billing plan, usage stats, and member count.
35
+ - Rules only (no timeline): {"include_timeline": false}`,{status:z.enum(["ok","triggered","all"]).optional().describe("Filter by status (default: all)"),metric:z.enum(["error_count","event_count","log_volume"]).optional().describe("Filter by metric type"),hours:z.number().optional().describe("Timeline lookback in hours (default: 24)"),include_timeline:z.boolean().optional().describe("Include trigger/resolve events (default: true)")},async e=>{let t=await l("/v1/alerts");e.status&&e.status!=="all"&&(t=t.filter(n=>n.status===e.status)),e.metric&&(t=t.filter(n=>n.metric===e.metric));let o=[];return e.include_timeline!==false&&(o=await l("/v1/alerts/timeline").catch(()=>[])),{content:[{type:"text",text:JSON.stringify({alert_rules:t,total:t.length,triggered:t.filter(n=>n.status==="triggered").length,timeline:o},null,2)}]}});s.tool("get_project_info",`Get project details, organization info, billing plan, usage stats, and member count.
45
36
 
46
- Returns: project name/slug, org name/plan/members/projects, monthly usage (events, logs bytes, AI queries), active API keys.`,{},async()=>{let t=await l("/v1/info");return {content:[{type:"text",text:JSON.stringify(t,null,2)}]}});n.resource("project-info","goodlogs://info",async()=>({contents:[{uri:"goodlogs://info",mimeType:"application/json",text:JSON.stringify({api_url:c,auth:"API key (gl_sk_...)",tools:["query","get_field_keys","get_alerts"],docs:"https://github.com/goodlogs/goodlogs"},null,2)}]}));async function b(){(!c||!a)&&(console.error("Error: Set required environment variables:"),c||console.error(" GOODLOGS_API_URL - Your GoodLogs API endpoint"),a||console.error(" GOODLOGS_API_KEY - API key (create at Dashboard \u2192 API Keys)"),console.error(""),console.error("Required scopes: data:read, alerts:read"),console.error("Usage:"),console.error(" GOODLOGS_API_URL=https://... GOODLOGS_API_KEY=gl_sk_... goodlogs-mcp"),process.exit(1));let t=new StdioServerTransport;await n.connect(t);}b().catch(t=>{console.error("Fatal:",t),process.exit(1);});
37
+ Returns: project name/slug, org name/plan/members/projects, monthly usage (events, logs bytes, AI queries), active API keys.`,{},async()=>{let e=await l("/v1/info");return {content:[{type:"text",text:JSON.stringify(e,null,2)}]}});s.resource("project-info","goodlogs://info",async()=>({contents:[{uri:"goodlogs://info",mimeType:"application/json",text:JSON.stringify({api_url:c,auth:"API key (gl_sk_...)",tools:["gql","get_schema","get_alerts","get_project_info"],docs:"https://github.com/goodlogs/goodlogs"},null,2)}]}));async function m(){(!c||!a)&&(console.error("Error: Set required environment variables:"),c||console.error(" GOODLOGS_API_URL - Your GoodLogs API endpoint"),a||console.error(" GOODLOGS_API_KEY - API key (create at Dashboard \u2192 API Keys)"),console.error(""),console.error("Required scopes: data:read, alerts:read"),console.error("Usage:"),console.error(" GOODLOGS_API_URL=https://... GOODLOGS_API_KEY=gl_sk_... goodlogs-mcp"),process.exit(1));let e=new StdioServerTransport;await s.connect(e);}m().catch(e=>{console.error("Fatal:",e),process.exit(1);});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aj-2000-test/goodlogs-mcp",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "GoodLogs MCP Server — AI agents can query your logs and events",
5
5
  "type": "module",
6
6
  "bin": {