@lightdash-tools/mcp 0.1.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/LICENSE +201 -0
- package/README.md +53 -0
- package/dist/config.d.ts +11 -0
- package/dist/config.js +16 -0
- package/dist/errors.d.ts +8 -0
- package/dist/errors.js +29 -0
- package/dist/http.d.ts +5 -0
- package/dist/http.js +172 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +36 -0
- package/dist/index.test.d.ts +1 -0
- package/dist/index.test.js +9 -0
- package/dist/tools/charts.d.ts +6 -0
- package/dist/tools/charts.js +55 -0
- package/dist/tools/dashboards.d.ts +6 -0
- package/dist/tools/dashboards.js +28 -0
- package/dist/tools/explores.d.ts +6 -0
- package/dist/tools/explores.js +40 -0
- package/dist/tools/groups.d.ts +6 -0
- package/dist/tools/groups.js +41 -0
- package/dist/tools/index.d.ts +6 -0
- package/dist/tools/index.js +24 -0
- package/dist/tools/projects.d.ts +6 -0
- package/dist/tools/projects.js +37 -0
- package/dist/tools/query.d.ts +6 -0
- package/dist/tools/query.js +34 -0
- package/dist/tools/shared.d.ts +39 -0
- package/dist/tools/shared.js +68 -0
- package/dist/tools/spaces.d.ts +6 -0
- package/dist/tools/spaces.js +40 -0
- package/dist/tools/users.d.ts +6 -0
- package/dist/tools/users.js +50 -0
- package/package.json +23 -0
- package/src/config.ts +16 -0
- package/src/errors.ts +27 -0
- package/src/http.ts +184 -0
- package/src/index.test.ts +8 -0
- package/src/index.ts +28 -0
- package/src/tools/charts.ts +85 -0
- package/src/tools/dashboards.ts +25 -0
- package/src/tools/explores.ts +46 -0
- package/src/tools/groups.ts +49 -0
- package/src/tools/index.ts +25 -0
- package/src/tools/projects.ts +39 -0
- package/src/tools/query.ts +47 -0
- package/src/tools/shared.ts +101 -0
- package/src/tools/spaces.ts +46 -0
- package/src/tools/users.ts +64 -0
- package/tsconfig.json +13 -0
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP tools: explores (list, get).
|
|
3
|
+
*/
|
|
4
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
5
|
+
import type { LightdashClient } from '@lightdash-tools/client';
|
|
6
|
+
export declare function registerExploresTools(server: McpServer, client: LightdashClient): void;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* MCP tools: explores (list, get).
|
|
4
|
+
*/
|
|
5
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
6
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
7
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
8
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
9
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
10
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
11
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.registerExploresTools = registerExploresTools;
|
|
16
|
+
const zod_1 = require("zod");
|
|
17
|
+
const shared_js_1 = require("./shared.js");
|
|
18
|
+
function registerExploresTools(server, client) {
|
|
19
|
+
(0, shared_js_1.registerToolSafe)(server, 'list_explores', {
|
|
20
|
+
title: 'List explores',
|
|
21
|
+
description: 'List all explores in a project',
|
|
22
|
+
inputSchema: { projectUuid: zod_1.z.string().describe('Project UUID') },
|
|
23
|
+
annotations: shared_js_1.READ_ONLY_DEFAULT,
|
|
24
|
+
}, (0, shared_js_1.wrapTool)(client, (c) => (_a) => __awaiter(this, [_a], void 0, function* ({ projectUuid }) {
|
|
25
|
+
const explores = yield c.v1.explores.listExplores(projectUuid);
|
|
26
|
+
return { content: [{ type: 'text', text: JSON.stringify(explores, null, 2) }] };
|
|
27
|
+
})));
|
|
28
|
+
(0, shared_js_1.registerToolSafe)(server, 'get_explore', {
|
|
29
|
+
title: 'Get explore',
|
|
30
|
+
description: 'Get an explore by project UUID and explore ID',
|
|
31
|
+
inputSchema: {
|
|
32
|
+
projectUuid: zod_1.z.string().describe('Project UUID'),
|
|
33
|
+
exploreId: zod_1.z.string().describe('Explore ID'),
|
|
34
|
+
},
|
|
35
|
+
annotations: shared_js_1.READ_ONLY_DEFAULT,
|
|
36
|
+
}, (0, shared_js_1.wrapTool)(client, (c) => (_a) => __awaiter(this, [_a], void 0, function* ({ projectUuid, exploreId }) {
|
|
37
|
+
const explore = yield c.v1.explores.getExplore(projectUuid, exploreId);
|
|
38
|
+
return { content: [{ type: 'text', text: JSON.stringify(explore, null, 2) }] };
|
|
39
|
+
})));
|
|
40
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP tools: groups (list, get).
|
|
3
|
+
*/
|
|
4
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
5
|
+
import type { LightdashClient } from '@lightdash-tools/client';
|
|
6
|
+
export declare function registerGroupTools(server: McpServer, client: LightdashClient): void;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* MCP tools: groups (list, get).
|
|
4
|
+
*/
|
|
5
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
6
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
7
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
8
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
9
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
10
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
11
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.registerGroupTools = registerGroupTools;
|
|
16
|
+
const zod_1 = require("zod");
|
|
17
|
+
const shared_js_1 = require("./shared.js");
|
|
18
|
+
function registerGroupTools(server, client) {
|
|
19
|
+
(0, shared_js_1.registerToolSafe)(server, 'list_groups', {
|
|
20
|
+
title: 'List groups',
|
|
21
|
+
description: 'List organization groups (one page)',
|
|
22
|
+
inputSchema: {
|
|
23
|
+
page: zod_1.z.number().optional().describe('Page number'),
|
|
24
|
+
pageSize: zod_1.z.number().optional().describe('Page size'),
|
|
25
|
+
searchQuery: zod_1.z.string().optional().describe('Search query'),
|
|
26
|
+
},
|
|
27
|
+
annotations: shared_js_1.READ_ONLY_DEFAULT,
|
|
28
|
+
}, (0, shared_js_1.wrapTool)(client, (c) => (params) => __awaiter(this, void 0, void 0, function* () {
|
|
29
|
+
const result = yield c.v1.groups.listGroups(params !== null && params !== void 0 ? params : {});
|
|
30
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
31
|
+
})));
|
|
32
|
+
(0, shared_js_1.registerToolSafe)(server, 'get_group', {
|
|
33
|
+
title: 'Get group',
|
|
34
|
+
description: 'Get a group by UUID',
|
|
35
|
+
inputSchema: { groupUuid: zod_1.z.string().describe('Group UUID') },
|
|
36
|
+
annotations: shared_js_1.READ_ONLY_DEFAULT,
|
|
37
|
+
}, (0, shared_js_1.wrapTool)(client, (c) => (_a) => __awaiter(this, [_a], void 0, function* ({ groupUuid }) {
|
|
38
|
+
const group = yield c.v1.groups.getGroup(groupUuid);
|
|
39
|
+
return { content: [{ type: 'text', text: JSON.stringify(group, null, 2) }] };
|
|
40
|
+
})));
|
|
41
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP tool registration: barrel that delegates to domain modules.
|
|
3
|
+
*/
|
|
4
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
5
|
+
import type { LightdashClient } from '@lightdash-tools/client';
|
|
6
|
+
export declare function registerTools(server: McpServer, client: LightdashClient): void;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* MCP tool registration: barrel that delegates to domain modules.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.registerTools = registerTools;
|
|
7
|
+
const projects_js_1 = require("./projects.js");
|
|
8
|
+
const charts_js_1 = require("./charts.js");
|
|
9
|
+
const dashboards_js_1 = require("./dashboards.js");
|
|
10
|
+
const spaces_js_1 = require("./spaces.js");
|
|
11
|
+
const users_js_1 = require("./users.js");
|
|
12
|
+
const groups_js_1 = require("./groups.js");
|
|
13
|
+
const query_js_1 = require("./query.js");
|
|
14
|
+
const explores_js_1 = require("./explores.js");
|
|
15
|
+
function registerTools(server, client) {
|
|
16
|
+
(0, projects_js_1.registerProjectTools)(server, client);
|
|
17
|
+
(0, charts_js_1.registerChartTools)(server, client);
|
|
18
|
+
(0, dashboards_js_1.registerDashboardTools)(server, client);
|
|
19
|
+
(0, spaces_js_1.registerSpaceTools)(server, client);
|
|
20
|
+
(0, users_js_1.registerUserTools)(server, client);
|
|
21
|
+
(0, groups_js_1.registerGroupTools)(server, client);
|
|
22
|
+
(0, query_js_1.registerQueryTools)(server, client);
|
|
23
|
+
(0, explores_js_1.registerExploresTools)(server, client);
|
|
24
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP tools: projects (list, get).
|
|
3
|
+
*/
|
|
4
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
5
|
+
import type { LightdashClient } from '@lightdash-tools/client';
|
|
6
|
+
export declare function registerProjectTools(server: McpServer, client: LightdashClient): void;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* MCP tools: projects (list, get).
|
|
4
|
+
*/
|
|
5
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
6
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
7
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
8
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
9
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
10
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
11
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.registerProjectTools = registerProjectTools;
|
|
16
|
+
const zod_1 = require("zod");
|
|
17
|
+
const shared_js_1 = require("./shared.js");
|
|
18
|
+
function registerProjectTools(server, client) {
|
|
19
|
+
(0, shared_js_1.registerToolSafe)(server, 'list_projects', {
|
|
20
|
+
title: 'List projects',
|
|
21
|
+
description: 'List all projects in the current organization',
|
|
22
|
+
inputSchema: {},
|
|
23
|
+
annotations: shared_js_1.READ_ONLY_DEFAULT,
|
|
24
|
+
}, (0, shared_js_1.wrapTool)(client, () => () => __awaiter(this, void 0, void 0, function* () {
|
|
25
|
+
const projects = yield client.v1.projects.listProjects();
|
|
26
|
+
return { content: [{ type: 'text', text: JSON.stringify(projects, null, 2) }] };
|
|
27
|
+
})));
|
|
28
|
+
(0, shared_js_1.registerToolSafe)(server, 'get_project', {
|
|
29
|
+
title: 'Get project',
|
|
30
|
+
description: 'Get a project by UUID',
|
|
31
|
+
inputSchema: { projectUuid: zod_1.z.string().describe('Project UUID') },
|
|
32
|
+
annotations: shared_js_1.READ_ONLY_DEFAULT,
|
|
33
|
+
}, (0, shared_js_1.wrapTool)(client, (c) => (_a) => __awaiter(this, [_a], void 0, function* ({ projectUuid }) {
|
|
34
|
+
const project = yield c.v1.projects.getProject(projectUuid);
|
|
35
|
+
return { content: [{ type: 'text', text: JSON.stringify(project, null, 2) }] };
|
|
36
|
+
})));
|
|
37
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP tools: query (compile).
|
|
3
|
+
*/
|
|
4
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
5
|
+
import type { LightdashClient } from '@lightdash-tools/client';
|
|
6
|
+
export declare function registerQueryTools(server: McpServer, client: LightdashClient): void;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* MCP tools: query (compile).
|
|
4
|
+
*/
|
|
5
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
6
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
7
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
8
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
9
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
10
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
11
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.registerQueryTools = registerQueryTools;
|
|
16
|
+
const zod_1 = require("zod");
|
|
17
|
+
const shared_js_1 = require("./shared.js");
|
|
18
|
+
function registerQueryTools(server, client) {
|
|
19
|
+
(0, shared_js_1.registerToolSafe)(server, 'compile_query', {
|
|
20
|
+
title: 'Compile query',
|
|
21
|
+
description: 'Compile a metric query for an explore without executing it',
|
|
22
|
+
inputSchema: {
|
|
23
|
+
projectUuid: zod_1.z.string().describe('Project UUID'),
|
|
24
|
+
exploreId: zod_1.z.string().describe('Explore ID'),
|
|
25
|
+
metricQuery: zod_1.z
|
|
26
|
+
.record(zod_1.z.string(), zod_1.z.unknown())
|
|
27
|
+
.describe('Metric query object (dimensions, metrics, filters, etc.)'),
|
|
28
|
+
},
|
|
29
|
+
annotations: shared_js_1.READ_ONLY_DEFAULT,
|
|
30
|
+
}, (0, shared_js_1.wrapTool)(client, (c) => (_a) => __awaiter(this, [_a], void 0, function* ({ projectUuid, exploreId, metricQuery, }) {
|
|
31
|
+
const result = yield c.v1.query.compileQuery(projectUuid, exploreId, metricQuery);
|
|
32
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
33
|
+
})));
|
|
34
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types and helpers for MCP tool registration.
|
|
3
|
+
*/
|
|
4
|
+
import type { LightdashClient } from '@lightdash-tools/client';
|
|
5
|
+
import type { z } from 'zod';
|
|
6
|
+
/** Prefix for all MCP tool names (disambiguation when multiple servers are connected). */
|
|
7
|
+
export declare const TOOL_PREFIX = "lightdash_tools__";
|
|
8
|
+
export type TextContent = {
|
|
9
|
+
content: Array<{
|
|
10
|
+
type: 'text';
|
|
11
|
+
text: string;
|
|
12
|
+
}>;
|
|
13
|
+
};
|
|
14
|
+
/** Tool handler type used to avoid deep instantiation with SDK/Zod. Accepts (args, extra) for SDK compatibility. */
|
|
15
|
+
export type ToolHandler = (args: unknown, extra?: unknown) => Promise<TextContent>;
|
|
16
|
+
/** MCP tool annotations (hints for client display and approval). See MCP spec Tool annotations. */
|
|
17
|
+
export type ToolAnnotations = {
|
|
18
|
+
title?: string;
|
|
19
|
+
readOnlyHint?: boolean;
|
|
20
|
+
destructiveHint?: boolean;
|
|
21
|
+
idempotentHint?: boolean;
|
|
22
|
+
openWorldHint?: boolean;
|
|
23
|
+
};
|
|
24
|
+
/** Options for registerTool; inputSchema typed as ZodRawShapeCompat for SDK compatibility. Pass annotations explicitly (e.g. READ_ONLY_DEFAULT or WRITE_IDEMPOTENT) for visibility. */
|
|
25
|
+
export type ToolOptions = {
|
|
26
|
+
description: string;
|
|
27
|
+
inputSchema: Record<string, z.ZodTypeAny>;
|
|
28
|
+
title?: string;
|
|
29
|
+
annotations?: ToolAnnotations;
|
|
30
|
+
};
|
|
31
|
+
/** Preset: read-only, non-destructive, idempotent, closed-world. Use for list/get/compile tools. */
|
|
32
|
+
export declare const READ_ONLY_DEFAULT: ToolAnnotations;
|
|
33
|
+
/** Preset: write, non-destructive, idempotent (e.g. upsert by slug). Use for create/update tools. */
|
|
34
|
+
export declare const WRITE_IDEMPOTENT: ToolAnnotations;
|
|
35
|
+
/** Preset: write, destructive, non-idempotent. Use for delete/remove tools; clients should prompt for user confirmation. */
|
|
36
|
+
export declare const WRITE_DESTRUCTIVE: ToolAnnotations;
|
|
37
|
+
/** Registers a tool with prefix and annotations. shortName is TOOL_PREFIX + shortName. Pass annotations explicitly (e.g. READ_ONLY_DEFAULT, WRITE_IDEMPOTENT, or WRITE_DESTRUCTIVE). */
|
|
38
|
+
export declare function registerToolSafe(server: unknown, shortName: string, options: ToolOptions, handler: ToolHandler): void;
|
|
39
|
+
export declare function wrapTool<T>(client: LightdashClient, fn: (client: LightdashClient) => (args: T) => Promise<TextContent>): ToolHandler;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Shared types and helpers for MCP tool registration.
|
|
4
|
+
*/
|
|
5
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
6
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
7
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
8
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
9
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
10
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
11
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.WRITE_DESTRUCTIVE = exports.WRITE_IDEMPOTENT = exports.READ_ONLY_DEFAULT = exports.TOOL_PREFIX = void 0;
|
|
16
|
+
exports.registerToolSafe = registerToolSafe;
|
|
17
|
+
exports.wrapTool = wrapTool;
|
|
18
|
+
const errors_js_1 = require("../errors.js");
|
|
19
|
+
/** Prefix for all MCP tool names (disambiguation when multiple servers are connected). */
|
|
20
|
+
exports.TOOL_PREFIX = 'lightdash_tools__';
|
|
21
|
+
/** Preset: read-only, non-destructive, idempotent, closed-world. Use for list/get/compile tools. */
|
|
22
|
+
exports.READ_ONLY_DEFAULT = {
|
|
23
|
+
readOnlyHint: true,
|
|
24
|
+
openWorldHint: false,
|
|
25
|
+
destructiveHint: false,
|
|
26
|
+
idempotentHint: true,
|
|
27
|
+
};
|
|
28
|
+
/** Preset: write, non-destructive, idempotent (e.g. upsert by slug). Use for create/update tools. */
|
|
29
|
+
exports.WRITE_IDEMPOTENT = {
|
|
30
|
+
readOnlyHint: false,
|
|
31
|
+
openWorldHint: false,
|
|
32
|
+
destructiveHint: false,
|
|
33
|
+
idempotentHint: true,
|
|
34
|
+
};
|
|
35
|
+
/** Preset: write, destructive, non-idempotent. Use for delete/remove tools; clients should prompt for user confirmation. */
|
|
36
|
+
exports.WRITE_DESTRUCTIVE = {
|
|
37
|
+
readOnlyHint: false,
|
|
38
|
+
openWorldHint: false,
|
|
39
|
+
destructiveHint: true,
|
|
40
|
+
idempotentHint: false,
|
|
41
|
+
};
|
|
42
|
+
/** Internal default for mergeAnnotations; READ_ONLY_DEFAULT is the exported preset. */
|
|
43
|
+
const DEFAULT_ANNOTATIONS = exports.READ_ONLY_DEFAULT;
|
|
44
|
+
/** Merges per-tool annotations with defaults; per-tool values win. */
|
|
45
|
+
function mergeAnnotations(overrides) {
|
|
46
|
+
return Object.assign(Object.assign({}, DEFAULT_ANNOTATIONS), overrides);
|
|
47
|
+
}
|
|
48
|
+
/** Registers a tool with prefix and annotations. shortName is TOOL_PREFIX + shortName. Pass annotations explicitly (e.g. READ_ONLY_DEFAULT, WRITE_IDEMPOTENT, or WRITE_DESTRUCTIVE). */
|
|
49
|
+
function registerToolSafe(server, shortName, options, handler) {
|
|
50
|
+
var _a, _b;
|
|
51
|
+
const name = exports.TOOL_PREFIX + shortName;
|
|
52
|
+
const annotations = mergeAnnotations(options.annotations);
|
|
53
|
+
const mergedOptions = Object.assign(Object.assign({}, options), { title: (_a = options.title) !== null && _a !== void 0 ? _a : (_b = options.annotations) === null || _b === void 0 ? void 0 : _b.title, annotations });
|
|
54
|
+
server.registerTool(name, mergedOptions, handler);
|
|
55
|
+
}
|
|
56
|
+
function wrapTool(client, fn) {
|
|
57
|
+
const handler = fn(client);
|
|
58
|
+
return (args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
59
|
+
void extra;
|
|
60
|
+
try {
|
|
61
|
+
return yield handler(args);
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
const text = (0, errors_js_1.toMcpErrorMessage)(err);
|
|
65
|
+
return { content: [{ type: 'text', text }] };
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP tools: spaces (list, get).
|
|
3
|
+
*/
|
|
4
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
5
|
+
import type { LightdashClient } from '@lightdash-tools/client';
|
|
6
|
+
export declare function registerSpaceTools(server: McpServer, client: LightdashClient): void;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* MCP tools: spaces (list, get).
|
|
4
|
+
*/
|
|
5
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
6
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
7
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
8
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
9
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
10
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
11
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.registerSpaceTools = registerSpaceTools;
|
|
16
|
+
const zod_1 = require("zod");
|
|
17
|
+
const shared_js_1 = require("./shared.js");
|
|
18
|
+
function registerSpaceTools(server, client) {
|
|
19
|
+
(0, shared_js_1.registerToolSafe)(server, 'list_spaces', {
|
|
20
|
+
title: 'List spaces',
|
|
21
|
+
description: 'List spaces in a project',
|
|
22
|
+
inputSchema: { projectUuid: zod_1.z.string().describe('Project UUID') },
|
|
23
|
+
annotations: shared_js_1.READ_ONLY_DEFAULT,
|
|
24
|
+
}, (0, shared_js_1.wrapTool)(client, (c) => (_a) => __awaiter(this, [_a], void 0, function* ({ projectUuid }) {
|
|
25
|
+
const spaces = yield c.v1.spaces.listSpacesInProject(projectUuid);
|
|
26
|
+
return { content: [{ type: 'text', text: JSON.stringify(spaces, null, 2) }] };
|
|
27
|
+
})));
|
|
28
|
+
(0, shared_js_1.registerToolSafe)(server, 'get_space', {
|
|
29
|
+
title: 'Get space',
|
|
30
|
+
description: 'Get a space by project and space UUID',
|
|
31
|
+
inputSchema: {
|
|
32
|
+
projectUuid: zod_1.z.string().describe('Project UUID'),
|
|
33
|
+
spaceUuid: zod_1.z.string().describe('Space UUID'),
|
|
34
|
+
},
|
|
35
|
+
annotations: shared_js_1.READ_ONLY_DEFAULT,
|
|
36
|
+
}, (0, shared_js_1.wrapTool)(client, (c) => (_a) => __awaiter(this, [_a], void 0, function* ({ projectUuid, spaceUuid }) {
|
|
37
|
+
const space = yield c.v1.spaces.getSpace(projectUuid, spaceUuid);
|
|
38
|
+
return { content: [{ type: 'text', text: JSON.stringify(space, null, 2) }] };
|
|
39
|
+
})));
|
|
40
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP tools: users / organization members (list, get, delete).
|
|
3
|
+
*/
|
|
4
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
5
|
+
import type { LightdashClient } from '@lightdash-tools/client';
|
|
6
|
+
export declare function registerUserTools(server: McpServer, client: LightdashClient): void;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* MCP tools: users / organization members (list, get, delete).
|
|
4
|
+
*/
|
|
5
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
6
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
7
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
8
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
9
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
10
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
11
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.registerUserTools = registerUserTools;
|
|
16
|
+
const zod_1 = require("zod");
|
|
17
|
+
const shared_js_1 = require("./shared.js");
|
|
18
|
+
function registerUserTools(server, client) {
|
|
19
|
+
(0, shared_js_1.registerToolSafe)(server, 'list_organization_members', {
|
|
20
|
+
title: 'List organization members',
|
|
21
|
+
description: 'List organization members (one page)',
|
|
22
|
+
inputSchema: {
|
|
23
|
+
page: zod_1.z.number().optional().describe('Page number'),
|
|
24
|
+
pageSize: zod_1.z.number().optional().describe('Page size'),
|
|
25
|
+
searchQuery: zod_1.z.string().optional().describe('Search query'),
|
|
26
|
+
},
|
|
27
|
+
annotations: shared_js_1.READ_ONLY_DEFAULT,
|
|
28
|
+
}, (0, shared_js_1.wrapTool)(client, (c) => (params) => __awaiter(this, void 0, void 0, function* () {
|
|
29
|
+
const result = yield c.v1.users.listMembers(params !== null && params !== void 0 ? params : {});
|
|
30
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
31
|
+
})));
|
|
32
|
+
(0, shared_js_1.registerToolSafe)(server, 'get_member', {
|
|
33
|
+
title: 'Get member',
|
|
34
|
+
description: 'Get an organization member by UUID',
|
|
35
|
+
inputSchema: { userUuid: zod_1.z.string().describe('User UUID') },
|
|
36
|
+
annotations: shared_js_1.READ_ONLY_DEFAULT,
|
|
37
|
+
}, (0, shared_js_1.wrapTool)(client, (c) => (_a) => __awaiter(this, [_a], void 0, function* ({ userUuid }) {
|
|
38
|
+
const member = yield c.v1.users.getMemberByUuid(userUuid);
|
|
39
|
+
return { content: [{ type: 'text', text: JSON.stringify(member, null, 2) }] };
|
|
40
|
+
})));
|
|
41
|
+
(0, shared_js_1.registerToolSafe)(server, 'delete_member', {
|
|
42
|
+
title: 'Delete member (destructive)',
|
|
43
|
+
description: 'Permanently remove a user from the organization. Destructive; clients and agents should obtain explicit user confirmation before calling.',
|
|
44
|
+
inputSchema: { userUuid: zod_1.z.string().describe('User UUID') },
|
|
45
|
+
annotations: shared_js_1.WRITE_DESTRUCTIVE,
|
|
46
|
+
}, (0, shared_js_1.wrapTool)(client, (c) => (_a) => __awaiter(this, [_a], void 0, function* ({ userUuid }) {
|
|
47
|
+
yield c.v1.users.deleteMember(userUuid);
|
|
48
|
+
return { content: [{ type: 'text', text: 'Member deleted.' }] };
|
|
49
|
+
})));
|
|
50
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lightdash-tools/mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server and utilities for Lightdash AI.",
|
|
5
|
+
"keywords": [],
|
|
6
|
+
"license": "ISC",
|
|
7
|
+
"author": "",
|
|
8
|
+
"main": "dist/index.js",
|
|
9
|
+
"types": "dist/index.d.ts",
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
12
|
+
"zod": "^4.3.6",
|
|
13
|
+
"@lightdash-tools/client": "0.1.0"
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"@types/node": "^25.2.3"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc",
|
|
20
|
+
"start": "node dist/index.js",
|
|
21
|
+
"start:http": "node dist/http.js"
|
|
22
|
+
}
|
|
23
|
+
}
|
package/src/config.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP server config: build Lightdash client config from environment.
|
|
3
|
+
* Uses same env vars as CLI: LIGHTDASH_URL, LIGHTDASH_API_KEY.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { LightdashClient, mergeConfig } from '@lightdash-tools/client';
|
|
7
|
+
import type { PartialLightdashClientConfig } from '@lightdash-tools/client';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Builds a LightdashClient from environment variables (and optional overrides).
|
|
11
|
+
* Throws if LIGHTDASH_URL or LIGHTDASH_API_KEY are missing.
|
|
12
|
+
*/
|
|
13
|
+
export function getClient(config?: PartialLightdashClientConfig): LightdashClient {
|
|
14
|
+
const merged = mergeConfig(config);
|
|
15
|
+
return new LightdashClient(merged);
|
|
16
|
+
}
|
package/src/errors.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Map Lightdash client errors to safe user-facing messages for MCP responses.
|
|
3
|
+
* Never expose stack traces or tokens.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { LightdashApiError, RateLimitError, NetworkError } from '@lightdash-tools/client';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Returns a short, safe message for MCP content from a client error.
|
|
10
|
+
*/
|
|
11
|
+
export function toMcpErrorMessage(err: unknown): string {
|
|
12
|
+
if (err instanceof RateLimitError) {
|
|
13
|
+
const after = err.retryAfter != null ? ` Retry after ${err.retryAfter}s.` : '';
|
|
14
|
+
return `Rate limited (${err.statusCode}).${after}`;
|
|
15
|
+
}
|
|
16
|
+
if (err instanceof LightdashApiError) {
|
|
17
|
+
const msg = err.error?.message ?? `HTTP ${err.statusCode}`;
|
|
18
|
+
return `Lightdash API error: ${msg}`;
|
|
19
|
+
}
|
|
20
|
+
if (err instanceof NetworkError) {
|
|
21
|
+
return `Network error: ${err.message}`;
|
|
22
|
+
}
|
|
23
|
+
if (err instanceof Error) {
|
|
24
|
+
return err.message || 'Unknown error';
|
|
25
|
+
}
|
|
26
|
+
return String(err);
|
|
27
|
+
}
|