@mackerel/mcp-server 0.1.0 → 0.2.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 CHANGED
@@ -19,7 +19,7 @@ The MCP server can be run either via Docker or npx.
19
19
  "-i",
20
20
  "-e",
21
21
  "MACKEREL_APIKEY",
22
- "-----TBD_IMAGE_NAME"
22
+ "ghcr.io/mackerelio-labs/mcp-server:latest"
23
23
  ],
24
24
  "env": {
25
25
  "MACKEREL_APIKEY": "${MACKEREL_APIKEY}"
@@ -38,7 +38,7 @@ The MCP server can be run either via Docker or npx.
38
38
  "command": "npx",
39
39
  "args": [
40
40
  "-y",
41
- "-----TBD_PACKAGE_NAME"
41
+ "@mackerel/mcp-server"
42
42
  ],
43
43
  "env": {
44
44
  "MACKEREL_APIKEY": "${MACKEREL_APIKEY}"
@@ -62,3 +62,4 @@ The MCP server can be run either via Docker or npx.
62
62
  * `get_service_metrics` - Retrieve metrics data for a specific service.
63
63
  * `list_monitors` - Retrieve monitor configurations.
64
64
  * `get_monitor` - Retrieve a specific monitor configuration.
65
+ * `get_trace` - Retrieve trace data by trace ID for distributed tracing analysis.
package/build/index.js CHANGED
@@ -125,7 +125,7 @@ ${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{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{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);if(n){let e=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}));return{dashboards:e,pageInfo:r.pageInfo}}return 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{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{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()=>{let s=await this.mackerelClient.getHosts(e,t,n,r,i,a,o);return s})},g=class{constructor(e){this.mackerelClient=e}static ListServicesToolInput=n.object({});listServices=async e=>await d(async()=>await this.mackerelClient.getServices())},_=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){let r=n?n.toString():``;return`${e}:${t}:${r}`}getFromCache(e){let t=this.cacheMap.get(e);if(!t)return null;let n=Date.now();return n-t.timestamp>this.cacheTTL?(this.cacheMap.delete(e),null):t.data}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={"X-Api-Key":this.apiKey,"Content-Type":`application/json`},o={method:e,headers:a};r&&(o.body=JSON.stringify(r));let s=await fetch(i,o);if(!s.ok){let e=await s.text();throw Error(`Mackerel API error: ${s.status} ${e}`)}let c=await s.json();if(e===`GET`){let r=this.getCacheKey(e,t,n);this.setCache(r,c)}return c}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,s=i(n.dashboards,{limit:r,offset:a}),c={hasPrevPage:a>0,hasNextPage:a+r<o};return{dashboards:s,pageInfo:c}}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,p=i(l.hosts,{limit:u,offset:d}),m={hasPrevPage:d>0,hasNextPage:d+u<f};return{hosts:p,pageInfo:m}}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}`)}},v=class{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`)},y=class{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`)};async function b(){let n=new _(`https://api.mackerelio.com`,x()),r=new f(n),i=new p(n),a=new m(n),o=new h(n),s=new y(n),c=new g(n),l=new v(n),u=new e({name:`@mackerel/mcp-server`,version:`0.0.1`});u.registerTool(`list_alerts`,{title:`List Alerts`,description:`Retrieve alerts from Mackerel.
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{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{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);if(n){let e=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}));return{dashboards:e,pageInfo:r.pageInfo}}return 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{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{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()=>{let s=await this.mackerelClient.getHosts(e,t,n,r,i,a,o);return s})},g=class{constructor(e){this.mackerelClient=e}static ListServicesToolInput=n.object({});listServices=async e=>await d(async()=>await this.mackerelClient.getServices())},_=class{constructor(e){this.mackerelClient=e}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)),t=Math.max(...l.map(e=>e.endTime));h=t-e}return{traceId:e,summary:{totalSpans:p,hasErrors:m,totalDuration:h,pageInfo:f},spans:d}})},v=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){let r=n?n.toString():``;return`${e}:${t}:${r}`}getFromCache(e){let t=this.cacheMap.get(e);if(!t)return null;let n=Date.now();return n-t.timestamp>this.cacheTTL?(this.cacheMap.delete(e),null):t.data}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={"X-Api-Key":this.apiKey,"Content-Type":`application/json`},o={method:e,headers:a};r&&(o.body=JSON.stringify(r));let s=await fetch(i,o);if(!s.ok){let e=await s.text();throw Error(`Mackerel API error: ${s.status} ${e}`)}let c=await s.json();if(e===`GET`){let r=this.getCacheKey(e,t,n);this.setCache(r,c)}return c}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,s=i(n.dashboards,{limit:r,offset:a}),c={hasPrevPage:a>0,hasNextPage:a+r<o};return{dashboards:s,pageInfo:c}}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,p=i(l.hosts,{limit:u,offset:d}),m={hasPrevPage:d>0,hasNextPage:d+u<f};return{hosts:p,pageInfo:m}}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}`)}},y=class{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`)},b=class{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`)};async function x(){let n=new v(`https://api.mackerelio.com`,S()),r=new f(n),i=new p(n),a=new m(n),o=new h(n),s=new b(n),c=new g(n),l=new y(n),u=new _(n),d=new e({name:`@mackerel/mcp-server`,version:`0.0.1`});d.registerTool(`list_alerts`,{title:`List Alerts`,description:`Retrieve alerts from Mackerel.
129
129
 
130
130
  🔍 USE THIS TOOL WHEN USERS:
131
131
  - Check currently active alerts
@@ -142,7 +142,7 @@ list_alerts()
142
142
  list_alerts(withClosed=true)
143
143
  \`\`\`
144
144
  </examples>
145
- `,inputSchema:f.ListAlertsToolInput.shape},r.listAlerts),u.registerTool(`get_alert`,{title:`Get Alert`,description:`Retrieve a specific alert by ID from Mackerel.
145
+ `,inputSchema:f.ListAlertsToolInput.shape},r.listAlerts),d.registerTool(`get_alert`,{title:`Get Alert`,description:`Retrieve a specific alert by ID from Mackerel.
146
146
 
147
147
  🔍 USE THIS TOOL WHEN USERS:
148
148
  - Investigate a particular alert
@@ -153,7 +153,7 @@ list_alerts(withClosed=true)
153
153
  get_alert(alertId=3Yr)
154
154
  \`\`\`
155
155
  </example>
156
- `,inputSchema:f.GetAlertToolInput.shape},r.getAlert),u.registerTool(`get_alert_logs`,{title:`Get Alert Logs`,description:`Retrieve logs for a specific alert by ID from Mackerel.
156
+ `,inputSchema:f.GetAlertToolInput.shape},r.getAlert),d.registerTool(`get_alert_logs`,{title:`Get Alert Logs`,description:`Retrieve logs for a specific alert by ID from Mackerel.
157
157
 
158
158
  🔍 USE THIS TOOL WHEN USERS:
159
159
  - View status change history for a specific alert
@@ -165,7 +165,7 @@ get_alert(alertId=3Yr)
165
165
  get_alert_logs(alertId=3Yr)
166
166
  \`\`\`
167
167
  </example>
168
- `,inputSchema:f.GetAlertLogsToolInput.shape},r.getAlertLogs),u.registerTool(`list_dashboards`,{title:`List Dashboards`,description:`Retrieve all dashboards from Mackerel.
168
+ `,inputSchema:f.GetAlertLogsToolInput.shape},r.getAlertLogs),d.registerTool(`list_dashboards`,{title:`List Dashboards`,description:`Retrieve all dashboards from Mackerel.
169
169
 
170
170
  🔍 USE THIS TOOL WHEN USERS:
171
171
  - Get a list of dashboards
@@ -177,7 +177,7 @@ get_alert_logs(alertId=3Yr)
177
177
  list_dashboards()
178
178
  \`\`\`
179
179
  </example>
180
- `,inputSchema:p.ListDashboardsToolInput.shape},i.listDashboards),u.registerTool(`get_dashboard`,{title:`Get Dashboard`,description:`Retrieve a specific dashboard by ID from Mackerel.
180
+ `,inputSchema:p.ListDashboardsToolInput.shape},i.listDashboards),d.registerTool(`get_dashboard`,{title:`Get Dashboard`,description:`Retrieve a specific dashboard by ID from Mackerel.
181
181
 
182
182
  🔍 USE THIS TOOL WHEN USERS:
183
183
  - Get details of a specific dashboard
@@ -189,7 +189,7 @@ list_dashboards()
189
189
  get_dashboard(dashboardId="3Yr")
190
190
  \`\`\`
191
191
  </examples>
192
- `,inputSchema:p.GetDashboardToolInput.shape},i.getDashboard),u.registerTool(`update_dashboard`,{title:`Update Dashboard`,description:`Update a specific dashboard by ID in Mackerel.
192
+ `,inputSchema:p.GetDashboardToolInput.shape},i.getDashboard),d.registerTool(`update_dashboard`,{title:`Update Dashboard`,description:`Update a specific dashboard by ID in Mackerel.
193
193
 
194
194
  🔍 USE THIS TOOL WHEN USERS:
195
195
  - Modify dashboard title, memo, or URL path
@@ -212,7 +212,7 @@ update_dashboard(
212
212
  )
213
213
  \`\`\`
214
214
  </examples>
215
- `,inputSchema:p.UpdateDashboardToolInput.shape},i.updateDashboard),u.registerTool(`list_hosts`,{title:`List Hosts`,description:`Retrieve hosts from Mackerel.
215
+ `,inputSchema:p.UpdateDashboardToolInput.shape},i.updateDashboard),d.registerTool(`list_hosts`,{title:`List Hosts`,description:`Retrieve hosts from Mackerel.
216
216
 
217
217
  🔍 USE THIS TOOL WHEN USERS:
218
218
  - Get a list of hosts
@@ -245,7 +245,7 @@ list_hosts(summary=true)
245
245
  list_hosts(limit=20, offset=20)
246
246
  \`\`\`
247
247
  </examples>
248
- `,inputSchema:h.ListHostsToolInput.shape},o.listHosts),u.registerTool(`get_host_metrics`,{title:`Get Host Metrics`,description:`Retrieve metrics data for a specific host from Mackerel.
248
+ `,inputSchema:h.ListHostsToolInput.shape},o.listHosts),d.registerTool(`get_host_metrics`,{title:`Get Host Metrics`,description:`Retrieve metrics data for a specific host from Mackerel.
249
249
 
250
250
  🔍 USE THIS TOOL WHEN USERS:
251
251
  - Get metrics data for a specific host
@@ -274,7 +274,7 @@ get_host_metrics(hostId="host123", name="custom.myapp.response_time", from=16094
274
274
  get_host_metrics(hostId="host123", name="ec2.cpu.used", from=1609459200, to=1609462800)
275
275
  \`\`\`
276
276
  </examples>
277
- `,inputSchema:y.GetHostMetricsToolInput.shape},s.getHostMetrics),u.registerTool(`list_services`,{title:`List Services`,description:`Retrieve all services from Mackerel.
277
+ `,inputSchema:b.GetHostMetricsToolInput.shape},s.getHostMetrics),d.registerTool(`list_services`,{title:`List Services`,description:`Retrieve all services from Mackerel.
278
278
 
279
279
  🔍 USE THIS TOOL WHEN USERS:
280
280
  - Get a list of services
@@ -286,7 +286,7 @@ get_host_metrics(hostId="host123", name="ec2.cpu.used", from=1609459200, to=1609
286
286
  list_services()
287
287
  \`\`\`
288
288
  </examples>
289
- `,inputSchema:g.ListServicesToolInput.shape},c.listServices),u.registerTool(`get_service_metrics`,{title:`Get Service Metrics`,description:`Retrieve metrics data for a specific service from Mackerel.
289
+ `,inputSchema:g.ListServicesToolInput.shape},c.listServices),d.registerTool(`get_service_metrics`,{title:`Get Service Metrics`,description:`Retrieve metrics data for a specific service from Mackerel.
290
290
 
291
291
  🔍 USE THIS TOOL WHEN USERS:
292
292
  - "Service metrics" are metrics that correspond to a service that consists of multiple hosts and their collective roles
@@ -310,7 +310,7 @@ get_service_metrics(serviceName="web", name="__externalhttp.responsetime.<monito
310
310
  get_service_metrics(serviceName="web", name="analytics.page_view", from=1609459200, to=1609462800)
311
311
  \`\`\`
312
312
 
313
- </examples>`,inputSchema:v.GetServiceMetricsToolInput.shape},l.getServiceMetrics),u.registerTool(`list_monitors`,{title:`List Monitors`,description:`Retrieve all monitor configurations from Mackerel.
313
+ </examples>`,inputSchema:y.GetServiceMetricsToolInput.shape},l.getServiceMetrics),d.registerTool(`list_monitors`,{title:`List Monitors`,description:`Retrieve all monitor configurations from Mackerel.
314
314
 
315
315
  🔍 USE THIS TOOL WHEN USERS:
316
316
  - Get a list of all monitors
@@ -322,7 +322,7 @@ get_service_metrics(serviceName="web", name="analytics.page_view", from=16094592
322
322
  list_monitors()
323
323
  \`\`\`
324
324
  </examples>
325
- `,inputSchema:m.ListMonitorsToolInput.shape},a.listMonitors),u.registerTool(`get_monitor`,{title:`Get Monitor`,description:`Retrieve a specific monitor configuration by ID from Mackerel.
325
+ `,inputSchema:m.ListMonitorsToolInput.shape},a.listMonitors),d.registerTool(`get_monitor`,{title:`Get Monitor`,description:`Retrieve a specific monitor configuration by ID from Mackerel.
326
326
 
327
327
  🔍 USE THIS TOOL WHEN USERS:
328
328
  - Get details of a specific monitor
@@ -333,4 +333,50 @@ list_monitors()
333
333
  get_monitor(monitorId="2cSZzK3XfmB")
334
334
  \`\`\`
335
335
  </examples>
336
- `,inputSchema:m.GetMonitorToolInput.shape},a.getMonitor);let d=new t;await u.connect(d),console.error(`Mackerel MCP Server running on stdio`)}function x(){let e=process.env.MACKEREL_APIKEY;if(!e)throw Error(`MACKEREL_APIKEY environment variable is not set.`);return e}b().catch(e=>{console.error(`Fatal error in main():`,e),process.exit(1)});export{};
336
+ `,inputSchema:m.GetMonitorToolInput.shape},a.getMonitor),d.registerTool(`get_trace`,{title:`Get Trace`,description:`Retrieve trace data by trace ID from Mackerel for distributed tracing analysis.
337
+
338
+ 🔍 USE THIS TOOL WHEN USERS:
339
+ - Analyze performance bottlenecks in distributed systems
340
+ - Investigate error propagation across microservices
341
+ - Understand request flow and service dependencies
342
+ - Debug latency issues and identify slow operations
343
+ - Generate documentation of system architecture from trace data
344
+
345
+ <examples>
346
+ ### Basic trace retrieval (first page)
347
+ \`\`\`
348
+ get_trace(traceId="abc123def456")
349
+ \`\`\`
350
+
351
+ ### Focus on errors only
352
+ \`\`\`
353
+ get_trace(traceId="abc123def456", errorSpansOnly=true)
354
+ \`\`\`
355
+
356
+ ### Filter spans with duration over 100ms
357
+ \`\`\`
358
+ get_trace(traceId="abc123def456", duration=100)
359
+ \`\`\`
360
+
361
+ ### Detailed analysis with attributes
362
+ \`\`\`
363
+ get_trace(traceId="abc123def456", includeAttributes=true, limit=50)
364
+ \`\`\`
365
+
366
+ ### Minimal view for overview
367
+ \`\`\`
368
+ get_trace(traceId="abc123def456", includeEvents=false, limit=10)
369
+ \`\`\`
370
+
371
+ ### Pagination through large traces
372
+ \`\`\`
373
+ get_trace(traceId="abc123def456", limit=20, offset=0) # First page
374
+ get_trace(traceId="abc123def456", limit=20, offset=20) # Second page
375
+ \`\`\`
376
+
377
+ ### Combined filtering and pagination
378
+ \`\`\`
379
+ get_trace(traceId="abc123def456", errorSpansOnly=true, limit=10, offset=0)
380
+ \`\`\`
381
+ </examples>
382
+ `,inputSchema:_.GetTraceToolInput.shape},u.getTrace);let x=new t;await d.connect(x),console.error(`Mackerel MCP Server running on stdio`)}function S(){let e=process.env.MACKEREL_APIKEY;if(!e)throw Error(`MACKEREL_APIKEY environment variable is not set.`);return e}x().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.1.0",
3
+ "version": "0.2.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },