huly-mcp-sdk 0.2.1
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/.env.example +16 -0
- package/README.md +189 -0
- package/dist/connection.d.ts +5 -0
- package/dist/connection.js +111 -0
- package/dist/connection.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/schemas.d.ts +177 -0
- package/dist/schemas.js +78 -0
- package/dist/schemas.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +48 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/comments.d.ts +9 -0
- package/dist/tools/comments.js +19 -0
- package/dist/tools/comments.js.map +1 -0
- package/dist/tools/documents.d.ts +14 -0
- package/dist/tools/documents.js +27 -0
- package/dist/tools/documents.js.map +1 -0
- package/dist/tools/issues.d.ts +51 -0
- package/dist/tools/issues.js +179 -0
- package/dist/tools/issues.js.map +1 -0
- package/dist/tools/labels.d.ts +35 -0
- package/dist/tools/labels.js +150 -0
- package/dist/tools/labels.js.map +1 -0
- package/dist/tools/members.d.ts +6 -0
- package/dist/tools/members.js +17 -0
- package/dist/tools/members.js.map +1 -0
- package/dist/tools/milestones.d.ts +8 -0
- package/dist/tools/milestones.js +26 -0
- package/dist/tools/milestones.js.map +1 -0
- package/dist/tools/projects.d.ts +14 -0
- package/dist/tools/projects.js +33 -0
- package/dist/tools/projects.js.map +1 -0
- package/dist/tools/relations.d.ts +27 -0
- package/dist/tools/relations.js +103 -0
- package/dist/tools/relations.js.map +1 -0
- package/dist/tools/search.d.ts +9 -0
- package/dist/tools/search.js +32 -0
- package/dist/tools/search.js.map +1 -0
- package/dist/utils/errors.d.ts +11 -0
- package/dist/utils/errors.js +24 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/format.d.ts +4 -0
- package/dist/utils/format.js +28 -0
- package/dist/utils/format.js.map +1 -0
- package/package.json +63 -0
- package/scripts/cleanup-issues.js +132 -0
- package/scripts/demo.js +193 -0
- package/scripts/import-csv.js +242 -0
- package/scripts/sample-tasks.csv +11 -0
- package/scripts/setup.js +103 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.listMilestones = void 0;
|
|
7
|
+
const tracker_1 = __importDefault(require("@hcengineering/tracker"));
|
|
8
|
+
const connection_1 = require("../connection");
|
|
9
|
+
const errors_1 = require("../utils/errors");
|
|
10
|
+
const format_1 = require("../utils/format");
|
|
11
|
+
exports.listMilestones = (0, errors_1.wrapToolHandler)(async (args) => {
|
|
12
|
+
const client = await (0, connection_1.getConnection)();
|
|
13
|
+
const project = await client.findOne(tracker_1.default.class.Project, { identifier: args.projectIdentifier });
|
|
14
|
+
if (project == null)
|
|
15
|
+
throw new Error(`Project '${args.projectIdentifier}' not found.`);
|
|
16
|
+
const milestones = await client.findAll(tracker_1.default.class.Milestone, { space: project._id });
|
|
17
|
+
if (milestones.length === 0)
|
|
18
|
+
return `No milestones found in project ${args.projectIdentifier}.`;
|
|
19
|
+
const lines = milestones.map((m) => {
|
|
20
|
+
const status = format_1.MILESTONE_STATUS_LABELS[m.status] ?? 'Unknown';
|
|
21
|
+
const target = (0, format_1.formatDate)(m.targetDate);
|
|
22
|
+
return `- **${m.label}** [${status}] ā Target: ${target}`;
|
|
23
|
+
});
|
|
24
|
+
return `## Milestones in ${args.projectIdentifier} (${milestones.length})\n\n${lines.join('\n')}`;
|
|
25
|
+
});
|
|
26
|
+
//# sourceMappingURL=milestones.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"milestones.js","sourceRoot":"","sources":["../../src/tools/milestones.ts"],"names":[],"mappings":";;;;;;AAAA,qEAA4C;AAC5C,8CAA6C;AAC7C,4CAAiD;AACjD,4CAAqE;AAIxD,QAAA,cAAc,GAAG,IAAA,wBAAe,EAAuC,KAAK,EAAE,IAAI,EAAE,EAAE;IACjG,MAAM,MAAM,GAAG,MAAM,IAAA,0BAAa,GAAE,CAAA;IACpC,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,iBAAO,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,UAAU,EAAE,IAAI,CAAC,iBAAiB,EAAE,CAAC,CAAA;IACnG,IAAI,OAAO,IAAI,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,YAAY,IAAI,CAAC,iBAAiB,cAAc,CAAC,CAAA;IAEtF,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,iBAAO,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAA;IAExF,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,kCAAkC,IAAI,CAAC,iBAAiB,GAAG,CAAA;IAE/F,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACjC,MAAM,MAAM,GAAG,gCAAuB,CAAC,CAAC,CAAC,MAAgB,CAAC,IAAI,SAAS,CAAA;QACvE,MAAM,MAAM,GAAG,IAAA,mBAAU,EAAC,CAAC,CAAC,UAAU,CAAC,CAAA;QACvC,OAAO,OAAO,CAAC,CAAC,KAAK,OAAO,MAAM,eAAe,MAAM,EAAE,CAAA;IAC3D,CAAC,CAAC,CAAA;IAEF,OAAO,oBAAoB,IAAI,CAAC,iBAAiB,KAAK,UAAU,CAAC,MAAM,QAAQ,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAA;AACnG,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare const listProjects: (args: Record<string, never>) => Promise<{
|
|
2
|
+
content: Array<{
|
|
3
|
+
type: "text";
|
|
4
|
+
text: string;
|
|
5
|
+
}>;
|
|
6
|
+
}>;
|
|
7
|
+
export declare const getProject: (args: {
|
|
8
|
+
identifier: string;
|
|
9
|
+
}) => Promise<{
|
|
10
|
+
content: Array<{
|
|
11
|
+
type: "text";
|
|
12
|
+
text: string;
|
|
13
|
+
}>;
|
|
14
|
+
}>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getProject = exports.listProjects = void 0;
|
|
7
|
+
const tracker_1 = __importDefault(require("@hcengineering/tracker"));
|
|
8
|
+
const connection_1 = require("../connection");
|
|
9
|
+
const errors_1 = require("../utils/errors");
|
|
10
|
+
exports.listProjects = (0, errors_1.wrapToolHandler)(async () => {
|
|
11
|
+
const client = await (0, connection_1.getConnection)();
|
|
12
|
+
const projects = await client.findAll(tracker_1.default.class.Project, {});
|
|
13
|
+
if (projects.length === 0)
|
|
14
|
+
return 'No projects found in this workspace.';
|
|
15
|
+
const lines = projects.map((p) => `- **${p.identifier}** ā ${p.name ?? '(no name)'}${p.description != null && p.description !== '' ? `\n ${p.description}` : ''}`);
|
|
16
|
+
return `## Projects (${projects.length})\n\n${lines.join('\n')}`;
|
|
17
|
+
});
|
|
18
|
+
exports.getProject = (0, errors_1.wrapToolHandler)(async (args) => {
|
|
19
|
+
const client = await (0, connection_1.getConnection)();
|
|
20
|
+
const project = await client.findOne(tracker_1.default.class.Project, { identifier: args.identifier });
|
|
21
|
+
if (project == null)
|
|
22
|
+
throw new Error(`Project '${args.identifier}' not found.`);
|
|
23
|
+
const statuses = await client.findAll(tracker_1.default.class.IssueStatus, { space: project._id });
|
|
24
|
+
const statusList = statuses.map((s) => ` - ${s.name}`).join('\n');
|
|
25
|
+
return [
|
|
26
|
+
`## Project: ${project.identifier}`,
|
|
27
|
+
`**Name:** ${project.name ?? '(no name)'}`,
|
|
28
|
+
project.description != null && project.description !== '' ? `**Description:** ${project.description}` : null,
|
|
29
|
+
`**Issues created:** ${project.sequence}`,
|
|
30
|
+
`\n**Statuses:**\n${statusList}`
|
|
31
|
+
].filter(Boolean).join('\n');
|
|
32
|
+
});
|
|
33
|
+
//# sourceMappingURL=projects.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projects.js","sourceRoot":"","sources":["../../src/tools/projects.ts"],"names":[],"mappings":";;;;;;AAAA,qEAA4C;AAC5C,8CAA6C;AAC7C,4CAAiD;AAIpC,QAAA,YAAY,GAAG,IAAA,wBAAe,EAAwB,KAAK,IAAI,EAAE;IAC5E,MAAM,MAAM,GAAG,MAAM,IAAA,0BAAa,GAAE,CAAA;IACpC,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,iBAAO,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC,CAAA;IAEhE,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,sCAAsC,CAAA;IAExE,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAC/B,OAAO,CAAC,CAAC,UAAU,QAAQ,CAAC,CAAC,IAAI,IAAI,WAAW,GAAG,CAAC,CAAC,WAAW,IAAI,IAAI,IAAI,CAAC,CAAC,WAAW,KAAK,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CACjI,CAAA;IACD,OAAO,gBAAgB,QAAQ,CAAC,MAAM,QAAQ,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAA;AAClE,CAAC,CAAC,CAAA;AAEW,QAAA,UAAU,GAAG,IAAA,wBAAe,EAAmC,KAAK,EAAE,IAAI,EAAE,EAAE;IACzF,MAAM,MAAM,GAAG,MAAM,IAAA,0BAAa,GAAE,CAAA;IACpC,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,iBAAO,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC,CAAA;IAC5F,IAAI,OAAO,IAAI,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,YAAY,IAAI,CAAC,UAAU,cAAc,CAAC,CAAA;IAE/E,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,iBAAO,CAAC,KAAK,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAA;IACxF,MAAM,UAAU,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAElE,OAAO;QACL,eAAe,OAAO,CAAC,UAAU,EAAE;QACnC,aAAa,OAAO,CAAC,IAAI,IAAI,WAAW,EAAE;QAC1C,OAAO,CAAC,WAAW,IAAI,IAAI,IAAI,OAAO,CAAC,WAAW,KAAK,EAAE,CAAC,CAAC,CAAC,oBAAoB,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,IAAI;QAC5G,uBAAuB,OAAO,CAAC,QAAQ,EAAE;QACzC,oBAAoB,UAAU,EAAE;KACjC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AAC9B,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export declare const addRelation: (args: {
|
|
2
|
+
identifier: string;
|
|
3
|
+
relatedTo: string;
|
|
4
|
+
}) => Promise<{
|
|
5
|
+
content: Array<{
|
|
6
|
+
type: "text";
|
|
7
|
+
text: string;
|
|
8
|
+
}>;
|
|
9
|
+
}>;
|
|
10
|
+
export declare const addBlockedBy: (args: {
|
|
11
|
+
identifier: string;
|
|
12
|
+
blockedBy: string;
|
|
13
|
+
}) => Promise<{
|
|
14
|
+
content: Array<{
|
|
15
|
+
type: "text";
|
|
16
|
+
text: string;
|
|
17
|
+
}>;
|
|
18
|
+
}>;
|
|
19
|
+
export declare const setParent: (args: {
|
|
20
|
+
identifier: string;
|
|
21
|
+
parentIdentifier?: string | null | undefined;
|
|
22
|
+
}) => Promise<{
|
|
23
|
+
content: Array<{
|
|
24
|
+
type: "text";
|
|
25
|
+
text: string;
|
|
26
|
+
}>;
|
|
27
|
+
}>;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.setParent = exports.addBlockedBy = exports.addRelation = void 0;
|
|
7
|
+
const tracker_1 = __importDefault(require("@hcengineering/tracker"));
|
|
8
|
+
const connection_1 = require("../connection");
|
|
9
|
+
const errors_1 = require("../utils/errors");
|
|
10
|
+
exports.addRelation = (0, errors_1.wrapToolHandler)(async (args) => {
|
|
11
|
+
const client = await (0, connection_1.getConnection)();
|
|
12
|
+
const issueA = await client.findOne(tracker_1.default.class.Issue, { identifier: args.identifier });
|
|
13
|
+
if (issueA == null)
|
|
14
|
+
throw new Error(`Issue '${args.identifier}' not found.`);
|
|
15
|
+
const issueB = await client.findOne(tracker_1.default.class.Issue, { identifier: args.relatedTo });
|
|
16
|
+
if (issueB == null)
|
|
17
|
+
throw new Error(`Issue '${args.relatedTo}' not found.`);
|
|
18
|
+
const relA = { _id: issueB._id, _class: issueB._class };
|
|
19
|
+
const relB = { _id: issueA._id, _class: issueA._class };
|
|
20
|
+
const existingRels = issueA.relations ?? [];
|
|
21
|
+
if (existingRels.some((r) => r._id === issueB._id)) {
|
|
22
|
+
return `**${args.identifier}** is already related to **${args.relatedTo}**.`;
|
|
23
|
+
}
|
|
24
|
+
// Add bidirectional relation
|
|
25
|
+
await client.updateDoc(tracker_1.default.class.Issue, issueA.space, issueA._id, {
|
|
26
|
+
relations: [...existingRels, relA]
|
|
27
|
+
});
|
|
28
|
+
await client.updateDoc(tracker_1.default.class.Issue, issueB.space, issueB._id, {
|
|
29
|
+
relations: [...(issueB.relations ?? []), relB]
|
|
30
|
+
});
|
|
31
|
+
return `ā
**${args.identifier}** is now related to **${args.relatedTo}**`;
|
|
32
|
+
});
|
|
33
|
+
exports.addBlockedBy = (0, errors_1.wrapToolHandler)(async (args) => {
|
|
34
|
+
const client = await (0, connection_1.getConnection)();
|
|
35
|
+
const issue = await client.findOne(tracker_1.default.class.Issue, { identifier: args.identifier });
|
|
36
|
+
if (issue == null)
|
|
37
|
+
throw new Error(`Issue '${args.identifier}' not found.`);
|
|
38
|
+
const blocker = await client.findOne(tracker_1.default.class.Issue, { identifier: args.blockedBy });
|
|
39
|
+
if (blocker == null)
|
|
40
|
+
throw new Error(`Issue '${args.blockedBy}' not found.`);
|
|
41
|
+
const blockerRef = { _id: blocker._id, _class: blocker._class };
|
|
42
|
+
const existing = issue.blockedBy ?? [];
|
|
43
|
+
if (existing.some((r) => r._id === blocker._id)) {
|
|
44
|
+
return `**${args.identifier}** is already blocked by **${args.blockedBy}**.`;
|
|
45
|
+
}
|
|
46
|
+
await client.updateDoc(tracker_1.default.class.Issue, issue.space, issue._id, {
|
|
47
|
+
blockedBy: [...existing, blockerRef]
|
|
48
|
+
});
|
|
49
|
+
return `ā
**${args.identifier}** is now blocked by **${args.blockedBy}**`;
|
|
50
|
+
});
|
|
51
|
+
exports.setParent = (0, errors_1.wrapToolHandler)(async (args) => {
|
|
52
|
+
const client = await (0, connection_1.getConnection)();
|
|
53
|
+
const child = await client.findOne(tracker_1.default.class.Issue, { identifier: args.identifier });
|
|
54
|
+
if (child == null)
|
|
55
|
+
throw new Error(`Issue '${args.identifier}' not found.`);
|
|
56
|
+
// Clear parent if requested
|
|
57
|
+
if (args.parentIdentifier == null) {
|
|
58
|
+
await client.updateDoc(tracker_1.default.class.Issue, child.space, child._id, { parents: [] });
|
|
59
|
+
// Remove from old parent's childInfo if there was one
|
|
60
|
+
if (child.parents.length > 0) {
|
|
61
|
+
const oldParentId = child.parents[0].parentId;
|
|
62
|
+
const oldParent = await client.findOne(tracker_1.default.class.Issue, { _id: oldParentId });
|
|
63
|
+
if (oldParent != null) {
|
|
64
|
+
const newChildInfo = (oldParent.childInfo ?? []).filter((c) => c.childId !== child._id);
|
|
65
|
+
await client.updateDoc(tracker_1.default.class.Issue, oldParent.space, oldParent._id, {
|
|
66
|
+
subIssues: Math.max(0, (oldParent.subIssues ?? 1) - 1),
|
|
67
|
+
childInfo: newChildInfo
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return `ā
Cleared parent from **${args.identifier}**`;
|
|
72
|
+
}
|
|
73
|
+
const parent = await client.findOne(tracker_1.default.class.Issue, { identifier: args.parentIdentifier });
|
|
74
|
+
if (parent == null)
|
|
75
|
+
throw new Error(`Parent issue '${args.parentIdentifier}' not found.`);
|
|
76
|
+
if (parent._id === child._id)
|
|
77
|
+
throw new Error('An issue cannot be its own parent.');
|
|
78
|
+
const parentInfo = {
|
|
79
|
+
parentId: parent._id,
|
|
80
|
+
identifier: parent.identifier,
|
|
81
|
+
parentTitle: parent.title,
|
|
82
|
+
space: parent.space
|
|
83
|
+
};
|
|
84
|
+
const childInfoEntry = {
|
|
85
|
+
childId: child._id,
|
|
86
|
+
estimation: child.estimation ?? 0,
|
|
87
|
+
reportedTime: child.reportedTime ?? 0
|
|
88
|
+
};
|
|
89
|
+
// Update child's parents list
|
|
90
|
+
await client.updateDoc(tracker_1.default.class.Issue, child.space, child._id, {
|
|
91
|
+
parents: [parentInfo, ...child.parents]
|
|
92
|
+
});
|
|
93
|
+
// Update parent's subIssues count and childInfo
|
|
94
|
+
const existingChildInfo = parent.childInfo ?? [];
|
|
95
|
+
if (!existingChildInfo.some((c) => c.childId === child._id)) {
|
|
96
|
+
await client.updateDoc(tracker_1.default.class.Issue, parent.space, parent._id, {
|
|
97
|
+
subIssues: (parent.subIssues ?? 0) + 1,
|
|
98
|
+
childInfo: [...existingChildInfo, childInfoEntry]
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
return `ā
**${args.identifier}** is now a sub-issue of **${args.parentIdentifier}**`;
|
|
102
|
+
});
|
|
103
|
+
//# sourceMappingURL=relations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relations.js","sourceRoot":"","sources":["../../src/tools/relations.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.searchIssues = void 0;
|
|
7
|
+
const tracker_1 = __importDefault(require("@hcengineering/tracker"));
|
|
8
|
+
const connection_1 = require("../connection");
|
|
9
|
+
const errors_1 = require("../utils/errors");
|
|
10
|
+
const format_1 = require("../utils/format");
|
|
11
|
+
exports.searchIssues = (0, errors_1.wrapToolHandler)(async (args) => {
|
|
12
|
+
const client = await (0, connection_1.getConnection)();
|
|
13
|
+
const result = await client.searchFulltext({ query: args.query, classes: [tracker_1.default.class.Issue] }, { limit: args.limit });
|
|
14
|
+
if (result.docs.length === 0)
|
|
15
|
+
return `No issues found for query: "${args.query}"`;
|
|
16
|
+
// Enrich each result with full issue data
|
|
17
|
+
const enriched = await Promise.all(result.docs.map(async (r) => {
|
|
18
|
+
const issue = await client.findOne(tracker_1.default.class.Issue, { _id: r.id });
|
|
19
|
+
if (issue == null)
|
|
20
|
+
return null;
|
|
21
|
+
const status = await client.findOne(tracker_1.default.class.IssueStatus, { _id: issue.status });
|
|
22
|
+
return { issue, statusName: status?.name ?? 'Unknown' };
|
|
23
|
+
}));
|
|
24
|
+
const lines = enriched
|
|
25
|
+
.filter((e) => e !== null)
|
|
26
|
+
.map((e) => {
|
|
27
|
+
const due = e.issue.dueDate != null ? ` | Due: ${(0, format_1.formatDate)(e.issue.dueDate)}` : '';
|
|
28
|
+
return `- **${e.issue.identifier}** [${e.statusName}] [${(0, format_1.priorityLabel)(e.issue.priority)}]${due}\n ${e.issue.title}`;
|
|
29
|
+
});
|
|
30
|
+
return `## Search results for "${args.query}" (${lines.length})\n\n${lines.join('\n')}`;
|
|
31
|
+
});
|
|
32
|
+
//# sourceMappingURL=search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/tools/search.ts"],"names":[],"mappings":";;;;;;AAAA,qEAA4C;AAC5C,8CAA6C;AAC7C,4CAAiD;AACjD,4CAA2D;AAI9C,QAAA,YAAY,GAAG,IAAA,wBAAe,EAAqC,KAAK,EAAE,IAAI,EAAE,EAAE;IAC7F,MAAM,MAAM,GAAG,MAAM,IAAA,0BAAa,GAAE,CAAA;IAEpC,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,cAAc,CACxC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,iBAAO,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,EACrD,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CACtB,CAAA;IAED,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,+BAA+B,IAAI,CAAC,KAAK,GAAG,CAAA;IAEjF,0CAA0C;IAC1C,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,GAAG,CAChC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;QAC1B,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,iBAAO,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,EAAS,EAAE,CAAC,CAAA;QAC7E,IAAI,KAAK,IAAI,IAAI;YAAE,OAAO,IAAI,CAAA;QAC9B,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,iBAAO,CAAC,KAAK,CAAC,WAAW,EAAE,EAAE,GAAG,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAA;QACrF,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,IAAI,SAAS,EAAE,CAAA;IACzD,CAAC,CAAC,CACH,CAAA;IAED,MAAM,KAAK,GAAG,QAAQ;SACnB,MAAM,CAAC,CAAC,CAAC,EAA8B,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC;SACrD,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACT,MAAM,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,WAAW,IAAA,mBAAU,EAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;QACnF,OAAO,OAAO,CAAC,CAAC,KAAK,CAAC,UAAU,OAAO,CAAC,CAAC,UAAU,MAAM,IAAA,sBAAa,EAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,GAAG,OAAO,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,CAAA;IACvH,CAAC,CAAC,CAAA;IAEJ,OAAO,0BAA0B,IAAI,CAAC,KAAK,MAAM,KAAK,CAAC,MAAM,QAAQ,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAA;AACzF,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare class HulyMcpError extends Error {
|
|
2
|
+
constructor(message: string);
|
|
3
|
+
}
|
|
4
|
+
type ToolResult = {
|
|
5
|
+
content: Array<{
|
|
6
|
+
type: 'text';
|
|
7
|
+
text: string;
|
|
8
|
+
}>;
|
|
9
|
+
};
|
|
10
|
+
export declare function wrapToolHandler<T extends Record<string, unknown>>(handler: (args: T) => Promise<string>): (args: T) => Promise<ToolResult>;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HulyMcpError = void 0;
|
|
4
|
+
exports.wrapToolHandler = wrapToolHandler;
|
|
5
|
+
class HulyMcpError extends Error {
|
|
6
|
+
constructor(message) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = 'HulyMcpError';
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
exports.HulyMcpError = HulyMcpError;
|
|
12
|
+
function wrapToolHandler(handler) {
|
|
13
|
+
return async (args) => {
|
|
14
|
+
try {
|
|
15
|
+
const text = await handler(args);
|
|
16
|
+
return { content: [{ type: 'text', text }] };
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
20
|
+
throw new Error(`Huly MCP: ${message}`);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":";;;AASA,0CAYC;AArBD,MAAa,YAAa,SAAQ,KAAK;IACrC,YAAa,OAAe;QAC1B,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,cAAc,CAAA;IAC5B,CAAC;CACF;AALD,oCAKC;AAID,SAAgB,eAAe,CAC7B,OAAqC;IAErC,OAAO,KAAK,EAAE,IAAO,EAAuB,EAAE;QAC5C,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;YAChC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,CAAA;QAC9C,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACtB,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;YAChE,MAAM,IAAI,KAAK,CAAC,aAAa,OAAO,EAAE,CAAC,CAAA;QACzC,CAAC;IACH,CAAC,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare const PRIORITY_LABELS: Record<number, string>;
|
|
2
|
+
export declare const MILESTONE_STATUS_LABELS: Record<number, string>;
|
|
3
|
+
export declare function formatDate(ts: number | null | undefined): string;
|
|
4
|
+
export declare function priorityLabel(priority: number): string;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MILESTONE_STATUS_LABELS = exports.PRIORITY_LABELS = void 0;
|
|
4
|
+
exports.formatDate = formatDate;
|
|
5
|
+
exports.priorityLabel = priorityLabel;
|
|
6
|
+
const tracker_1 = require("@hcengineering/tracker");
|
|
7
|
+
exports.PRIORITY_LABELS = {
|
|
8
|
+
[tracker_1.IssuePriority.NoPriority]: 'No Priority',
|
|
9
|
+
[tracker_1.IssuePriority.Urgent]: 'Urgent',
|
|
10
|
+
[tracker_1.IssuePriority.High]: 'High',
|
|
11
|
+
[tracker_1.IssuePriority.Medium]: 'Medium',
|
|
12
|
+
[tracker_1.IssuePriority.Low]: 'Low'
|
|
13
|
+
};
|
|
14
|
+
exports.MILESTONE_STATUS_LABELS = {
|
|
15
|
+
0: 'Planned',
|
|
16
|
+
1: 'In Progress',
|
|
17
|
+
2: 'Completed',
|
|
18
|
+
3: 'Canceled'
|
|
19
|
+
};
|
|
20
|
+
function formatDate(ts) {
|
|
21
|
+
if (ts == null)
|
|
22
|
+
return 'None';
|
|
23
|
+
return new Date(ts).toISOString().split('T')[0];
|
|
24
|
+
}
|
|
25
|
+
function priorityLabel(priority) {
|
|
26
|
+
return exports.PRIORITY_LABELS[priority] ?? 'Unknown';
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/utils/format.ts"],"names":[],"mappings":";;;AAiBA,gCAGC;AAED,sCAEC;AAxBD,oDAAsD;AAEzC,QAAA,eAAe,GAA2B;IACrD,CAAC,uBAAa,CAAC,UAAU,CAAC,EAAE,aAAa;IACzC,CAAC,uBAAa,CAAC,MAAM,CAAC,EAAE,QAAQ;IAChC,CAAC,uBAAa,CAAC,IAAI,CAAC,EAAE,MAAM;IAC5B,CAAC,uBAAa,CAAC,MAAM,CAAC,EAAE,QAAQ;IAChC,CAAC,uBAAa,CAAC,GAAG,CAAC,EAAE,KAAK;CAC3B,CAAA;AAEY,QAAA,uBAAuB,GAA2B;IAC7D,CAAC,EAAE,SAAS;IACZ,CAAC,EAAE,aAAa;IAChB,CAAC,EAAE,WAAW;IACd,CAAC,EAAE,UAAU;CACd,CAAA;AAED,SAAgB,UAAU,CAAE,EAA6B;IACvD,IAAI,EAAE,IAAI,IAAI;QAAE,OAAO,MAAM,CAAA;IAC7B,OAAO,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;AACjD,CAAC;AAED,SAAgB,aAAa,CAAE,QAAgB;IAC7C,OAAO,uBAAe,CAAC,QAAQ,CAAC,IAAI,SAAS,CAAA;AAC/C,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "huly-mcp-sdk",
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "MCP server for Huly ā connect Claude Desktop to your Huly workspace via the native WebSocket SDK",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"huly",
|
|
7
|
+
"mcp",
|
|
8
|
+
"model-context-protocol",
|
|
9
|
+
"claude",
|
|
10
|
+
"project-management",
|
|
11
|
+
"issue-tracker",
|
|
12
|
+
"anthropic"
|
|
13
|
+
],
|
|
14
|
+
"homepage": "https://github.com/varaprasadreddy9676/huly-mcp#readme",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/varaprasadreddy9676/huly-mcp.git"
|
|
18
|
+
},
|
|
19
|
+
"license": "EPL-2.0",
|
|
20
|
+
"author": "SaiVaraprasad Medapati",
|
|
21
|
+
"bin": {
|
|
22
|
+
"huly-mcp": "dist/index.js",
|
|
23
|
+
"huly-mcp-setup": "scripts/setup.js"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist/",
|
|
27
|
+
"scripts/",
|
|
28
|
+
".env.example",
|
|
29
|
+
"README.md"
|
|
30
|
+
],
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=20"
|
|
33
|
+
},
|
|
34
|
+
"scripts": {
|
|
35
|
+
"build": "node --max-old-space-size=4096 node_modules/typescript/bin/tsc",
|
|
36
|
+
"dev": "tsx watch src/index.ts",
|
|
37
|
+
"start": "node dist/index.js",
|
|
38
|
+
"setup": "node scripts/setup.js",
|
|
39
|
+
"prepublishOnly": "npm run build"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@hcengineering/account-client": "^0.7.0",
|
|
43
|
+
"@hcengineering/chunter": "^0.7.0",
|
|
44
|
+
"@hcengineering/contact": "^0.7.0",
|
|
45
|
+
"@hcengineering/core": "^0.7.0",
|
|
46
|
+
"@hcengineering/document": "^0.7.0",
|
|
47
|
+
"@hcengineering/platform": "^0.7.0",
|
|
48
|
+
"@hcengineering/rank": "^0.7.0",
|
|
49
|
+
"@hcengineering/tags": "^0.7.0",
|
|
50
|
+
"@hcengineering/server-client": "^0.7.0",
|
|
51
|
+
"@hcengineering/task": "^0.7.0",
|
|
52
|
+
"@hcengineering/tracker": "^0.7.0",
|
|
53
|
+
"@modelcontextprotocol/sdk": "^1.10.0",
|
|
54
|
+
"ws": "^8.18.0",
|
|
55
|
+
"zod": "^3.23.0"
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"@types/node": "^22.0.0",
|
|
59
|
+
"@types/ws": "^8.5.0",
|
|
60
|
+
"tsx": "^4.19.0",
|
|
61
|
+
"typescript": "^5.7.0"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Delete a range of issues from a Huly project.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* node scripts/cleanup-issues.js <project> <start> <end> [--confirm]
|
|
7
|
+
*
|
|
8
|
+
* Example (dry run ā shows what would be deleted):
|
|
9
|
+
* node scripts/cleanup-issues.js VISIO 59 68
|
|
10
|
+
*
|
|
11
|
+
* Example (actually delete):
|
|
12
|
+
* node scripts/cleanup-issues.js VISIO 59 68 --confirm
|
|
13
|
+
*
|
|
14
|
+
* Auth is read from .env (HULY_TOKEN + HULY_WORKSPACE, or HULY_EMAIL + HULY_PASSWORD).
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
'use strict'
|
|
18
|
+
|
|
19
|
+
const fs = require('fs')
|
|
20
|
+
const path = require('path')
|
|
21
|
+
|
|
22
|
+
// Load .env
|
|
23
|
+
const envPath = path.join(__dirname, '..', '.env')
|
|
24
|
+
if (fs.existsSync(envPath)) {
|
|
25
|
+
for (const line of fs.readFileSync(envPath, 'utf8').split('\n')) {
|
|
26
|
+
const m = line.match(/^([^#=\s]+)\s*=\s*(.*)$/)
|
|
27
|
+
if (m) process.env[m[1]] = m[2].trim()
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Load compiled modules
|
|
32
|
+
const distDir = path.join(__dirname, '..', 'dist')
|
|
33
|
+
if (!fs.existsSync(path.join(distDir, 'connection.js'))) {
|
|
34
|
+
console.error('ā dist/ not found. Run `npm run build` first.')
|
|
35
|
+
process.exit(1)
|
|
36
|
+
}
|
|
37
|
+
const { getConnection, closeConnection } = require(path.join(distDir, 'connection'))
|
|
38
|
+
const tracker = require('@hcengineering/tracker')
|
|
39
|
+
|
|
40
|
+
// Parse args
|
|
41
|
+
const args = process.argv.slice(2)
|
|
42
|
+
const confirm = args.includes('--confirm')
|
|
43
|
+
const positional = args.filter(a => !a.startsWith('--'))
|
|
44
|
+
|
|
45
|
+
const [projectIdentifier, startStr, endStr] = positional
|
|
46
|
+
if (!projectIdentifier || !startStr || !endStr) {
|
|
47
|
+
console.error('Usage: node scripts/cleanup-issues.js <project> <start> <end> [--confirm]')
|
|
48
|
+
console.error('Example: node scripts/cleanup-issues.js VISIO 59 68 --confirm')
|
|
49
|
+
process.exit(1)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const start = parseInt(startStr, 10)
|
|
53
|
+
const end = parseInt(endStr, 10)
|
|
54
|
+
if (isNaN(start) || isNaN(end) || start > end) {
|
|
55
|
+
console.error('ā start and end must be valid numbers with start <= end')
|
|
56
|
+
process.exit(1)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function main () {
|
|
60
|
+
const identifiers = []
|
|
61
|
+
for (let n = start; n <= end; n++) {
|
|
62
|
+
identifiers.push(`${projectIdentifier}-${n}`)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
console.log(`\nš Huly Issue Cleanup`)
|
|
66
|
+
console.log(` Project: ${projectIdentifier}`)
|
|
67
|
+
console.log(` Range: ${projectIdentifier}-${start} ā ${projectIdentifier}-${end} (${identifiers.length} issues)`)
|
|
68
|
+
console.log(` Mode: ${confirm ? 'ā ļø DELETING for real' : 'š DRY RUN (pass --confirm to delete)'}\n`)
|
|
69
|
+
|
|
70
|
+
console.log('š Connecting to Huly...')
|
|
71
|
+
const client = await getConnection()
|
|
72
|
+
console.log('ā
Connected.\n')
|
|
73
|
+
|
|
74
|
+
// Find all issues in the range
|
|
75
|
+
const issues = await client.findAll(
|
|
76
|
+
tracker.default.class.Issue,
|
|
77
|
+
{ identifier: { $in: identifiers } }
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
if (issues.length === 0) {
|
|
81
|
+
console.log('ā
No matching issues found ā nothing to delete.')
|
|
82
|
+
await closeConnection()
|
|
83
|
+
return
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
console.log(`Found ${issues.length} issue(s) to delete:\n`)
|
|
87
|
+
for (const issue of issues) {
|
|
88
|
+
console.log(` ⢠${issue.identifier} "${issue.title}"`)
|
|
89
|
+
}
|
|
90
|
+
console.log()
|
|
91
|
+
|
|
92
|
+
if (!confirm) {
|
|
93
|
+
console.log('ā¹ļø This was a DRY RUN. To actually delete, run:')
|
|
94
|
+
console.log(` node scripts/cleanup-issues.js ${projectIdentifier} ${start} ${end} --confirm\n`)
|
|
95
|
+
await closeConnection()
|
|
96
|
+
return
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Delete
|
|
100
|
+
let deleted = 0
|
|
101
|
+
let failed = 0
|
|
102
|
+
|
|
103
|
+
for (const issue of issues) {
|
|
104
|
+
process.stdout.write(` Deleting ${issue.identifier}... `)
|
|
105
|
+
try {
|
|
106
|
+
await client.removeCollection(
|
|
107
|
+
tracker.default.class.Issue,
|
|
108
|
+
issue.space,
|
|
109
|
+
issue._id,
|
|
110
|
+
issue.attachedTo,
|
|
111
|
+
issue.attachedToClass,
|
|
112
|
+
issue.collection
|
|
113
|
+
)
|
|
114
|
+
console.log('ā
')
|
|
115
|
+
deleted++
|
|
116
|
+
} catch (err) {
|
|
117
|
+
console.log(`ā ${err.message}`)
|
|
118
|
+
failed++
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
await closeConnection()
|
|
123
|
+
|
|
124
|
+
console.log(`\n${'ā'.repeat(50)}`)
|
|
125
|
+
console.log(`ā
Deleted: ${deleted} ā Failed: ${failed}`)
|
|
126
|
+
console.log(`${'ā'.repeat(50)}\n`)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
main().catch(err => {
|
|
130
|
+
console.error('\nā Fatal error:', err.message)
|
|
131
|
+
process.exit(1)
|
|
132
|
+
})
|