@mackerel/mcp-server 0.9.0 → 0.11.0
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/README.md +1 -0
- package/build/index.d.ts +2 -0
- package/build/index.js +22 -20
- package/package.json +6 -6
- package/build/index-BNjKmUhF.d.ts +0 -1
package/README.md
CHANGED
|
@@ -66,3 +66,4 @@ The MCP server can be run either via Docker or npx.
|
|
|
66
66
|
* `list_traces`: - Search and retrieve traces.
|
|
67
67
|
* `list_http_server_stats` - Retrieve HTTP server statistics.
|
|
68
68
|
* `list_db_query_stats` - Retrieve database query statistics.
|
|
69
|
+
* `search_logs` - Search logs of a service.
|
package/build/index.d.ts
ADDED
package/build/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{McpServer as e}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as t}from"@modelcontextprotocol/sdk/server/stdio.js";import{z as n}from"zod";const r=25e3;function i(e,t){let{limit:n=20,offset:r=0}=t;return e.slice(r,r+n)}function a(e){return Math.ceil(e.length/4)}function o(e
|
|
3
|
-
- Using the limit parameter to reduce the number of items returned
|
|
2
|
+
import{McpServer as e}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as t}from"@modelcontextprotocol/sdk/server/stdio.js";import{z as n}from"zod";const r=25e3;function i(e,t){let{limit:n=20,offset:r=0}=t;return e.slice(r,r+n)}function a(e){return Math.ceil(e.length/4)}function o(e,t=`- Using the limit parameter to reduce the number of items returned
|
|
4
3
|
- Using offset or nextId parameters for pagination
|
|
5
|
-
- Applying more specific filters to narrow down the results`}
|
|
4
|
+
- Applying more specific filters to narrow down the results`){return`Response too large (estimated ${e.toLocaleString()} tokens, limit ${r.toLocaleString()} tokens). Please resolve this by:
|
|
5
|
+
${t}`}function s(){return`
|
|
6
6
|
## Available Host Metrics:
|
|
7
7
|
- **loadavg**: loadavg1, loadavg5, loadavg15
|
|
8
8
|
- **cpu**: cpu.user.percentage, cpu.system.percentage, cpu.idle.percentage, cpu.iowait.percentage, cpu.nice.percentage, cpu.irq.percentage, cpu.softirq.percentage, cpu.steal.percentage, cpu.guest.percentage
|
|
@@ -125,7 +125,9 @@ ${e}
|
|
|
125
125
|
${c()}
|
|
126
126
|
|
|
127
127
|
## Valid Metric Name Examples
|
|
128
|
-
- response_time, custom.my_metric, throughput`}async function d(e,t){try{let t=await e(),n=JSON.stringify(t),i=a(n);return i>r?{content:[{type:`text`,text:o(i)}],isError:!0}:{content:[{type:`text`,text:n}]}}catch(e){let n=e instanceof Error?e.message:String(e);if(t&&n.includes(`404`)&&(t===`get_host_metrics`||t===`get_service_metrics`)){let e;return e=t===`get_host_metrics`?l(n):u(n),{content:[{type:`text`,text:e}],isError:!0}}return{content:[{type:`text`,text:`Error occurred: ${n}`}],isError:!0}}}var f=class{mackerelClient;constructor(e){this.mackerelClient=e}static ListAlertsToolInput=n.object({withClosed:n.boolean().optional().describe(`Whether or not to get resolved alerts. If true, resolved alerts as well as open alerts are retrieved`),nextId:n.string().optional().describe(`If specified, alerts older than the alert with the specified id are retrieved`),limit:n.number().min(1).max(100).optional().describe(`The maximum number of alerts to retrieve. When omitted, up to 100 cases are retrieved. The most that can be specified is 100`)});listAlerts=async({withClosed:e,nextId:t,limit:n})=>await d(async()=>await this.mackerelClient.getAlerts(e,t,n));static GetAlertToolInput=n.object({alertId:n.string().describe(`The ID of the alert to retrieve`)});getAlert=async({alertId:e})=>await d(async()=>await this.mackerelClient.getAlert(e));static GetAlertLogsToolInput=n.object({alertId:n.string().describe(`The ID of the alert to retrieve logs for`),nextId:n.string().optional().describe(`If specified, alert logs older than the specified are retrieved`),limit:n.number().min(1).max(100).optional().describe(`The maximum number of alert logs to retrieve. When omitted, up to 100 logs are retrieved. The most that can be specified is 100`)});getAlertLogs=async({alertId:e,nextId:t,limit:n})=>await d(async()=>await this.mackerelClient.getAlertLogs(e,t,n))},p=class e{mackerelClient;constructor(e){this.mackerelClient=e}static ListDashboardsToolInput=n.object({limit:n.number().int().positive().optional().describe(`Maximum number of dashboards to return (default: 20)`),offset:n.number().int().min(0).optional().describe(`Number of dashboards to skip (default: 0)`),summary:n.boolean().optional().default(!0).describe(`If true, omit widget details to reduce response size (default: true)`)});listDashboards=async({limit:e,offset:t,summary:n=!0})=>await d(async()=>{let r=await this.mackerelClient.getDashboards(e,t);return n?{dashboards:r.dashboards.map(e=>({id:e.id,title:e.title,memo:e.memo,urlPath:e.urlPath,createdAt:e.createdAt,updatedAt:e.updatedAt,widgetCount:e.widgets?.length||0})),pageInfo:r.pageInfo}:r});static GetDashboardToolInput=n.object({dashboardId:n.string().describe(`The ID of the dashboard to retrieve`)});getDashboard=async({dashboardId:e})=>await d(async()=>await this.mackerelClient.getDashboard(e));static LayoutSchema=n.object({x:n.number().describe(`X coordinate of widget`),y:n.number().describe(`Y coordinate of widget`),width:n.number().describe(`Width of widget`),height:n.number().describe(`Height of widget`)});static RangeSchema=n.union([n.object({type:n.literal(`relative`).describe(`Relative time range`),period:n.number().describe(`Length of the period (in seconds)`),offset:n.number().describe(`Difference from the current time (in seconds)`)}),n.object({type:n.literal(`absolute`).describe(`Absolute time range`),start:n.number().describe(`Start time (in epoch seconds)`),end:n.number().describe(`End time (in epoch seconds)`)}),n.object({min:n.number().optional().describe(`Minimum value on vertical axis`),max:n.number().optional().describe(`Maximum value on vertical axis`)})]);static ValueRangeSchema=n.object({min:n.number().optional().describe(`Minimum value on vertical axis`),max:n.number().optional().describe(`Maximum value on vertical axis`)});static GraphSchema=n.discriminatedUnion(`type`,[n.object({type:n.literal(`host`).describe(`Host graph`),hostId:n.string().describe(`Host ID`),name:n.string().describe(`Graph name (e.g., loadavg, cpu)`)}),n.object({type:n.literal(`role`).describe(`Role graph`),roleFullname:n.string().describe("The service name and role name linked with `:`"),name:n.string().describe(`Graph name`),isStacked:n.boolean().optional().describe(`Whether the graph is stacked or line graph`)}),n.object({type:n.literal(`service`).describe(`Service graph`),serviceName:n.string().describe(`Service name`),name:n.string().describe(`Graph name`)}),n.object({type:n.literal(`expression`).describe(`Expression graph`),expression:n.string().describe(`Expression representing a graph`)}),n.object({type:n.literal(`query`).describe(`Query graph`),query:n.string().describe(`PromQL-style queries`),legend:n.string().optional().describe('Query legend. by specifiying the label key in `{{ }}`, it can be expanded to the value of the label. e.g. if you have a label `{ http.method: "GET" }` and you specify `method: {{http.method}}` in the `legend`, it will expand to `method: GET`.')}),n.object({type:n.literal(`unknown`).describe(`Unknown graph`)})]);static ReferenceLineSchema=n.object({label:n.string().describe(`Label of the reference line`),value:n.number().describe(`Value of the reference line`)});static MetricSchema=n.discriminatedUnion(`type`,[n.object({type:n.literal(`host`).describe(`Host metric`),hostId:n.string().describe(`Host ID`),name:n.string().describe("The name of the metric (`loadavg5` etc)")}),n.object({type:n.literal(`service`).describe(`Service metric`),serviceName:n.string().describe(`The name of the service`),name:n.string().describe(`The name of the metric`)}),n.object({type:n.literal(`expression`).describe(`Expression metric`),expression:n.string().describe(`An expression that represents a metric`)}),n.object({type:n.literal(`query`).describe(`Query metric`),query:n.string().describe(`PromQL-style queries`),legend:n.string().describe('Query legend. by specifying the label key in `{{ }}`, it can be expanded to the value of the label. e.g. if you have a label `{ http.method: "GET" }` and you specify `method: {{http.method}}` in the `legend`, it will expand to `method: GET`')}),n.object({type:n.literal(`unknown`).describe(`Unknown metric type (fallback when source is deleted)`)})]);static FormatRuleSchema=n.object({name:n.string().optional().describe(`Name of the format rule`),threshold:n.number().describe(`Base value of the conditional expression`),operator:n.enum([`>`,`<`]).describe(`Specify the conditions under which the emphasized style will be applied.`)});static WidgetSchema=n.discriminatedUnion(`type`,[n.object({type:n.literal(`graph`).describe(`Graph widget`),title:n.string().describe(`The title of the widget`),graph:e.GraphSchema.describe(`Object representing a graph`),range:e.RangeSchema.optional().describe(`Object representing the graph display range`),valueRange:e.ValueRangeSchema.optional().describe(`Object representing the value range of vertical axis`),referenceLines:n.array(e.ReferenceLineSchema).optional().describe(`Array of objects representing the reference line. If you want to remove the reference line setting, specify an empty array. If reference line is not set when get dashboards, an empty array is returned. Cannot specify more than one element in an array.`),layout:e.LayoutSchema.describe(`Object representing the layout`)}),n.object({type:n.literal(`value`).describe(`Value widget`),title:n.string().describe(`The title of the widget`),metric:e.MetricSchema.describe(`Object representing a metric`),fractionSize:n.number().optional().describe(`Decimal places displayed on the widget (0–16)`),suffix:n.string().optional().describe(`The units to be displayed after the value`),formatRules:n.array(e.FormatRuleSchema).optional().describe(`Array of objects representing the format rule. If you want to remove the format rule setting, specify an empty array. If format rule is not set when get dashboards, an empty array is returned. Cannot specify more than one element in an array.`),layout:e.LayoutSchema.describe(`Object representing the layout`)}),n.object({type:n.literal(`markdown`).describe(`Markdown widget`),title:n.string().describe(`The title of the widget`),markdown:n.string().describe(`A character string in Markdown format`),layout:e.LayoutSchema.describe(`Object representing the layout`)}),n.object({type:n.literal(`alertStatus`).describe(`Alert status widget`),title:n.string().describe(`The title of the widget`),roleFullname:n.string().describe("The service name and role name linked by `:`.However, if the relevant role or service has been deleted when the dashboard is retrieved, `roleFullname` will be set as `null`. "),layout:e.LayoutSchema.describe(`Object representing the layout`)})]);static UpdateDashboardToolInput=n.object({dashboardId:n.string().describe(`The ID of the dashboard to update`),title:n.string().describe(`Dashboard name`),memo:n.string().describe(`Dashboard notes`),urlPath:n.string().describe(`Dashboard URL path`),widgets:n.array(e.WidgetSchema).describe(`List of widget objects`)});updateDashboard=async({dashboardId:e,title:t,memo:n,urlPath:r,widgets:i})=>await d(async()=>await this.mackerelClient.updateDashboard(e,{title:t,memo:n,urlPath:r,widgets:i}))},m=class{mackerelClient;constructor(e){this.mackerelClient=e}static ListMonitorsToolInput=n.object({});listMonitors=async({})=>await d(async()=>await this.mackerelClient.getMonitors());static GetMonitorToolInput=n.object({monitorId:n.string().describe(`Monitor ID`)});getMonitor=async({monitorId:e})=>await d(async()=>await this.mackerelClient.getMonitor(e))},h=class{mackerelClient;constructor(e){this.mackerelClient=e}static ListHostsToolInput=n.object({service:n.string().optional().describe(`Service name`),role:n.array(n.string()).optional().describe(`Role names in the service, multiple assignments possible (result will be a unit of the hosts that belong to each role) if service has not been assigned it will be ignored.`),name:n.string().optional().describe(`Host name`),status:n.array(n.string()).optional().describe("Filters the host status. multiple specifications are possible. defaults are `working` and `standby`."),customIdentifier:n.string().optional().describe(`An identifier for the host that is user-specific and unique to the organization (registered at Register Host Information or Update Host Information API)`),limit:n.number().int().positive().max(100).optional().describe(`Maximum number of hosts to return (default: 20, max: 100)`),offset:n.number().int().min(0).optional().describe(`Number of hosts to skip (default: 0)`)});listHosts=async({service:e,role:t,name:n,status:r,customIdentifier:i,limit:a=20,offset:o=0})=>await d(async()=>await this.mackerelClient.getHosts(e,t,n,r,i,a,o))},g=class{mackerelClient;constructor(e){this.mackerelClient=e}static ListServicesToolInput=n.object({});listServices=async e=>await d(async()=>await this.mackerelClient.getServices())};const _=n.object({key:n.string().describe(`Attribute key`),value:n.string().describe(`Attribute value as a string`),type:n.enum([`string`,`int`,`double`,`bool`]).describe(`Value data type: string, int, double, or bool`),operator:n.enum([`EQ`,`NEQ`,`GT`,`GTE`,`LT`,`LTE`,`STARTS_WITH`]).describe(`Comparison operator. Available operators depend on type: string supports EQ, NEQ, STARTS_WITH; int/double support EQ, GT, GTE, LT, LTE; bool supports EQ, NEQ`)});var v=class{mackerelClient;constructor(e){this.mackerelClient=e}static ListTracesToolInput=n.object({serviceName:n.string().describe("Service name. Corresponds to `service.name` in OpenTelemetry semantic conventions"),from:n.number().int().positive().describe(`Start time for trace search (Unix epoch seconds)`),to:n.number().int().positive().describe(`End time for trace search (Unix epoch seconds)`),serviceNamespace:n.string().optional().describe("Service namespace. Corresponds to `service.namespace` in OpenTelemetry semantic conventions"),environment:n.string().optional().describe("Environment name. Corresponds to `deployment.environment` or `deployment.environment.name` in OpenTelemetry semantic conventions"),traceId:n.string().optional().describe(`Filter by trace ID (32-digit hexadecimal string)`),spanName:n.string().optional().describe(`Filter by span name`),version:n.string().optional().describe("Version. Corresponds to `service.version` in OpenTelemetry semantic conventions"),issueFingerprint:n.string().optional().describe(`Filter by issue fingerprint`),minLatencyMillis:n.number().min(0).optional().describe(`Minimum latency in milliseconds`),maxLatencyMillis:n.number().min(0).optional().describe(`Maximum latency in milliseconds`),attributes:n.array(_).optional().describe(`Custom attribute filter conditions`),resourceAttributes:n.array(_).optional().describe(`Resource-level attribute filters`),page:n.number().int().positive().optional().describe(`Page number (starting from 1). Default is 1`),perPage:n.number().int().min(1).max(100).optional().describe(`Number of items per page (1-100). Default is 20`),order:n.object({column:n.enum([`LATENCY`,`START_AT`]).describe("Sort column. One of `LATENCY` (trace latency) or `START_AT` (trace timestamp)"),direction:n.enum([`ASC`,`DESC`]).describe("Sort order. Either `ASC` (ascending) or `DESC` (descending)")}).optional().describe(`Sort preferences. Default is { column: 'START_AT', direction: 'DESC' }`)});listTraces=async({serviceName:e,from:t,to:n,serviceNamespace:r,environment:i,traceId:a,spanName:o,version:s,issueFingerprint:c,minLatencyMillis:l,maxLatencyMillis:u,attributes:f,resourceAttributes:p,page:m,perPage:h,order:g})=>await d(async()=>await this.mackerelClient.listTraces({serviceName:e,from:t,to:n,serviceNamespace:r,environment:i,traceId:a,spanName:o,version:s,issueFingerprint:c,minLatencyMillis:l,maxLatencyMillis:u,attributes:f,resourceAttributes:p,page:m,perPage:h,order:g}));static GetTraceToolInput=n.object({traceId:n.string().describe(`The ID of the trace to retrieve`),includeAttributes:n.boolean().optional().default(!1).describe(`Whether to include span attributes. Default is false to reduce response size`),includeEvents:n.boolean().optional().default(!0).describe(`Whether to include span events. Default is true as events contain important error information`),limit:n.number().int().positive().max(100).optional().default(20).describe(`Maximum number of spans to return per page (default: 20, max: 100)`),offset:n.number().int().min(0).optional().default(0).describe(`Number of spans to skip (default: 0)`),duration:n.number().min(0).optional().describe(`Only return spans with duration (in milliseconds) equal to or greater than this value. Useful for identifying bottlenecks`),errorSpansOnly:n.boolean().optional().default(!1).describe(`If true, only return spans that have errors or status code indicating failure`)});optimizeSpan(e,t,n){let r=e.endTime-e.startTime,i=this.hasSpanError(e),a={spanId:e.spanId,name:e.name,startTime:e.startTime,endTime:e.endTime,duration:r,hasError:i};return t&&(a.attributes=e.attributes),n&&(a.events=e.events),e.status&&e.status.code.toLowerCase()===`error`&&(a.status=e.status),a}hasSpanError(e){return e.status&&e.status.code.toLowerCase()===`error`?!0:e.events?e.events.some(e=>e.name.toLowerCase().includes(`exception`)):!1}filterAndSortSpans(e,t,n){let r=e;return n&&(r=r.filter(e=>e.hasError)),t!==void 0&&(r=r.filter(e=>e.duration>=t)),r.sort((e,t)=>e.hasError&&!t.hasError?-1:!e.hasError&&t.hasError?1:t.duration-e.duration),r}createPageInfo(e,t,n){let r=Math.floor(n/t)+1,i=Math.ceil(e/t);return{hasNextPage:n+t<e,hasPrevPage:n>0,currentPage:r,totalPages:i}}getTrace=async({traceId:e,includeAttributes:t=!1,includeEvents:n=!0,limit:r=20,offset:a=0,duration:o,errorSpansOnly:s=!1})=>await d(async()=>{let c=await this.mackerelClient.getTrace(e),l=c.spans.map(e=>this.optimizeSpan(e,t,n)),u=this.filterAndSortSpans(l,o,s),d=i(u,{limit:r,offset:a}),f=this.createPageInfo(u.length,r,a),p=c.spans.length,m=d.some(e=>e.hasError),h=0;if(l.length>0){let e=Math.min(...l.map(e=>e.startTime));h=Math.max(...l.map(e=>e.endTime))-e}return{traceId:e,summary:{totalSpans:p,hasErrors:m,totalDuration:h,pageInfo:f},spans:d}})},y=class{baseUrl;apiKey;cacheMap=new Map;cacheTTL;constructor(e,t,n=300){this.baseUrl=e,this.apiKey=t,this.cacheTTL=n*1e3}getCacheKey(e,t,n){return`${e}:${t}:${n?n.toString():``}`}getFromCache(e){let t=this.cacheMap.get(e);return t?Date.now()-t.timestamp>this.cacheTTL?(this.cacheMap.delete(e),null):t.data:null}setCache(e,t){this.cacheMap.set(e,{data:t,timestamp:Date.now()})}clearCache(){this.cacheMap.clear()}async request(e,t,{searchParams:n,body:r}={}){if(e===`GET`){let r=this.getCacheKey(e,t,n),i=this.getFromCache(r);if(i!==null)return i}let i=`${this.baseUrl}${t}${n?`?`+n.toString():``}`,a={method:e,headers:{"X-Api-Key":this.apiKey,"Content-Type":`application/json`}};r&&(a.body=JSON.stringify(r));let o=await fetch(i,a);if(!o.ok){let e=await o.text();throw Error(`Mackerel API error: ${o.status} ${e}`)}let s=await o.json();if(e===`GET`){let r=this.getCacheKey(e,t,n);this.setCache(r,s)}return s}async getAlerts(e,t,n){let r=new URLSearchParams;return e!==void 0&&r.append(`withClosed`,e.toString()),t&&r.append(`nextId`,t),n!==void 0&&r.append(`limit`,n.toString()),this.request(`GET`,`/api/v0/alerts`,{searchParams:r})}async getAlert(e){return this.request(`GET`,`/api/v0/alerts/${e}`)}async getAlertLogs(e,t,n){let r=new URLSearchParams;return t&&r.append(`nextId`,t),n!==void 0&&r.append(`limit`,n.toString()),this.request(`GET`,`/api/v0/alerts/${e}/logs`,{searchParams:r})}async getDashboards(e,t){let n=await this.request(`GET`,`/api/v0/dashboards`),r=e||20,a=t||0,o=n.dashboards.length;return{dashboards:i(n.dashboards,{limit:r,offset:a}),pageInfo:{hasPrevPage:a>0,hasNextPage:a+r<o}}}async getDashboard(e){return this.request(`GET`,`/api/v0/dashboards/${e}`)}static WidgetArray=n.array(p.WidgetSchema);async updateDashboard(e,t){return this.request(`PUT`,`/api/v0/dashboards/${e}`,{body:t})}async getHosts(e,t,n,r,a,o,s){let c=new URLSearchParams;if(e&&c.append(`service`,e),t)for(let e of t)c.append(`role`,e);if(n&&c.append(`name`,n),r)for(let e of r)c.append(`status`,e);a&&c.append(`customIdentifier`,a);let l=await this.request(`GET`,`/api/v0/hosts`,{searchParams:c}),u=o||20,d=s||0,f=l.hosts.length;return{hosts:i(l.hosts,{limit:u,offset:d}),pageInfo:{hasPrevPage:d>0,hasNextPage:d+u<f}}}async getHostMetrics(e,t,n,r){let i=new URLSearchParams;return i.append(`name`,t),i.append(`from`,n.toString()),i.append(`to`,r.toString()),this.request(`GET`,`/api/v0/hosts/${e}/metrics`,{searchParams:i})}async getServices(){return this.request(`GET`,`/api/v0/services`)}async getServiceMetrics(e,t,n,r){let i=new URLSearchParams;return i.append(`name`,t),i.append(`from`,n.toString()),i.append(`to`,r.toString()),this.request(`GET`,`/api/v0/services/${e}/metrics`,{searchParams:i})}async getMonitors(){return this.request(`GET`,`/api/v0/monitors`)}async getMonitor(e){return this.request(`GET`,`/api/v0/monitors/${e}`)}async getTrace(e){return this.request(`GET`,`/api/v0/traces/${e}`)}async getHttpServerStats(e,t,n,r,i,a,o,s,c,l,u,d){let f=new URLSearchParams;return f.append(`serviceName`,e),f.append(`from`,t.toString()),f.append(`to`,n.toString()),r&&f.append(`serviceNamespace`,r),i&&f.append(`environment`,i),a&&f.append(`version`,a),o&&f.append(`method`,o),s&&f.append(`route`,s),c&&f.append(`orderColumn`,c),l&&f.append(`orderDirection`,l),u!==void 0&&f.append(`page`,u.toString()),d!==void 0&&f.append(`perPage`,d.toString()),this.request(`GET`,`/api/v0/apm/http-server-stats`,{searchParams:f})}async getDbQueryStats(e,t,n,r,i,a,o,s,c,l,u){let d=new URLSearchParams;return d.append(`serviceName`,e),d.append(`from`,t.toString()),d.append(`to`,n.toString()),r&&d.append(`serviceNamespace`,r),i&&d.append(`environment`,i),a&&d.append(`version`,a),o&&d.append(`query`,o),s&&d.append(`orderColumn`,s),c&&d.append(`orderDirection`,c),l!==void 0&&d.append(`page`,l.toString()),u!==void 0&&d.append(`perPage`,u.toString()),this.request(`GET`,`/api/v0/apm/db-query-stats`,{searchParams:d})}async listTraces(e){let t={serviceName:e.serviceName,from:e.from,to:e.to};return e.serviceNamespace&&(t.serviceNamespace=e.serviceNamespace),e.environment&&(t.environment=e.environment),e.traceId&&(t.traceId=e.traceId),e.spanName&&(t.spanName=e.spanName),e.version&&(t.version=e.version),e.issueFingerprint&&(t.issueFingerprint=e.issueFingerprint),e.minLatencyMillis!==void 0&&(t.minLatencyMillis=e.minLatencyMillis),e.maxLatencyMillis!==void 0&&(t.maxLatencyMillis=e.maxLatencyMillis),e.attributes&&e.attributes.length>0&&(t.attributes=e.attributes),e.resourceAttributes&&e.resourceAttributes.length>0&&(t.resourceAttributes=e.resourceAttributes),e.page!==void 0&&(t.page=e.page),e.perPage!==void 0&&(t.perPage=e.perPage),e.order&&(t.order=e.order),this.request(`POST`,`/api/v0/traces`,{body:t})}},b=class{mackerelClient;constructor(e){this.mackerelClient=e}static GetServiceMetricsToolInput=n.object({serviceName:n.string().describe(`Service name`),name:n.string().describe(`Metric name (e.g., __externalhttp.responsetime.<monitorId>, sales.count, analytics.page_view, etc.)`),from:n.number().describe(`The start of the time period you want metrics for (unix time)`),to:n.number().describe(`The end of the time period you want metrics for (unix time)`)});getServiceMetrics=async({serviceName:e,name:t,from:n,to:r})=>await d(async()=>await this.mackerelClient.getServiceMetrics(e,t,n,r),`get_service_metrics`)},x=class{mackerelClient;constructor(e){this.mackerelClient=e}static GetHostMetricsToolInput=n.object({hostId:n.string().describe(`Host ID`),name:n.string().describe(`Metric name (e.g., loadavg5, cpu.user, memory.used, custom.myapp.*, ec2.cpu.used, etc.)`),from:n.number().describe(`The start of the time period you want metrics for (unix time)`),to:n.number().describe(`The end of the time period you want metrics for (unix time)`)});getHostMetrics=async({hostId:e,name:t,from:n,to:r})=>await d(async()=>await this.mackerelClient.getHostMetrics(e,t,n,r),`get_host_metrics`)},S=class{mackerelClient;constructor(e){this.mackerelClient=e}static ListHttpServerStatsToolInput=n.object({serviceName:n.string().describe("Service name. Corresponds to `service.name` in OpenTelemetry semantic conventions"),from:n.number().int().positive().describe("Start time for retrieving statistics (Unix epoch seconds). The range between `to` is limited to 30 minutes"),to:n.number().int().positive().describe("End time for retrieving statistics (Unix epoch seconds). The range between `from` is limited to 30 minutes"),serviceNamespace:n.string().optional().describe("Service namespace. Corresponds to `service.namespace` in OpenTelemetry semantic conventions"),environment:n.string().optional().describe("Environment name. Corresponds to `deployment.environment` or `deployment.environment.name` in OpenTelemetry semantic conventions"),version:n.string().optional().describe("Version. Corresponds to `service.version` in OpenTelemetry semantic conventions"),method:n.string().optional().describe("Filter by HTTP method. Corresponds to `http.request.method` or `http.method` in OpenTelemetry semantic conventions. `http.request.method` is preferred"),route:n.string().optional().describe("Filter by route (partial match). Corresponds to `http.route`, `url.path`, `http.target`, or `http.url` in OpenTelemetry semantic conventions. Preferred in this order"),orderColumn:n.enum([`SUM`,`AVERAGE`,`P95`,`ERROR_RATE`,`REQUEST_COUNT`]).optional().describe("Sort column. One of `SUM` (total latency), `AVERAGE` (average latency), `P95` (95th percentile latency), `ERROR_RATE` (error rate percentage), or `REQUEST_COUNT` (request count). Default is `P95`"),orderDirection:n.enum([`ASC`,`DESC`]).optional().describe("Sort order. Either `ASC` (ascending) or `DESC` (descending). Default is `DESC`"),page:n.number().int().positive().optional().describe(`Page number (starting from 1). Default is 1`),perPage:n.number().int().min(1).max(100).optional().describe(`Number of items per page (1-100). Default is 20`)});listHttpServerStats=async({serviceName:e,from:t,to:n,serviceNamespace:r,environment:i,version:a,method:o,route:s,orderColumn:c,orderDirection:l,page:u,perPage:f})=>await d(async()=>await this.mackerelClient.getHttpServerStats(e,t,n,r,i,a,o,s,c,l,u,f));static ListDbQueryStatsToolInput=n.object({serviceName:n.string().describe("Service name. Corresponds to `service.name` in OpenTelemetry semantic conventions."),from:n.number().int().positive().describe("Start time for retrieving statistics (Unix epoch seconds). The range between `to` is limited to 30 minutes"),to:n.number().int().positive().describe("End time for retrieving statistics (Unix epoch seconds). The range between `from` is limited to 30 minutes"),serviceNamespace:n.string().optional().describe("Service namespace. Corresponds to `service.namespace` in OpenTelemetry semantic conventions"),environment:n.string().optional().describe("Environment name. Corresponds to `deployment.environment` or `deployment.environment.name` in OpenTelemetry semantic conventions"),version:n.string().optional().describe("Version. Corresponds to `service.version` in OpenTelemetry semantic conventions"),query:n.string().optional().describe("Filter by SQL query (partial match). Corresponds to `db.query.text` or `db.statement` in OpenTelemetry semantic conventions. db.query.text is preferred"),orderColumn:n.enum([`SUM`,`AVERAGE`,`P95`,`EXECUTION_COUNT`]).optional().describe("Sort column. One of `SUM` (total latency), `AVERAGE` (average latency), `P95` (95th percentile latency), or `EXECUTION_COUNT` (execution count). Default is `P95`."),orderDirection:n.enum([`ASC`,`DESC`]).optional().describe("Sort order. Either `ASC` (ascending) or `DESC` (descending). Default is `DESC`"),page:n.number().int().positive().optional().describe(`Page number (starting from 1). Default is 1`),perPage:n.number().int().min(1).max(100).optional().describe(`Number of items per page (1-100). Default is 20`)});listDbQueryStats=async({serviceName:e,from:t,to:n,serviceNamespace:r,environment:i,version:a,query:o,orderColumn:s,orderDirection:c,page:l,perPage:u})=>await d(async()=>await this.mackerelClient.getDbQueryStats(e,t,n,r,i,a,o,s,c,l,u))};async function C(){let n=new y(`https://api.mackerelio.com`,w()),r=new f(n),i=new p(n),a=new m(n),o=new h(n),s=new x(n),c=new g(n),l=new b(n),u=new v(n),d=new S(n),_=new e({name:`@mackerel/mcp-server`,version:`0.0.1`});_.registerTool(`list_alerts`,{title:`List Alerts`,description:`Retrieve alerts from Mackerel.
|
|
128
|
+
- response_time, custom.my_metric, throughput`}async function d(e,t,n){try{let t=await e(),i=JSON.stringify(t),s=a(i);return s>r?{content:[{type:`text`,text:o(s,n)}],isError:!0}:{content:[{type:`text`,text:i}]}}catch(e){let n=e instanceof Error?e.message:String(e);if(t&&n.includes(`404`)&&(t===`get_host_metrics`||t===`get_service_metrics`)){let e;return e=t===`get_host_metrics`?l(n):u(n),{content:[{type:`text`,text:e}],isError:!0}}return{content:[{type:`text`,text:`Error occurred: ${n}`}],isError:!0}}}var f=class{mackerelClient;constructor(e){this.mackerelClient=e}static ListAlertsToolInput=n.object({withClosed:n.boolean().optional().describe(`Whether or not to get resolved alerts. If true, resolved alerts as well as open alerts are retrieved`),nextId:n.string().optional().describe(`If specified, alerts older than the alert with the specified id are retrieved`),limit:n.number().min(1).max(100).optional().describe(`The maximum number of alerts to retrieve. When omitted, up to 100 cases are retrieved. The most that can be specified is 100`)});listAlerts=async({withClosed:e,nextId:t,limit:n})=>await d(async()=>await this.mackerelClient.getAlerts(e,t,n));static GetAlertToolInput=n.object({alertId:n.string().describe(`The ID of the alert to retrieve`)});getAlert=async({alertId:e})=>await d(async()=>await this.mackerelClient.getAlert(e));static GetAlertLogsToolInput=n.object({alertId:n.string().describe(`The ID of the alert to retrieve logs for`),nextId:n.string().optional().describe(`If specified, alert logs older than the specified are retrieved`),limit:n.number().min(1).max(100).optional().describe(`The maximum number of alert logs to retrieve. When omitted, up to 100 logs are retrieved. The most that can be specified is 100`)});getAlertLogs=async({alertId:e,nextId:t,limit:n})=>await d(async()=>await this.mackerelClient.getAlertLogs(e,t,n))},p=class e{mackerelClient;constructor(e){this.mackerelClient=e}static ListDashboardsToolInput=n.object({limit:n.number().int().positive().optional().describe(`Maximum number of dashboards to return (default: 20)`),offset:n.number().int().min(0).optional().describe(`Number of dashboards to skip (default: 0)`),summary:n.boolean().optional().default(!0).describe(`If true, omit widget details to reduce response size (default: true)`)});listDashboards=async({limit:e,offset:t,summary:n=!0})=>await d(async()=>{let r=await this.mackerelClient.getDashboards(e,t);return n?{dashboards:r.dashboards.map(e=>({id:e.id,title:e.title,memo:e.memo,urlPath:e.urlPath,createdAt:e.createdAt,updatedAt:e.updatedAt,widgetCount:e.widgets?.length||0})),pageInfo:r.pageInfo}:r});static GetDashboardToolInput=n.object({dashboardId:n.string().describe(`The ID of the dashboard to retrieve`)});getDashboard=async({dashboardId:e})=>await d(async()=>await this.mackerelClient.getDashboard(e));static LayoutSchema=n.object({x:n.number().describe(`X coordinate of widget`),y:n.number().describe(`Y coordinate of widget`),width:n.number().describe(`Width of widget`),height:n.number().describe(`Height of widget`)});static RangeSchema=n.union([n.object({type:n.literal(`relative`).describe(`Relative time range`),period:n.number().describe(`Length of the period (in seconds)`),offset:n.number().describe(`Difference from the current time (in seconds)`)}),n.object({type:n.literal(`absolute`).describe(`Absolute time range`),start:n.number().describe(`Start time (in epoch seconds)`),end:n.number().describe(`End time (in epoch seconds)`)}),n.object({min:n.number().optional().describe(`Minimum value on vertical axis`),max:n.number().optional().describe(`Maximum value on vertical axis`)})]);static ValueRangeSchema=n.object({min:n.number().optional().describe(`Minimum value on vertical axis`),max:n.number().optional().describe(`Maximum value on vertical axis`)});static GraphSchema=n.discriminatedUnion(`type`,[n.object({type:n.literal(`host`).describe(`Host graph`),hostId:n.string().describe(`Host ID`),name:n.string().describe(`Graph name (e.g., loadavg, cpu)`)}),n.object({type:n.literal(`role`).describe(`Role graph`),roleFullname:n.string().describe("The service name and role name linked with `:`"),name:n.string().describe(`Graph name`),isStacked:n.boolean().optional().describe(`Whether the graph is stacked or line graph`)}),n.object({type:n.literal(`service`).describe(`Service graph`),serviceName:n.string().describe(`Service name`),name:n.string().describe(`Graph name`)}),n.object({type:n.literal(`expression`).describe(`Expression graph`),expression:n.string().describe(`Expression representing a graph`)}),n.object({type:n.literal(`query`).describe(`Query graph`),query:n.string().describe(`PromQL-style queries`),legend:n.string().optional().describe('Query legend. by specifiying the label key in `{{ }}`, it can be expanded to the value of the label. e.g. if you have a label `{ http.method: "GET" }` and you specify `method: {{http.method}}` in the `legend`, it will expand to `method: GET`.')}),n.object({type:n.literal(`unknown`).describe(`Unknown graph`)})]);static ReferenceLineSchema=n.object({label:n.string().describe(`Label of the reference line`),value:n.number().describe(`Value of the reference line`)});static MetricSchema=n.discriminatedUnion(`type`,[n.object({type:n.literal(`host`).describe(`Host metric`),hostId:n.string().describe(`Host ID`),name:n.string().describe("The name of the metric (`loadavg5` etc)")}),n.object({type:n.literal(`service`).describe(`Service metric`),serviceName:n.string().describe(`The name of the service`),name:n.string().describe(`The name of the metric`)}),n.object({type:n.literal(`expression`).describe(`Expression metric`),expression:n.string().describe(`An expression that represents a metric`)}),n.object({type:n.literal(`query`).describe(`Query metric`),query:n.string().describe(`PromQL-style queries`),legend:n.string().describe('Query legend. by specifying the label key in `{{ }}`, it can be expanded to the value of the label. e.g. if you have a label `{ http.method: "GET" }` and you specify `method: {{http.method}}` in the `legend`, it will expand to `method: GET`')}),n.object({type:n.literal(`unknown`).describe(`Unknown metric type (fallback when source is deleted)`)})]);static FormatRuleSchema=n.object({name:n.string().optional().describe(`Name of the format rule`),threshold:n.number().describe(`Base value of the conditional expression`),operator:n.enum([`>`,`<`]).describe(`Specify the conditions under which the emphasized style will be applied.`)});static WidgetSchema=n.discriminatedUnion(`type`,[n.object({type:n.literal(`graph`).describe(`Graph widget`),title:n.string().describe(`The title of the widget`),graph:e.GraphSchema.describe(`Object representing a graph`),range:e.RangeSchema.optional().describe(`Object representing the graph display range`),valueRange:e.ValueRangeSchema.optional().describe(`Object representing the value range of vertical axis`),referenceLines:n.array(e.ReferenceLineSchema).optional().describe(`Array of objects representing the reference line. If you want to remove the reference line setting, specify an empty array. If reference line is not set when get dashboards, an empty array is returned. Cannot specify more than one element in an array.`),layout:e.LayoutSchema.describe(`Object representing the layout`)}),n.object({type:n.literal(`value`).describe(`Value widget`),title:n.string().describe(`The title of the widget`),metric:e.MetricSchema.describe(`Object representing a metric`),fractionSize:n.number().optional().describe(`Decimal places displayed on the widget (0–16)`),suffix:n.string().optional().describe(`The units to be displayed after the value`),formatRules:n.array(e.FormatRuleSchema).optional().describe(`Array of objects representing the format rule. If you want to remove the format rule setting, specify an empty array. If format rule is not set when get dashboards, an empty array is returned. Cannot specify more than one element in an array.`),layout:e.LayoutSchema.describe(`Object representing the layout`)}),n.object({type:n.literal(`markdown`).describe(`Markdown widget`),title:n.string().describe(`The title of the widget`),markdown:n.string().describe(`A character string in Markdown format`),layout:e.LayoutSchema.describe(`Object representing the layout`)}),n.object({type:n.literal(`alertStatus`).describe(`Alert status widget`),title:n.string().describe(`The title of the widget`),roleFullname:n.string().describe("The service name and role name linked by `:`.However, if the relevant role or service has been deleted when the dashboard is retrieved, `roleFullname` will be set as `null`. "),layout:e.LayoutSchema.describe(`Object representing the layout`)})]);static UpdateDashboardToolInput=n.object({dashboardId:n.string().describe(`The ID of the dashboard to update`),title:n.string().describe(`Dashboard name`),memo:n.string().describe(`Dashboard notes`),urlPath:n.string().describe(`Dashboard URL path`),widgets:n.array(e.WidgetSchema).describe(`List of widget objects`)});updateDashboard=async({dashboardId:e,title:t,memo:n,urlPath:r,widgets:i})=>await d(async()=>await this.mackerelClient.updateDashboard(e,{title:t,memo:n,urlPath:r,widgets:i}))},m=class{mackerelClient;constructor(e){this.mackerelClient=e}static ListMonitorsToolInput=n.object({});listMonitors=async({})=>await d(async()=>await this.mackerelClient.getMonitors());static GetMonitorToolInput=n.object({monitorId:n.string().describe(`Monitor ID`)});getMonitor=async({monitorId:e})=>await d(async()=>await this.mackerelClient.getMonitor(e))},h=class{mackerelClient;constructor(e){this.mackerelClient=e}static ListHostsToolInput=n.object({service:n.string().optional().describe(`Service name`),role:n.array(n.string()).optional().describe(`Role names in the service, multiple assignments possible (result will be a unit of the hosts that belong to each role) if service has not been assigned it will be ignored.`),name:n.string().optional().describe(`Host name`),status:n.array(n.string()).optional().describe("Filters the host status. multiple specifications are possible. defaults are `working` and `standby`."),customIdentifier:n.string().optional().describe(`An identifier for the host that is user-specific and unique to the organization (registered at Register Host Information or Update Host Information API)`),limit:n.number().int().positive().max(100).optional().describe(`Maximum number of hosts to return (default: 20, max: 100)`),offset:n.number().int().min(0).optional().describe(`Number of hosts to skip (default: 0)`)});listHosts=async({service:e,role:t,name:n,status:r,customIdentifier:i,limit:a=20,offset:o=0})=>await d(async()=>await this.mackerelClient.getHosts(e,t,n,r,i,a,o))},g=class{mackerelClient;constructor(e){this.mackerelClient=e}static ListServicesToolInput=n.object({});listServices=async e=>await d(async()=>await this.mackerelClient.getServices())};const _=n.object({key:n.string().describe(`Attribute key`),value:n.string().describe(`Attribute value as a string`),type:n.enum([`string`,`int`,`double`,`bool`]).describe(`Value data type: string, int, double, or bool`),operator:n.enum([`EQ`,`NEQ`,`GT`,`GTE`,`LT`,`LTE`,`STARTS_WITH`]).describe(`Comparison operator. Available operators depend on type: string supports EQ, NEQ, STARTS_WITH; int/double support EQ, GT, GTE, LT, LTE; bool supports EQ, NEQ`)});var v=class{mackerelClient;constructor(e){this.mackerelClient=e}static ListTracesToolInput=n.object({serviceName:n.string().describe("Service name. Corresponds to `service.name` in OpenTelemetry semantic conventions"),from:n.number().int().positive().describe(`Start time for trace search (Unix epoch seconds)`),to:n.number().int().positive().describe(`End time for trace search (Unix epoch seconds)`),serviceNamespace:n.string().optional().describe("Service namespace. Corresponds to `service.namespace` in OpenTelemetry semantic conventions"),environment:n.string().optional().describe("Environment name. Corresponds to `deployment.environment` or `deployment.environment.name` in OpenTelemetry semantic conventions"),traceId:n.string().optional().describe(`Filter by trace ID (32-digit hexadecimal string)`),spanName:n.string().optional().describe(`Filter by span name`),version:n.string().optional().describe("Version. Corresponds to `service.version` in OpenTelemetry semantic conventions"),issueFingerprint:n.string().optional().describe(`Filter by issue fingerprint`),minLatencyMillis:n.number().min(0).optional().describe(`Minimum latency in milliseconds`),maxLatencyMillis:n.number().min(0).optional().describe(`Maximum latency in milliseconds`),attributes:n.array(_).optional().describe(`Custom attribute filter conditions`),resourceAttributes:n.array(_).optional().describe(`Resource-level attribute filters`),page:n.number().int().positive().optional().describe(`Page number (starting from 1). Default is 1`),perPage:n.number().int().min(1).max(100).optional().describe(`Number of items per page (1-100). Default is 20`),order:n.object({column:n.enum([`LATENCY`,`START_AT`]).describe("Sort column. One of `LATENCY` (trace latency) or `START_AT` (trace timestamp)"),direction:n.enum([`ASC`,`DESC`]).describe("Sort order. Either `ASC` (ascending) or `DESC` (descending)")}).optional().describe(`Sort preferences. Default is { column: 'START_AT', direction: 'DESC' }`)});listTraces=async({serviceName:e,from:t,to:n,serviceNamespace:r,environment:i,traceId:a,spanName:o,version:s,issueFingerprint:c,minLatencyMillis:l,maxLatencyMillis:u,attributes:f,resourceAttributes:p,page:m,perPage:h,order:g})=>await d(async()=>await this.mackerelClient.listTraces({serviceName:e,from:t,to:n,serviceNamespace:r,environment:i,traceId:a,spanName:o,version:s,issueFingerprint:c,minLatencyMillis:l,maxLatencyMillis:u,attributes:f,resourceAttributes:p,page:m,perPage:h,order:g}));static GetTraceToolInput=n.object({traceId:n.string().describe(`The ID of the trace to retrieve`),includeAttributes:n.boolean().optional().default(!1).describe(`Whether to include span attributes. Default is false to reduce response size`),includeEvents:n.boolean().optional().default(!0).describe(`Whether to include span events. Default is true as events contain important error information`),limit:n.number().int().positive().max(100).optional().default(20).describe(`Maximum number of spans to return per page (default: 20, max: 100)`),offset:n.number().int().min(0).optional().default(0).describe(`Number of spans to skip (default: 0)`),duration:n.number().min(0).optional().describe(`Only return spans with duration (in milliseconds) equal to or greater than this value. Useful for identifying bottlenecks`),errorSpansOnly:n.boolean().optional().default(!1).describe(`If true, only return spans that have errors or status code indicating failure`)});optimizeSpan(e,t,n){let r=e.endTime-e.startTime,i=this.hasSpanError(e),a={spanId:e.spanId,name:e.name,startTime:e.startTime,endTime:e.endTime,duration:r,hasError:i};return t&&(a.attributes=e.attributes),n&&(a.events=e.events),e.status&&e.status.code.toLowerCase()===`error`&&(a.status=e.status),a}hasSpanError(e){return e.status&&e.status.code.toLowerCase()===`error`?!0:e.events?e.events.some(e=>e.name.toLowerCase().includes(`exception`)):!1}filterAndSortSpans(e,t,n){let r=e;return n&&(r=r.filter(e=>e.hasError)),t!==void 0&&(r=r.filter(e=>e.duration>=t)),r.sort((e,t)=>e.hasError&&!t.hasError?-1:!e.hasError&&t.hasError?1:t.duration-e.duration),r}createPageInfo(e,t,n){let r=Math.floor(n/t)+1,i=Math.ceil(e/t);return{hasNextPage:n+t<e,hasPrevPage:n>0,currentPage:r,totalPages:i}}getTrace=async({traceId:e,includeAttributes:t=!1,includeEvents:n=!0,limit:r=20,offset:a=0,duration:o,errorSpansOnly:s=!1})=>await d(async()=>{let c=await this.mackerelClient.getTrace(e),l=c.spans.map(e=>this.optimizeSpan(e,t,n)),u=this.filterAndSortSpans(l,o,s),d=i(u,{limit:r,offset:a}),f=this.createPageInfo(u.length,r,a),p=c.spans.length,m=d.some(e=>e.hasError),h=0;if(l.length>0){let e=Math.min(...l.map(e=>e.startTime));h=Math.max(...l.map(e=>e.endTime))-e}return{traceId:e,summary:{totalSpans:p,hasErrors:m,totalDuration:h,pageInfo:f},spans:d}})},y=class{baseUrl;apiKey;cacheMap=/* @__PURE__ */ new Map;cacheTTL;constructor(e,t,n=300){this.baseUrl=e,this.apiKey=t,this.cacheTTL=n*1e3}getCacheKey(e,t,n){return`${e}:${t}:${n?n.toString():``}`}getFromCache(e){let t=this.cacheMap.get(e);return t?Date.now()-t.timestamp>this.cacheTTL?(this.cacheMap.delete(e),null):t.data:null}setCache(e,t){this.cacheMap.set(e,{data:t,timestamp:Date.now()})}clearCache(){this.cacheMap.clear()}async request(e,t,{searchParams:n,body:r}={}){if(e===`GET`){let r=this.getCacheKey(e,t,n),i=this.getFromCache(r);if(i!==null)return i}let i=`${this.baseUrl}${t}${n?`?`+n.toString():``}`,a={method:e,headers:{"X-Api-Key":this.apiKey,"Content-Type":`application/json`}};r&&(a.body=JSON.stringify(r));let o=await fetch(i,a);if(!o.ok){let e=await o.text(),t=o.headers.get(`Retry-After`);throw Error(`Mackerel API error: ${o.status} ${e}${t?` (Retry-After: ${t} seconds)`:``}`)}let s=await o.json();if(e===`GET`){let r=this.getCacheKey(e,t,n);this.setCache(r,s)}return s}async getAlerts(e,t,n){let r=new URLSearchParams;return e!==void 0&&r.append(`withClosed`,e.toString()),t&&r.append(`nextId`,t),n!==void 0&&r.append(`limit`,n.toString()),this.request(`GET`,`/api/v0/alerts`,{searchParams:r})}async getAlert(e){return this.request(`GET`,`/api/v0/alerts/${e}`)}async getAlertLogs(e,t,n){let r=new URLSearchParams;return t&&r.append(`nextId`,t),n!==void 0&&r.append(`limit`,n.toString()),this.request(`GET`,`/api/v0/alerts/${e}/logs`,{searchParams:r})}async getDashboards(e,t){let n=await this.request(`GET`,`/api/v0/dashboards`),r=e||20,a=t||0,o=n.dashboards.length;return{dashboards:i(n.dashboards,{limit:r,offset:a}),pageInfo:{hasPrevPage:a>0,hasNextPage:a+r<o}}}async getDashboard(e){return this.request(`GET`,`/api/v0/dashboards/${e}`)}static WidgetArray=n.array(p.WidgetSchema);async updateDashboard(e,t){return this.request(`PUT`,`/api/v0/dashboards/${e}`,{body:t})}async getHosts(e,t,n,r,a,o,s){let c=new URLSearchParams;if(e&&c.append(`service`,e),t)for(let e of t)c.append(`role`,e);if(n&&c.append(`name`,n),r)for(let e of r)c.append(`status`,e);a&&c.append(`customIdentifier`,a);let l=await this.request(`GET`,`/api/v0/hosts`,{searchParams:c}),u=o||20,d=s||0,f=l.hosts.length;return{hosts:i(l.hosts,{limit:u,offset:d}),pageInfo:{hasPrevPage:d>0,hasNextPage:d+u<f}}}async getHostMetrics(e,t,n,r){let i=new URLSearchParams;return i.append(`name`,t),i.append(`from`,n.toString()),i.append(`to`,r.toString()),this.request(`GET`,`/api/v0/hosts/${e}/metrics`,{searchParams:i})}async getServices(){return this.request(`GET`,`/api/v0/services`)}async getServiceMetrics(e,t,n,r){let i=new URLSearchParams;return i.append(`name`,t),i.append(`from`,n.toString()),i.append(`to`,r.toString()),this.request(`GET`,`/api/v0/services/${e}/metrics`,{searchParams:i})}async getMonitors(){return this.request(`GET`,`/api/v0/monitors`)}async getMonitor(e){return this.request(`GET`,`/api/v0/monitors/${e}`)}async getTrace(e){return this.request(`GET`,`/api/v0/traces/${e}`)}async getHttpServerStats(e,t,n,r,i,a,o,s,c,l,u,d){let f=new URLSearchParams;return f.append(`serviceName`,e),f.append(`from`,t.toString()),f.append(`to`,n.toString()),r&&f.append(`serviceNamespace`,r),i&&f.append(`environment`,i),a&&f.append(`version`,a),o&&f.append(`method`,o),s&&f.append(`route`,s),c&&f.append(`orderColumn`,c),l&&f.append(`orderDirection`,l),u!==void 0&&f.append(`page`,u.toString()),d!==void 0&&f.append(`perPage`,d.toString()),this.request(`GET`,`/api/v0/apm/http-server-stats`,{searchParams:f})}async getDbQueryStats(e,t,n,r,i,a,o,s,c,l,u){let d=new URLSearchParams;return d.append(`serviceName`,e),d.append(`from`,t.toString()),d.append(`to`,n.toString()),r&&d.append(`serviceNamespace`,r),i&&d.append(`environment`,i),a&&d.append(`version`,a),o&&d.append(`query`,o),s&&d.append(`orderColumn`,s),c&&d.append(`orderDirection`,c),l!==void 0&&d.append(`page`,l.toString()),u!==void 0&&d.append(`perPage`,u.toString()),this.request(`GET`,`/api/v0/apm/db-query-stats`,{searchParams:d})}async listTraces(e){let t={serviceName:e.serviceName,from:e.from,to:e.to};return e.serviceNamespace&&(t.serviceNamespace=e.serviceNamespace),e.environment&&(t.environment=e.environment),e.traceId&&(t.traceId=e.traceId),e.spanName&&(t.spanName=e.spanName),e.version&&(t.version=e.version),e.issueFingerprint&&(t.issueFingerprint=e.issueFingerprint),e.minLatencyMillis!==void 0&&(t.minLatencyMillis=e.minLatencyMillis),e.maxLatencyMillis!==void 0&&(t.maxLatencyMillis=e.maxLatencyMillis),e.attributes&&e.attributes.length>0&&(t.attributes=e.attributes),e.resourceAttributes&&e.resourceAttributes.length>0&&(t.resourceAttributes=e.resourceAttributes),e.page!==void 0&&(t.page=e.page),e.perPage!==void 0&&(t.perPage=e.perPage),e.order&&(t.order=e.order),this.request(`POST`,`/api/v0/traces`,{body:t})}async searchLogs(e){return this.request(`POST`,`/api/v0/logs`,{body:e})}},b=class{mackerelClient;constructor(e){this.mackerelClient=e}static GetServiceMetricsToolInput=n.object({serviceName:n.string().describe(`Service name`),name:n.string().describe(`Metric name (e.g., __externalhttp.responsetime.<monitorId>, sales.count, analytics.page_view, etc.)`),from:n.number().describe(`The start of the time period you want metrics for (unix time)`),to:n.number().describe(`The end of the time period you want metrics for (unix time)`)});getServiceMetrics=async({serviceName:e,name:t,from:n,to:r})=>await d(async()=>await this.mackerelClient.getServiceMetrics(e,t,n,r),`get_service_metrics`)},x=class{mackerelClient;constructor(e){this.mackerelClient=e}static GetHostMetricsToolInput=n.object({hostId:n.string().describe(`Host ID`),name:n.string().describe(`Metric name (e.g., loadavg5, cpu.user, memory.used, custom.myapp.*, ec2.cpu.used, etc.)`),from:n.number().describe(`The start of the time period you want metrics for (unix time)`),to:n.number().describe(`The end of the time period you want metrics for (unix time)`)});getHostMetrics=async({hostId:e,name:t,from:n,to:r})=>await d(async()=>await this.mackerelClient.getHostMetrics(e,t,n,r),`get_host_metrics`)},S=class{mackerelClient;constructor(e){this.mackerelClient=e}static ListHttpServerStatsToolInput=n.object({serviceName:n.string().describe("Service name. Corresponds to `service.name` in OpenTelemetry semantic conventions"),from:n.number().int().positive().describe("Start time for retrieving statistics (Unix epoch seconds). The range between `to` is limited to 30 minutes"),to:n.number().int().positive().describe("End time for retrieving statistics (Unix epoch seconds). The range between `from` is limited to 30 minutes"),serviceNamespace:n.string().optional().describe("Service namespace. Corresponds to `service.namespace` in OpenTelemetry semantic conventions"),environment:n.string().optional().describe("Environment name. Corresponds to `deployment.environment` or `deployment.environment.name` in OpenTelemetry semantic conventions"),version:n.string().optional().describe("Version. Corresponds to `service.version` in OpenTelemetry semantic conventions"),method:n.string().optional().describe("Filter by HTTP method. Corresponds to `http.request.method` or `http.method` in OpenTelemetry semantic conventions. `http.request.method` is preferred"),route:n.string().optional().describe("Filter by route (partial match). Corresponds to `http.route`, `url.path`, `http.target`, or `http.url` in OpenTelemetry semantic conventions. Preferred in this order"),orderColumn:n.enum([`SUM`,`AVERAGE`,`P95`,`ERROR_RATE`,`REQUEST_COUNT`]).optional().describe("Sort column. One of `SUM` (total latency), `AVERAGE` (average latency), `P95` (95th percentile latency), `ERROR_RATE` (error rate percentage), or `REQUEST_COUNT` (request count). Default is `P95`"),orderDirection:n.enum([`ASC`,`DESC`]).optional().describe("Sort order. Either `ASC` (ascending) or `DESC` (descending). Default is `DESC`"),page:n.number().int().positive().optional().describe(`Page number (starting from 1). Default is 1`),perPage:n.number().int().min(1).max(100).optional().describe(`Number of items per page (1-100). Default is 20`)});listHttpServerStats=async({serviceName:e,from:t,to:n,serviceNamespace:r,environment:i,version:a,method:o,route:s,orderColumn:c,orderDirection:l,page:u,perPage:f})=>await d(async()=>await this.mackerelClient.getHttpServerStats(e,t,n,r,i,a,o,s,c,l,u,f));static ListDbQueryStatsToolInput=n.object({serviceName:n.string().describe("Service name. Corresponds to `service.name` in OpenTelemetry semantic conventions."),from:n.number().int().positive().describe("Start time for retrieving statistics (Unix epoch seconds). The range between `to` is limited to 30 minutes"),to:n.number().int().positive().describe("End time for retrieving statistics (Unix epoch seconds). The range between `from` is limited to 30 minutes"),serviceNamespace:n.string().optional().describe("Service namespace. Corresponds to `service.namespace` in OpenTelemetry semantic conventions"),environment:n.string().optional().describe("Environment name. Corresponds to `deployment.environment` or `deployment.environment.name` in OpenTelemetry semantic conventions"),version:n.string().optional().describe("Version. Corresponds to `service.version` in OpenTelemetry semantic conventions"),query:n.string().optional().describe("Filter by SQL query (partial match). Corresponds to `db.query.text` or `db.statement` in OpenTelemetry semantic conventions. db.query.text is preferred"),orderColumn:n.enum([`SUM`,`AVERAGE`,`P95`,`EXECUTION_COUNT`]).optional().describe("Sort column. One of `SUM` (total latency), `AVERAGE` (average latency), `P95` (95th percentile latency), or `EXECUTION_COUNT` (execution count). Default is `P95`."),orderDirection:n.enum([`ASC`,`DESC`]).optional().describe("Sort order. Either `ASC` (ascending) or `DESC` (descending). Default is `DESC`"),page:n.number().int().positive().optional().describe(`Page number (starting from 1). Default is 1`),perPage:n.number().int().min(1).max(100).optional().describe(`Number of items per page (1-100). Default is 20`)});listDbQueryStats=async({serviceName:e,from:t,to:n,serviceNamespace:r,environment:i,version:a,query:o,orderColumn:s,orderDirection:c,page:l,perPage:u})=>await d(async()=>await this.mackerelClient.getDbQueryStats(e,t,n,r,i,a,o,s,c,l,u))};const C=[`UNSPECIFIED`,`TRACE`,`DEBUG`,`INFO`,`WARN`,`ERROR`,`FATAL`];function w(e){return Object.fromEntries(e.map(({key:e,value:t})=>[e,t.stringValue]))}const T={timestamp:e=>e.timestamp,severity:e=>e.severity,severityNumber:e=>e.severityNumber,body:e=>e.body,traceId:e=>e.traceId,spanId:e=>e.spanId,serviceName:e=>e.serviceName,serviceNamespace:e=>e.serviceNamespace,attributes:e=>w(e.attributes),resourceAttributes:e=>w(e.resourceAttributes),scopeAttributes:e=>w(e.scopeAttributes)},E=Object.keys(T),D=[`timestamp`,`severity`,`body`,`traceId`,`spanId`],O=43200;function k(e,t){return{from:e,to:t,fromTime:(/* @__PURE__ */ new Date(e*1e3)).toISOString(),toTime:(/* @__PURE__ */ new Date(t*1e3)).toISOString()}}const A=n.object({direction:n.enum([`NEXT`,`PREVIOUS`]).describe(`NEXT returns logs after the cursor in sort order, PREVIOUS returns logs before it`),cursor:n.string().describe(`Cursor string returned by a previous call`),from:n.number().int().positive().describe(`Start time of the search the cursor belongs to`),to:n.number().int().positive().describe(`End time of the search the cursor belongs to`),order:n.enum([`ASC`,`DESC`]).describe(`Sort order of the search the cursor belongs to`)}),j=(e,t)=>n.object({keyPath:n.array(n.string()).min(1).describe(`Path to the field in a JSON log body, e.g. ["req", "method"] for {"req": {"method": "GET"}}`),value:n.string().describe(`Value to compare, always given as a string`),type:n.literal(e).describe(`Value data type`),operator:n.enum(t).describe(`Comparison operator for ${e}: ${t.join(`, `)}`)}),M=n.discriminatedUnion(`type`,[j(`string`,[`EQ`,`NEQ`,`STARTS_WITH`]),j(`int`,[`EQ`,`GT`,`GTE`,`LT`,`LTE`]),j(`double`,[`EQ`,`GT`,`GTE`,`LT`,`LTE`]),j(`bool`,[`EQ`,`NEQ`])]),N={400:"Check that `to` is not earlier than `from`, the range is 30 days or less, and `traceId` / `bodyAttributes` are well-formed (e.g. operator is supported for the type).",403:`Logs may not be enabled for this organization, or the API key lacks read permission.`,429:`Rate limit is 1 request per second. Wait for the Retry-After period before calling again, and avoid fetching pages in rapid succession.`};function P(e){let t=e instanceof Error?e.message:String(e),n=Number(t.match(/Mackerel API error: (\d{3})/)?.[1]),r=N[n];return Error(r?`${t}\nHint: ${r}`:t)}function F({from:e,to:t}){if(t<=e)throw Error(`Invalid time range: \`to\` (${t}) must be later than \`from\` (${e}).`);if(t-e>O*60)throw Error(`Invalid time range: the range from \`from\` to \`to\` must be 30 days or less (got ${Math.ceil((t-e)/86400)} days).`)}function I(e,t,n,r){return t&&n?{direction:e,cursor:n,...r}:null}var L=class{mackerelClient;constructor(e){this.mackerelClient=e}static SearchLogsToolInput=n.object({serviceName:n.string().describe(`Service name`),serviceNamespace:n.string().optional().describe("Service namespace. Corresponds to `service.namespace` in OpenTelemetry semantic conventions. When omitted, only services without a namespace are searched, so always pass it for services that have one"),from:n.number().int().positive().optional().describe("Start time (Unix epoch seconds, inclusive). Defaults to `lookbackMinutes` before `to`"),to:n.number().int().positive().optional().describe("End time (Unix epoch seconds, exclusive). Defaults to the current time. The range from `from` must be 30 days or less"),lookbackMinutes:n.number().int().min(1).max(O).optional().default(60).describe("When `from` is omitted, search this many minutes before `to` (default 60). Use this for relative ranges such as 'the last 15 minutes' without knowing the current time"),keywords:n.array(n.string().min(1)).min(1).optional().describe(`Keywords that must all appear in the log body (partial match, AND)`),severities:n.array(n.enum(C)).min(1).optional().describe(`Severities to include (OR)`),traceId:n.string().optional().describe(`Only logs associated with this trace ID (32-digit hex)`),bodyAttributes:n.array(M).min(1).optional().describe(`Filters on fields of JSON log bodies (AND). Logs whose body is not JSON never match`),order:n.enum([`ASC`,`DESC`]).optional().describe(`Sort order by timestamp. Default is DESC (newest first)`),pageCursor:A.optional().describe("Pass `pagination.nextPageCursor` or `pagination.previousPageCursor` from a previous response as-is. The cursor carries the time range and sort order, so `from`, `to`, `lookbackMinutes` and `order` are ignored. Keep the filters unchanged between pages"),perPage:n.number().int().min(1).max(1e3).optional().default(20).describe(`Number of logs per page (1-1000). Default is 20`),fields:n.array(n.enum(E)).min(1).optional().describe(`Fields to include for each log. Default is ${JSON.stringify(D)}. Attribute fields are returned as {key: value} objects`),maxBodyLength:n.number().int().positive().optional().default(1e3).describe("Truncate each log body to this many characters (default 1000). Truncated logs have `bodyTruncated: true`")});searchLogs=async({serviceName:e,serviceNamespace:t,from:n,to:r=Math.floor(Date.now()/1e3),lookbackMinutes:i=60,keywords:a,severities:o,traceId:s,bodyAttributes:c,order:l,pageCursor:u,perPage:f=20,fields:p=D,maxBodyLength:m=1e3})=>{let h=u?k(u.from,u.to):k(n??r-i*60,r),g=u?.order??l??`DESC`;return await d(async()=>{F(h);let n=await this.mackerelClient.searchLogs({serviceName:e,serviceNamespace:t,from:h.from,to:h.to,keywords:a,severities:o,traceId:s,bodyAttributes:c,order:{column:`TIMESTAMP`,direction:g},pageCursor:u&&{direction:u.direction,cursor:u.cursor},perPage:f}).catch(e=>{throw P(e)});return{timeRange:h,logs:n.results.map(e=>this.projectLog(e,p,m)),pagination:this.buildPagination(n.pagination,{from:h.from,to:h.to,order:g})}},`search_logs`,`- Reducing perPage and following pagination.nextPageCursor
|
|
129
|
+
- Requesting fewer fields (attributes / resourceAttributes are large) or a smaller maxBodyLength
|
|
130
|
+
- Narrowing the time range or adding severities / keywords / bodyAttributes filters`)};projectLog(e,t,n){let r=Object.fromEntries(t.map(t=>[t,T[t](e)]));return t.includes(`body`)&&e.body.length>n&&(r.body=e.body.slice(0,n),r.bodyTruncated=!0),r}buildPagination(e,t){return{hasNextPage:e.hasNextPage,hasPreviousPage:e.hasPreviousPage,nextPageCursor:I(`NEXT`,e.hasNextPage,e.endCursor,t),previousPageCursor:I(`PREVIOUS`,e.hasPreviousPage,e.startCursor,t)}}};async function R(){let n=new y(`https://api.mackerelio.com`,z()),r=new f(n),i=new p(n),a=new m(n),o=new h(n),s=new x(n),c=new g(n),l=new b(n),u=new v(n),d=new S(n),_=new L(n),C=new e({name:`@mackerel/mcp-server`,version:`0.0.1`});C.registerTool(`list_alerts`,{title:`List Alerts`,description:`Retrieve alerts from Mackerel.
|
|
129
131
|
|
|
130
132
|
🔍 USE THIS TOOL WHEN USERS:
|
|
131
133
|
- Check currently active alerts
|
|
@@ -142,7 +144,7 @@ list_alerts()
|
|
|
142
144
|
list_alerts(withClosed=true)
|
|
143
145
|
\`\`\`
|
|
144
146
|
</examples>
|
|
145
|
-
`,inputSchema:f.ListAlertsToolInput.shape,annotations:{readOnlyHint:!0}},r.listAlerts),
|
|
147
|
+
`,inputSchema:f.ListAlertsToolInput.shape,annotations:{readOnlyHint:!0}},r.listAlerts),C.registerTool(`get_alert`,{title:`Get Alert`,description:`Retrieve a specific alert by ID from Mackerel.
|
|
146
148
|
|
|
147
149
|
🔍 USE THIS TOOL WHEN USERS:
|
|
148
150
|
- Investigate a particular alert
|
|
@@ -153,7 +155,7 @@ list_alerts(withClosed=true)
|
|
|
153
155
|
get_alert(alertId=3Yr)
|
|
154
156
|
\`\`\`
|
|
155
157
|
</example>
|
|
156
|
-
`,inputSchema:f.GetAlertToolInput.shape,annotations:{readOnlyHint:!0}},r.getAlert),
|
|
158
|
+
`,inputSchema:f.GetAlertToolInput.shape,annotations:{readOnlyHint:!0}},r.getAlert),C.registerTool(`get_alert_logs`,{title:`Get Alert Logs`,description:`Retrieve logs for a specific alert by ID from Mackerel.
|
|
157
159
|
|
|
158
160
|
🔍 USE THIS TOOL WHEN USERS:
|
|
159
161
|
- View status change history for a specific alert
|
|
@@ -165,7 +167,7 @@ get_alert(alertId=3Yr)
|
|
|
165
167
|
get_alert_logs(alertId=3Yr)
|
|
166
168
|
\`\`\`
|
|
167
169
|
</example>
|
|
168
|
-
`,inputSchema:f.GetAlertLogsToolInput.shape,annotations:{readOnlyHint:!0}},r.getAlertLogs),
|
|
170
|
+
`,inputSchema:f.GetAlertLogsToolInput.shape,annotations:{readOnlyHint:!0}},r.getAlertLogs),C.registerTool(`list_dashboards`,{title:`List Dashboards`,description:`Retrieve all dashboards from Mackerel.
|
|
169
171
|
|
|
170
172
|
🔍 USE THIS TOOL WHEN USERS:
|
|
171
173
|
- Get a list of dashboards
|
|
@@ -177,7 +179,7 @@ get_alert_logs(alertId=3Yr)
|
|
|
177
179
|
list_dashboards()
|
|
178
180
|
\`\`\`
|
|
179
181
|
</example>
|
|
180
|
-
`,inputSchema:p.ListDashboardsToolInput.shape,annotations:{readOnlyHint:!0}},i.listDashboards),
|
|
182
|
+
`,inputSchema:p.ListDashboardsToolInput.shape,annotations:{readOnlyHint:!0}},i.listDashboards),C.registerTool(`get_dashboard`,{title:`Get Dashboard`,description:`Retrieve a specific dashboard by ID from Mackerel.
|
|
181
183
|
|
|
182
184
|
🔍 USE THIS TOOL WHEN USERS:
|
|
183
185
|
- Get details of a specific dashboard
|
|
@@ -189,7 +191,7 @@ list_dashboards()
|
|
|
189
191
|
get_dashboard(dashboardId="3Yr")
|
|
190
192
|
\`\`\`
|
|
191
193
|
</examples>
|
|
192
|
-
`,inputSchema:p.GetDashboardToolInput.shape,annotations:{readOnlyHint:!0}},i.getDashboard),
|
|
194
|
+
`,inputSchema:p.GetDashboardToolInput.shape,annotations:{readOnlyHint:!0}},i.getDashboard),C.registerTool(`update_dashboard`,{title:`Update Dashboard`,description:`Update a specific dashboard by ID in Mackerel.
|
|
193
195
|
|
|
194
196
|
🔍 USE THIS TOOL WHEN USERS:
|
|
195
197
|
- Modify dashboard title, memo, or URL path
|
|
@@ -212,7 +214,7 @@ update_dashboard(
|
|
|
212
214
|
)
|
|
213
215
|
\`\`\`
|
|
214
216
|
</examples>
|
|
215
|
-
`,inputSchema:p.UpdateDashboardToolInput.shape},i.updateDashboard),
|
|
217
|
+
`,inputSchema:p.UpdateDashboardToolInput.shape},i.updateDashboard),C.registerTool(`list_hosts`,{title:`List Hosts`,description:`Retrieve hosts from Mackerel.
|
|
216
218
|
|
|
217
219
|
🔍 USE THIS TOOL WHEN USERS:
|
|
218
220
|
- Get a list of hosts
|
|
@@ -245,7 +247,7 @@ list_hosts(summary=true)
|
|
|
245
247
|
list_hosts(limit=20, offset=20)
|
|
246
248
|
\`\`\`
|
|
247
249
|
</examples>
|
|
248
|
-
`,inputSchema:h.ListHostsToolInput.shape,annotations:{readOnlyHint:!0}},o.listHosts),
|
|
250
|
+
`,inputSchema:h.ListHostsToolInput.shape,annotations:{readOnlyHint:!0}},o.listHosts),C.registerTool(`get_host_metrics`,{title:`Get Host Metrics`,description:`Retrieve metrics data for a specific host from Mackerel.
|
|
249
251
|
|
|
250
252
|
🔍 USE THIS TOOL WHEN USERS:
|
|
251
253
|
- Get metrics data for a specific host
|
|
@@ -274,7 +276,7 @@ get_host_metrics(hostId="host123", name="custom.myapp.response_time", from=16094
|
|
|
274
276
|
get_host_metrics(hostId="host123", name="ec2.cpu.used", from=1609459200, to=1609462800)
|
|
275
277
|
\`\`\`
|
|
276
278
|
</examples>
|
|
277
|
-
`,inputSchema:x.GetHostMetricsToolInput.shape,annotations:{readOnlyHint:!0}},s.getHostMetrics),
|
|
279
|
+
`,inputSchema:x.GetHostMetricsToolInput.shape,annotations:{readOnlyHint:!0}},s.getHostMetrics),C.registerTool(`list_services`,{title:`List Services`,description:`Retrieve all services from Mackerel.
|
|
278
280
|
|
|
279
281
|
🔍 USE THIS TOOL WHEN USERS:
|
|
280
282
|
- Get a list of services
|
|
@@ -286,7 +288,7 @@ get_host_metrics(hostId="host123", name="ec2.cpu.used", from=1609459200, to=1609
|
|
|
286
288
|
list_services()
|
|
287
289
|
\`\`\`
|
|
288
290
|
</examples>
|
|
289
|
-
`,inputSchema:g.ListServicesToolInput.shape,annotations:{readOnlyHint:!0}},c.listServices),
|
|
291
|
+
`,inputSchema:g.ListServicesToolInput.shape,annotations:{readOnlyHint:!0}},c.listServices),C.registerTool(`get_service_metrics`,{title:`Get Service Metrics`,description:`Retrieve metrics data for a specific service from Mackerel.
|
|
290
292
|
|
|
291
293
|
🔍 USE THIS TOOL WHEN USERS:
|
|
292
294
|
- "Service metrics" are metrics that correspond to a service that consists of multiple hosts and their collective roles
|
|
@@ -310,7 +312,7 @@ get_service_metrics(serviceName="web", name="__externalhttp.responsetime.<monito
|
|
|
310
312
|
get_service_metrics(serviceName="web", name="analytics.page_view", from=1609459200, to=1609462800)
|
|
311
313
|
\`\`\`
|
|
312
314
|
|
|
313
|
-
</examples>`,inputSchema:b.GetServiceMetricsToolInput.shape,annotations:{readOnlyHint:!0}},l.getServiceMetrics),
|
|
315
|
+
</examples>`,inputSchema:b.GetServiceMetricsToolInput.shape,annotations:{readOnlyHint:!0}},l.getServiceMetrics),C.registerTool(`list_monitors`,{title:`List Monitors`,description:`Retrieve all monitor configurations from Mackerel.
|
|
314
316
|
|
|
315
317
|
🔍 USE THIS TOOL WHEN USERS:
|
|
316
318
|
- Get a list of all monitors
|
|
@@ -322,7 +324,7 @@ get_service_metrics(serviceName="web", name="analytics.page_view", from=16094592
|
|
|
322
324
|
list_monitors()
|
|
323
325
|
\`\`\`
|
|
324
326
|
</examples>
|
|
325
|
-
`,inputSchema:m.ListMonitorsToolInput.shape,annotations:{readOnlyHint:!0}},a.listMonitors),
|
|
327
|
+
`,inputSchema:m.ListMonitorsToolInput.shape,annotations:{readOnlyHint:!0}},a.listMonitors),C.registerTool(`get_monitor`,{title:`Get Monitor`,description:`Retrieve a specific monitor configuration by ID from Mackerel.
|
|
326
328
|
|
|
327
329
|
🔍 USE THIS TOOL WHEN USERS:
|
|
328
330
|
- Get details of a specific monitor
|
|
@@ -333,7 +335,7 @@ list_monitors()
|
|
|
333
335
|
get_monitor(monitorId="2cSZzK3XfmB")
|
|
334
336
|
\`\`\`
|
|
335
337
|
</examples>
|
|
336
|
-
`,inputSchema:m.GetMonitorToolInput.shape,annotations:{readOnlyHint:!0}},a.getMonitor),
|
|
338
|
+
`,inputSchema:m.GetMonitorToolInput.shape,annotations:{readOnlyHint:!0}},a.getMonitor),C.registerTool(`get_trace`,{title:`Get Trace`,description:`Retrieve trace data by trace ID from Mackerel for distributed tracing analysis.
|
|
337
339
|
|
|
338
340
|
🔍 USE THIS TOOL WHEN USERS:
|
|
339
341
|
- Analyze performance bottlenecks in distributed systems
|
|
@@ -379,7 +381,7 @@ get_trace(traceId="abc123def456", limit=20, offset=20) # Second page
|
|
|
379
381
|
get_trace(traceId="abc123def456", errorSpansOnly=true, limit=10, offset=0)
|
|
380
382
|
\`\`\`
|
|
381
383
|
</examples>
|
|
382
|
-
`,inputSchema:v.GetTraceToolInput.shape,annotations:{readOnlyHint:!0}},u.getTrace),
|
|
384
|
+
`,inputSchema:v.GetTraceToolInput.shape,annotations:{readOnlyHint:!0}},u.getTrace),C.registerTool(`list_traces`,{title:`List Traces`,description:`Search and retrieve traces from Mackerel for distributed tracing analysis.
|
|
383
385
|
|
|
384
386
|
🔍 USE THIS TOOL WHEN USERS:
|
|
385
387
|
- Search for traces within a time range
|
|
@@ -437,7 +439,7 @@ list_traces(
|
|
|
437
439
|
)
|
|
438
440
|
\`\`\`
|
|
439
441
|
</examples>
|
|
440
|
-
`,inputSchema:v.ListTracesToolInput.shape,annotations:{readOnlyHint:!0}},u.listTraces),
|
|
442
|
+
`,inputSchema:v.ListTracesToolInput.shape,annotations:{readOnlyHint:!0}},u.listTraces),C.registerTool(`list_http_server_stats`,{title:`List HTTP Server Statistics`,description:`Retrieve HTTP server statistics from Mackerel.
|
|
441
443
|
|
|
442
444
|
🔍 USE THIS TOOL WHEN USERS:
|
|
443
445
|
- Analyze slow HTTP endpoints
|
|
@@ -484,7 +486,7 @@ list_http_server_stats(
|
|
|
484
486
|
)
|
|
485
487
|
\`\`\`
|
|
486
488
|
</examples>
|
|
487
|
-
`,inputSchema:S.ListHttpServerStatsToolInput.shape,annotations:{readOnlyHint:!0}},d.listHttpServerStats),
|
|
489
|
+
`,inputSchema:S.ListHttpServerStatsToolInput.shape,annotations:{readOnlyHint:!0}},d.listHttpServerStats),C.registerTool(`list_db_query_stats`,{title:`List Database Query Statistics`,description:`Retrieve database query statistics from Mackerel.
|
|
488
490
|
|
|
489
491
|
🔍 USE THIS TOOL WHEN USERS:
|
|
490
492
|
- Analyze slow database queries
|
|
@@ -530,4 +532,4 @@ list_db_query_stats(
|
|
|
530
532
|
)
|
|
531
533
|
\`\`\`
|
|
532
534
|
</examples>
|
|
533
|
-
`,inputSchema:S.ListDbQueryStatsToolInput.shape,annotations:{readOnlyHint:!0}},d.listDbQueryStats);let
|
|
535
|
+
`,inputSchema:S.ListDbQueryStatsToolInput.shape,annotations:{readOnlyHint:!0}},d.listDbQueryStats),C.registerTool(`search_logs`,{title:`Search Logs`,description:'Search logs of a service from Mackerel.\n\n🔍 USE THIS TOOL WHEN USERS:\n- Investigate errors or incidents by reading application logs\n- Find logs related to a specific trace (traceId)\n- Look for logs containing specific keywords or matching structured (JSON) body fields\n\n📏 KEEPING RESPONSES SMALL:\n- Pass `serviceNamespace` when the service has one. Without it, only services without a namespace are searched\n- Omit `from` / `to` to search relative to the current time (`lookbackMinutes`, default 60). The response includes the actual `timeRange` searched\n- Narrow the time range and use severities / keywords / bodyAttributes filters first\n- By default only timestamp, severity, body, traceId and spanId are returned. Use `fields` to request attributes only when needed\n- Log bodies longer than `maxBodyLength` are truncated and marked with `bodyTruncated: true`\n- To get more logs, pass `pagination.nextPageCursor` as `pageCursor` as-is with the same filters. The cursor carries the time range and sort order, so `from` / `to` / `order` are not needed. The API allows 1 request per second\n\n<examples>\n### Error logs in the last hour (from/to default to the last 60 minutes)\n```\nsearch_logs(serviceName="my-service", serviceNamespace="my-namespace", severities=["ERROR", "FATAL"])\n```\n\n### Logs in the last 15 minutes\n```\nsearch_logs(serviceName="my-service", serviceNamespace="my-namespace", lookbackMinutes=15)\n```\n\n### Logs related to a trace in chronological order\n```\nsearch_logs(serviceName="my-service", serviceNamespace="my-namespace", from=1700000000, to=1700003600, traceId="0af7651916cd43dd8448eb211c80319c", order="ASC")\n```\n\n### Keyword search\n```\nsearch_logs(serviceName="my-service", serviceNamespace="my-namespace", from=1700000000, to=1700003600, keywords=["timeout", "payment"])\n```\n\n### Filter JSON bodies: {"res": {"status": 500+}}\n```\nsearch_logs(\n serviceName="my-service",\n serviceNamespace="my-namespace",\n from=1700000000,\n to=1700003600,\n bodyAttributes=[{keyPath: ["res", "status"], value: "500", type: "int", operator: "GTE"}]\n)\n```\n\n### Include log attributes\n```\nsearch_logs(serviceName="my-service", serviceNamespace="my-namespace", from=1700000000, to=1700003600, fields=["timestamp", "body", "attributes", "resourceAttributes"])\n```\n\n### Next page\n```\nsearch_logs(serviceName="my-service", serviceNamespace="my-namespace", severities=["ERROR"], pageCursor=<pagination.nextPageCursor>)\n```\n</examples>\n',inputSchema:L.SearchLogsToolInput.shape,annotations:{readOnlyHint:!0}},_.searchLogs);let w=new t;await C.connect(w),console.error(`Mackerel MCP Server running on stdio`)}function z(){let e=process.env.MACKEREL_APIKEY;if(!e)throw Error(`MACKEREL_APIKEY environment variable is not set.`);return e}R().catch(e=>{console.error(`Fatal error in main():`,e),process.exit(1)});export{};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mackerel/mcp-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -41,15 +41,15 @@
|
|
|
41
41
|
],
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@modelcontextprotocol/sdk": "^1.17.2",
|
|
44
|
-
"zod": "^
|
|
44
|
+
"zod": "^4.6.7"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
|
-
"@types/node": "^
|
|
47
|
+
"@types/node": "^26.6.5",
|
|
48
48
|
"msw": "^2.10.5",
|
|
49
49
|
"prettier": "^3.6.2",
|
|
50
|
-
"tsdown": "^0.
|
|
50
|
+
"tsdown": "^0.23.4",
|
|
51
51
|
"tsx": "^4.20.3",
|
|
52
|
-
"typescript": "^
|
|
53
|
-
"vitest": "^
|
|
52
|
+
"typescript": "^7.0.2",
|
|
53
|
+
"vitest": "^5.0.5"
|
|
54
54
|
}
|
|
55
55
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|