huly-mcp-selfhost 3.1.0 → 3.3.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/.env.example +12 -0
- package/CHANGELOG.md +211 -0
- package/README.md +178 -327
- package/dist/connection.d.ts +8 -0
- package/dist/connection.js +13 -1
- package/dist/connection.js.map +1 -1
- package/dist/guard.d.ts +11 -0
- package/dist/guard.js +13 -0
- package/dist/guard.js.map +1 -1
- package/dist/guides/costs.md +48 -0
- package/dist/guides/features.md +49 -0
- package/dist/guides/onboarding.md +56 -0
- package/dist/guides/workflow.md +57 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -1
- package/dist/registry.js +2 -0
- package/dist/registry.js.map +1 -1
- package/dist/resources.d.ts +7 -0
- package/dist/resources.js +48 -5
- package/dist/resources.js.map +1 -1
- package/dist/schemas.d.ts +23 -0
- package/dist/schemas.js +29 -6
- package/dist/schemas.js.map +1 -1
- package/dist/server.d.ts +14 -0
- package/dist/server.js +67 -3
- package/dist/server.js.map +1 -1
- package/dist/tools/agent.d.ts +35 -0
- package/dist/tools/agent.js +134 -11
- package/dist/tools/agent.js.map +1 -1
- package/dist/tools/documents.js +2 -1
- package/dist/tools/documents.js.map +1 -1
- package/dist/tools/issues.d.ts +3 -0
- package/dist/tools/issues.js +35 -20
- package/dist/tools/issues.js.map +1 -1
- package/dist/tools/organizations.js +2 -1
- package/dist/tools/organizations.js.map +1 -1
- package/dist/utils/content.d.ts +5 -0
- package/dist/utils/content.js +34 -2
- package/dist/utils/content.js.map +1 -1
- package/dist/utils/meeting.d.ts +13 -0
- package/dist/utils/meeting.js +118 -0
- package/dist/utils/meeting.js.map +1 -0
- package/dist/utils/redact.d.ts +3 -0
- package/dist/utils/redact.js +26 -0
- package/dist/utils/redact.js.map +1 -0
- package/dist/utils/spec.d.ts +10 -0
- package/dist/utils/spec.js +90 -0
- package/dist/utils/spec.js.map +1 -0
- package/dist/utils/storage.js +7 -3
- package/dist/utils/storage.js.map +1 -1
- package/package.json +17 -8
- package/scripts/copy-guides.js +18 -0
- package/scripts/setup.js +89 -33
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redact.js","sourceRoot":"","sources":["../../src/utils/redact.ts"],"names":[],"mappings":";;;;AAAA;;;GAGG;AACH,MAAM,GAAG,GAAG,qCAAqC,CAAA;AAEjD,uBAA+B,KAAc;IAC3C,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;IAC/D,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,MAAM,IAAI,GAAG,IAAI,KAAK,CAAC,aAAa,CAAC,KAAK,CAAC,OAAO,CAAW,CAAC,CAAA;QAC9D,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,KAAK,CAAW,CAAC,CAAC,CAAC,SAAS,CAAA;QACnF,OAAO,IAAI,CAAA;IACb,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,6GAA6G;AAC7G;IACE,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IACzC,MAAM,GAAG,GAAG,CAAC,GAAG,IAAe,EAAQ,EAAE,GAAG,KAAK,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAA,CAAC,CAAC,CAAA;IAC/E,OAAO,CAAC,GAAG,GAAG,OAAO,CAAC,IAAI,GAAG,OAAO,CAAC,KAAK,GAAG,OAAO,CAAC,IAAI,GAAG,OAAO,CAAC,KAAK,GAAG,GAAG,CAAA;AACjF,CAAC"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.planFromMarkup = planFromMarkup;
|
|
4
|
+
// Spec → issues: turn a document's structure into planned issues without an LLM round trip.
|
|
5
|
+
// Open checklist items become issues; without a checklist, the sections (lowest heading level
|
|
6
|
+
// that occurs more than once, else level 2/1) become issues with their text as description.
|
|
7
|
+
const markdown_1 = require("./markdown");
|
|
8
|
+
const CHECK = /^\s*\[( |x|X)\]\s+/;
|
|
9
|
+
function childrenOf(node) {
|
|
10
|
+
return Array.isArray(node?.content) ? node.content : [];
|
|
11
|
+
}
|
|
12
|
+
/** Open checklist items: Huly todo/task nodes, or "[ ] text" list items from Markdown imports. */
|
|
13
|
+
function checklist(root) {
|
|
14
|
+
const open = [];
|
|
15
|
+
let done = 0;
|
|
16
|
+
const walk = (node) => {
|
|
17
|
+
const type = String(node?.type ?? '');
|
|
18
|
+
if (type === 'todoItem' || type === 'taskItem') {
|
|
19
|
+
const [first, ...rest] = childrenOf(node);
|
|
20
|
+
const title = (0, markdown_1.extractText)(first ?? node).trim();
|
|
21
|
+
const nested = rest.map(markdown_1.extractText).join('\n').trim();
|
|
22
|
+
if (node.attrs?.checked === true)
|
|
23
|
+
done++;
|
|
24
|
+
else if (title !== '')
|
|
25
|
+
open.push({ title, description: nested !== '' ? nested : undefined });
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (type === 'listItem') {
|
|
29
|
+
const [first, ...rest] = childrenOf(node);
|
|
30
|
+
const text = (0, markdown_1.extractText)(first ?? node).trim();
|
|
31
|
+
const m = CHECK.exec(text);
|
|
32
|
+
if (m != null) {
|
|
33
|
+
const nested = rest.map(markdown_1.extractText).join('\n').trim();
|
|
34
|
+
if (m[1] !== ' ')
|
|
35
|
+
done++;
|
|
36
|
+
else
|
|
37
|
+
open.push({ title: text.replace(CHECK, '').trim(), description: nested !== '' ? nested : undefined });
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
for (const c of childrenOf(node))
|
|
42
|
+
walk(c);
|
|
43
|
+
};
|
|
44
|
+
walk(root);
|
|
45
|
+
return { open, done };
|
|
46
|
+
}
|
|
47
|
+
function headings(root) {
|
|
48
|
+
const blocks = childrenOf(root);
|
|
49
|
+
const levels = blocks.filter((b) => b?.type === 'heading').map((b) => Number(b.attrs?.level ?? 1));
|
|
50
|
+
if (levels.length === 0)
|
|
51
|
+
return [];
|
|
52
|
+
const count = (l) => levels.filter((x) => x === l).length;
|
|
53
|
+
// Sections are the most common heading level among 2/3; a single H1 is usually the title.
|
|
54
|
+
const level = [2, 3, 1].find((l) => count(l) > 1) ?? [2, 3, 1].find((l) => count(l) > 0) ?? levels[0];
|
|
55
|
+
const tasks = [];
|
|
56
|
+
let current;
|
|
57
|
+
const flush = () => {
|
|
58
|
+
if (current == null)
|
|
59
|
+
return;
|
|
60
|
+
const body = current.body.join('\n').trim();
|
|
61
|
+
tasks.push({ title: current.title, description: body !== '' ? body.slice(0, 2000) : undefined });
|
|
62
|
+
};
|
|
63
|
+
for (const b of blocks) {
|
|
64
|
+
if (b?.type === 'heading') {
|
|
65
|
+
const l = Number(b.attrs?.level ?? 1);
|
|
66
|
+
if (l === level) {
|
|
67
|
+
flush();
|
|
68
|
+
current = { title: (0, markdown_1.extractText)(b).trim(), body: [] };
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (l < level) {
|
|
72
|
+
flush();
|
|
73
|
+
current = undefined;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (current != null)
|
|
78
|
+
current.body.push((0, markdown_1.extractText)(b));
|
|
79
|
+
}
|
|
80
|
+
flush();
|
|
81
|
+
return tasks.filter((t) => t.title !== '');
|
|
82
|
+
}
|
|
83
|
+
function planFromMarkup(root) {
|
|
84
|
+
const list = checklist(root);
|
|
85
|
+
if (list.open.length > 0 || list.done > 0)
|
|
86
|
+
return { source: 'checklist', tasks: list.open, skippedDone: list.done };
|
|
87
|
+
const secs = headings(root);
|
|
88
|
+
return { source: secs.length > 0 ? 'headings' : 'none', tasks: secs, skippedDone: 0 };
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spec.js","sourceRoot":"","sources":["../../src/utils/spec.ts"],"names":[],"mappings":"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"}
|
package/dist/utils/storage.js
CHANGED
|
@@ -24,6 +24,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
24
24
|
exports.uploadMarkupBlob = uploadMarkupBlob;
|
|
25
25
|
exports.uploadBlob = uploadBlob;
|
|
26
26
|
exports.deleteBlob = deleteBlob;
|
|
27
|
+
/** Uploads can be large files; without a limit a stalled connection hangs the tool call. */
|
|
28
|
+
const UPLOAD_TIMEOUT_MS = 120_000;
|
|
27
29
|
const CLOUD_DATALAKE_URL = 'https://dl-eu.huly.app';
|
|
28
30
|
async function uploadMarkupBlob(wsToken, workspaceUuid, blobId, content) {
|
|
29
31
|
const result = await uploadBlob(wsToken, workspaceUuid, blobId, new Blob([content], { type: 'application/json' }));
|
|
@@ -43,7 +45,8 @@ async function uploadViaSelfHostedFront(frontUrl, wsToken, blobId, blob) {
|
|
|
43
45
|
const uploadRes = await fetch(uploadUrl, {
|
|
44
46
|
method: 'POST',
|
|
45
47
|
headers: { Authorization: `Bearer ${wsToken}` },
|
|
46
|
-
body: form
|
|
48
|
+
body: form,
|
|
49
|
+
signal: AbortSignal.timeout(UPLOAD_TIMEOUT_MS)
|
|
47
50
|
});
|
|
48
51
|
if (!uploadRes.ok) {
|
|
49
52
|
const body = await uploadRes.text().catch(() => '');
|
|
@@ -63,7 +66,8 @@ async function uploadViaCloudDatalake(workspaceUuid, wsToken, blobId, blob) {
|
|
|
63
66
|
const uploadRes = await fetch(uploadUrl, {
|
|
64
67
|
method: 'POST',
|
|
65
68
|
headers: { Authorization: `Bearer ${wsToken}` },
|
|
66
|
-
body: form
|
|
69
|
+
body: form,
|
|
70
|
+
signal: AbortSignal.timeout(UPLOAD_TIMEOUT_MS)
|
|
67
71
|
});
|
|
68
72
|
if (!uploadRes.ok) {
|
|
69
73
|
const body = await uploadRes.text().catch(() => '');
|
|
@@ -77,6 +81,6 @@ async function deleteBlob(wsToken, workspaceUuid, blobId) {
|
|
|
77
81
|
const url = frontUrl != null && frontUrl !== ''
|
|
78
82
|
? `${frontUrl.replace(/\/$/, '')}/files?file=${encodeURIComponent(blobId)}`
|
|
79
83
|
: `${CLOUD_DATALAKE_URL}/blob/${encodeURIComponent(workspaceUuid)}/${encodeURIComponent(blobId)}`;
|
|
80
|
-
await fetch(url, { method: 'DELETE', headers: { Authorization: `Bearer ${wsToken}` } }).catch(() => { });
|
|
84
|
+
await fetch(url, { method: 'DELETE', headers: { Authorization: `Bearer ${wsToken}` }, signal: AbortSignal.timeout(30_000) }).catch(() => { });
|
|
81
85
|
}
|
|
82
86
|
//# sourceMappingURL=storage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../../src/utils/storage.ts"],"names":[],"mappings":";AAAA,uEAAuE;AACvE,qDAAqD;AACrD,EAAE;AACF,2DAA2D;AAC3D,qEAAqE;AACrE,gFAAgF;AAChF,yEAAyE;AACzE,EAAE;AACF,0EAA0E;AAC1E,wEAAwE;AACxE,kEAAkE;AAClE,8DAA8D;AAC9D,iFAAiF;AACjF,iCAAiC;AACjC,4CAA4C;AAC5C,mEAAmE;AACnE,EAAE;AACF,uEAAuE;AACvE,0EAA0E;AAC1E,sEAAsE;AACtE,iEAAiE;;;;;AAEjE,MAAM,kBAAkB,GAAG,wBAAwB,CAAA;AAO5C,KAAK,2BACV,OAAe,EACf,aAAqB,EACrB,MAAc,EACd,OAAe;IAEf,MAAM,MAAM,GAAG,MAAM,UAAU,CAC7B,OAAO,EACP,aAAa,EACb,MAAM,EACN,IAAI,IAAI,CAAC,CAAC,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,kBAAkB,EAAE,CAAC,CAClD,CAAA;IACD,OAAO,MAAM,CAAC,EAAE,CAAA;AAClB,CAAC;AAEM,KAAK,qBACV,OAAe,EACf,aAAqB,EACrB,MAAc,EACd,IAAU;IAEV,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAA;IAE3C,IAAI,QAAQ,IAAI,IAAI,IAAI,QAAQ,KAAK,EAAE,EAAE,CAAC;QACxC,OAAO,MAAM,wBAAwB,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,CAAA;IACxE,CAAC;IACD,OAAO,MAAM,sBAAsB,CAAC,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,CAAA;AAC3E,CAAC;AAED,KAAK,UAAU,wBAAwB,CACrC,QAAgB,EAChB,OAAe,EACf,MAAc,EACd,IAAU;IAEV,MAAM,IAAI,GAAG,IAAI,QAAQ,EAAE,CAAA;IAC3B,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,CAAA;IAEjC,MAAM,SAAS,GAAG,GAAG,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAA;IACxD,MAAM,SAAS,GAAG,MAAM,KAAK,CAAC,SAAS,EAAE;QACvC,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,OAAO,EAAE,EAAE;QAC/C,IAAI,EAAE,IAAI;
|
|
1
|
+
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../../src/utils/storage.ts"],"names":[],"mappings":";AAAA,uEAAuE;AACvE,qDAAqD;AACrD,EAAE;AACF,2DAA2D;AAC3D,qEAAqE;AACrE,gFAAgF;AAChF,yEAAyE;AACzE,EAAE;AACF,0EAA0E;AAC1E,wEAAwE;AACxE,kEAAkE;AAClE,8DAA8D;AAC9D,iFAAiF;AACjF,iCAAiC;AACjC,4CAA4C;AAC5C,mEAAmE;AACnE,EAAE;AACF,uEAAuE;AACvE,0EAA0E;AAC1E,sEAAsE;AACtE,iEAAiE;;;;;AAEjE,4FAA4F;AAC5F,MAAM,iBAAiB,GAAG,OAAO,CAAA;AAEjC,MAAM,kBAAkB,GAAG,wBAAwB,CAAA;AAO5C,KAAK,2BACV,OAAe,EACf,aAAqB,EACrB,MAAc,EACd,OAAe;IAEf,MAAM,MAAM,GAAG,MAAM,UAAU,CAC7B,OAAO,EACP,aAAa,EACb,MAAM,EACN,IAAI,IAAI,CAAC,CAAC,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,kBAAkB,EAAE,CAAC,CAClD,CAAA;IACD,OAAO,MAAM,CAAC,EAAE,CAAA;AAClB,CAAC;AAEM,KAAK,qBACV,OAAe,EACf,aAAqB,EACrB,MAAc,EACd,IAAU;IAEV,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAA;IAE3C,IAAI,QAAQ,IAAI,IAAI,IAAI,QAAQ,KAAK,EAAE,EAAE,CAAC;QACxC,OAAO,MAAM,wBAAwB,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,CAAA;IACxE,CAAC;IACD,OAAO,MAAM,sBAAsB,CAAC,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,CAAA;AAC3E,CAAC;AAED,KAAK,UAAU,wBAAwB,CACrC,QAAgB,EAChB,OAAe,EACf,MAAc,EACd,IAAU;IAEV,MAAM,IAAI,GAAG,IAAI,QAAQ,EAAE,CAAA;IAC3B,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,CAAA;IAEjC,MAAM,SAAS,GAAG,GAAG,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAA;IACxD,MAAM,SAAS,GAAG,MAAM,KAAK,CAAC,SAAS,EAAE;QACvC,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,OAAO,EAAE,EAAE;QAC/C,IAAI,EAAE,IAAI;QACV,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,iBAAiB,CAAC;KAC/C,CAAC,CAAA;IAEF,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC;QAClB,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAA;QACnD,MAAM,IAAI,KAAK,CAAC,mCAAmC,SAAS,CAAC,MAAM,QAAQ,SAAS,KAAK,IAAI,EAAE,CAAC,CAAA;IAClG,CAAC;IAED,MAAM,UAAU,GAAqE,MAAM,SAAS,CAAC,IAAI,EAAE,CAAA;IAC3G,MAAM,UAAU,GAAG,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,CAAA;IACpC,IAAI,UAAU,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,oDAAoD,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,EAAE,CAAC,CAAA;IACnG,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,CAAA;AAC7E,CAAC;AAED,KAAK,UAAU,sBAAsB,CACnC,aAAqB,EACrB,OAAe,EACf,MAAc,EACd,IAAU;IAEV,MAAM,IAAI,GAAG,IAAI,QAAQ,EAAE,CAAA;IAC3B,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,CAAA;IAEjC,MAAM,SAAS,GAAG,GAAG,kBAAkB,qBAAqB,aAAa,EAAE,CAAA;IAC3E,MAAM,SAAS,GAAG,MAAM,KAAK,CAAC,SAAS,EAAE;QACvC,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,OAAO,EAAE,EAAE;QAC/C,IAAI,EAAE,IAAI;QACV,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,iBAAiB,CAAC;KAC/C,CAAC,CAAA;IAEF,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC;QAClB,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAA;QACnD,MAAM,IAAI,KAAK,CAAC,2BAA2B,SAAS,CAAC,MAAM,MAAM,IAAI,EAAE,CAAC,CAAA;IAC1E,CAAC;IAED,MAAM,UAAU,GAAqE,MAAM,SAAS,CAAC,IAAI,EAAE,CAAA;IAC3G,OAAO,EAAE,EAAE,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,MAAM,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,CAAA;AAC9F,CAAC;AAEM,KAAK,qBAAsB,OAAe,EAAE,aAAqB,EAAE,MAAc;IACtF,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAA;IAC3C,MAAM,GAAG,GAAG,QAAQ,IAAI,IAAI,IAAI,QAAQ,KAAK,EAAE;QAC7C,CAAC,CAAC,GAAG,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,eAAe,kBAAkB,CAAC,MAAM,CAAC,EAAE;QAC3E,CAAC,CAAC,GAAG,kBAAkB,SAAS,kBAAkB,CAAC,aAAa,CAAC,IAAI,kBAAkB,CAAC,MAAM,CAAC,EAAE,CAAA;IAEnG,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;AAC9I,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,26 +1,31 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "huly-mcp-selfhost",
|
|
3
|
-
"version": "3.
|
|
4
|
-
"description": "MCP server for Huly (self-hosted
|
|
3
|
+
"version": "3.3.0",
|
|
4
|
+
"description": "MCP server for Huly (self-hosted and Huly Cloud): 224 tools for Claude and other AI agents — issues, epics, documents, chat, planner, test management — with toolsets for low token use, an agent workflow and a Claude Code plugin.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"huly",
|
|
7
|
+
"huly-selfhost",
|
|
7
8
|
"mcp",
|
|
9
|
+
"mcp-server",
|
|
8
10
|
"model-context-protocol",
|
|
9
11
|
"claude",
|
|
12
|
+
"claude-code",
|
|
13
|
+
"claude-desktop",
|
|
14
|
+
"ai-agents",
|
|
10
15
|
"project-management",
|
|
11
16
|
"issue-tracker",
|
|
12
|
-
"
|
|
17
|
+
"self-hosted"
|
|
13
18
|
],
|
|
14
|
-
"homepage": "https://github.
|
|
19
|
+
"homepage": "https://jokeks2023.github.io/huly-mcp/",
|
|
15
20
|
"repository": {
|
|
16
21
|
"type": "git",
|
|
17
22
|
"url": "git+https://github.com/JoKeks2023/huly-mcp.git"
|
|
18
23
|
},
|
|
19
24
|
"mcpName": "io.github.jokeks2023/huly-mcp-selfhost",
|
|
20
25
|
"license": "EPL-2.0",
|
|
21
|
-
"author": "
|
|
26
|
+
"author": "Joris Conrad (https://github.com/JoKeks2023)",
|
|
22
27
|
"contributors": [
|
|
23
|
-
"
|
|
28
|
+
"SaiVaraprasad Medapati (original huly-mcp-sdk: https://github.com/varaprasadreddy9676/huly-mcp)"
|
|
24
29
|
],
|
|
25
30
|
"bin": {
|
|
26
31
|
"huly-mcp": "dist/index.js",
|
|
@@ -31,13 +36,14 @@
|
|
|
31
36
|
"dist/",
|
|
32
37
|
"scripts/",
|
|
33
38
|
".env.example",
|
|
34
|
-
"README.md"
|
|
39
|
+
"README.md",
|
|
40
|
+
"CHANGELOG.md"
|
|
35
41
|
],
|
|
36
42
|
"engines": {
|
|
37
43
|
"node": ">=20"
|
|
38
44
|
},
|
|
39
45
|
"scripts": {
|
|
40
|
-
"build": "node --max-old-space-size=4096 node_modules/typescript/bin/tsc",
|
|
46
|
+
"build": "node -e \"require('fs').rmSync('dist', { recursive: true, force: true })\" && node --max-old-space-size=4096 node_modules/typescript/bin/tsc && node scripts/copy-guides.js",
|
|
41
47
|
"dev": "tsx watch src/index.ts",
|
|
42
48
|
"start": "node dist/index.js",
|
|
43
49
|
"setup": "node scripts/setup.js",
|
|
@@ -70,5 +76,8 @@
|
|
|
70
76
|
"overrides": {
|
|
71
77
|
"dompurify": "^3.4.17",
|
|
72
78
|
"uuid": "^11.1.1"
|
|
79
|
+
},
|
|
80
|
+
"bugs": {
|
|
81
|
+
"url": "https://github.com/JoKeks2023/huly-mcp/issues"
|
|
73
82
|
}
|
|
74
83
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Build step: the plugin skill files are the one source for the agent guides. They are copied to
|
|
3
|
+
// dist/guides/ (without the skill frontmatter) so every MCP client can read them as huly://guide/*.
|
|
4
|
+
'use strict'
|
|
5
|
+
const fs = require('fs')
|
|
6
|
+
const path = require('path')
|
|
7
|
+
|
|
8
|
+
const root = path.join(__dirname, '..')
|
|
9
|
+
const src = path.join(root, 'plugin', 'skills', 'huly')
|
|
10
|
+
const out = path.join(root, 'dist', 'guides')
|
|
11
|
+
const GUIDES = { workflow: 'SKILL.md', costs: 'costs.md', onboarding: 'onboarding.md', features: 'features.md' }
|
|
12
|
+
|
|
13
|
+
fs.mkdirSync(out, { recursive: true })
|
|
14
|
+
for (const [name, file] of Object.entries(GUIDES)) {
|
|
15
|
+
const text = fs.readFileSync(path.join(src, file), 'utf8').replace(/\r\n/g, '\n').replace(/^---\n[\s\S]*?\n---\n+/, '')
|
|
16
|
+
fs.writeFileSync(path.join(out, `${name}.md`), text)
|
|
17
|
+
}
|
|
18
|
+
console.log(`guides: ${Object.keys(GUIDES).length} copied to dist/guides`)
|
package/scripts/setup.js
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* One-time setup:
|
|
3
|
+
* One-time setup for SSO accounts (Google/GitHub): log in with a one-time code and get a token.
|
|
4
4
|
*
|
|
5
5
|
* Usage:
|
|
6
|
-
*
|
|
6
|
+
* npx huly-mcp-selfhost setup (anywhere — prints the token to paste into your client)
|
|
7
|
+
* npm run setup (in a checkout of this repo — also saves it to .env)
|
|
8
|
+
* ... setup --url https://huly.example.com (self-hosted; or set HULY_ACCOUNTS_URL)
|
|
7
9
|
*
|
|
8
10
|
* What it does:
|
|
9
|
-
* 1.
|
|
10
|
-
* 2.
|
|
11
|
+
* 1. Asks for your Huly address (empty = Huly Cloud) unless HULY_ACCOUNTS_URL / --url is given
|
|
12
|
+
* 2. Sends a one-time code to your email; you paste the code
|
|
11
13
|
* 3. Lists the workspaces your account can access (discovery test)
|
|
12
|
-
* 4.
|
|
13
|
-
*
|
|
14
|
-
* No workspace is configured: the MCP server discovers all workspaces of the
|
|
15
|
-
* account at runtime and every tool takes a `workspace` argument.
|
|
14
|
+
* 4. In a repo checkout: saves HULY_TOKEN to .env. Otherwise: prints what to put where.
|
|
16
15
|
*/
|
|
17
16
|
|
|
18
17
|
'use strict'
|
|
@@ -23,18 +22,47 @@ const readline = require('readline')
|
|
|
23
22
|
|
|
24
23
|
const { getClient } = require('@hcengineering/account-client')
|
|
25
24
|
|
|
26
|
-
const
|
|
27
|
-
const
|
|
25
|
+
const CLOUD_ACCOUNTS = 'https://account.huly.app'
|
|
26
|
+
const root = path.join(__dirname, '..')
|
|
27
|
+
const envFile = path.join(root, '.env')
|
|
28
|
+
// Started via npx the package sits in the npm cache: a .env there would be lost and never read.
|
|
29
|
+
const isCheckout = fs.existsSync(path.join(root, '.git'))
|
|
28
30
|
|
|
31
|
+
// One interface for the whole run: a new one per question would drop lines already buffered (piped input).
|
|
32
|
+
let rl
|
|
33
|
+
const lines = []
|
|
34
|
+
const waiting = []
|
|
29
35
|
function ask (question) {
|
|
30
|
-
|
|
31
|
-
|
|
36
|
+
if (rl == null) {
|
|
37
|
+
rl = readline.createInterface({ input: process.stdin })
|
|
38
|
+
rl.on('line', line => { const next = waiting.shift(); if (next != null) next(line.trim()); else lines.push(line.trim()) })
|
|
39
|
+
rl.on('close', () => { while (waiting.length > 0) waiting.shift()('') })
|
|
40
|
+
}
|
|
41
|
+
process.stdout.write(question)
|
|
42
|
+
if (lines.length > 0) return Promise.resolve(lines.shift())
|
|
43
|
+
return new Promise(resolve => waiting.push(resolve))
|
|
32
44
|
}
|
|
33
45
|
|
|
34
|
-
|
|
46
|
+
/** "https://huly.example.com", ".../_accounts" or empty (cloud) → { accounts, front } */
|
|
47
|
+
function resolveUrls (input) {
|
|
48
|
+
const value = (input ?? '').trim().replace(/\/+$/, '')
|
|
49
|
+
if (value === '' || /(^|\/\/)(app\.)?huly\.app$/.test(value) || value === CLOUD_ACCOUNTS) return { accounts: CLOUD_ACCOUNTS, front: undefined }
|
|
50
|
+
if (!/^https?:\/\//.test(value)) throw new Error(`Not a URL: ${value} (expected e.g. https://huly.example.com)`)
|
|
51
|
+
if (value.endsWith('/_accounts')) return { accounts: value, front: value.slice(0, -'/_accounts'.length) }
|
|
52
|
+
return { accounts: `${value}/_accounts`, front: value }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function urlArg (argv) {
|
|
56
|
+
const i = argv.indexOf('--url')
|
|
57
|
+
if (i >= 0) return argv[i + 1] ?? ''
|
|
58
|
+
const eq = argv.find(a => a.startsWith('--url='))
|
|
59
|
+
return eq != null ? eq.slice('--url='.length) : undefined
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function writeEnv (token, urls) {
|
|
35
63
|
const lines = [
|
|
36
64
|
`HULY_TOKEN=${token}`,
|
|
37
|
-
|
|
65
|
+
urls.accounts !== CLOUD_ACCOUNTS ? `HULY_ACCOUNTS_URL=${urls.accounts}` : null
|
|
38
66
|
].filter(Boolean)
|
|
39
67
|
|
|
40
68
|
// Merge with existing .env (don't overwrite unrelated vars)
|
|
@@ -51,17 +79,38 @@ function writeEnv (token, accountsUrl) {
|
|
|
51
79
|
})
|
|
52
80
|
|
|
53
81
|
fs.writeFileSync(envFile, [...keep.filter(Boolean), ...lines].join('\n') + '\n')
|
|
82
|
+
return /^HULY_WORKSPACE=/m.test(existing)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function printConfig (token, urls) {
|
|
86
|
+
console.log('\n Put these values into your MCP client:')
|
|
87
|
+
console.log(' Claude Code plugin: /plugin → huly → configure (fields "Huly token", "Accounts URL", "Front URL")')
|
|
88
|
+
console.log(' Other clients: the "env" block of the huly server\n')
|
|
89
|
+
console.log(` HULY_TOKEN=${token}`)
|
|
90
|
+
if (urls.accounts !== CLOUD_ACCOUNTS) {
|
|
91
|
+
console.log(` HULY_ACCOUNTS_URL=${urls.accounts}`)
|
|
92
|
+
console.log(` HULY_FRONT_URL=${urls.front}`)
|
|
93
|
+
}
|
|
94
|
+
console.log('\n Keep the token secret — it logs in as you.')
|
|
54
95
|
}
|
|
55
96
|
|
|
56
97
|
async function main () {
|
|
57
98
|
console.log('\n🔧 Huly MCP Setup\n')
|
|
58
99
|
|
|
59
|
-
const
|
|
100
|
+
const fromEnv = (process.env.HULY_ACCOUNTS_URL ?? '').trim()
|
|
101
|
+
const given = urlArg(process.argv.slice(2)) ?? (fromEnv !== '' ? fromEnv : undefined)
|
|
102
|
+
const urls = resolveUrls(given ?? await ask(' Huly address (empty = Huly Cloud, e.g. https://huly.example.com): '))
|
|
103
|
+
if (urls.accounts !== CLOUD_ACCOUNTS) console.log(` Accounts service: ${urls.accounts}`)
|
|
104
|
+
|
|
105
|
+
const email = await ask(' Email address of your Huly account: ')
|
|
60
106
|
if (!email.includes('@')) { console.error('Invalid email.'); process.exit(1) }
|
|
61
107
|
|
|
62
|
-
console.log(`\n Sending
|
|
63
|
-
const unauthClient = getClient(
|
|
64
|
-
await unauthClient.loginOtp(email)
|
|
108
|
+
console.log(`\n Sending a one-time code to ${email}...`)
|
|
109
|
+
const unauthClient = getClient(urls.accounts)
|
|
110
|
+
await unauthClient.loginOtp(email).catch(e => {
|
|
111
|
+
const cause = e.cause?.code ?? e.message
|
|
112
|
+
throw new Error(`Could not reach the Huly accounts service at ${urls.accounts} (${cause}). Check the address — self-hosted: your Huly URL, e.g. https://huly.example.com`)
|
|
113
|
+
})
|
|
65
114
|
console.log(' ✅ Code sent!\n')
|
|
66
115
|
|
|
67
116
|
const code = await ask(' Enter the 6-digit code from your email: ')
|
|
@@ -75,7 +124,7 @@ async function main () {
|
|
|
75
124
|
}
|
|
76
125
|
|
|
77
126
|
// Discovery test: which workspaces can this account use?
|
|
78
|
-
const authedClient = getClient(
|
|
127
|
+
const authedClient = getClient(urls.accounts, loginInfo.token)
|
|
79
128
|
const workspaces = await authedClient.getUserWorkspaces().catch(e => {
|
|
80
129
|
console.error(` ❌ Workspace discovery failed: ${e.message}`)
|
|
81
130
|
process.exit(1)
|
|
@@ -86,22 +135,29 @@ async function main () {
|
|
|
86
135
|
process.exit(1)
|
|
87
136
|
}
|
|
88
137
|
|
|
89
|
-
|
|
90
|
-
writeEnv(loginInfo.token, accountsUrl)
|
|
91
|
-
|
|
92
|
-
console.log(`\n ✅ Setup complete! Token saved to .env`)
|
|
93
|
-
console.log(`\n Workspaces available to this account (${workspaces.length}):`)
|
|
138
|
+
console.log(`\n ✅ Logged in. Workspaces available to this account (${workspaces.length}):`)
|
|
94
139
|
for (const ws of workspaces) {
|
|
95
|
-
console.log(` - ${ws.name ?? ws.url} (slug: ${ws.url}
|
|
140
|
+
console.log(` - ${ws.name ?? ws.url} (slug: ${ws.url})${ws.isDisabled ? ' [disabled]' : ''}`)
|
|
96
141
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
142
|
+
|
|
143
|
+
if (isCheckout) {
|
|
144
|
+
const hadWorkspace = writeEnv(loginInfo.token, urls)
|
|
145
|
+
console.log('\n Token saved to .env')
|
|
146
|
+
if (hadWorkspace) {
|
|
147
|
+
console.log(' ℹ️ Removed HULY_WORKSPACE from .env — the server now uses all workspaces above.')
|
|
148
|
+
console.log(' Re-add it only if you want to restrict this server to a single workspace.')
|
|
149
|
+
}
|
|
150
|
+
} else {
|
|
151
|
+
printConfig(loginInfo.token, urls)
|
|
100
152
|
}
|
|
101
|
-
console.log('\n
|
|
153
|
+
console.log('\n Optional: pin one workspace with HULY_WORKSPACE (a slug above) — fewer tokens per session.\n')
|
|
102
154
|
}
|
|
103
155
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
156
|
+
module.exports = { resolveUrls, urlArg }
|
|
157
|
+
|
|
158
|
+
if (require.main === module || process.env.HULY_SETUP_RUN === '1') {
|
|
159
|
+
main().then(() => rl?.close()).catch(err => {
|
|
160
|
+
console.error('\n❌ Setup failed:', err.message)
|
|
161
|
+
process.exit(1)
|
|
162
|
+
})
|
|
163
|
+
}
|