Skip to content
This repository was archived by the owner on Jan 19, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
889 changes: 31 additions & 858 deletions src/index.ts

Large diffs are not rendered by default.

50 changes: 50 additions & 0 deletions src/tools/dashboards/addInsight.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { z } from "zod";
import type { Context, Tool } from "../types";
import { AddInsightToDashboardSchema } from "../../schema/dashboards";
import { getProjectBaseUrl } from "../../lib/utils/api";

const schema = z.object({
data: AddInsightToDashboardSchema,
});

type Params = z.infer<typeof schema>;

export const addInsightHandler = async (context: Context, params: Params) => {
const { data } = params;
const projectId = await context.getProjectId();

const insightResult = await context.api
.insights({ projectId })
.get({ insightId: data.insight_id });

if (!insightResult.success) {
throw new Error(`Failed to get insight: ${insightResult.error.message}`);
}

const result = await context.api.dashboards({ projectId }).addInsight({ data });

if (!result.success) {
throw new Error(`Failed to add insight to dashboard: ${result.error.message}`);
}

const resultWithUrls = {
...result.data,
dashboard_url: `${getProjectBaseUrl(projectId)}/dashboard/${data.dashboard_id}`,
insight_url: `${getProjectBaseUrl(projectId)}/insights/${insightResult.data.short_id}`,
};

return { content: [{ type: "text", text: JSON.stringify(resultWithUrls) }] };
Comment thread
joshsny marked this conversation as resolved.
};

const tool = (): Tool<typeof schema> => ({
name: "add-insight-to-dashboard",
description: `
- Add an existing insight to a dashboard.
- Requires insight ID and dashboard ID.
- Optionally supports layout and color customization.
`,
schema,
handler: addInsightHandler,
});

export default tool;
39 changes: 39 additions & 0 deletions src/tools/dashboards/create.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { z } from "zod";
import type { Context, Tool } from "../types";
import { CreateDashboardInputSchema } from "../../schema/dashboards";
import { getProjectBaseUrl } from "../../lib/utils/api";

const schema = z.object({
data: CreateDashboardInputSchema,
});

type Params = z.infer<typeof schema>;

export const createHandler = async (context: Context, params: Params) => {
const { data } = params;
const projectId = await context.getProjectId();
const dashboardResult = await context.api.dashboards({ projectId }).create({ data });

if (!dashboardResult.success) {
throw new Error(`Failed to create dashboard: ${dashboardResult.error.message}`);
}

const dashboardWithUrl = {
...dashboardResult.data,
url: `${getProjectBaseUrl(projectId)}/dashboard/${dashboardResult.data.id}`,
};

return { content: [{ type: "text", text: JSON.stringify(dashboardWithUrl) }] };
};

const tool = (): Tool<typeof schema> => ({
name: "dashboard-create",
description: `
- Create a new dashboard in the project.
- Requires name and optional description, tags, and other properties.
`,
schema,
handler: createHandler,
});

export default tool;
31 changes: 31 additions & 0 deletions src/tools/dashboards/delete.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { z } from "zod";
import type { Context, Tool } from "../types";

const schema = z.object({
dashboardId: z.number(),
});

type Params = z.infer<typeof schema>;

export const deleteHandler = async (context: Context, params: Params) => {
const { dashboardId } = params;
const projectId = await context.getProjectId();
const result = await context.api.dashboards({ projectId }).delete({ dashboardId });

if (!result.success) {
throw new Error(`Failed to delete dashboard: ${result.error.message}`);
}

return { content: [{ type: "text", text: JSON.stringify(result.data) }] };
};

const tool = (): Tool<typeof schema> => ({
name: "dashboard-delete",
description: `
- Delete a dashboard by ID (soft delete - marks as deleted).
`,
schema,
handler: deleteHandler,
});

export default tool;
31 changes: 31 additions & 0 deletions src/tools/dashboards/get.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { z } from "zod";
import type { Context, Tool } from "../types";

const schema = z.object({
dashboardId: z.number(),
});

type Params = z.infer<typeof schema>;

export const getHandler = async (context: Context, params: Params) => {
const { dashboardId } = params;
const projectId = await context.getProjectId();
const dashboardResult = await context.api.dashboards({ projectId }).get({ dashboardId });

if (!dashboardResult.success) {
throw new Error(`Failed to get dashboard: ${dashboardResult.error.message}`);
}

return { content: [{ type: "text", text: JSON.stringify(dashboardResult.data) }] };
};

const tool = (): Tool<typeof schema> => ({
name: "dashboard-get",
description: `
- Get a specific dashboard by ID.
`,
schema,
handler: getHandler,
});

export default tool;
33 changes: 33 additions & 0 deletions src/tools/dashboards/getAll.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { z } from "zod";
import type { Context, Tool } from "../types";
import { ListDashboardsSchema } from "../../schema/dashboards";

const schema = z.object({
data: ListDashboardsSchema.optional(),
});

type Params = z.infer<typeof schema>;

export const getAllHandler = async (context: Context, params: Params) => {
const { data } = params;
const projectId = await context.getProjectId();
const dashboardsResult = await context.api.dashboards({ projectId }).list({ params: data });

if (!dashboardsResult.success) {
throw new Error(`Failed to get dashboards: ${dashboardsResult.error.message}`);
}

return { content: [{ type: "text", text: JSON.stringify(dashboardsResult.data) }] };
};

const tool = (): Tool<typeof schema> => ({
name: "dashboards-get-all",
description: `
- Get all dashboards in the project with optional filtering.
- Can filter by pinned status, search term, or pagination.
`,
schema,
handler: getAllHandler,
});

export default tool;
42 changes: 42 additions & 0 deletions src/tools/dashboards/update.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { z } from "zod";
import type { Context, Tool } from "../types";
import { UpdateDashboardInputSchema } from "../../schema/dashboards";
import { getProjectBaseUrl } from "../../lib/utils/api";

const schema = z.object({
dashboardId: z.number(),
data: UpdateDashboardInputSchema,
});

type Params = z.infer<typeof schema>;

export const updateHandler = async (context: Context, params: Params) => {
const { dashboardId, data } = params;
const projectId = await context.getProjectId();
const dashboardResult = await context.api
.dashboards({ projectId })
.update({ dashboardId, data });

if (!dashboardResult.success) {
throw new Error(`Failed to update dashboard: ${dashboardResult.error.message}`);
}

const dashboardWithUrl = {
...dashboardResult.data,
url: `${getProjectBaseUrl(projectId)}/dashboard/${dashboardResult.data.id}`,
};

return { content: [{ type: "text", text: JSON.stringify(dashboardWithUrl) }] };
};

const tool = (): Tool<typeof schema> => ({
name: "dashboard-update",
description: `
- Update an existing dashboard by ID.
- Can update name, description, pinned status or tags.
`,
schema,
handler: updateHandler,
});

export default tool;
39 changes: 39 additions & 0 deletions src/tools/documentation/searchDocs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { z } from "zod";
import type { Context, Tool } from "../types";
import { docsSearch } from "../../inkeepApi";

const schema = z.object({
query: z.string(),
});

type Params = z.infer<typeof schema>;

export const searchDocsHandler = async (context: Context, params: Params) => {
const { query } = params;
const inkeepApiKey = context.env.INKEEP_API_KEY;

if (!inkeepApiKey) {
return {
content: [
{
type: "text",
text: "Error: INKEEP_API_KEY is not configured.",
},
],
};
}
const resultText = await docsSearch(inkeepApiKey, query);
return { content: [{ type: "text", text: resultText }] };
};

const tool = (): Tool<typeof schema> => ({
name: "docs-search",
description: `
- Use this tool to search the PostHog documentation for information that can help the user with their request.
- Use it as a fallback when you cannot answer the user's request using other tools in this MCP.
`,
schema,
handler: searchDocsHandler,
});

export default tool;
46 changes: 46 additions & 0 deletions src/tools/errorTracking/errorDetails.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { z } from "zod";
import type { Context, Tool } from "../types";
import { ErrorDetailsSchema } from "../../schema/errors";

const schema = z.object({
data: ErrorDetailsSchema,
});

type Params = z.infer<typeof schema>;

export const errorDetailsHandler = async (context: Context, params: Params) => {
const { data } = params;
const projectId = await context.getProjectId();

const errorQuery = {
kind: "ErrorTrackingQuery",
dateRange: {
date_from:
data.dateFrom?.toISOString() ||
new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString(),
date_to: data.dateTo?.toISOString() || new Date().toISOString(),
},
volumeResolution: 0,
issueId: data.issueId,
};

const errorsResult = await context.api.query({ projectId }).execute({ queryBody: errorQuery });
if (!errorsResult.success) {
throw new Error(`Failed to get error details: ${errorsResult.error.message}`);
}

return {
content: [{ type: "text", text: JSON.stringify(errorsResult.data.results) }],
};
};

const tool = (): Tool<typeof schema> => ({
name: "error-details",
description: `
- Use this tool to get the details of an error in the project.
`,
schema,
handler: errorDetailsHandler,
});

export default tool;
49 changes: 49 additions & 0 deletions src/tools/errorTracking/listErrors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { z } from "zod";
import type { Context, Tool } from "../types";
import { ListErrorsSchema } from "../../schema/errors";

const schema = z.object({
data: ListErrorsSchema,
});

type Params = z.infer<typeof schema>;

export const listErrorsHandler = async (context: Context, params: Params) => {
const { data } = params;
const projectId = await context.getProjectId();

const errorQuery = {
kind: "ErrorTrackingQuery",
orderBy: data.orderBy || "occurrences",
dateRange: {
date_from:
data.dateFrom?.toISOString() ||
new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString(),
date_to: data.dateTo?.toISOString() || new Date().toISOString(),
},
volumeResolution: 1,
orderDirection: data.orderDirection || "DESC",
filterTestAccounts: data.filterTestAccounts ?? true,
status: data.status || "active",
};

const errorsResult = await context.api.query({ projectId }).execute({ queryBody: errorQuery });
if (!errorsResult.success) {
throw new Error(`Failed to list errors: ${errorsResult.error.message}`);
}

return {
content: [{ type: "text", text: JSON.stringify(errorsResult.data.results) }],
};
};

const tool = (): Tool<typeof schema> => ({
name: "list-errors",
description: `
- Use this tool to list errors in the project.
`,
schema,
handler: listErrorsHandler,
});

export default tool;
Loading