@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.
- package/dist/index.js +26 -35
- 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
|
|
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
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
|
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
|
|
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);});
|