@hiai-gg/docsmint 0.6.1 → 0.6.2

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 CHANGED
@@ -40,7 +40,19 @@ server.
40
40
  - **Own the full stack**: application data, vectors, graph, queue, and files run
41
41
  on infrastructure you control.
42
42
 
43
- ## What's new in DocsMint 0.6.1?
43
+ ## What's new in DocsMint 0.6.2?
44
+
45
+ - **Official MCP identity.** DocsMint now publishes the verified
46
+ `io.github.hiai-gg/docsmint` registry identity for both the npm stdio server
47
+ and the hosted Streamable HTTP endpoint.
48
+ - **One capability implementation.** The public `@hiai-gg/docsmint/mcp`
49
+ contract lets hosts provide a scoped API client while reusing the same 17
50
+ tools, two prompts, three resources, and bundled document-manager Skill.
51
+ - **Current protocol runtime.** The MCP server uses the stable v2 server
52
+ package and retains compatible stdio negotiation for existing clients.
53
+
54
+ DocsMint 0.6.2 includes the reliable knowledge refresh and scoped automation
55
+ foundation introduced in 0.6.1:
44
56
 
45
57
  - **Reliable knowledge refreshes.** Explicit full reindexing now supersedes
46
58
  failed or stuck generations, retries release stale extraction claims, and
@@ -234946,7 +234946,7 @@ var swaggerConfig = {
234946
234946
  },
234947
234947
  info: {
234948
234948
  title: "DocsMint API",
234949
- version: "0.6.1",
234949
+ version: "0.6.2",
234950
234950
  description: "Self-hosted AI-first documentation platform. Full-text + semantic search, version history, sharing, and folder organization.",
234951
234951
  contact: { name: "HiAi-gg", url: "https://github.com/HiAi-gg/docsmint" },
234952
234952
  license: {
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@hiai-gg/docsmint",
3
- "version": "0.6.1",
3
+ "mcpName": "io.github.hiai-gg/docsmint",
4
+ "version": "0.6.2",
4
5
  "type": "module",
5
6
  "browser": {
6
7
  "./dist/backend-launcher.js": false,
@@ -57,6 +58,10 @@
57
58
  "import": "./dist/index.js",
58
59
  "types": "./dist/index.d.ts"
59
60
  },
61
+ "./mcp": {
62
+ "import": "./packages/mcp-server/src/server.ts",
63
+ "types": "./packages/mcp-server/src/server.ts"
64
+ },
60
65
  "./lifecycle": {
61
66
  "browser": "./dist/server-only-browser-entry.js",
62
67
  "import": "./dist/lifecycle.js",
@@ -362,6 +367,7 @@
362
367
  "backend/src/lib/api-keys.ts",
363
368
  "backend/src/lib/api-key-encryption.ts",
364
369
  "backend/src/lib/lifecycle-service.ts",
370
+ "server.json",
365
371
  "README.md",
366
372
  "LICENSE"
367
373
  ],
@@ -380,7 +386,7 @@
380
386
  "prepublishOnly": "cd packages/sdk && bun run build"
381
387
  },
382
388
  "dependencies": {
383
- "@modelcontextprotocol/sdk": "^1.12.1",
389
+ "@modelcontextprotocol/server": "2.0.0",
384
390
  "bullmq": "^5.80.2",
385
391
  "commander": "^13.1.0",
386
392
  "ioredis": "^5.11.1",
@@ -24,7 +24,7 @@ import { registerSearch } from "./commands/search.js";
24
24
  import { registerSnapshot } from "./commands/snapshot.js";
25
25
  import { registerUpdate } from "./commands/update.js";
26
26
 
27
- const VERSION = "0.6.1";
27
+ const VERSION = "0.6.2";
28
28
 
29
29
  const program = new Command();
30
30
  program
@@ -1,165 +1,189 @@
1
- import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import { z } from "zod";
1
+ import type { McpServer } from '@modelcontextprotocol/server';
2
+ import { z } from 'zod';
3
3
 
4
- import { client } from "./client.js";
4
+ import type { HiaiDocsClient } from './client.js';
5
5
 
6
6
  export const capabilityCatalog = {
7
- tools: [
8
- "search_documents",
9
- "get_document",
10
- "create_document",
11
- "update_document",
12
- "list_documents",
13
- "list_folders",
14
- "create_folder",
15
- "create_snapshot",
16
- "get_version_history",
17
- "export_document",
18
- "list_categories",
19
- "create_category",
20
- "list_tags",
21
- "get_related_documents",
22
- "search_knowledge_graph",
23
- "get_document_index_status",
24
- "refresh_document_index",
25
- ] as const,
26
- prompts: ["organize_workspace", "research_workspace"] as const,
27
- resources: [
28
- "docsmint://guide/editor",
29
- "docsmint://guide/search",
30
- "docsmint://workspace/catalog",
31
- ] as const,
7
+ tools: [
8
+ 'search_documents',
9
+ 'get_document',
10
+ 'create_document',
11
+ 'update_document',
12
+ 'list_documents',
13
+ 'list_folders',
14
+ 'create_folder',
15
+ 'create_snapshot',
16
+ 'get_version_history',
17
+ 'export_document',
18
+ 'list_categories',
19
+ 'create_category',
20
+ 'list_tags',
21
+ 'get_related_documents',
22
+ 'search_knowledge_graph',
23
+ 'get_document_index_status',
24
+ 'refresh_document_index',
25
+ ] as const,
26
+ prompts: ['organize_workspace', 'research_workspace'] as const,
27
+ resources: [
28
+ 'docsmint://guide/editor',
29
+ 'docsmint://guide/search',
30
+ 'docsmint://workspace/catalog',
31
+ ] as const,
32
32
  };
33
33
 
34
- type TextResult = { content: Array<{ type: "text"; text: string }> };
34
+ type TextResult = { content: Array<{ type: 'text'; text: string }> };
35
35
 
36
36
  function jsonResult(value: unknown): TextResult {
37
- return { content: [{ type: "text", text: JSON.stringify(value, null, 2) }] };
37
+ return { content: [{ type: 'text', text: JSON.stringify(value, null, 2) }] };
38
38
  }
39
39
 
40
- export function registerExtendedCapabilities(server: McpServer): void {
41
- server.tool(
42
- "list_categories",
43
- "List categories visible to the API key. Category keys receive only their bound category.",
44
- {},
45
- async () => jsonResult(await client.listCategories()),
46
- );
47
- server.tool(
48
- "create_category",
49
- "Create a category. Requires a workspace key with write access; category keys cannot mutate categories.",
50
- { name: z.string().min(1), description: z.string().optional() },
51
- async (input) => jsonResult(await client.createCategory(input)),
52
- );
53
- server.tool(
54
- "list_tags",
55
- "List tags visible in the workspace or bound category.",
56
- {},
57
- async () => jsonResult(await client.listTags()),
58
- );
59
- server.tool(
60
- "get_related_documents",
61
- "Traverse the knowledge graph from one authorized document.",
62
- {
63
- documentId: z.string().min(1),
64
- limit: z.number().int().min(1).max(50).optional(),
65
- },
66
- async ({ documentId, limit }) =>
67
- jsonResult(await client.getRelatedDocuments(documentId, limit)),
68
- );
69
- server.tool(
70
- "search_knowledge_graph",
71
- "Search connected knowledge using authorized seed documents. Category keys may use only documents in their category.",
72
- {
73
- query: z.string().min(1).max(1000),
74
- docIds: z.array(z.string().min(1)).min(1),
75
- limit: z.number().int().min(1).max(50).optional(),
76
- },
77
- async (input) => jsonResult(await client.searchGraph(input)),
78
- );
79
- server.tool(
80
- "get_document_index_status",
81
- "Read the current indexing and knowledge-pipeline status of a document.",
82
- { documentId: z.string().min(1) },
83
- async ({ documentId }) =>
84
- jsonResult(await client.getDocumentIndexStatus(documentId)),
85
- );
86
- server.tool(
87
- "refresh_document_index",
88
- "Request reindexing after a document or metadata change. Requires write access.",
89
- { documentId: z.string().min(1) },
90
- async ({ documentId }) =>
91
- jsonResult(await client.refreshDocumentIndex(documentId)),
92
- );
40
+ export function registerExtendedCapabilities(server: McpServer, client: HiaiDocsClient): void {
41
+ server.registerTool(
42
+ 'list_categories',
43
+ {
44
+ description:
45
+ 'List categories visible to the API key. Category keys receive only their bound category.',
46
+ inputSchema: z.object({}),
47
+ },
48
+ async () => jsonResult(await client.listCategories())
49
+ );
50
+ server.registerTool(
51
+ 'create_category',
52
+ {
53
+ description:
54
+ 'Create a category. Requires a workspace key with write access; category keys cannot mutate categories.',
55
+ inputSchema: z.object({
56
+ name: z.string().min(1),
57
+ description: z.string().optional(),
58
+ }),
59
+ },
60
+ async (input) => jsonResult(await client.createCategory(input))
61
+ );
62
+ server.registerTool(
63
+ 'list_tags',
64
+ {
65
+ description: 'List tags visible in the workspace or bound category.',
66
+ inputSchema: z.object({}),
67
+ },
68
+ async () => jsonResult(await client.listTags())
69
+ );
70
+ server.registerTool(
71
+ 'get_related_documents',
72
+ {
73
+ description: 'Traverse the knowledge graph from one authorized document.',
74
+ inputSchema: z.object({
75
+ documentId: z.string().min(1),
76
+ limit: z.number().int().min(1).max(50).optional(),
77
+ }),
78
+ },
79
+ async ({ documentId, limit }) => jsonResult(await client.getRelatedDocuments(documentId, limit))
80
+ );
81
+ server.registerTool(
82
+ 'search_knowledge_graph',
83
+ {
84
+ description:
85
+ 'Search connected knowledge using authorized seed documents. Category keys may use only documents in their category.',
86
+ inputSchema: z.object({
87
+ query: z.string().min(1).max(1000),
88
+ docIds: z.array(z.string().min(1)).min(1),
89
+ limit: z.number().int().min(1).max(50).optional(),
90
+ }),
91
+ },
92
+ async (input) => jsonResult(await client.searchGraph(input))
93
+ );
94
+ server.registerTool(
95
+ 'get_document_index_status',
96
+ {
97
+ description: 'Read the current indexing and knowledge-pipeline status of a document.',
98
+ inputSchema: z.object({ documentId: z.string().min(1) }),
99
+ },
100
+ async ({ documentId }) => jsonResult(await client.getDocumentIndexStatus(documentId))
101
+ );
102
+ server.registerTool(
103
+ 'refresh_document_index',
104
+ {
105
+ description: 'Request reindexing after a document or metadata change. Requires write access.',
106
+ inputSchema: z.object({ documentId: z.string().min(1) }),
107
+ },
108
+ async ({ documentId }) => jsonResult(await client.refreshDocumentIndex(documentId))
109
+ );
93
110
 
94
- server.prompt(
95
- "organize_workspace",
96
- "Plan safe document organization using DocsMint categories and folders.",
97
- { objective: z.string(), language: z.string().optional() },
98
- ({ objective, language }) => ({
99
- messages: [
100
- {
101
- role: "user",
102
- content: {
103
- type: "text",
104
- 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.`,
105
- },
106
- },
107
- ],
108
- }),
109
- );
110
- server.prompt(
111
- "research_workspace",
112
- "Research a question with hybrid search and GraphRAG while citing DocsMint document IDs.",
113
- { question: z.string(), language: z.string().optional() },
114
- ({ question, language }) => ({
115
- messages: [
116
- {
117
- role: "user",
118
- content: {
119
- type: "text",
120
- 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.`,
121
- },
122
- },
123
- ],
124
- }),
125
- );
111
+ server.registerPrompt(
112
+ 'organize_workspace',
113
+ {
114
+ description: 'Plan safe document organization using DocsMint categories and folders.',
115
+ argsSchema: z.object({
116
+ objective: z.string(),
117
+ language: z.string().optional(),
118
+ }),
119
+ },
120
+ ({ objective, language }) => ({
121
+ messages: [
122
+ {
123
+ role: 'user',
124
+ content: {
125
+ type: 'text',
126
+ 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.`,
127
+ },
128
+ },
129
+ ],
130
+ })
131
+ );
132
+ server.registerPrompt(
133
+ 'research_workspace',
134
+ {
135
+ description:
136
+ 'Research a question with hybrid search and GraphRAG while citing DocsMint document IDs.',
137
+ argsSchema: z.object({
138
+ question: z.string(),
139
+ language: z.string().optional(),
140
+ }),
141
+ },
142
+ ({ question, language }) => ({
143
+ messages: [
144
+ {
145
+ role: 'user',
146
+ content: {
147
+ type: 'text',
148
+ 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.`,
149
+ },
150
+ },
151
+ ],
152
+ })
153
+ );
126
154
 
127
- server.resource("editor-guide", "docsmint://guide/editor", async (uri) => ({
128
- contents: [
129
- {
130
- uri: uri.href,
131
- mimeType: "text/markdown",
132
- text: "# DocsMint editing rules\n\nRead 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.",
133
- },
134
- ],
135
- }));
136
- server.resource("search-guide", "docsmint://guide/search", async (uri) => ({
137
- contents: [
138
- {
139
- uri: uri.href,
140
- mimeType: "text/markdown",
141
- text: "# DocsMint retrieval rules\n\nUse 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.",
142
- },
143
- ],
144
- }));
145
- server.resource(
146
- "workspace-catalog",
147
- "docsmint://workspace/catalog",
148
- async (uri) => {
149
- const [categories, folders, tags] = await Promise.all([
150
- client.listCategories(),
151
- client.listFolders({}),
152
- client.listTags(),
153
- ]);
154
- return {
155
- contents: [
156
- {
157
- uri: uri.href,
158
- mimeType: "application/json",
159
- text: JSON.stringify({ categories, folders, tags }, null, 2),
160
- },
161
- ],
162
- };
163
- },
164
- );
155
+ server.registerResource('editor-guide', 'docsmint://guide/editor', {}, async (uri) => ({
156
+ contents: [
157
+ {
158
+ uri: uri.href,
159
+ mimeType: 'text/markdown',
160
+ text: '# DocsMint editing rules\n\nRead 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.',
161
+ },
162
+ ],
163
+ }));
164
+ server.registerResource('search-guide', 'docsmint://guide/search', {}, async (uri) => ({
165
+ contents: [
166
+ {
167
+ uri: uri.href,
168
+ mimeType: 'text/markdown',
169
+ text: '# DocsMint retrieval rules\n\nUse 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.',
170
+ },
171
+ ],
172
+ }));
173
+ server.registerResource('workspace-catalog', 'docsmint://workspace/catalog', {}, async (uri) => {
174
+ const [categories, folders, tags] = await Promise.all([
175
+ client.listCategories(),
176
+ client.listFolders({}),
177
+ client.listTags(),
178
+ ]);
179
+ return {
180
+ contents: [
181
+ {
182
+ uri: uri.href,
183
+ mimeType: 'application/json',
184
+ text: JSON.stringify({ categories, folders, tags }, null, 2),
185
+ },
186
+ ],
187
+ };
188
+ });
165
189
  }
@@ -1,9 +1,8 @@
1
1
  #!/usr/bin/env bun
2
2
  /** DocsMint MCP stdio entry point. */
3
3
 
4
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { serveStdio } from '@modelcontextprotocol/server/stdio';
5
5
 
6
- import { createDocsmintMcpServer } from "./server.js";
6
+ import { createDocsmintMcpServer } from './server.js';
7
7
 
8
- const server = createDocsmintMcpServer();
9
- await server.connect(new StdioServerTransport());
8
+ serveStdio(() => createDocsmintMcpServer());
@@ -1,82 +1,87 @@
1
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import type { ZodRawShape } from "zod";
1
+ import { McpServer } from '@modelcontextprotocol/server';
2
+ import { z, type ZodRawShape } from 'zod';
3
3
 
4
- import { registerExtendedCapabilities } from "./capabilities.js";
5
- import { HiaiDocsError } from "./client.js";
6
- import * as createDocument from "./tools/create-document.js";
7
- import * as createFolder from "./tools/create-folder.js";
8
- import * as createSnapshot from "./tools/create-snapshot.js";
9
- import * as exportDocument from "./tools/export-document.js";
10
- import * as getDocument from "./tools/get-document.js";
11
- import * as listDocuments from "./tools/list-documents.js";
12
- import * as listFolders from "./tools/list-folders.js";
13
- import * as search from "./tools/search.js";
14
- import * as updateDocument from "./tools/update-document.js";
15
- import * as versionHistory from "./tools/version-history.js";
4
+ import { registerExtendedCapabilities } from './capabilities.js';
5
+ import { client as defaultClient, HiaiDocsError, type HiaiDocsClient } from './client.js';
6
+ import * as createDocument from './tools/create-document.js';
7
+ import * as createFolder from './tools/create-folder.js';
8
+ import * as createSnapshot from './tools/create-snapshot.js';
9
+ import * as exportDocument from './tools/export-document.js';
10
+ import * as getDocument from './tools/get-document.js';
11
+ import * as listDocuments from './tools/list-documents.js';
12
+ import * as listFolders from './tools/list-folders.js';
13
+ import * as search from './tools/search.js';
14
+ import * as updateDocument from './tools/update-document.js';
15
+ import * as versionHistory from './tools/version-history.js';
16
16
 
17
17
  type ToolHandler = (args: Record<string, unknown>) => Promise<unknown>;
18
18
  interface McpToolResult {
19
- content: Array<{ type: "text"; text: string }>;
20
- isError?: boolean;
19
+ content: Array<{ type: 'text'; text: string }>;
20
+ isError?: boolean;
21
21
  }
22
22
 
23
23
  function wrapHandler(
24
- name: string,
25
- handler: ToolHandler,
24
+ name: string,
25
+ handler: ToolHandler
26
26
  ): (args: Record<string, unknown>) => Promise<McpToolResult> {
27
- return async (args) => {
28
- try {
29
- return {
30
- content: [
31
- { type: "text", text: JSON.stringify(await handler(args), null, 2) },
32
- ],
33
- };
34
- } catch (error) {
35
- const message =
36
- error instanceof HiaiDocsError
37
- ? `DocsMint API error (${error.status}): ${error.message}`
38
- : `Tool '${name}' failed: ${error instanceof Error ? error.message : String(error)}`;
39
- return { isError: true, content: [{ type: "text", text: message }] };
40
- }
41
- };
27
+ return async (args) => {
28
+ try {
29
+ return {
30
+ content: [{ type: 'text', text: JSON.stringify(await handler(args), null, 2) }],
31
+ };
32
+ } catch (error) {
33
+ const message =
34
+ error instanceof HiaiDocsError
35
+ ? `DocsMint API error (${error.status}): ${error.message}`
36
+ : `Tool '${name}' failed: ${error instanceof Error ? error.message : String(error)}`;
37
+ return { isError: true, content: [{ type: 'text', text: message }] };
38
+ }
39
+ };
42
40
  }
43
41
 
44
- export function createDocsmintMcpServer(): McpServer {
45
- const server = new McpServer({ name: "docsmint", version: "0.6.1" });
46
- const register = <Args>(
47
- name: string,
48
- description: string,
49
- inputSchema: ZodRawShape,
50
- handler: (args: Args) => Promise<unknown>,
51
- ): void => {
52
- server.tool(
53
- name,
54
- description,
55
- inputSchema as never,
56
- wrapHandler(name, handler as ToolHandler) as never,
57
- );
58
- };
42
+ export function registerDocsmintMcpCapabilities(server: McpServer, client: HiaiDocsClient): void {
43
+ const register = <Args>(
44
+ name: string,
45
+ description: string,
46
+ inputSchema: ZodRawShape,
47
+ handler: (args: Args) => Promise<unknown>
48
+ ): void => {
49
+ server.registerTool(
50
+ name,
51
+ { description, inputSchema: z.object(inputSchema) },
52
+ wrapHandler(name, handler as ToolHandler) as never
53
+ );
54
+ };
59
55
 
60
- const tools = [
61
- search,
62
- getDocument,
63
- createDocument,
64
- updateDocument,
65
- listDocuments,
66
- listFolders,
67
- createFolder,
68
- createSnapshot,
69
- versionHistory,
70
- exportDocument,
71
- ] as const;
72
- for (const tool of tools) {
73
- register(
74
- tool.definition.name,
75
- tool.definition.description,
76
- tool.definition.inputSchema as ZodRawShape,
77
- tool.handler as ToolHandler,
78
- );
79
- }
80
- registerExtendedCapabilities(server);
81
- return server;
56
+ const tools = [
57
+ search,
58
+ getDocument,
59
+ createDocument,
60
+ updateDocument,
61
+ listDocuments,
62
+ listFolders,
63
+ createFolder,
64
+ createSnapshot,
65
+ versionHistory,
66
+ exportDocument,
67
+ ] as const;
68
+ for (const tool of tools) {
69
+ register(
70
+ tool.definition.name,
71
+ tool.definition.description,
72
+ tool.definition.inputSchema as ZodRawShape,
73
+ tool.createHandler(client) as ToolHandler
74
+ );
75
+ }
76
+ registerExtendedCapabilities(server, client);
77
+ }
78
+
79
+ export interface CreateDocsmintMcpServerOptions {
80
+ client?: HiaiDocsClient;
81
+ }
82
+
83
+ export function createDocsmintMcpServer(options: CreateDocsmintMcpServerOptions = {}): McpServer {
84
+ const server = new McpServer({ name: 'docsmint', version: '0.6.2' });
85
+ registerDocsmintMcpCapabilities(server, options.client ?? defaultClient);
86
+ return server;
82
87
  }
@@ -1,39 +1,34 @@
1
- import { z } from "zod";
2
- import { client } from "../client.js";
3
- import type { DocumentDetail } from "../types.js";
1
+ import { z } from 'zod';
2
+ import { client, type HiaiDocsClient } from '../client.js';
3
+ import type { DocumentDetail } from '../types.js';
4
4
 
5
5
  export const definition = {
6
- name: "create_document",
7
- description:
8
- "Create a new document. Optionally provide initial markdown content and a folder ID.",
9
- inputSchema: {
10
- title: z.string().describe("Document title."),
11
- content: z
12
- .string()
13
- .optional()
14
- .describe("Initial markdown content for the document."),
15
- folderId: z
16
- .string()
17
- .optional()
18
- .describe("Optional folder ID to place the document in."),
19
- categoryId: z
20
- .string()
21
- .optional()
22
- .describe(
23
- "Optional category ID. Category keys are always rebound to their configured category.",
24
- ),
25
- },
6
+ name: 'create_document',
7
+ description:
8
+ 'Create a new document. Optionally provide initial markdown content and a folder ID.',
9
+ inputSchema: {
10
+ title: z.string().describe('Document title.'),
11
+ content: z.string().optional().describe('Initial markdown content for the document.'),
12
+ folderId: z.string().optional().describe('Optional folder ID to place the document in.'),
13
+ categoryId: z
14
+ .string()
15
+ .optional()
16
+ .describe(
17
+ 'Optional category ID. Category keys are always rebound to their configured category.'
18
+ ),
19
+ },
26
20
  } as const;
27
21
 
28
22
  export interface CreateDocumentArgs {
29
- title: string;
30
- content?: string;
31
- folderId?: string;
32
- categoryId?: string;
23
+ title: string;
24
+ content?: string;
25
+ folderId?: string;
26
+ categoryId?: string;
33
27
  }
34
28
 
35
- export async function handler(
36
- args: CreateDocumentArgs,
37
- ): Promise<DocumentDetail> {
38
- return (await client.createDocument(args)) as DocumentDetail;
39
- }
29
+ export const createHandler = (api: HiaiDocsClient) =>
30
+ async function createDocument(args: CreateDocumentArgs): Promise<DocumentDetail> {
31
+ return (await api.createDocument(args)) as DocumentDetail;
32
+ };
33
+
34
+ export const handler = createHandler(client);
@@ -1,31 +1,31 @@
1
- import { z } from "zod";
2
- import { client } from "../client.js";
3
- import type { Folder } from "../types.js";
1
+ import { z } from 'zod';
2
+ import { client, type HiaiDocsClient } from '../client.js';
3
+ import type { Folder } from '../types.js';
4
4
 
5
5
  export const definition = {
6
- name: "create_folder",
7
- description: "Create a new folder, optionally nested under a parent folder.",
8
- inputSchema: {
9
- name: z.string().describe("Folder name."),
10
- parentId: z
11
- .string()
12
- .optional()
13
- .describe("Optional parent folder ID for nesting."),
14
- categoryId: z
15
- .string()
16
- .optional()
17
- .describe(
18
- "Optional category ID. Category keys are always rebound to their configured category.",
19
- ),
20
- },
6
+ name: 'create_folder',
7
+ description: 'Create a new folder, optionally nested under a parent folder.',
8
+ inputSchema: {
9
+ name: z.string().describe('Folder name.'),
10
+ parentId: z.string().optional().describe('Optional parent folder ID for nesting.'),
11
+ categoryId: z
12
+ .string()
13
+ .optional()
14
+ .describe(
15
+ 'Optional category ID. Category keys are always rebound to their configured category.'
16
+ ),
17
+ },
21
18
  } as const;
22
19
 
23
20
  export interface CreateFolderArgs {
24
- name: string;
25
- parentId?: string;
26
- categoryId?: string;
21
+ name: string;
22
+ parentId?: string;
23
+ categoryId?: string;
27
24
  }
28
25
 
29
- export async function handler(args: CreateFolderArgs): Promise<Folder> {
30
- return (await client.createFolder(args)) as Folder;
31
- }
26
+ export const createHandler = (api: HiaiDocsClient) =>
27
+ async function createFolder(args: CreateFolderArgs): Promise<Folder> {
28
+ return (await api.createFolder(args)) as Folder;
29
+ };
30
+
31
+ export const handler = createHandler(client);
@@ -1,20 +1,14 @@
1
- import { z } from "zod";
2
- import { client } from "../client.js";
3
- import type { Version } from "../types.js";
1
+ import { z } from 'zod';
2
+ import { client, type HiaiDocsClient } from '../client.js';
3
+ import type { Version } from '../types.js';
4
4
 
5
5
  export const definition = {
6
- name: "create_snapshot",
7
- description:
8
- "Create a named snapshot (labelled version) of a document from its current content.",
6
+ name: 'create_snapshot',
7
+ description: 'Create a named snapshot (labelled version) of a document from its current content.',
9
8
  inputSchema: {
10
- documentId: z.string().describe("Document ID to snapshot."),
11
- label: z
12
- .string()
13
- .describe("Short label for the snapshot (e.g. 'v1.0-release')."),
14
- description: z
15
- .string()
16
- .optional()
17
- .describe("Optional longer description of the snapshot."),
9
+ documentId: z.string().describe('Document ID to snapshot.'),
10
+ label: z.string().describe("Short label for the snapshot (e.g. 'v1.0-release')."),
11
+ description: z.string().optional().describe('Optional longer description of the snapshot.'),
18
12
  },
19
13
  } as const;
20
14
 
@@ -24,7 +18,10 @@ export interface CreateSnapshotArgs {
24
18
  description?: string;
25
19
  }
26
20
 
27
- export async function handler(args: CreateSnapshotArgs): Promise<Version> {
28
- const { documentId, ...input } = args;
29
- return (await client.createSnapshot(documentId, input)) as Version;
30
- }
21
+ export const createHandler = (api: HiaiDocsClient) =>
22
+ async function createSnapshot(args: CreateSnapshotArgs): Promise<Version> {
23
+ const { documentId, ...input } = args;
24
+ return (await api.createSnapshot(documentId, input)) as Version;
25
+ };
26
+
27
+ export const handler = createHandler(client);
@@ -1,13 +1,12 @@
1
- import { z } from "zod";
2
- import { client } from "../client.js";
3
- import type { ExportResponse } from "../types.js";
1
+ import { z } from 'zod';
2
+ import { client, type HiaiDocsClient } from '../client.js';
3
+ import type { ExportResponse } from '../types.js';
4
4
 
5
5
  export const definition = {
6
- name: "export_document",
7
- description:
8
- "Export a document as markdown. Returns the rendered markdown content.",
6
+ name: 'export_document',
7
+ description: 'Export a document as markdown. Returns the rendered markdown content.',
9
8
  inputSchema: {
10
- id: z.string().describe("Document ID to export."),
9
+ id: z.string().describe('Document ID to export.'),
11
10
  },
12
11
  } as const;
13
12
 
@@ -15,8 +14,9 @@ export interface ExportDocumentArgs {
15
14
  id: string;
16
15
  }
17
16
 
18
- export async function handler(
19
- args: ExportDocumentArgs,
20
- ): Promise<ExportResponse> {
21
- return (await client.exportDocument(args.id)) as ExportResponse;
22
- }
17
+ export const createHandler = (api: HiaiDocsClient) =>
18
+ async function exportDocument(args: ExportDocumentArgs): Promise<ExportResponse> {
19
+ return (await api.exportDocument(args.id)) as ExportResponse;
20
+ };
21
+
22
+ export const handler = createHandler(client);
@@ -1,13 +1,12 @@
1
- import { z } from "zod";
2
- import { client } from "../client.js";
3
- import type { DocumentDetail } from "../types.js";
1
+ import { z } from 'zod';
2
+ import { client, type HiaiDocsClient } from '../client.js';
3
+ import type { DocumentDetail } from '../types.js';
4
4
 
5
5
  export const definition = {
6
- name: "get_document",
7
- description:
8
- "Fetch a single document by ID. Returns full content, metadata, and tags.",
6
+ name: 'get_document',
7
+ description: 'Fetch a single document by ID. Returns full content, metadata, and tags.',
9
8
  inputSchema: {
10
- id: z.string().describe("Document ID."),
9
+ id: z.string().describe('Document ID.'),
11
10
  },
12
11
  } as const;
13
12
 
@@ -15,6 +14,9 @@ export interface GetDocumentArgs {
15
14
  id: string;
16
15
  }
17
16
 
18
- export async function handler(args: GetDocumentArgs): Promise<DocumentDetail> {
19
- return (await client.getDocument(args.id)) as DocumentDetail;
20
- }
17
+ export const createHandler = (api: HiaiDocsClient) =>
18
+ async function getDocument(args: GetDocumentArgs): Promise<DocumentDetail> {
19
+ return (await api.getDocument(args.id)) as DocumentDetail;
20
+ };
21
+
22
+ export const handler = createHandler(client);
@@ -1,30 +1,21 @@
1
- import { z } from "zod";
2
- import { client } from "../client.js";
3
- import type { ListDocumentsResponse } from "../types.js";
1
+ import { z } from 'zod';
2
+ import { client, type HiaiDocsClient } from '../client.js';
3
+ import type { ListDocumentsResponse } from '../types.js';
4
4
 
5
5
  export const definition = {
6
- name: "list_documents",
7
- description:
8
- "List documents with pagination, optionally filtered by folder or tag.",
6
+ name: 'list_documents',
7
+ description: 'List documents with pagination, optionally filtered by folder or tag.',
9
8
  inputSchema: {
10
- folderId: z
11
- .string()
12
- .optional()
13
- .describe("Optional folder ID to filter by."),
14
- tag: z.string().optional().describe("Optional tag ID to filter by."),
15
- page: z
16
- .number()
17
- .int()
18
- .positive()
19
- .optional()
20
- .describe("Page number (1-indexed, default 1)."),
9
+ folderId: z.string().optional().describe('Optional folder ID to filter by.'),
10
+ tag: z.string().optional().describe('Optional tag ID to filter by.'),
11
+ page: z.number().int().positive().optional().describe('Page number (1-indexed, default 1).'),
21
12
  limit: z
22
13
  .number()
23
14
  .int()
24
15
  .positive()
25
16
  .max(100)
26
17
  .optional()
27
- .describe("Items per page (default 20, max 100)."),
18
+ .describe('Items per page (default 20, max 100).'),
28
19
  },
29
20
  } as const;
30
21
 
@@ -35,8 +26,9 @@ export interface ListDocumentsArgs {
35
26
  limit?: number;
36
27
  }
37
28
 
38
- export async function handler(
39
- args: ListDocumentsArgs,
40
- ): Promise<ListDocumentsResponse> {
41
- return (await client.listDocuments(args)) as ListDocumentsResponse;
42
- }
29
+ export const createHandler = (api: HiaiDocsClient) =>
30
+ async function listDocuments(args: ListDocumentsArgs): Promise<ListDocumentsResponse> {
31
+ return (await api.listDocuments(args)) as ListDocumentsResponse;
32
+ };
33
+
34
+ export const handler = createHandler(client);
@@ -1,18 +1,16 @@
1
- import { z } from "zod";
2
- import { client } from "../client.js";
3
- import type { Folder } from "../types.js";
1
+ import { z } from 'zod';
2
+ import { client, type HiaiDocsClient } from '../client.js';
3
+ import type { Folder } from '../types.js';
4
4
 
5
5
  export const definition = {
6
- name: "list_folders",
6
+ name: 'list_folders',
7
7
  description:
8
- "List folders, optionally scoped to a parent folder. Returns a flat list of immediate children.",
8
+ 'List folders, optionally scoped to a parent folder. Returns a flat list of immediate children.',
9
9
  inputSchema: {
10
10
  parentId: z
11
11
  .string()
12
12
  .optional()
13
- .describe(
14
- "Optional parent folder ID. Omit to list top-level (root) folders.",
15
- ),
13
+ .describe('Optional parent folder ID. Omit to list top-level (root) folders.'),
16
14
  },
17
15
  } as const;
18
16
 
@@ -20,6 +18,9 @@ export interface ListFoldersArgs {
20
18
  parentId?: string;
21
19
  }
22
20
 
23
- export async function handler(args: ListFoldersArgs): Promise<Folder[]> {
24
- return (await client.listFolders(args)) as Folder[];
25
- }
21
+ export const createHandler = (api: HiaiDocsClient) =>
22
+ async function listFolders(args: ListFoldersArgs): Promise<Folder[]> {
23
+ return (await api.listFolders(args)) as Folder[];
24
+ };
25
+
26
+ export const handler = createHandler(client);
@@ -1,27 +1,21 @@
1
- import { z } from "zod";
2
- import { client } from "../client.js";
3
- import type { SearchResponse } from "../types.js";
1
+ import { z } from 'zod';
2
+ import { client, type HiaiDocsClient } from '../client.js';
3
+ import type { SearchResponse } from '../types.js';
4
4
 
5
5
  export const definition = {
6
- name: "search_documents",
6
+ name: 'search_documents',
7
7
  description:
8
- "Hybrid search across documents (full-text + semantic). Supports filtering by folder and tags.",
8
+ 'Hybrid search across documents (full-text + semantic). Supports filtering by folder and tags.',
9
9
  inputSchema: {
10
- query: z.string().describe("Search query string."),
11
- folder: z
12
- .string()
13
- .optional()
14
- .describe("Optional folder ID to scope the search to."),
15
- tags: z
16
- .array(z.string())
17
- .optional()
18
- .describe("Optional tag IDs to filter by."),
10
+ query: z.string().describe('Search query string.'),
11
+ folder: z.string().optional().describe('Optional folder ID to scope the search to.'),
12
+ tags: z.array(z.string()).optional().describe('Optional tag IDs to filter by.'),
19
13
  limit: z
20
14
  .number()
21
15
  .int()
22
16
  .positive()
23
17
  .optional()
24
- .describe("Maximum number of results to return (default 20)."),
18
+ .describe('Maximum number of results to return (default 20).'),
25
19
  },
26
20
  } as const;
27
21
 
@@ -32,11 +26,14 @@ export type SearchArgs = {
32
26
  limit?: number;
33
27
  };
34
28
 
35
- export async function handler(args: SearchArgs): Promise<SearchResponse> {
36
- return (await client.search({
37
- query: args.query,
38
- folder: args.folder,
39
- tags: args.tags,
40
- limit: args.limit,
41
- })) as SearchResponse;
42
- }
29
+ export const createHandler = (api: HiaiDocsClient) =>
30
+ async function searchDocuments(args: SearchArgs): Promise<SearchResponse> {
31
+ return (await api.search({
32
+ query: args.query,
33
+ folder: args.folder,
34
+ tags: args.tags,
35
+ limit: args.limit,
36
+ })) as SearchResponse;
37
+ };
38
+
39
+ export const handler = createHandler(client);
@@ -1,44 +1,38 @@
1
- import { z } from "zod";
2
- import { client } from "../client.js";
3
- import type { DocumentDetail } from "../types.js";
1
+ import { z } from 'zod';
2
+ import { client, type HiaiDocsClient } from '../client.js';
3
+ import type { DocumentDetail } from '../types.js';
4
4
 
5
5
  export const definition = {
6
- name: "update_document",
7
- description:
8
- "Update an existing document's title and/or content. The server creates a new version on each update.",
9
- inputSchema: {
10
- id: z.string().describe("Document ID to update."),
11
- title: z.string().optional().describe("New title for the document."),
12
- content: z
13
- .string()
14
- .optional()
15
- .describe("New markdown content for the document."),
16
- folderId: z
17
- .string()
18
- .nullable()
19
- .optional()
20
- .describe("Move the document to a folder."),
21
- categoryId: z
22
- .string()
23
- .nullable()
24
- .optional()
25
- .describe(
26
- "Move the document to a category. Category keys cannot escape their configured category.",
27
- ),
28
- },
6
+ name: 'update_document',
7
+ description:
8
+ "Update an existing document's title and/or content. The server creates a new version on each update.",
9
+ inputSchema: {
10
+ id: z.string().describe('Document ID to update.'),
11
+ title: z.string().optional().describe('New title for the document.'),
12
+ content: z.string().optional().describe('New markdown content for the document.'),
13
+ folderId: z.string().nullable().optional().describe('Move the document to a folder.'),
14
+ categoryId: z
15
+ .string()
16
+ .nullable()
17
+ .optional()
18
+ .describe(
19
+ 'Move the document to a category. Category keys cannot escape their configured category.'
20
+ ),
21
+ },
29
22
  } as const;
30
23
 
31
24
  export interface UpdateDocumentArgs {
32
- id: string;
33
- title?: string;
34
- content?: string;
35
- folderId?: string | null;
36
- categoryId?: string | null;
25
+ id: string;
26
+ title?: string;
27
+ content?: string;
28
+ folderId?: string | null;
29
+ categoryId?: string | null;
37
30
  }
38
31
 
39
- export async function handler(
40
- args: UpdateDocumentArgs,
41
- ): Promise<DocumentDetail> {
42
- const { id, ...patch } = args;
43
- return (await client.updateDocument(id, patch)) as DocumentDetail;
44
- }
32
+ export const createHandler = (api: HiaiDocsClient) =>
33
+ async function updateDocument(args: UpdateDocumentArgs): Promise<DocumentDetail> {
34
+ const { id, ...patch } = args;
35
+ return (await api.updateDocument(id, patch)) as DocumentDetail;
36
+ };
37
+
38
+ export const handler = createHandler(client);
@@ -1,21 +1,16 @@
1
- import { z } from "zod";
2
- import { client } from "../client.js";
3
- import type { Version } from "../types.js";
1
+ import { z } from 'zod';
2
+ import { client, type HiaiDocsClient } from '../client.js';
3
+ import type { Version } from '../types.js';
4
4
 
5
5
  export const definition = {
6
- name: "get_version_history",
7
- description:
8
- "List the version history for a document. Optionally restrict to named snapshots.",
6
+ name: 'get_version_history',
7
+ description: 'List the version history for a document. Optionally restrict to named snapshots.',
9
8
  inputSchema: {
10
- documentId: z
11
- .string()
12
- .describe("Document ID whose versions should be listed."),
9
+ documentId: z.string().describe('Document ID whose versions should be listed.'),
13
10
  onlySnapshots: z
14
11
  .boolean()
15
12
  .optional()
16
- .describe(
17
- "When true, only return named snapshots (skip auto-saved revisions).",
18
- ),
13
+ .describe('When true, only return named snapshots (skip auto-saved revisions).'),
19
14
  },
20
15
  } as const;
21
16
 
@@ -24,9 +19,9 @@ export interface VersionHistoryArgs {
24
19
  onlySnapshots?: boolean;
25
20
  }
26
21
 
27
- export async function handler(args: VersionHistoryArgs): Promise<Version[]> {
28
- return (await client.getVersionHistory(
29
- args.documentId,
30
- args.onlySnapshots,
31
- )) as Version[];
32
- }
22
+ export const createHandler = (api: HiaiDocsClient) =>
23
+ async function getVersionHistory(args: VersionHistoryArgs): Promise<Version[]> {
24
+ return (await api.getVersionHistory(args.documentId, args.onlySnapshots)) as Version[];
25
+ };
26
+
27
+ export const handler = createHandler(client);
package/server.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
+ "name": "io.github.hiai-gg/docsmint",
4
+ "title": "DocsMint",
5
+ "description": "Manage scoped DocsMint documents, folders, categories, search, GraphRAG, and indexing through MCP.",
6
+ "repository": {
7
+ "url": "https://github.com/HiAi-gg/docsmint",
8
+ "source": "github",
9
+ "id": "1249550690"
10
+ },
11
+ "version": "0.6.2",
12
+ "packages": [
13
+ {
14
+ "registryType": "npm",
15
+ "identifier": "@hiai-gg/docsmint",
16
+ "version": "0.6.2",
17
+ "transport": {
18
+ "type": "stdio"
19
+ },
20
+ "environmentVariables": [
21
+ {
22
+ "name": "HIAI_DOCS_URL",
23
+ "description": "Base URL of the DocsMint API.",
24
+ "default": "http://localhost:50700"
25
+ },
26
+ {
27
+ "name": "HIAI_DOCS_API_KEY",
28
+ "description": "Workspace or category scoped DocsMint API key.",
29
+ "isRequired": true,
30
+ "isSecret": true
31
+ }
32
+ ]
33
+ }
34
+ ],
35
+ "remotes": [
36
+ {
37
+ "type": "streamable-http",
38
+ "url": "https://docsmint.com/mcp",
39
+ "headers": [
40
+ {
41
+ "name": "Authorization",
42
+ "description": "Bearer DocsMint API key.",
43
+ "isRequired": true,
44
+ "isSecret": true
45
+ },
46
+ {
47
+ "name": "X-Docsmint-Workspace",
48
+ "description": "Optional workspace override for a workspace scoped key.",
49
+ "isRequired": false,
50
+ "isSecret": false
51
+ }
52
+ ]
53
+ }
54
+ ]
55
+ }