Skip to main content
Glama

honeycomb-mcp-server

MIT License
19
2
  • Linux
  • Apple
index.ts25.9 kB
#!/usr/bin/env node import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequest, CallToolRequestSchema, ListToolsRequestSchema, Tool, } from "@modelcontextprotocol/sdk/types.js"; // Honeycomb APIのインターフェース定義 interface DatasetGetArgs { datasetSlug: string; } interface ColumnListArgs { datasetSlug: string; key_name?: string; // オプショナル:特定のカラム名でフィルタリング } // クエリの計算操作の型定義 interface QueryCalculation { op: string; // 計算操作(COUNT, AVGなど) column?: string; // 計算対象の列名(必要な場合) } // クエリの並び順の型定義 interface QueryOrder { op?: string; // 並び替えの操作 column?: string; // 並び替えの列 order: 'ascending' | 'descending'; } // クエリのフィルタの型定義 interface QueryFilter { column: string; // フィルタ対象の列 op: string; // フィルタ操作(EXISTS, =, !=など) value?: any; // フィルタ値 } // Honeycomb クエリの型定義 interface HoneycombQuery { calculations: QueryCalculation[]; breakdowns?: string[]; // グループ化する列 filters?: QueryFilter[]; // フィルタ条件 filter_combination?: 'AND' | 'OR'; // フィルタの組み合わせ方法 time_range?: number; // 秒単位の時間範囲 start_time?: number; // UNIXエポック秒からの開始時間 end_time?: number; // UNIXエポック秒からの終了時間 limit?: number; // 返される結果の最大数 orders?: QueryOrder[]; // 結果の並び順 granularity?: number; // グラフの時間解像度(秒単位) having?: any; // 結果テーブルに対するフィルタ } interface QueryCreateArgs { datasetSlug: string; query: HoneycombQuery; } interface QueryGetArgs { datasetSlug: string; queryId: string; } interface QueryResultCreateArgs { datasetSlug: string; queryId: string; disable_series?: boolean; disable_total_by_aggregate?: boolean; disable_other_by_aggregate?: boolean; limit?: number; } interface QueryResultGetArgs { datasetSlug: string; queryResultId: string; } interface DatasetDefinitionsListArgs { page?: number; limit?: number; sort_by?: string; sort_order?: string; } interface BoardGetArgs { boardId: string; } const authTool: Tool = { name: "honeycomb_auth", description: "API Keys have various scopes permissions and belong to a specific Team or Environment. Use this to validate authentication for a key, to determine what authorizations have been granted to a key, and to determine the Team and Environment that a key belongs to.", inputSchema: { type: "object", properties: {}, }, }; const datasetsListTool: Tool = { name: "honeycomb_datasets_list", description: "List all datasets in the environment. A Dataset represents a collection of related events that come from the same source, or are related to the same source.", inputSchema: { type: "object", properties: {}, }, }; const datasetGetTool: Tool = { name: "honeycomb_dataset_get", description: "Get information about a specific dataset. A Dataset represents a collection of related events that come from the same source, or are related to the same source.", inputSchema: { type: "object", properties: { datasetSlug: { type: "string", description: "The dataset slug.", }, }, required: ["datasetSlug"], }, }; const columnsListTool: Tool = { name: "honeycomb_columns_list", description: "List all columns in a dataset. Columns are fields in the events you send to Honeycomb.", inputSchema: { type: "object", properties: { datasetSlug: { type: "string", description: "The dataset slug.", }, key_name: { type: "string", description: "Optional: Filter columns by a specific name.", }, }, required: ["datasetSlug"], }, }; const queryCreateTool: Tool = { name: "honeycomb_query_create", description: "Create a query from a specification. DOES NOT run the query to retrieve results.", inputSchema: { type: "object", properties: { datasetSlug: { type: "string", description: "The dataset slug or use `__all__` for endpoints that support environment-wide operations.", }, query: { type: "object", description: "Query specification object that defines what data to retrieve and how to process it", properties: { calculations: { type: "array", description: "The calculations to return as a time series and summary table", items: { type: "object", properties: { op: { type: "string", description: "Operation to perform (e.g., COUNT, SUM, AVG, P50, P90, P95, P99, MAX, MIN, RATE, RATE_MAX, RATE_AVG, HEATMAP, CONCURRENCY, FREQUENCY, HISTOGRAM, PERCENTILES)" }, column: { type: "string", description: "The name of the column to perform the operation on" } } } }, filters: { type: "array", description: "The filters with which to restrict the considered events", items: { type: "object", properties: { column: { type: "string", description: "Column name to filter on" }, op: { type: "string", description: "Operator: exists, does-not-exist, =, !=, >, <, >=, <=, starts-with, ends-with, contains, does-not-contain, in, not-in" }, value: { type: ["string", "number", "boolean", "array"], description: "Value to compare against (required for operators other than exists/does-not-exist)" } } } }, breakdowns: { type: "array", description: "The columns by which to break events down into groups", items: { type: "string" } }, orders: { type: "array", description: "The terms on which to order the query results. Each term must appear in either the breakdowns field or the calculations field.", items: { type: "object", properties: { column: { type: "string", description: "Column name to sort by (not required when sorting by COUNT)" }, op: { type: "string", description: "Operation to sort by: COUNT, SUM, AVG, P50, P90, P95, P99, MAX, MIN, RATE, RATE_MAX, RATE_AVG" }, order: { type: "string", description: "Sort order (asc, desc, ascending, descending)" } } } }, time_range: { type: "number", description: "Relative time range in seconds from the present" }, start_time: { type: "integer", description: "Absolute start time of query, in seconds since UNIX epoch. Must be <= end_time." }, end_time: { type: "integer", description: "Absolute end time of query, in seconds since UNIX epoch." }, granularity: { type: "number", description: "The time resolution of the query's graph, in seconds. Given a query time range T, valid values (T/1000...T/10)." }, limit: { type: "number", description: "The maximum number of unique groups returned in 'results'. Aggregating many unique groups across a large time range is computationally expensive. Normal maximum: 1,000. For 'disable_series' queries: up to 10,000." }, havings: { type: "array", description: "The Having clause allows you to filter on the results table. This operation is distinct from filters, which filter the underlying events.", items: { type: "object", properties: { column: { type: "string", description: "The name of the column to filter against" }, op: { type: "string", description: "Comparison operator for the having clause (e.g., =, !=, >, <, >=, <=)" }, value: { type: ["string", "number"], description: "The value to filter against" } } } } } }, }, required: ["datasetSlug", "query"], }, }; const queryGetTool: Tool = { name: "honeycomb_query_get", description: "Get information about a specific query. Returns the query specification, not the query results.", inputSchema: { type: "object", properties: { datasetSlug: { type: "string", description: "The dataset slug or use `__all__` for endpoints that support environment-wide operations.", }, queryId: { type: "string", description: "The unique identifier (ID) of the query.", }, }, required: ["datasetSlug", "queryId"], }, }; const queryResultCreateTool: Tool = { name: "honeycomb_query_result_create", description: "Run a previously created query and return a query result ID that can be used to retrieve the results.", inputSchema: { type: "object", properties: { datasetSlug: { type: "string", description: "The dataset slug or use `__all__` for endpoints that support environment-wide operations.", }, queryId: { type: "string", description: "The unique identifier (ID) of the query to run.", }, disable_series: { type: "boolean", description: "Whether to disable series in the query result", }, disable_total_by_aggregate: { type: "boolean", description: "Whether to disable total by aggregate in the query result", }, disable_other_by_aggregate: { type: "boolean", description: "Whether to disable other by aggregate in the query result", }, limit: { type: "integer", description: "Maximum number of results to return", }, }, required: ["datasetSlug", "queryId"], }, }; const queryResultGetTool: Tool = { name: "honeycomb_query_result_get", description: "Get query results for a previously executed query. The response body will be a JSON object with 'complete': true and the results populated once the query is complete.", inputSchema: { type: "object", properties: { datasetSlug: { type: "string", description: "The dataset slug or use `__all__` for endpoints that support environment-wide operations.", }, queryResultId: { type: "string", description: "The unique identifier (ID) of the query result.", }, }, required: ["datasetSlug", "queryResultId"], }, }; // Dataset Definitionsのツール定義 const datasetDefinitionsListTool: Tool = { name: "honeycomb_dataset_definitions_list", description: "List dataset definitions with pagination. Dataset definitions describe the fields with special meaning in the Dataset. Honeycomb automatically creates these Dataset definition fields when the Dataset is created.", inputSchema: { type: "object", properties: { page: { type: "number", description: "Page number (starting from 1)", }, limit: { type: "number", description: "Number of results per page (default: 100, max: 1000)", }, sort_by: { type: "string", description: "Field to sort by (e.g., 'name', 'description')", }, sort_order: { type: "string", description: "Sort order ('asc' or 'desc')", }, }, }, }; const boardsListTool: Tool = { name: "honeycomb_boards_list", description: "List all boards. Boards are a place to pin and save useful queries and graphs you want to retain for later reuse and reference.", inputSchema: { type: "object", properties: {}, }, }; const boardGetTool: Tool = { name: "honeycomb_board_get", description: "Get information about a specific board. Boards are a place to pin and save useful queries and graphs you want to retain for later reuse and reference.", inputSchema: { type: "object", properties: { boardId: { type: "string", description: "The unique identifier (ID) of a Board.", }, }, required: ["boardId"], }, }; class HoneycombClient { private baseUrl: string; private headers: Record<string, string>; constructor(apiKey: string) { this.baseUrl = "https://api.honeycomb.io/1"; this.headers = { "X-Honeycomb-Team": apiKey, "Content-Type": "application/json", }; } // Auth async auth(): Promise<any> { const response = await fetch(`${this.baseUrl}/auth`, { method: "GET", headers: this.headers, }); if (!response.ok) { throw new Error(`Failed to authenticate: ${response.statusText}`); } return await response.json(); } // Dataset Definitions operations async listDatasetDefinitions(options?: DatasetDefinitionsListArgs): Promise<any> { let url = `${this.baseUrl}/dataset_definitions`; // クエリパラメータを構築 if (options) { const params = new URLSearchParams(); if (options.page) params.append('page', options.page.toString()); if (options.limit) params.append('limit', options.limit.toString()); if (options.sort_by) params.append('sort_by', options.sort_by); if (options.sort_order) params.append('sort_order', options.sort_order); const queryString = params.toString(); if (queryString) { url += `?${queryString}`; } } const response = await fetch(url, { method: "GET", headers: this.headers, }); if (!response.ok) { const errorBody = await response.text(); console.error(`Dataset Definitions list error: Status=${response.status}, Body=${errorBody}`); throw new Error(`データセット定義の取得に失敗しました: ${response.statusText}`); } return await response.json(); } // Dataset operations async listDatasets(): Promise<any> { const response = await fetch(`${this.baseUrl}/datasets`, { method: "GET", headers: this.headers, }); if (!response.ok) { throw new Error(`Failed to list datasets: ${response.statusText}`); } return await response.json(); } async getDataset(slug: string): Promise<any> { const response = await fetch(`${this.baseUrl}/datasets/${slug}`, { method: "GET", headers: this.headers, }); if (!response.ok) { throw new Error(`Failed to get dataset: ${response.statusText}`); } return await response.json(); } // Column operations async listColumns(datasetSlug: string, keyName?: string): Promise<any> { let url = `${this.baseUrl}/columns/${datasetSlug}`; // key_nameパラメータが指定されている場合、URLクエリに追加 if (keyName) { url += `?key_name=${encodeURIComponent(keyName)}`; } const response = await fetch(url, { method: "GET", headers: this.headers, }); if (!response.ok) { throw new Error(`Failed to list columns: ${response.statusText}`); } return await response.json(); } // Query operations async createQuerySpec(datasetSlug: string, querySpec: any): Promise<any> { const response = await fetch(`${this.baseUrl}/queries/${datasetSlug}`, { method: "POST", headers: this.headers, body: JSON.stringify(querySpec), }); if (!response.ok) { const errorBody = await response.text(); console.error( `Query creation error: Status=${response.status}, Body=${errorBody}` ); throw new Error(`Failed to create query spec: ${response.statusText}`); } return await response.json(); } async getQuerySpec(datasetSlug: string, queryId: string): Promise<any> { const response = await fetch(`${this.baseUrl}/queries/${datasetSlug}/${queryId}`, { method: "GET", headers: this.headers, }); if (!response.ok) { throw new Error(`Failed to get query spec: ${response.statusText}`); } return await response.json(); } async createQueryResult(datasetSlug: string, queryId: string, options?: { disable_series?: boolean; disable_total_by_aggregate?: boolean; disable_other_by_aggregate?: boolean; limit?: number; }): Promise<any> { const response = await fetch(`${this.baseUrl}/query_results/${datasetSlug}`, { method: "POST", headers: this.headers, body: JSON.stringify({ query_id: queryId, disable_series: options?.disable_series ?? false, disable_total_by_aggregate: options?.disable_total_by_aggregate ?? true, disable_other_by_aggregate: options?.disable_other_by_aggregate ?? true, limit: options?.limit ?? 10000 }), }); if (!response.ok) { const errorBody = await response.text(); console.error(`Query result creation error: Status=${response.status}, Body=${errorBody}`); throw new Error(`Failed to create query result: ${response.statusText}`); } return await response.json(); } async getQueryResult(datasetSlug: string, queryResultId: string): Promise<any> { const response = await fetch( `${this.baseUrl}/query_results/${datasetSlug}/${queryResultId}`, { method: "GET", headers: this.headers, } ); if (!response.ok) { const errorBody = await response.text(); console.error(`Query result error: Status=${response.status}, Body=${errorBody}`); throw new Error(`Failed to get query result: ${response.statusText}`); } return await response.json(); } // Board operations async listBoards(): Promise<any> { const response = await fetch(`${this.baseUrl}/boards`, { method: "GET", headers: this.headers, }); if (!response.ok) { throw new Error(`Failed to list boards: ${response.statusText}`); } return await response.json(); } async getBoard(boardId: string): Promise<any> { const response = await fetch(`${this.baseUrl}/boards/${boardId}`, { method: "GET", headers: this.headers, }); if (!response.ok) { throw new Error(`Failed to get board: ${response.statusText}`); } return await response.json(); } } async function main() { const apiKey = process.env.HONEYCOMB_API_KEY; if (!apiKey) { console.error("Error: HONEYCOMB_API_KEY environment variable is required."); process.exit(1); } console.error("Starting Honeycomb MCP Server..."); const server = new Server( { name: "Honeycomb MCP Server", version: "1.0.0", }, { capabilities: { tools: {}, }, } ); const client = new HoneycombClient(apiKey); server.setRequestHandler( CallToolRequestSchema, async (request: CallToolRequest) => { console.error("CallToolRequest received:", request); try { switch (request.params.name) { // Auth case "honeycomb_auth": { const response = await client.auth(); return { content: [{ type: "text", text: JSON.stringify(response) }], }; } // Dataset management case "honeycomb_datasets_list": { const response = await client.listDatasets(); return { content: [{ type: "text", text: JSON.stringify(response) }], }; } case "honeycomb_dataset_get": { const args = request.params.arguments as unknown as DatasetGetArgs; if (!args.datasetSlug) { throw new Error("datasetSlug is required"); } const response = await client.getDataset(args.datasetSlug); return { content: [{ type: "text", text: JSON.stringify(response) }], }; } // Column management case "honeycomb_columns_list": { const args = request.params.arguments as unknown as ColumnListArgs; if (!args.datasetSlug) { throw new Error("datasetSlug is required"); } const response = await client.listColumns(args.datasetSlug, args.key_name); return { content: [{ type: "text", text: JSON.stringify(response) }], }; } // Query management case "honeycomb_query_create": { const args = request.params.arguments as unknown as QueryCreateArgs; if (!args.datasetSlug || !args.query) { throw new Error("datasetSlug and query are required"); } const response = await client.createQuerySpec( args.datasetSlug, args.query ); return { content: [{ type: "text", text: JSON.stringify(response) }], }; } case "honeycomb_query_get": { const args = request.params.arguments as unknown as QueryGetArgs; if (!args.datasetSlug || !args.queryId) { throw new Error("datasetSlug and queryId are required"); } const response = await client.getQuerySpec( args.datasetSlug, args.queryId ); return { content: [{ type: "text", text: JSON.stringify(response) }], }; } case "honeycomb_query_result_create": { const args = request.params .arguments as unknown as QueryResultCreateArgs; if (!args.datasetSlug || !args.queryId) { throw new Error("datasetSlug and queryId are required"); } const response = await client.createQueryResult( args.datasetSlug, args.queryId, { disable_series: args.disable_series, disable_total_by_aggregate: args.disable_total_by_aggregate, disable_other_by_aggregate: args.disable_other_by_aggregate, limit: args.limit } ); return { content: [{ type: "text", text: JSON.stringify(response) }], }; } case "honeycomb_query_result_get": { const args = request.params .arguments as unknown as QueryResultGetArgs; if (!args.datasetSlug || !args.queryResultId) { throw new Error("datasetSlugとqueryResultIdが必要です"); } const response = await client.getQueryResult( args.datasetSlug, args.queryResultId ); return { content: [{ type: "text", text: JSON.stringify(response) }], }; } // Dataset Definitions case "honeycomb_dataset_definitions_list": { const args = request.params.arguments as unknown as DatasetDefinitionsListArgs; const response = await client.listDatasetDefinitions(args); return { content: [{ type: "text", text: JSON.stringify(response) }], }; } // - Board management case "honeycomb_boards_list": { const response = await client.listBoards(); return { content: [{ type: "text", text: JSON.stringify(response) }], }; } case "honeycomb_board_get": { const args = request.params.arguments as unknown as BoardGetArgs; if (!args.boardId) { throw new Error("boardId is required"); } const response = await client.getBoard(args.boardId); return { content: [{ type: "text", text: JSON.stringify(response) }], }; } default: throw new Error(`Unknown tool: ${request.params.name}`); } } catch (error: any) { console.error("Error handling request:", error); return { content: [ { type: "text", text: JSON.stringify({ error: error.message }) }, ], }; } } ); server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ authTool, datasetsListTool, datasetGetTool, columnsListTool, queryCreateTool, queryGetTool, queryResultCreateTool, queryResultGetTool, datasetDefinitionsListTool, boardsListTool, boardGetTool, ], }; }); const transport = new StdioServerTransport(); console.error("Connecting server to transport..."); await server.connect(transport); console.error("Honeycomb MCP Server running on stdio"); } main().catch((error) => { console.error("Fatal error in main():", error); process.exit(1); });

MCP directory API

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

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

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