@hiai-gg/docsmint 0.6.8 → 0.7.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/README.md +90 -99
- package/backend/src/lib/api-keys.ts +14 -6
- package/backend/src/lib/lifecycle-service.ts +397 -73
- package/dist/backend/index.js +13039 -10865
- package/dist/backend-account-runtime-cleanup.js +152 -42
- package/dist/backend-api-key-facade.js +207 -61
- package/dist/backend-pipeline-cancellation.js +144 -42
- package/dist/client.d.ts +12 -2
- package/dist/client.js +83 -30
- package/dist/frontend/{CategoryDialog-CbXBMBZB.js → CategoryDialog-t_WIvFAG.js} +5 -5
- package/dist/frontend/{ConfirmDialog-DgMTQBq4.js → ConfirmDialog-_LlrA--f.js} +3 -3
- package/dist/frontend/{CreateSnapshotDialog-FUq-ZfBH.js → CreateSnapshotDialog-DW3PwAt5.js} +4 -4
- package/dist/frontend/{DatePicker-D6E-EEtZ.js → DatePicker-CAw0wZZK.js} +9 -9
- package/dist/frontend/{DeleteDialog-8k6JYWCx.js → DeleteDialog-L1A3FhMM.js} +1 -1
- package/dist/frontend/{FolderNode-DAAn_T8_.js → FolderNode-DzTqitR8.js} +2 -2
- package/dist/frontend/{HiAiEditor-DSVG3GNd.js → HiAiEditor-B7MK19_j.js} +7 -7
- package/dist/frontend/{SettingsDialog-BDNgf5rv.js → SettingsDialog-CS5L4nnU.js} +13 -13
- package/dist/frontend/ShareDialog-CwFa8mlk.js +281 -0
- package/dist/frontend/{Sidebar-DX08utdV.js → Sidebar-CDaFFmaQ.js} +18 -17
- package/dist/frontend/{TagCreateDialog-Bb0-Vtzh.js → TagCreateDialog-C7Avn2_h.js} +4 -4
- package/dist/frontend/api/attachments.js +1 -1
- package/dist/frontend/api/settings.js +1 -1
- package/dist/frontend/app-shell.js +3 -3
- package/dist/frontend/{attachments-B8AuDjno.js → attachments-B2aH3hXF.js} +10 -9
- package/dist/frontend/{auth-client-Cygmp1I2.js → auth-client-CnDZwPfp.js} +11 -11
- package/dist/frontend/components/category-dialog.js +1 -1
- package/dist/frontend/components/create-snapshot-dialog.js +1 -1
- package/dist/frontend/components/delete-dialog.js +1 -1
- package/dist/frontend/components/editor/compact-editor.js +1 -1
- package/dist/frontend/components/editor/document-editor.js +1 -1
- package/dist/frontend/components/editor/document-title.js +1 -1
- package/dist/frontend/components/editor/markdown-toggle.js +1 -1
- package/dist/frontend/components/folder-node.js +1 -1
- package/dist/frontend/components/save-as-dialog.js +4 -4
- package/dist/frontend/components/settings.js +1 -1
- package/dist/frontend/components/share-dialog.d.ts +2 -1
- package/dist/frontend/components/share-dialog.js +1 -1
- package/dist/frontend/components/sidebar.js +1 -1
- package/dist/frontend/components/tag-create-dialog.js +1 -1
- package/dist/frontend/components/version-history.js +3 -3
- package/dist/frontend/{confirm-dialog-CzmoZw3a.js → confirm-dialog-BW2h2NE8.js} +5 -5
- package/dist/frontend/dashboard.js +1008 -998
- package/dist/frontend/{db-ByrlYfaI.js → db-Bo1t_76T.js} +1 -1
- package/dist/frontend/{dialog-Ce79C9fd.js → dialog-C2iKObEu.js} +7 -7
- package/dist/frontend/{dropdown-menu-CJ_7V3N0.js → dropdown-menu-BXIl2E26.js} +12 -12
- package/dist/frontend/{floating-layer-anchor-S5RSefGN.js → floating-layer-anchor-BGIndLY-.js} +63 -63
- package/dist/frontend/i18n.js +1 -1
- package/dist/frontend/{identity-DyWeNzu_.js → identity-Ynrglr5i.js} +1 -1
- package/dist/frontend/{input-BWlOML_m.js → input-eHFb2qiL.js} +2 -2
- package/dist/frontend/{label-C2HfJNQ2.js → label-_I1iiaNU.js} +2 -2
- package/dist/frontend/messages-DTRe4U2V.js +1093 -0
- package/dist/frontend/offline/identity.js +1 -1
- package/dist/frontend/search.js +3 -3
- package/dist/frontend/{select-value-JCAp2PmU.js → select-value-CtMyrvDJ.js} +18 -18
- package/dist/frontend/{settings-DSrUl4hW.js → settings-BR48ihB4.js} +6 -23
- package/dist/frontend/{textarea-DOAE-h42.js → textarea-2eslfANY.js} +2 -2
- package/dist/frontend/{utils-BPDRytUU.js → utils-CDBqTgoV.js} +1 -1
- package/dist/frontend-ssr/api/attachments.js +1 -1
- package/dist/frontend-ssr/api/settings.js +1 -1
- package/dist/frontend-ssr/app-shell.js +3 -3
- package/dist/frontend-ssr/assets/{CategoryDialog-QHEv_9N4.js → CategoryDialog-B3MrZUho.js} +5 -5
- package/dist/frontend-ssr/assets/{ConfirmDialog-BhAelejz.js → ConfirmDialog-D1BDXXll.js} +3 -3
- package/dist/frontend-ssr/assets/{CreateSnapshotDialog-CI8vXXcN.js → CreateSnapshotDialog-NM0n1Whr.js} +4 -4
- package/dist/frontend-ssr/assets/{DatePicker-D84cJ-Eq.js → DatePicker-Dcok9XVm.js} +4 -4
- package/dist/frontend-ssr/assets/{DeleteDialog-DG9Lm38J.js → DeleteDialog-oHT_p_vG.js} +1 -1
- package/dist/frontend-ssr/assets/{FolderNode-kmilhDjO.js → FolderNode-B84NL8Rg.js} +2 -2
- package/dist/frontend-ssr/assets/{HiAiEditor-CUOJmKzT.js → HiAiEditor-tU0jmp6L.js} +6 -6
- package/dist/frontend-ssr/assets/{SettingsDialog-BjJIiVLc.js → SettingsDialog-Dkp79L1B.js} +13 -13
- package/dist/frontend-ssr/assets/{ShareDialog-C9C-XTm4.js → ShareDialog-B1-30sRR.js} +20 -7
- package/dist/frontend-ssr/assets/{Sidebar-C2sUfGNk.js → Sidebar-CGp45ngo.js} +14 -13
- package/dist/frontend-ssr/assets/{TagCreateDialog-DUPMitlL.js → TagCreateDialog-CGGtYSso.js} +4 -4
- package/dist/frontend-ssr/assets/{attachments-Cj3fQyUp.js → attachments-BdkOp8ha.js} +5 -4
- package/dist/frontend-ssr/assets/{auth-client-x26kqY8Z.js → auth-client-CojKIzqy.js} +13 -13
- package/dist/frontend-ssr/assets/{confirm-dialog-XuyaL8sr.js → confirm-dialog-B-oV5wYk.js} +5 -5
- package/dist/frontend-ssr/assets/{db-CnxeO66n.js → db-BhwJPEkn.js} +1 -1
- package/dist/frontend-ssr/assets/{dialog-BOV1u6zP.js → dialog-1SB5neow.js} +8 -8
- package/dist/frontend-ssr/assets/{dropdown-menu-Dn_vQq9b.js → dropdown-menu-DqoOkdqB.js} +12 -12
- package/dist/frontend-ssr/assets/{floating-layer-anchor-BIAu-252.js → floating-layer-anchor-BoiiIEiK.js} +71 -71
- package/dist/frontend-ssr/assets/{identity-B3uHoDtf.js → identity-DcMMBKWg.js} +1 -1
- package/dist/frontend-ssr/assets/{input-DezGsU3y.js → input-Df-QP3Km.js} +2 -2
- package/dist/frontend-ssr/assets/{label-Bj8-TfFN.js → label-CbUOUz6L.js} +2 -2
- package/dist/frontend-ssr/assets/{messages-BAQzILs4.js → messages-B1TjVIH8.js} +2 -2
- package/dist/frontend-ssr/assets/{select-value-DBhbkNcc.js → select-value-CXEDWrZg.js} +18 -18
- package/dist/frontend-ssr/assets/{settings-Bk5Hk9Vq.js → settings-Mp8EuFSn.js} +3 -21
- package/dist/frontend-ssr/assets/{textarea-Dev-qSEc.js → textarea-CHh8NuSS.js} +2 -2
- package/dist/frontend-ssr/assets/{utils-DtlPPXpJ.js → utils-DtX0ta7y.js} +1 -1
- package/dist/frontend-ssr/components/category-dialog.js +1 -1
- package/dist/frontend-ssr/components/create-snapshot-dialog.js +1 -1
- package/dist/frontend-ssr/components/delete-dialog.js +1 -1
- package/dist/frontend-ssr/components/editor/compact-editor.js +1 -1
- package/dist/frontend-ssr/components/editor/document-editor.js +1 -1
- package/dist/frontend-ssr/components/editor/document-title.js +1 -1
- package/dist/frontend-ssr/components/editor/markdown-toggle.js +1 -1
- package/dist/frontend-ssr/components/folder-node.js +1 -1
- package/dist/frontend-ssr/components/save-as-dialog.js +4 -4
- package/dist/frontend-ssr/components/settings.js +1 -1
- package/dist/frontend-ssr/components/share-dialog.js +1 -1
- package/dist/frontend-ssr/components/sidebar.js +1 -1
- package/dist/frontend-ssr/components/tag-create-dialog.js +1 -1
- package/dist/frontend-ssr/components/version-history.js +3 -3
- package/dist/frontend-ssr/dashboard.js +32 -30
- package/dist/frontend-ssr/i18n.js +1 -1
- package/dist/frontend-ssr/offline/identity.js +1 -1
- package/dist/frontend-ssr/search.js +3 -3
- package/dist/index.d.ts +5 -5
- package/dist/index.js +2 -2
- package/dist/lifecycle-persistent.d.ts +1 -1
- package/dist/lifecycle-runtime.js +52659 -338
- package/dist/mcp-cli.js +1056 -0
- package/dist/mcp-server.d.ts +39 -0
- package/dist/mcp-server.js +1053 -0
- package/dist/storage-quota.d.ts +5 -1
- package/dist/types.d.ts +7 -0
- package/dist/workspace.d.ts +11 -2
- package/dist/workspace.js +82 -10
- package/package.json +7 -13
- package/packages/cli/src/index.ts +4 -3
- package/packages/db/src/client.ts +56 -5
- package/packages/db/src/schema.ts +192 -1
- package/packages/db/src/with-tenant.ts +20 -2
- package/server.json +42 -5
- package/dist/frontend/ShareDialog-2Mty7hm4.js +0 -258
- package/dist/frontend/messages-DIovIZrK.js +0 -1093
- package/packages/mcp-server/src/capabilities.ts +0 -189
- package/packages/mcp-server/src/client.ts +0 -225
- package/packages/mcp-server/src/index.ts +0 -8
- package/packages/mcp-server/src/server.ts +0 -87
- package/packages/mcp-server/src/tools/create-document.ts +0 -34
- package/packages/mcp-server/src/tools/create-folder.ts +0 -31
- package/packages/mcp-server/src/tools/create-snapshot.ts +0 -27
- package/packages/mcp-server/src/tools/export-document.ts +0 -22
- package/packages/mcp-server/src/tools/get-document.ts +0 -22
- package/packages/mcp-server/src/tools/list-documents.ts +0 -34
- package/packages/mcp-server/src/tools/list-folders.ts +0 -26
- package/packages/mcp-server/src/tools/search.ts +0 -39
- package/packages/mcp-server/src/tools/update-document.ts +0 -38
- package/packages/mcp-server/src/tools/version-history.ts +0 -27
- package/packages/mcp-server/src/types.ts +0 -126
package/dist/mcp-cli.js
ADDED
|
@@ -0,0 +1,1056 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
// @bun
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __returnValue = (v) => v;
|
|
5
|
+
function __exportSetter(name, newValue) {
|
|
6
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
7
|
+
}
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, {
|
|
11
|
+
get: all[name],
|
|
12
|
+
enumerable: true,
|
|
13
|
+
configurable: true,
|
|
14
|
+
set: __exportSetter.bind(all, name)
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// ../mcp-server/src/index.ts
|
|
19
|
+
import { serveStdio } from "@modelcontextprotocol/server/stdio";
|
|
20
|
+
|
|
21
|
+
// ../mcp-server/src/server.ts
|
|
22
|
+
import { McpServer } from "@modelcontextprotocol/server";
|
|
23
|
+
import { z as z12 } from "zod";
|
|
24
|
+
|
|
25
|
+
// dist/client.js
|
|
26
|
+
var docsApiErrorBrand = Symbol.for("io.github.hiai-gg.docsmint.DocsApiError");
|
|
27
|
+
|
|
28
|
+
class DocsApiError extends Error {
|
|
29
|
+
status;
|
|
30
|
+
code;
|
|
31
|
+
body;
|
|
32
|
+
url;
|
|
33
|
+
requestId;
|
|
34
|
+
constructor(status, body, message, metadata, code) {
|
|
35
|
+
super(message ?? `hiai-docs API error ${status}`);
|
|
36
|
+
this.name = "DocsApiError";
|
|
37
|
+
this.status = status;
|
|
38
|
+
this.code = code ?? DocsApiError.codeFromBody(status, body);
|
|
39
|
+
this.body = body;
|
|
40
|
+
this.url = metadata?.url;
|
|
41
|
+
this.requestId = metadata?.requestId;
|
|
42
|
+
Object.defineProperty(this, docsApiErrorBrand, { value: true });
|
|
43
|
+
}
|
|
44
|
+
static codeFromBody(status, body) {
|
|
45
|
+
return body && typeof body === "object" && "code" in body && typeof body.code === "string" ? body.code : `http_${status}`;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function isDocsApiError(error) {
|
|
49
|
+
if (!(error instanceof Error))
|
|
50
|
+
return false;
|
|
51
|
+
const candidate = error;
|
|
52
|
+
return candidate[docsApiErrorBrand] === true && candidate.name === "DocsApiError" && Number.isInteger(candidate.status) && candidate.status >= 300 && candidate.status <= 599 && typeof candidate.code === "string" && candidate.code.length > 0 && Object.hasOwn(candidate, "body");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
class DocsNetworkError extends Error {
|
|
56
|
+
requestId;
|
|
57
|
+
constructor(message, options) {
|
|
58
|
+
super(message, { cause: options?.cause });
|
|
59
|
+
this.name = "DocsNetworkError";
|
|
60
|
+
this.requestId = options?.requestId;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
class DocsTimeoutError extends DocsNetworkError {
|
|
65
|
+
timeout;
|
|
66
|
+
constructor(timeout, options) {
|
|
67
|
+
super(`hiai-docs request timed out after ${timeout}ms`, options);
|
|
68
|
+
this.name = "DocsTimeoutError";
|
|
69
|
+
this.timeout = timeout;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
class DocsClient {
|
|
74
|
+
config;
|
|
75
|
+
constructor(config) {
|
|
76
|
+
if (!config.baseUrl) {
|
|
77
|
+
throw new Error("DocsClient: `baseUrl` is required");
|
|
78
|
+
}
|
|
79
|
+
this.config = {
|
|
80
|
+
baseUrl: config.baseUrl.replace(/\/+$/, ""),
|
|
81
|
+
apiKey: config.apiKey,
|
|
82
|
+
requestContext: this.snapshotContext(config.requestContext),
|
|
83
|
+
fetch: config.fetch ?? fetch,
|
|
84
|
+
timeout: config.timeout ?? 1e4,
|
|
85
|
+
retries: config.retries ?? 3,
|
|
86
|
+
retryBackoffMs: config.retryBackoffMs ?? 250
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
withRequestContext(context) {
|
|
90
|
+
return new DocsClient({
|
|
91
|
+
...this.config,
|
|
92
|
+
requestContext: this.mergeContext(this.config.requestContext, context)
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
async createDoc(input, context) {
|
|
96
|
+
return this.request("POST", "/api/documents", { json: input }, context);
|
|
97
|
+
}
|
|
98
|
+
async getDoc(id, context) {
|
|
99
|
+
return this.request("GET", `/api/documents/${encodeURIComponent(id)}`, undefined, context);
|
|
100
|
+
}
|
|
101
|
+
async getDocMarkdown(id, context) {
|
|
102
|
+
const res = await this.fetchRaw("GET", `/api/documents/${encodeURIComponent(id)}/export`, undefined, context);
|
|
103
|
+
if (!res.ok) {
|
|
104
|
+
throw await this.toApiError(res);
|
|
105
|
+
}
|
|
106
|
+
return res.text();
|
|
107
|
+
}
|
|
108
|
+
async updateDoc(id, updates, context) {
|
|
109
|
+
return this.request("PATCH", `/api/documents/${encodeURIComponent(id)}`, {
|
|
110
|
+
json: updates
|
|
111
|
+
}, context);
|
|
112
|
+
}
|
|
113
|
+
async deleteDoc(id, context) {
|
|
114
|
+
await this.request("DELETE", `/api/documents/${encodeURIComponent(id)}`, undefined, context);
|
|
115
|
+
}
|
|
116
|
+
async listDocs(options, context) {
|
|
117
|
+
return this.request("GET", "/api/documents", {
|
|
118
|
+
query: this.cleanQuery({
|
|
119
|
+
folderId: options?.folderId,
|
|
120
|
+
tag: options?.tag,
|
|
121
|
+
page: options?.page,
|
|
122
|
+
limit: options?.limit
|
|
123
|
+
})
|
|
124
|
+
}, context);
|
|
125
|
+
}
|
|
126
|
+
async listDocuments(options = {}, context) {
|
|
127
|
+
return this.request("GET", "/api/documents/cursor", {
|
|
128
|
+
query: this.cleanQuery({
|
|
129
|
+
categoryId: options.categoryId,
|
|
130
|
+
cursor: options.cursor,
|
|
131
|
+
limit: options.limit,
|
|
132
|
+
sortBy: options.sortBy,
|
|
133
|
+
sortOrder: options.sortOrder
|
|
134
|
+
})
|
|
135
|
+
}, context);
|
|
136
|
+
}
|
|
137
|
+
async duplicateDoc(id, context) {
|
|
138
|
+
return this.request("POST", `/api/documents/${encodeURIComponent(id)}/duplicate`, undefined, context);
|
|
139
|
+
}
|
|
140
|
+
async getDocumentPipeline(id, context) {
|
|
141
|
+
return this.request("GET", `/api/documents/${encodeURIComponent(id)}/pipeline`, undefined, context);
|
|
142
|
+
}
|
|
143
|
+
async getDocumentKnowledgeSummary(id, context) {
|
|
144
|
+
return this.request("GET", `/api/documents/${encodeURIComponent(id)}/knowledge-summary`, undefined, context);
|
|
145
|
+
}
|
|
146
|
+
async getDocumentIndexStatus(id, context) {
|
|
147
|
+
return this.request("GET", `/api/documents/${encodeURIComponent(id)}/index-status`, undefined, context);
|
|
148
|
+
}
|
|
149
|
+
async refreshDocumentIndex(id, context) {
|
|
150
|
+
return this.request("POST", `/api/documents/${encodeURIComponent(id)}/index/refresh`, undefined, context);
|
|
151
|
+
}
|
|
152
|
+
async publishDoc(id, context) {
|
|
153
|
+
return this.request("POST", `/api/documents/${encodeURIComponent(id)}/publish`, undefined, context);
|
|
154
|
+
}
|
|
155
|
+
async unpublishDoc(id, context) {
|
|
156
|
+
return this.request("POST", `/api/documents/${encodeURIComponent(id)}/unpublish`, undefined, context);
|
|
157
|
+
}
|
|
158
|
+
async exportDoc(id, context) {
|
|
159
|
+
return this.getDocMarkdown(id, context);
|
|
160
|
+
}
|
|
161
|
+
async importDoc(input, context) {
|
|
162
|
+
return this.request("POST", "/api/documents/import", { json: input }, context);
|
|
163
|
+
}
|
|
164
|
+
async listFolders(parentId, context) {
|
|
165
|
+
return this.request("GET", "/api/folders", {
|
|
166
|
+
query: this.cleanQuery({ parentId })
|
|
167
|
+
}, context);
|
|
168
|
+
}
|
|
169
|
+
async getFolder(id, context) {
|
|
170
|
+
return this.request("GET", `/api/folders/${encodeURIComponent(id)}`, undefined, context);
|
|
171
|
+
}
|
|
172
|
+
async createFolder(input, context) {
|
|
173
|
+
return this.request("POST", "/api/folders", {
|
|
174
|
+
json: input
|
|
175
|
+
}, context);
|
|
176
|
+
}
|
|
177
|
+
async updateFolder(id, updates, context) {
|
|
178
|
+
return this.request("PATCH", `/api/folders/${encodeURIComponent(id)}`, {
|
|
179
|
+
json: updates
|
|
180
|
+
}, context);
|
|
181
|
+
}
|
|
182
|
+
async deleteFolder(id, context) {
|
|
183
|
+
await this.request("DELETE", `/api/folders/${encodeURIComponent(id)}`, undefined, context);
|
|
184
|
+
}
|
|
185
|
+
async listTags(context) {
|
|
186
|
+
return this.request("GET", "/api/tags", undefined, context);
|
|
187
|
+
}
|
|
188
|
+
async createTag(input, context) {
|
|
189
|
+
return this.request("POST", "/api/tags", { json: input }, context);
|
|
190
|
+
}
|
|
191
|
+
async updateTag(id, updates, context) {
|
|
192
|
+
return this.request("PATCH", `/api/tags/${encodeURIComponent(id)}`, {
|
|
193
|
+
json: updates
|
|
194
|
+
}, context);
|
|
195
|
+
}
|
|
196
|
+
async deleteTag(id, context) {
|
|
197
|
+
await this.request("DELETE", `/api/tags/${encodeURIComponent(id)}`, undefined, context);
|
|
198
|
+
}
|
|
199
|
+
async addTagToDoc(documentId, tagId, context) {
|
|
200
|
+
await this.request("POST", `/api/documents/${encodeURIComponent(documentId)}/tags`, { json: { tagId } }, context);
|
|
201
|
+
}
|
|
202
|
+
async removeTagFromDoc(documentId, tagId, context) {
|
|
203
|
+
await this.request("DELETE", `/api/documents/${encodeURIComponent(documentId)}/tags/${encodeURIComponent(tagId)}`, undefined, context);
|
|
204
|
+
}
|
|
205
|
+
async listCategories(context) {
|
|
206
|
+
return this.request("GET", "/api/categories", undefined, context);
|
|
207
|
+
}
|
|
208
|
+
async createCategory(input, context) {
|
|
209
|
+
return this.request("POST", "/api/categories", { json: input }, context);
|
|
210
|
+
}
|
|
211
|
+
async updateCategory(id, updates, context) {
|
|
212
|
+
return this.request("PATCH", `/api/categories/${encodeURIComponent(id)}`, { json: updates }, context);
|
|
213
|
+
}
|
|
214
|
+
async deleteCategory(id, context) {
|
|
215
|
+
await this.request("DELETE", `/api/categories/${encodeURIComponent(id)}`, undefined, context);
|
|
216
|
+
}
|
|
217
|
+
async createGlobalApiKey(name, context) {
|
|
218
|
+
return this.request("POST", "/api/keys/global", { json: name ? { name } : {} }, context);
|
|
219
|
+
}
|
|
220
|
+
async createCategoryApiKey(categoryId, name, context) {
|
|
221
|
+
return this.request("POST", `/api/categories/${encodeURIComponent(categoryId)}/keys`, { json: name ? { name } : {} }, context);
|
|
222
|
+
}
|
|
223
|
+
async listApiKeys(context) {
|
|
224
|
+
return this.request("GET", "/api/keys", undefined, context);
|
|
225
|
+
}
|
|
226
|
+
async revealCategoryApiKey(id, context) {
|
|
227
|
+
return this.request("GET", `/api/keys/${encodeURIComponent(id)}/secret`, undefined, context);
|
|
228
|
+
}
|
|
229
|
+
async revokeApiKey(id, context) {
|
|
230
|
+
return this.request("DELETE", `/api/keys/${encodeURIComponent(id)}`, undefined, context);
|
|
231
|
+
}
|
|
232
|
+
async search(query, options, context) {
|
|
233
|
+
return this.request("GET", "/api/search", {
|
|
234
|
+
query: this.cleanQuery({
|
|
235
|
+
q: query,
|
|
236
|
+
folder: options?.folder,
|
|
237
|
+
tags: options?.tags,
|
|
238
|
+
category: options?.category,
|
|
239
|
+
dateFrom: options?.dateFrom,
|
|
240
|
+
dateTo: options?.dateTo,
|
|
241
|
+
sort: options?.sort,
|
|
242
|
+
page: options?.page,
|
|
243
|
+
limit: options?.limit,
|
|
244
|
+
graph: options?.graph,
|
|
245
|
+
graphHops: options?.graphHops,
|
|
246
|
+
graphBoost: options?.graphBoost,
|
|
247
|
+
includeChunks: options?.includeChunks
|
|
248
|
+
})
|
|
249
|
+
}, context);
|
|
250
|
+
}
|
|
251
|
+
async searchDocuments(input, context) {
|
|
252
|
+
return this.search(input.query, { ...input.options, graph: input.retrievalMode === "graph" }, context);
|
|
253
|
+
}
|
|
254
|
+
async suggest(query, context) {
|
|
255
|
+
return this.request("GET", "/api/search/suggest", {
|
|
256
|
+
query: this.cleanQuery({ q: query })
|
|
257
|
+
}, context);
|
|
258
|
+
}
|
|
259
|
+
async getGraphEntities(docId, context) {
|
|
260
|
+
return this.request("GET", "/api/graph/entities", {
|
|
261
|
+
query: this.cleanQuery({ docId })
|
|
262
|
+
}, context);
|
|
263
|
+
}
|
|
264
|
+
async listGraphEntities(docId, context) {
|
|
265
|
+
return this.getGraphEntities(docId, context);
|
|
266
|
+
}
|
|
267
|
+
async getRelatedDocuments(docId, context, options) {
|
|
268
|
+
return this.request("GET", `/api/graph/related/${encodeURIComponent(docId)}`, { query: this.cleanQuery({ limit: options?.limit }) }, context);
|
|
269
|
+
}
|
|
270
|
+
async listRelatedDocuments(docId, context) {
|
|
271
|
+
return this.getRelatedDocuments(docId, context);
|
|
272
|
+
}
|
|
273
|
+
async graphSearch(input, context) {
|
|
274
|
+
return this.request("POST", "/api/graph/search", {
|
|
275
|
+
json: input
|
|
276
|
+
}, context);
|
|
277
|
+
}
|
|
278
|
+
async searchGraph(input, context) {
|
|
279
|
+
return this.graphSearch(input, context);
|
|
280
|
+
}
|
|
281
|
+
async createShare(input, context) {
|
|
282
|
+
return this.request("POST", "/api/share", { json: input }, context);
|
|
283
|
+
}
|
|
284
|
+
async listShares(context) {
|
|
285
|
+
return this.request("GET", "/api/share", undefined, context);
|
|
286
|
+
}
|
|
287
|
+
async deleteShare(id, context) {
|
|
288
|
+
await this.request("DELETE", `/api/share/${encodeURIComponent(id)}`, undefined, context);
|
|
289
|
+
}
|
|
290
|
+
async updateShare(id, updates, context) {
|
|
291
|
+
return this.request("PATCH", `/api/share/${encodeURIComponent(id)}`, { json: updates }, context);
|
|
292
|
+
}
|
|
293
|
+
async getShareByToken(token, context) {
|
|
294
|
+
return this.request("GET", `/api/share/${encodeURIComponent(token)}`, undefined, context);
|
|
295
|
+
}
|
|
296
|
+
async uploadAttachment(documentId, file, filename, mimeType, context) {
|
|
297
|
+
const form = new FormData;
|
|
298
|
+
const blob = this.toBlob(file, mimeType);
|
|
299
|
+
form.append("file", blob, filename);
|
|
300
|
+
const res = await this.fetchRaw("POST", `/api/documents/${encodeURIComponent(documentId)}/attachments`, { body: form }, context);
|
|
301
|
+
if (!res.ok) {
|
|
302
|
+
throw await this.toApiError(res);
|
|
303
|
+
}
|
|
304
|
+
return await res.json();
|
|
305
|
+
}
|
|
306
|
+
async presignAttachment(documentId, input, context) {
|
|
307
|
+
return this.request("POST", `/api/documents/${encodeURIComponent(documentId)}/attachments/presign`, { json: input }, context);
|
|
308
|
+
}
|
|
309
|
+
async confirmAttachment(documentId, input, context) {
|
|
310
|
+
return this.request("POST", `/api/documents/${encodeURIComponent(documentId)}/attachments/confirm`, { json: input }, context);
|
|
311
|
+
}
|
|
312
|
+
async listAttachments(documentId, context) {
|
|
313
|
+
return this.request("GET", `/api/documents/${encodeURIComponent(documentId)}/attachments`, undefined, context);
|
|
314
|
+
}
|
|
315
|
+
async deleteAttachment(id, context) {
|
|
316
|
+
await this.request("DELETE", `/api/attachments/${encodeURIComponent(id)}`, undefined, context);
|
|
317
|
+
}
|
|
318
|
+
async listVersions(documentId, options, context) {
|
|
319
|
+
return this.request("GET", `/api/documents/${encodeURIComponent(documentId)}/versions`, {
|
|
320
|
+
query: this.cleanQuery({
|
|
321
|
+
onlySnapshots: options?.onlySnapshots,
|
|
322
|
+
limit: options?.limit
|
|
323
|
+
})
|
|
324
|
+
}, context);
|
|
325
|
+
}
|
|
326
|
+
async getVersion(documentId, versionId, context) {
|
|
327
|
+
return this.request("GET", `/api/documents/${encodeURIComponent(documentId)}/versions/${encodeURIComponent(versionId)}`, undefined, context);
|
|
328
|
+
}
|
|
329
|
+
async createSnapshot(documentId, input, context) {
|
|
330
|
+
return this.request("POST", `/api/documents/${encodeURIComponent(documentId)}/versions`, { json: input }, context);
|
|
331
|
+
}
|
|
332
|
+
async restoreVersion(documentId, versionId, context) {
|
|
333
|
+
return this.request("POST", `/api/documents/${encodeURIComponent(documentId)}/versions/${encodeURIComponent(versionId)}/restore`, undefined, context);
|
|
334
|
+
}
|
|
335
|
+
async diffVersions(documentId, from, to, context) {
|
|
336
|
+
return this.request("GET", `/api/documents/${encodeURIComponent(documentId)}/versions/diff`, { query: { from, to } }, context);
|
|
337
|
+
}
|
|
338
|
+
async health(context) {
|
|
339
|
+
return this.request("GET", "/api/health", undefined, context);
|
|
340
|
+
}
|
|
341
|
+
async request(method, path, options, context) {
|
|
342
|
+
const res = await this.fetchRaw(method, path, {
|
|
343
|
+
json: options?.json,
|
|
344
|
+
query: options?.query
|
|
345
|
+
}, context);
|
|
346
|
+
if (!res.ok) {
|
|
347
|
+
throw await this.toApiError(res);
|
|
348
|
+
}
|
|
349
|
+
const contentType = res.headers.get("content-type") ?? "";
|
|
350
|
+
if (res.status === 204 || res.body === null) {
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
if (contentType.includes("application/json")) {
|
|
354
|
+
return await res.json();
|
|
355
|
+
}
|
|
356
|
+
return await res.text();
|
|
357
|
+
}
|
|
358
|
+
async fetchRaw(method, path, options, context) {
|
|
359
|
+
const url = this.buildUrl(path, options?.query);
|
|
360
|
+
const requestContext = this.mergeContext(this.config.requestContext, context);
|
|
361
|
+
const headers = new Headers(requestContext?.headers);
|
|
362
|
+
const hasWorkspaceAssertion = Boolean(requestContext?.workspaceAssertion);
|
|
363
|
+
if (hasWorkspaceAssertion) {
|
|
364
|
+
headers.delete("Authorization");
|
|
365
|
+
headers.delete("Cookie");
|
|
366
|
+
}
|
|
367
|
+
if (this.config.apiKey && (hasWorkspaceAssertion || !headers.has("Authorization"))) {
|
|
368
|
+
headers.set("Authorization", `Bearer ${this.config.apiKey}`);
|
|
369
|
+
}
|
|
370
|
+
if (!hasWorkspaceAssertion && requestContext?.authorization)
|
|
371
|
+
headers.set("Authorization", requestContext.authorization);
|
|
372
|
+
if (!hasWorkspaceAssertion && requestContext?.cookie)
|
|
373
|
+
headers.set("Cookie", requestContext.cookie);
|
|
374
|
+
if (requestContext?.requestId)
|
|
375
|
+
headers.set("X-Request-Id", requestContext.requestId);
|
|
376
|
+
if (requestContext?.idempotencyKey)
|
|
377
|
+
headers.set("Idempotency-Key", requestContext.idempotencyKey);
|
|
378
|
+
if (requestContext?.workspaceAssertion) {
|
|
379
|
+
headers.set("X-Docsmint-Workspace-Context", requestContext.workspaceAssertion);
|
|
380
|
+
}
|
|
381
|
+
let body;
|
|
382
|
+
if (options?.body !== undefined) {
|
|
383
|
+
body = options.body;
|
|
384
|
+
} else if (options?.json !== undefined) {
|
|
385
|
+
body = JSON.stringify(options.json);
|
|
386
|
+
headers.set("Content-Type", "application/json");
|
|
387
|
+
}
|
|
388
|
+
const init = { method, headers };
|
|
389
|
+
if (body !== undefined)
|
|
390
|
+
init.body = body;
|
|
391
|
+
let lastError = null;
|
|
392
|
+
const maxAttempts = Math.max(1, this.config.retries);
|
|
393
|
+
const timeoutMs = requestContext?.timeoutMs ?? this.config.timeout;
|
|
394
|
+
for (let attempt = 0;attempt < maxAttempts; attempt++) {
|
|
395
|
+
const timeoutController = new AbortController;
|
|
396
|
+
const timeoutTimer = setTimeout(() => {
|
|
397
|
+
timeoutController.abort(new DOMException("timed out", "TimeoutError"));
|
|
398
|
+
}, timeoutMs);
|
|
399
|
+
const signal = requestContext?.signal ? AbortSignal.any([requestContext.signal, timeoutController.signal]) : timeoutController.signal;
|
|
400
|
+
let response;
|
|
401
|
+
let requestError;
|
|
402
|
+
try {
|
|
403
|
+
response = await this.config.fetch(url, { ...init, signal });
|
|
404
|
+
} catch (err) {
|
|
405
|
+
requestError = err;
|
|
406
|
+
} finally {
|
|
407
|
+
clearTimeout(timeoutTimer);
|
|
408
|
+
}
|
|
409
|
+
if (response) {
|
|
410
|
+
if (this.shouldRetryStatus(response.status) && attempt < maxAttempts - 1) {
|
|
411
|
+
await this.sleep(this.backoffDelay(attempt), requestContext?.signal);
|
|
412
|
+
continue;
|
|
413
|
+
}
|
|
414
|
+
return response;
|
|
415
|
+
}
|
|
416
|
+
lastError = requestError;
|
|
417
|
+
if (requestContext?.signal?.aborted) {
|
|
418
|
+
throw requestContext.signal.reason ?? requestError;
|
|
419
|
+
}
|
|
420
|
+
if (!this.isRetryableError(requestError) || attempt === maxAttempts - 1) {
|
|
421
|
+
if (this.isTimeoutError(requestError)) {
|
|
422
|
+
throw new DocsTimeoutError(timeoutMs, {
|
|
423
|
+
cause: requestError,
|
|
424
|
+
requestId: requestContext?.requestId
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
throw this.wrapNetworkError(requestError, requestContext?.requestId);
|
|
428
|
+
}
|
|
429
|
+
await this.sleep(this.backoffDelay(attempt), requestContext?.signal);
|
|
430
|
+
}
|
|
431
|
+
throw this.wrapNetworkError(lastError, requestContext?.requestId);
|
|
432
|
+
}
|
|
433
|
+
buildUrl(path, query) {
|
|
434
|
+
let url = `${this.config.baseUrl}${path}`;
|
|
435
|
+
if (query && Object.keys(query).length > 0) {
|
|
436
|
+
const params = new URLSearchParams;
|
|
437
|
+
for (const [key, value] of Object.entries(query)) {
|
|
438
|
+
if (value === undefined || value === null)
|
|
439
|
+
continue;
|
|
440
|
+
params.append(key, String(value));
|
|
441
|
+
}
|
|
442
|
+
const qs = params.toString();
|
|
443
|
+
if (qs)
|
|
444
|
+
url += (url.includes("?") ? "&" : "?") + qs;
|
|
445
|
+
}
|
|
446
|
+
return url;
|
|
447
|
+
}
|
|
448
|
+
cleanQuery(query) {
|
|
449
|
+
const out = {};
|
|
450
|
+
for (const [key, value] of Object.entries(query)) {
|
|
451
|
+
if (value === undefined || value === null)
|
|
452
|
+
continue;
|
|
453
|
+
out[key] = value;
|
|
454
|
+
}
|
|
455
|
+
return out;
|
|
456
|
+
}
|
|
457
|
+
shouldRetryStatus(status) {
|
|
458
|
+
return status === 502 || status === 503 || status === 504;
|
|
459
|
+
}
|
|
460
|
+
isRetryableError(err) {
|
|
461
|
+
if (!(err instanceof Error))
|
|
462
|
+
return false;
|
|
463
|
+
if (err.name === "TimeoutError" || err.name === "AbortError")
|
|
464
|
+
return true;
|
|
465
|
+
const cause = err.cause;
|
|
466
|
+
if (cause?.code === "ECONNRESET")
|
|
467
|
+
return true;
|
|
468
|
+
if (cause?.code === "ECONNREFUSED")
|
|
469
|
+
return true;
|
|
470
|
+
if (cause?.code === "ETIMEDOUT")
|
|
471
|
+
return true;
|
|
472
|
+
return false;
|
|
473
|
+
}
|
|
474
|
+
backoffDelay(attempt) {
|
|
475
|
+
const base = this.config.retryBackoffMs * 2 ** attempt;
|
|
476
|
+
const jitter = Math.random() * base * 0.25;
|
|
477
|
+
return Math.floor(base + jitter);
|
|
478
|
+
}
|
|
479
|
+
async sleep(ms, signal) {
|
|
480
|
+
if (signal?.aborted) {
|
|
481
|
+
throw signal.reason ?? new DOMException("The operation was aborted", "AbortError");
|
|
482
|
+
}
|
|
483
|
+
await new Promise((resolve, reject) => {
|
|
484
|
+
const timer = setTimeout(() => {
|
|
485
|
+
signal?.removeEventListener("abort", onAbort);
|
|
486
|
+
resolve();
|
|
487
|
+
}, ms);
|
|
488
|
+
const onAbort = () => {
|
|
489
|
+
clearTimeout(timer);
|
|
490
|
+
signal?.removeEventListener("abort", onAbort);
|
|
491
|
+
reject(signal?.reason ?? new DOMException("The operation was aborted", "AbortError"));
|
|
492
|
+
};
|
|
493
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
async toApiError(res) {
|
|
497
|
+
const contentType = res.headers.get("content-type") ?? "";
|
|
498
|
+
let body;
|
|
499
|
+
try {
|
|
500
|
+
body = contentType.includes("application/json") ? await res.json() : await res.text();
|
|
501
|
+
} catch {
|
|
502
|
+
body = null;
|
|
503
|
+
}
|
|
504
|
+
const message = body && typeof body === "object" && "error" in body && typeof body.error === "string" ? body.error : body && typeof body === "object" && ("message" in body) && typeof body.message === "string" ? body.message : `hiai-docs API error ${res.status}`;
|
|
505
|
+
return new DocsApiError(res.status, body, message, {
|
|
506
|
+
url: res.url || undefined,
|
|
507
|
+
requestId: res.headers.get("x-request-id") ?? undefined
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
wrapNetworkError(err, requestId) {
|
|
511
|
+
if (err instanceof Error) {
|
|
512
|
+
return new DocsNetworkError(`hiai-docs network error: ${err.message}`, {
|
|
513
|
+
cause: err,
|
|
514
|
+
requestId
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
return new DocsNetworkError(`hiai-docs network error: ${String(err)}`, {
|
|
518
|
+
requestId
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
isTimeoutError(err) {
|
|
522
|
+
return err instanceof Error && (err.name === "TimeoutError" || err.name === "AbortError");
|
|
523
|
+
}
|
|
524
|
+
mergeContext(base, override) {
|
|
525
|
+
if (!base && !override)
|
|
526
|
+
return;
|
|
527
|
+
return {
|
|
528
|
+
...base,
|
|
529
|
+
...override,
|
|
530
|
+
headers: {
|
|
531
|
+
...base?.headers ? Object.fromEntries(new Headers(base.headers).entries()) : {},
|
|
532
|
+
...override?.headers ? Object.fromEntries(new Headers(override.headers).entries()) : {}
|
|
533
|
+
}
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
snapshotContext(context) {
|
|
537
|
+
if (!context)
|
|
538
|
+
return;
|
|
539
|
+
const headers = Object.freeze(Object.fromEntries(new Headers(context.headers).entries()));
|
|
540
|
+
return Object.freeze({ ...context, headers });
|
|
541
|
+
}
|
|
542
|
+
toBlob(file, mimeType) {
|
|
543
|
+
if (file instanceof Blob) {
|
|
544
|
+
if (file.type && file.type !== mimeType) {
|
|
545
|
+
return new Blob([file], { type: mimeType });
|
|
546
|
+
}
|
|
547
|
+
return file;
|
|
548
|
+
}
|
|
549
|
+
if (file instanceof ArrayBuffer) {
|
|
550
|
+
return new Blob([file], { type: mimeType });
|
|
551
|
+
}
|
|
552
|
+
if (file instanceof Uint8Array) {
|
|
553
|
+
const copy = new Uint8Array(file.byteLength);
|
|
554
|
+
copy.set(file);
|
|
555
|
+
return new Blob([copy], { type: mimeType });
|
|
556
|
+
}
|
|
557
|
+
throw new Error("DocsClient.uploadAttachment: unsupported file type");
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
// dist/workspace.js
|
|
561
|
+
var encoder = new TextEncoder;
|
|
562
|
+
var decoder = new TextDecoder;
|
|
563
|
+
var CONTEXT_FIELDS = new Set([
|
|
564
|
+
"actorUserId",
|
|
565
|
+
"workspaceId",
|
|
566
|
+
"actorRole",
|
|
567
|
+
"resourceScope",
|
|
568
|
+
"issuedAt",
|
|
569
|
+
"expiresAt",
|
|
570
|
+
"issuer"
|
|
571
|
+
]);
|
|
572
|
+
var RESOURCE_SCOPE_FIELDS = new Set(["kind", "categoryId", "permissions"]);
|
|
573
|
+
var RESOURCE_PERMISSIONS = new Set([
|
|
574
|
+
"read",
|
|
575
|
+
"edit",
|
|
576
|
+
"write"
|
|
577
|
+
]);
|
|
578
|
+
// ../mcp-server/src/capabilities.ts
|
|
579
|
+
import { z } from "zod";
|
|
580
|
+
function registerExtendedCapabilities(server, client, wrapHandler) {
|
|
581
|
+
server.registerTool("list_categories", {
|
|
582
|
+
description: "List categories visible to the API key. Category keys receive only their bound category.",
|
|
583
|
+
inputSchema: z.object({})
|
|
584
|
+
}, wrapHandler(async () => client.listCategories()));
|
|
585
|
+
server.registerTool("create_category", {
|
|
586
|
+
description: "Create a category. Requires a workspace key with write access; category keys cannot mutate categories.",
|
|
587
|
+
inputSchema: z.object({
|
|
588
|
+
name: z.string().min(1),
|
|
589
|
+
description: z.string().optional()
|
|
590
|
+
})
|
|
591
|
+
}, wrapHandler(async (input) => client.createCategory(input)));
|
|
592
|
+
server.registerTool("list_tags", {
|
|
593
|
+
description: "List tags visible in the workspace or bound category.",
|
|
594
|
+
inputSchema: z.object({})
|
|
595
|
+
}, wrapHandler(async () => client.listTags()));
|
|
596
|
+
server.registerTool("get_related_documents", {
|
|
597
|
+
description: "Traverse the knowledge graph from one authorized document.",
|
|
598
|
+
inputSchema: z.object({
|
|
599
|
+
documentId: z.string().min(1),
|
|
600
|
+
limit: z.number().int().min(1).max(50).optional()
|
|
601
|
+
})
|
|
602
|
+
}, wrapHandler(async ({ documentId, limit }) => client.getRelatedDocuments(documentId, limit)));
|
|
603
|
+
server.registerTool("search_knowledge_graph", {
|
|
604
|
+
description: "Search connected knowledge using authorized seed documents. Category keys may use only documents in their category.",
|
|
605
|
+
inputSchema: z.object({
|
|
606
|
+
query: z.string().min(1).max(1000),
|
|
607
|
+
docIds: z.array(z.string().min(1)).min(1),
|
|
608
|
+
limit: z.number().int().min(1).max(50).optional()
|
|
609
|
+
})
|
|
610
|
+
}, wrapHandler(async (input) => client.searchGraph(input)));
|
|
611
|
+
server.registerTool("get_document_index_status", {
|
|
612
|
+
description: "Read the current indexing and knowledge-pipeline status of a document.",
|
|
613
|
+
inputSchema: z.object({ documentId: z.string().min(1) })
|
|
614
|
+
}, wrapHandler(async ({ documentId }) => client.getDocumentIndexStatus(documentId)));
|
|
615
|
+
server.registerTool("refresh_document_index", {
|
|
616
|
+
description: "Request reindexing after a document or metadata change. Requires write access.",
|
|
617
|
+
inputSchema: z.object({ documentId: z.string().min(1) })
|
|
618
|
+
}, wrapHandler(async ({ documentId }) => client.refreshDocumentIndex(documentId)));
|
|
619
|
+
server.registerPrompt("organize_workspace", {
|
|
620
|
+
description: "Plan safe document organization using DocsMint categories and folders.",
|
|
621
|
+
argsSchema: z.object({
|
|
622
|
+
objective: z.string(),
|
|
623
|
+
language: z.string().optional()
|
|
624
|
+
})
|
|
625
|
+
}, ({ objective, language }) => ({
|
|
626
|
+
messages: [
|
|
627
|
+
{
|
|
628
|
+
role: "user",
|
|
629
|
+
content: {
|
|
630
|
+
type: "text",
|
|
631
|
+
text: `Organize this DocsMint workspace for: ${objective}. Work in ${language ?? "the document language"}. Inspect categories, folders, tags, and documents before proposing or applying changes. Preserve document content and obey the API key scope.`
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
]
|
|
635
|
+
}));
|
|
636
|
+
server.registerPrompt("research_workspace", {
|
|
637
|
+
description: "Research a question with hybrid search and GraphRAG while citing DocsMint document IDs.",
|
|
638
|
+
argsSchema: z.object({
|
|
639
|
+
question: z.string(),
|
|
640
|
+
language: z.string().optional()
|
|
641
|
+
})
|
|
642
|
+
}, ({ question, language }) => ({
|
|
643
|
+
messages: [
|
|
644
|
+
{
|
|
645
|
+
role: "user",
|
|
646
|
+
content: {
|
|
647
|
+
type: "text",
|
|
648
|
+
text: `Answer this question from DocsMint: ${question}. Respond in ${language ?? "the question language"}. Start with hybrid search, use graph traversal only from authorized result documents, and cite document IDs. Distinguish retrieved facts from inference.`
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
]
|
|
652
|
+
}));
|
|
653
|
+
server.registerResource("editor-guide", "docsmint://guide/editor", {}, async (uri) => ({
|
|
654
|
+
contents: [
|
|
655
|
+
{
|
|
656
|
+
uri: uri.href,
|
|
657
|
+
mimeType: "text/markdown",
|
|
658
|
+
text: `# DocsMint editing rules
|
|
659
|
+
|
|
660
|
+
Read a document before updating it. Preserve its language, title intent, TipTap/Markdown structure, category, folder, and tags unless the user explicitly requests a change. After content changes, verify index status and request refresh only when needed.`
|
|
661
|
+
}
|
|
662
|
+
]
|
|
663
|
+
}));
|
|
664
|
+
server.registerResource("search-guide", "docsmint://guide/search", {}, async (uri) => ({
|
|
665
|
+
contents: [
|
|
666
|
+
{
|
|
667
|
+
uri: uri.href,
|
|
668
|
+
mimeType: "text/markdown",
|
|
669
|
+
text: `# DocsMint retrieval rules
|
|
670
|
+
|
|
671
|
+
Use search_documents for normal retrieval. Use get_related_documents or search_knowledge_graph only with document IDs already authorized by the active API key. Keep multilingual queries in their original language and cite document IDs in answers.`
|
|
672
|
+
}
|
|
673
|
+
]
|
|
674
|
+
}));
|
|
675
|
+
server.registerResource("workspace-catalog", "docsmint://workspace/catalog", {}, async (uri) => {
|
|
676
|
+
const [categories, folders, tags] = await Promise.all([
|
|
677
|
+
client.listCategories(),
|
|
678
|
+
client.listFolders({}),
|
|
679
|
+
client.listTags()
|
|
680
|
+
]);
|
|
681
|
+
return {
|
|
682
|
+
contents: [
|
|
683
|
+
{
|
|
684
|
+
uri: uri.href,
|
|
685
|
+
mimeType: "application/json",
|
|
686
|
+
text: JSON.stringify({ categories, folders, tags }, null, 2)
|
|
687
|
+
}
|
|
688
|
+
]
|
|
689
|
+
};
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
// ../mcp-server/src/client.ts
|
|
694
|
+
var DEFAULT_BASE_URL = "http://localhost:50700";
|
|
695
|
+
function readConfig() {
|
|
696
|
+
const baseUrl = (process.env.HIAI_DOCS_URL ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
697
|
+
const apiKey = process.env.HIAI_DOCS_API_KEY;
|
|
698
|
+
return { baseUrl, apiKey: apiKey || undefined };
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
class HiaiDocsError extends Error {
|
|
702
|
+
status;
|
|
703
|
+
body;
|
|
704
|
+
code;
|
|
705
|
+
constructor(message, status, body, code = `http_${status}`) {
|
|
706
|
+
super(message);
|
|
707
|
+
this.status = status;
|
|
708
|
+
this.body = body;
|
|
709
|
+
this.code = code;
|
|
710
|
+
this.name = "HiaiDocsError";
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
function sanitizeMcpRequestContext(context) {
|
|
714
|
+
if (!context)
|
|
715
|
+
return;
|
|
716
|
+
const headers = new Headers(context.headers);
|
|
717
|
+
if (context.workspaceAssertion) {
|
|
718
|
+
headers.delete("Authorization");
|
|
719
|
+
headers.delete("Cookie");
|
|
720
|
+
}
|
|
721
|
+
return {
|
|
722
|
+
...context,
|
|
723
|
+
authorization: context.workspaceAssertion ? undefined : context.authorization,
|
|
724
|
+
cookie: context.workspaceAssertion ? undefined : context.cookie,
|
|
725
|
+
headers
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
function createMcpDocsClient(docsClient, requestContext) {
|
|
729
|
+
const context = sanitizeMcpRequestContext(requestContext);
|
|
730
|
+
return {
|
|
731
|
+
search: (params) => docsClient.search(params.query, {
|
|
732
|
+
folder: params.folder,
|
|
733
|
+
tags: params.tags?.join(","),
|
|
734
|
+
limit: params.limit
|
|
735
|
+
}, context),
|
|
736
|
+
getDocument: (id) => docsClient.getDoc(id, context),
|
|
737
|
+
createDocument: (input) => docsClient.createDoc({
|
|
738
|
+
...input,
|
|
739
|
+
folderId: input.folderId ?? undefined,
|
|
740
|
+
categoryId: input.categoryId ?? undefined
|
|
741
|
+
}, context),
|
|
742
|
+
updateDocument: (id, input) => docsClient.updateDoc(id, input, context),
|
|
743
|
+
listDocuments: (params) => docsClient.listDocs(params, context),
|
|
744
|
+
listFolders: (params) => docsClient.listFolders(params.parentId, context),
|
|
745
|
+
createFolder: (input) => docsClient.createFolder(input, context),
|
|
746
|
+
listCategories: () => docsClient.listCategories(context),
|
|
747
|
+
createCategory: (input) => docsClient.createCategory(input, context),
|
|
748
|
+
listTags: () => docsClient.listTags(context),
|
|
749
|
+
getRelatedDocuments: (documentId, limit) => docsClient.getRelatedDocuments(documentId, context, { limit }),
|
|
750
|
+
searchGraph: (input) => docsClient.searchGraph({ query: input.query, docIds: input.docIds, maxResults: input.limit }, context),
|
|
751
|
+
getDocumentIndexStatus: (documentId) => docsClient.getDocumentIndexStatus(documentId, context),
|
|
752
|
+
refreshDocumentIndex: (documentId) => docsClient.refreshDocumentIndex(documentId, context),
|
|
753
|
+
createSnapshot: (documentId, input) => docsClient.createSnapshot(documentId, input, context),
|
|
754
|
+
getVersionHistory: (documentId, onlySnapshots) => docsClient.listVersions(documentId, { onlySnapshots }, context),
|
|
755
|
+
exportDocument: async (id) => ({ markdown: await docsClient.exportDoc(id, context) })
|
|
756
|
+
};
|
|
757
|
+
}
|
|
758
|
+
function createDefaultDocsClient() {
|
|
759
|
+
const { baseUrl, apiKey } = readConfig();
|
|
760
|
+
return new DocsClient({ baseUrl, apiKey, retries: 1 });
|
|
761
|
+
}
|
|
762
|
+
var client = new Proxy({}, {
|
|
763
|
+
get(_target, property) {
|
|
764
|
+
const configured = createMcpDocsClient(createDefaultDocsClient());
|
|
765
|
+
const value = configured[property];
|
|
766
|
+
return typeof value === "function" ? value.bind(configured) : value;
|
|
767
|
+
}
|
|
768
|
+
});
|
|
769
|
+
|
|
770
|
+
// ../mcp-server/src/tools/create-document.ts
|
|
771
|
+
var exports_create_document = {};
|
|
772
|
+
__export(exports_create_document, {
|
|
773
|
+
createHandler: () => createHandler,
|
|
774
|
+
definition: () => definition,
|
|
775
|
+
handler: () => handler
|
|
776
|
+
});
|
|
777
|
+
import { z as z2 } from "zod";
|
|
778
|
+
var definition = {
|
|
779
|
+
name: "create_document",
|
|
780
|
+
description: "Create a new document. Optionally provide initial markdown content and a folder ID.",
|
|
781
|
+
inputSchema: {
|
|
782
|
+
title: z2.string().describe("Document title."),
|
|
783
|
+
content: z2.string().optional().describe("Initial markdown content for the document."),
|
|
784
|
+
folderId: z2.string().optional().describe("Optional folder ID to place the document in."),
|
|
785
|
+
categoryId: z2.string().optional().describe("Optional category ID. Category keys are always rebound to their configured category.")
|
|
786
|
+
}
|
|
787
|
+
};
|
|
788
|
+
var createHandler = (api) => async function createDocument(args) {
|
|
789
|
+
return await api.createDocument(args);
|
|
790
|
+
};
|
|
791
|
+
var handler = createHandler(client);
|
|
792
|
+
|
|
793
|
+
// ../mcp-server/src/tools/create-folder.ts
|
|
794
|
+
var exports_create_folder = {};
|
|
795
|
+
__export(exports_create_folder, {
|
|
796
|
+
createHandler: () => createHandler2,
|
|
797
|
+
definition: () => definition2,
|
|
798
|
+
handler: () => handler2
|
|
799
|
+
});
|
|
800
|
+
import { z as z3 } from "zod";
|
|
801
|
+
var definition2 = {
|
|
802
|
+
name: "create_folder",
|
|
803
|
+
description: "Create a new folder, optionally nested under a parent folder.",
|
|
804
|
+
inputSchema: {
|
|
805
|
+
name: z3.string().describe("Folder name."),
|
|
806
|
+
parentId: z3.string().optional().describe("Optional parent folder ID for nesting."),
|
|
807
|
+
categoryId: z3.string().optional().describe("Optional category ID. Category keys are always rebound to their configured category.")
|
|
808
|
+
}
|
|
809
|
+
};
|
|
810
|
+
var createHandler2 = (api) => async function createFolder(args) {
|
|
811
|
+
return await api.createFolder(args);
|
|
812
|
+
};
|
|
813
|
+
var handler2 = createHandler2(client);
|
|
814
|
+
|
|
815
|
+
// ../mcp-server/src/tools/create-snapshot.ts
|
|
816
|
+
var exports_create_snapshot = {};
|
|
817
|
+
__export(exports_create_snapshot, {
|
|
818
|
+
createHandler: () => createHandler3,
|
|
819
|
+
definition: () => definition3,
|
|
820
|
+
handler: () => handler3
|
|
821
|
+
});
|
|
822
|
+
import { z as z4 } from "zod";
|
|
823
|
+
var definition3 = {
|
|
824
|
+
name: "create_snapshot",
|
|
825
|
+
description: "Create a named snapshot (labelled version) of a document from its current content.",
|
|
826
|
+
inputSchema: {
|
|
827
|
+
documentId: z4.string().describe("Document ID to snapshot."),
|
|
828
|
+
label: z4.string().describe("Short label for the snapshot (e.g. 'v1.0-release')."),
|
|
829
|
+
description: z4.string().optional().describe("Optional longer description of the snapshot.")
|
|
830
|
+
}
|
|
831
|
+
};
|
|
832
|
+
var createHandler3 = (api) => async function createSnapshot(args) {
|
|
833
|
+
const { documentId, ...input } = args;
|
|
834
|
+
return await api.createSnapshot(documentId, input);
|
|
835
|
+
};
|
|
836
|
+
var handler3 = createHandler3(client);
|
|
837
|
+
|
|
838
|
+
// ../mcp-server/src/tools/export-document.ts
|
|
839
|
+
var exports_export_document = {};
|
|
840
|
+
__export(exports_export_document, {
|
|
841
|
+
createHandler: () => createHandler4,
|
|
842
|
+
definition: () => definition4,
|
|
843
|
+
handler: () => handler4
|
|
844
|
+
});
|
|
845
|
+
import { z as z5 } from "zod";
|
|
846
|
+
var definition4 = {
|
|
847
|
+
name: "export_document",
|
|
848
|
+
description: "Export a document as markdown. Returns the rendered markdown content.",
|
|
849
|
+
inputSchema: {
|
|
850
|
+
id: z5.string().describe("Document ID to export.")
|
|
851
|
+
}
|
|
852
|
+
};
|
|
853
|
+
var createHandler4 = (api) => async function exportDocument(args) {
|
|
854
|
+
return await api.exportDocument(args.id);
|
|
855
|
+
};
|
|
856
|
+
var handler4 = createHandler4(client);
|
|
857
|
+
|
|
858
|
+
// ../mcp-server/src/tools/get-document.ts
|
|
859
|
+
var exports_get_document = {};
|
|
860
|
+
__export(exports_get_document, {
|
|
861
|
+
createHandler: () => createHandler5,
|
|
862
|
+
definition: () => definition5,
|
|
863
|
+
handler: () => handler5
|
|
864
|
+
});
|
|
865
|
+
import { z as z6 } from "zod";
|
|
866
|
+
var definition5 = {
|
|
867
|
+
name: "get_document",
|
|
868
|
+
description: "Fetch a single document by ID. Returns full content, metadata, and tags.",
|
|
869
|
+
inputSchema: {
|
|
870
|
+
id: z6.string().describe("Document ID.")
|
|
871
|
+
}
|
|
872
|
+
};
|
|
873
|
+
var createHandler5 = (api) => async function getDocument(args) {
|
|
874
|
+
return await api.getDocument(args.id);
|
|
875
|
+
};
|
|
876
|
+
var handler5 = createHandler5(client);
|
|
877
|
+
|
|
878
|
+
// ../mcp-server/src/tools/list-documents.ts
|
|
879
|
+
var exports_list_documents = {};
|
|
880
|
+
__export(exports_list_documents, {
|
|
881
|
+
createHandler: () => createHandler6,
|
|
882
|
+
definition: () => definition6,
|
|
883
|
+
handler: () => handler6
|
|
884
|
+
});
|
|
885
|
+
import { z as z7 } from "zod";
|
|
886
|
+
var definition6 = {
|
|
887
|
+
name: "list_documents",
|
|
888
|
+
description: "List documents with pagination, optionally filtered by folder or tag.",
|
|
889
|
+
inputSchema: {
|
|
890
|
+
folderId: z7.string().optional().describe("Optional folder ID to filter by."),
|
|
891
|
+
tag: z7.string().optional().describe("Optional tag ID to filter by."),
|
|
892
|
+
page: z7.number().int().positive().optional().describe("Page number (1-indexed, default 1)."),
|
|
893
|
+
limit: z7.number().int().positive().max(100).optional().describe("Items per page (default 20, max 100).")
|
|
894
|
+
}
|
|
895
|
+
};
|
|
896
|
+
var createHandler6 = (api) => async function listDocuments(args) {
|
|
897
|
+
return await api.listDocuments(args);
|
|
898
|
+
};
|
|
899
|
+
var handler6 = createHandler6(client);
|
|
900
|
+
|
|
901
|
+
// ../mcp-server/src/tools/list-folders.ts
|
|
902
|
+
var exports_list_folders = {};
|
|
903
|
+
__export(exports_list_folders, {
|
|
904
|
+
createHandler: () => createHandler7,
|
|
905
|
+
definition: () => definition7,
|
|
906
|
+
handler: () => handler7
|
|
907
|
+
});
|
|
908
|
+
import { z as z8 } from "zod";
|
|
909
|
+
var definition7 = {
|
|
910
|
+
name: "list_folders",
|
|
911
|
+
description: "List folders, optionally scoped to a parent folder. Returns a flat list of immediate children.",
|
|
912
|
+
inputSchema: {
|
|
913
|
+
parentId: z8.string().optional().describe("Optional parent folder ID. Omit to list top-level (root) folders.")
|
|
914
|
+
}
|
|
915
|
+
};
|
|
916
|
+
var createHandler7 = (api) => async function listFolders(args) {
|
|
917
|
+
return await api.listFolders(args);
|
|
918
|
+
};
|
|
919
|
+
var handler7 = createHandler7(client);
|
|
920
|
+
|
|
921
|
+
// ../mcp-server/src/tools/search.ts
|
|
922
|
+
var exports_search = {};
|
|
923
|
+
__export(exports_search, {
|
|
924
|
+
createHandler: () => createHandler8,
|
|
925
|
+
definition: () => definition8,
|
|
926
|
+
handler: () => handler8
|
|
927
|
+
});
|
|
928
|
+
import { z as z9 } from "zod";
|
|
929
|
+
var definition8 = {
|
|
930
|
+
name: "search_documents",
|
|
931
|
+
description: "Hybrid search across documents (full-text + semantic). Supports filtering by folder and tags.",
|
|
932
|
+
inputSchema: {
|
|
933
|
+
query: z9.string().describe("Search query string."),
|
|
934
|
+
folder: z9.string().optional().describe("Optional folder ID to scope the search to."),
|
|
935
|
+
tags: z9.array(z9.string()).optional().describe("Optional tag IDs to filter by."),
|
|
936
|
+
limit: z9.number().int().positive().optional().describe("Maximum number of results to return (default 20).")
|
|
937
|
+
}
|
|
938
|
+
};
|
|
939
|
+
var createHandler8 = (api) => async function searchDocuments(args) {
|
|
940
|
+
return await api.search({
|
|
941
|
+
query: args.query,
|
|
942
|
+
folder: args.folder,
|
|
943
|
+
tags: args.tags,
|
|
944
|
+
limit: args.limit
|
|
945
|
+
});
|
|
946
|
+
};
|
|
947
|
+
var handler8 = createHandler8(client);
|
|
948
|
+
|
|
949
|
+
// ../mcp-server/src/tools/update-document.ts
|
|
950
|
+
var exports_update_document = {};
|
|
951
|
+
__export(exports_update_document, {
|
|
952
|
+
createHandler: () => createHandler9,
|
|
953
|
+
definition: () => definition9,
|
|
954
|
+
handler: () => handler9
|
|
955
|
+
});
|
|
956
|
+
import { z as z10 } from "zod";
|
|
957
|
+
var definition9 = {
|
|
958
|
+
name: "update_document",
|
|
959
|
+
description: "Update an existing document's title and/or content. The server creates a new version on each update.",
|
|
960
|
+
inputSchema: {
|
|
961
|
+
id: z10.string().describe("Document ID to update."),
|
|
962
|
+
title: z10.string().optional().describe("New title for the document."),
|
|
963
|
+
content: z10.string().optional().describe("New markdown content for the document."),
|
|
964
|
+
folderId: z10.string().nullable().optional().describe("Move the document to a folder."),
|
|
965
|
+
categoryId: z10.string().nullable().optional().describe("Move the document to a category. Category keys cannot escape their configured category.")
|
|
966
|
+
}
|
|
967
|
+
};
|
|
968
|
+
var createHandler9 = (api) => async function updateDocument(args) {
|
|
969
|
+
const { id, ...patch } = args;
|
|
970
|
+
return await api.updateDocument(id, patch);
|
|
971
|
+
};
|
|
972
|
+
var handler9 = createHandler9(client);
|
|
973
|
+
|
|
974
|
+
// ../mcp-server/src/tools/version-history.ts
|
|
975
|
+
var exports_version_history = {};
|
|
976
|
+
__export(exports_version_history, {
|
|
977
|
+
createHandler: () => createHandler10,
|
|
978
|
+
definition: () => definition10,
|
|
979
|
+
handler: () => handler10
|
|
980
|
+
});
|
|
981
|
+
import { z as z11 } from "zod";
|
|
982
|
+
var definition10 = {
|
|
983
|
+
name: "get_version_history",
|
|
984
|
+
description: "List the version history for a document. Optionally restrict to named snapshots.",
|
|
985
|
+
inputSchema: {
|
|
986
|
+
documentId: z11.string().describe("Document ID whose versions should be listed."),
|
|
987
|
+
onlySnapshots: z11.boolean().optional().describe("When true, only return named snapshots (skip auto-saved revisions).")
|
|
988
|
+
}
|
|
989
|
+
};
|
|
990
|
+
var createHandler10 = (api) => async function getVersionHistory(args) {
|
|
991
|
+
return await api.getVersionHistory(args.documentId, args.onlySnapshots);
|
|
992
|
+
};
|
|
993
|
+
var handler10 = createHandler10(client);
|
|
994
|
+
|
|
995
|
+
// ../mcp-server/src/server.ts
|
|
996
|
+
function wrapHandler(name, handler11) {
|
|
997
|
+
return async (args) => {
|
|
998
|
+
try {
|
|
999
|
+
return {
|
|
1000
|
+
content: [{ type: "text", text: JSON.stringify(await handler11(args), null, 2) }]
|
|
1001
|
+
};
|
|
1002
|
+
} catch (error) {
|
|
1003
|
+
if (isDocsApiError(error) || error instanceof HiaiDocsError) {
|
|
1004
|
+
return {
|
|
1005
|
+
isError: true,
|
|
1006
|
+
content: [
|
|
1007
|
+
{
|
|
1008
|
+
type: "text",
|
|
1009
|
+
text: JSON.stringify({
|
|
1010
|
+
type: error.name,
|
|
1011
|
+
status: error.status,
|
|
1012
|
+
code: error.code,
|
|
1013
|
+
message: error.message,
|
|
1014
|
+
body: error.body
|
|
1015
|
+
})
|
|
1016
|
+
}
|
|
1017
|
+
]
|
|
1018
|
+
};
|
|
1019
|
+
}
|
|
1020
|
+
return {
|
|
1021
|
+
isError: true,
|
|
1022
|
+
content: [{ type: "text", text: `Tool '${name}' failed: ${error instanceof Error ? error.message : String(error)}` }]
|
|
1023
|
+
};
|
|
1024
|
+
}
|
|
1025
|
+
};
|
|
1026
|
+
}
|
|
1027
|
+
function registerDocsmintMcpCapabilities(server, client2) {
|
|
1028
|
+
const register = (name, description, inputSchema, handler11) => {
|
|
1029
|
+
server.registerTool(name, { description, inputSchema: z12.object(inputSchema) }, wrapHandler(name, handler11));
|
|
1030
|
+
};
|
|
1031
|
+
const tools = [
|
|
1032
|
+
exports_search,
|
|
1033
|
+
exports_get_document,
|
|
1034
|
+
exports_create_document,
|
|
1035
|
+
exports_update_document,
|
|
1036
|
+
exports_list_documents,
|
|
1037
|
+
exports_list_folders,
|
|
1038
|
+
exports_create_folder,
|
|
1039
|
+
exports_create_snapshot,
|
|
1040
|
+
exports_version_history,
|
|
1041
|
+
exports_export_document
|
|
1042
|
+
];
|
|
1043
|
+
for (const tool of tools) {
|
|
1044
|
+
register(tool.definition.name, tool.definition.description, tool.definition.inputSchema, tool.createHandler(client2));
|
|
1045
|
+
}
|
|
1046
|
+
registerExtendedCapabilities(server, client2, (handler11) => wrapHandler("extended", handler11));
|
|
1047
|
+
}
|
|
1048
|
+
function createDocsmintMcpServer(options = {}) {
|
|
1049
|
+
const server = new McpServer({ name: "docsmint", version: "0.7.1" });
|
|
1050
|
+
const client2 = options.docsClient ? createMcpDocsClient(options.docsClient, options.requestContext) : options.client ?? createMcpDocsClient(createDefaultDocsClient(), options.requestContext);
|
|
1051
|
+
registerDocsmintMcpCapabilities(server, client2);
|
|
1052
|
+
return server;
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
// ../mcp-server/src/index.ts
|
|
1056
|
+
serveStdio(() => createDocsmintMcpServer());
|