@hiai-gg/docsmint 0.8.6 → 0.8.8

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/dist/mcp-cli.js CHANGED
@@ -20,7 +20,7 @@ import { serveStdio } from "@modelcontextprotocol/server/stdio";
20
20
 
21
21
  // ../mcp-server/src/server.ts
22
22
  import { McpServer } from "@modelcontextprotocol/server";
23
- import { z as z12 } from "zod";
23
+ import { z as z13 } from "zod";
24
24
 
25
25
  // dist/client.js
26
26
  var docsApiErrorBrand = Symbol.for("io.github.hiai-gg.docsmint.DocsApiError");
@@ -581,52 +581,87 @@ var RESOURCE_PERMISSIONS = new Set([
581
581
  "edit",
582
582
  "write"
583
583
  ]);
584
- // ../mcp-server/src/capabilities.ts
584
+ // ../mcp-server/src/lifecycle.ts
585
585
  import { z } from "zod";
586
+ function unsupported(name) {
587
+ throw new Error(`The injected legacy MCP client does not support ${name}; use a public DocsClient.`);
588
+ }
589
+ var annotations = { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false };
590
+ function registerLifecycleCapabilities(server, client, wrap) {
591
+ const deletions = [
592
+ { name: "delete_document", description: "Move a document to trash. Requires write access to the document; category keys are limited to their category. Does not permanently purge content. Returns the document ID and deleted=true. Use only when the user intends deletion.", action: (id) => client.deleteDocument ? client.deleteDocument(id) : unsupported("delete_document"), idDescription: "UUID of the document to move to trash; obtain it from search_documents, list_documents, or get_document." },
593
+ { name: "delete_folder", description: "Delete a folder while preserving its documents. Direct child folders and documents are detached according to the server folder rules, and reindexing is queued for affected documents. Requires write access in the permitted workspace or category. Returns the folder ID and deleted=true.", action: (id) => client.deleteFolder ? client.deleteFolder(id) : unsupported("delete_folder"), idDescription: "UUID of the folder to delete; obtain it from list_folders. This does not delete the documents inside it." },
594
+ { name: "delete_category", description: "Delete a category and detach its folders and documents without deleting their content. Requires full workspace write access; category-scoped keys cannot delete any category, including their own. Returns the category ID and deleted=true.", action: (id) => client.deleteCategory ? client.deleteCategory(id) : unsupported("delete_category"), idDescription: "UUID of the category to delete; obtain it from list_categories using a workspace key." }
595
+ ];
596
+ for (const tool of deletions) {
597
+ server.registerTool(tool.name, { description: tool.description, annotations, inputSchema: z.object({ id: z.string().uuid().describe(tool.idDescription) }) }, wrap(async ({ id }) => {
598
+ await tool.action(id);
599
+ return { id, deleted: true };
600
+ }));
601
+ }
602
+ server.registerTool("restore_document_version", {
603
+ description: "Restore document content from a version or named snapshot returned by get_version_history. Requires edit access to that document. The server backs up the current content, records the restoration, and queues document reindexing. Returns the updated document. Does not restore a document from trash or restore folder/category placement.",
604
+ annotations,
605
+ inputSchema: z.object({
606
+ documentId: z.string().uuid().describe("UUID of the document whose content should be restored; it must be visible in the active workspace or category."),
607
+ versionId: z.string().uuid().describe("UUID of a version belonging to this document, from get_version_history. Use the version ID, not a snapshot label.")
608
+ })
609
+ }, wrap(async ({ documentId, versionId }) => client.restoreDocumentVersion ? client.restoreDocumentVersion(documentId, versionId) : unsupported("restore_document_version")));
610
+ }
611
+
612
+ // ../mcp-server/src/capabilities.ts
613
+ import { z as z2 } from "zod";
586
614
  function registerExtendedCapabilities(server, client, wrapHandler) {
587
615
  server.registerTool("list_categories", {
616
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
588
617
  description: "List categories visible to the API key. Category keys receive only their bound category.",
589
- inputSchema: z.object({})
618
+ inputSchema: z2.object({})
590
619
  }, wrapHandler(async () => client.listCategories()));
591
620
  server.registerTool("create_category", {
621
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
592
622
  description: "Create a category. Requires a workspace key with write access; category keys cannot mutate categories.",
593
- inputSchema: z.object({
594
- name: z.string().min(1),
595
- description: z.string().optional()
623
+ inputSchema: z2.object({
624
+ name: z2.string().min(1).describe("Non-empty display name for the new category, for example Project notes."),
625
+ description: z2.string().optional().describe("Optional plain-text explanation of the category purpose; omit if not needed.")
596
626
  })
597
627
  }, wrapHandler(async (input) => client.createCategory(input)));
598
628
  server.registerTool("list_tags", {
629
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
599
630
  description: "List tags visible in the workspace or bound category.",
600
- inputSchema: z.object({})
631
+ inputSchema: z2.object({})
601
632
  }, wrapHandler(async () => client.listTags()));
602
633
  server.registerTool("get_related_documents", {
603
- description: "Traverse the knowledge graph from one authorized document.",
604
- inputSchema: z.object({
605
- documentId: z.string().min(1),
606
- limit: z.number().int().min(1).max(50).optional()
634
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
635
+ description: "Find graph-related documents from one readable document, without a text query. Returns related documents and relation metadata. Use search_knowledge_graph to filter graph neighbors by text, or search_documents for normal retrieval. Results stay within the active scope and may be empty when graph data is unavailable.",
636
+ inputSchema: z2.object({
637
+ documentId: z2.string().min(1).describe("Document UUID returned by search_documents or list_documents; must be readable in the active scope."),
638
+ limit: z2.number().int().min(1).max(50).optional().describe("Maximum related documents to return, from 1 to 50. Omit to use the server default.")
607
639
  })
608
640
  }, wrapHandler(async ({ documentId, limit }) => client.getRelatedDocuments(documentId, limit)));
609
641
  server.registerTool("search_knowledge_graph", {
610
- description: "Search connected knowledge using authorized seed documents. Category keys may use only documents in their category.",
611
- inputSchema: z.object({
612
- query: z.string().min(1).max(1000),
613
- docIds: z.array(z.string().min(1)).min(1),
614
- limit: z.number().int().min(1).max(50).optional()
642
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
643
+ description: "Retrieve graph context from one or more readable seed documents and filter/rank related documents by query text. Returns seed entities and relatedDocs; use search_documents first to obtain seed IDs. Returned documents stay within the active category. Graph data may be empty when unavailable.",
644
+ inputSchema: z2.object({
645
+ query: z2.string().min(1).max(1000).describe("Search text used to filter and rank graph-related documents. Preserve the original query language."),
646
+ docIds: z2.array(z2.string().min(1)).min(1).max(50).describe("Between 1 and 50 authorized document UUIDs from search_documents or list_documents to use as graph traversal seeds."),
647
+ limit: z2.number().int().min(1).max(50).optional().describe("Maximum related documents to return, from 1 to 50. Omit to use the server default.")
615
648
  })
616
649
  }, wrapHandler(async (input) => client.searchGraph(input)));
617
650
  server.registerTool("get_document_index_status", {
618
- description: "Read the current indexing and knowledge-pipeline status of a document.",
619
- inputSchema: z.object({ documentId: z.string().min(1) })
651
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
652
+ description: "Read a document indexing and knowledge-pipeline status without starting any work. Use after a save or refresh_document_index to inspect progress and failures. Requires read access to the document.",
653
+ inputSchema: z2.object({ documentId: z2.string().min(1).describe("Document UUID returned by search_documents or list_documents; must be readable in the active scope.") })
620
654
  }, wrapHandler(async ({ documentId }) => client.getDocumentIndexStatus(documentId)));
621
655
  server.registerTool("refresh_document_index", {
622
- description: "Request reindexing after a document or metadata change. Requires write access.",
623
- inputSchema: z.object({ documentId: z.string().min(1) })
656
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
657
+ description: "Queue document reindexing and return the server acknowledgment; processing completes asynchronously. Requires write access. Normal saves already schedule indexing; use this for an explicit refresh and inspect progress with get_document_index_status.",
658
+ inputSchema: z2.object({ documentId: z2.string().min(1).describe("Document UUID returned by search_documents or list_documents; must be readable in the active scope.") })
624
659
  }, wrapHandler(async ({ documentId }) => client.refreshDocumentIndex(documentId)));
625
660
  server.registerPrompt("organize_workspace", {
626
661
  description: "Plan safe document organization using DocsMint categories and folders.",
627
- argsSchema: z.object({
628
- objective: z.string(),
629
- language: z.string().optional()
662
+ argsSchema: z2.object({
663
+ objective: z2.string(),
664
+ language: z2.string().optional()
630
665
  })
631
666
  }, ({ objective, language }) => ({
632
667
  messages: [
@@ -641,9 +676,9 @@ function registerExtendedCapabilities(server, client, wrapHandler) {
641
676
  }));
642
677
  server.registerPrompt("research_workspace", {
643
678
  description: "Research a question with hybrid search and GraphRAG while citing DocsMint document IDs.",
644
- argsSchema: z.object({
645
- question: z.string(),
646
- language: z.string().optional()
679
+ argsSchema: z2.object({
680
+ question: z2.string(),
681
+ language: z2.string().optional()
647
682
  })
648
683
  }, ({ question, language }) => ({
649
684
  messages: [
@@ -734,6 +769,10 @@ function sanitizeMcpRequestContext(context) {
734
769
  function createMcpDocsClient(docsClient, requestContext) {
735
770
  const context = sanitizeMcpRequestContext(requestContext);
736
771
  return {
772
+ deleteDocument: (id) => docsClient.deleteDoc(id, context),
773
+ deleteFolder: (id) => docsClient.deleteFolder(id, context),
774
+ deleteCategory: (id) => docsClient.deleteCategory(id, context),
775
+ restoreDocumentVersion: (documentId, versionId) => docsClient.restoreVersion(documentId, versionId, context),
737
776
  search: (params) => docsClient.search(params.query, {
738
777
  folder: params.folder,
739
778
  tags: params.tags?.join(","),
@@ -780,15 +819,15 @@ __export(exports_create_document, {
780
819
  definition: () => definition,
781
820
  handler: () => handler
782
821
  });
783
- import { z as z2 } from "zod";
822
+ import { z as z3 } from "zod";
784
823
  var definition = {
785
824
  name: "create_document",
786
825
  description: "Create a new document. Optionally provide initial markdown content and a folder ID.",
787
826
  inputSchema: {
788
- title: z2.string().describe("Document title."),
789
- content: z2.string().optional().describe("Initial markdown content for the document."),
790
- folderId: z2.string().optional().describe("Optional folder ID to place the document in."),
791
- categoryId: z2.string().optional().describe("Optional category ID. Category keys are always rebound to their configured category.")
827
+ title: z3.string().describe("Document title."),
828
+ content: z3.string().optional().describe("Initial markdown content for the document."),
829
+ folderId: z3.string().optional().describe("Optional folder ID to place the document in."),
830
+ categoryId: z3.string().optional().describe("Optional category ID. Category keys are always rebound to their configured category.")
792
831
  }
793
832
  };
794
833
  var createHandler = (api) => async function createDocument(args) {
@@ -803,14 +842,14 @@ __export(exports_create_folder, {
803
842
  definition: () => definition2,
804
843
  handler: () => handler2
805
844
  });
806
- import { z as z3 } from "zod";
845
+ import { z as z4 } from "zod";
807
846
  var definition2 = {
808
847
  name: "create_folder",
809
848
  description: "Create a new folder, optionally nested under a parent folder.",
810
849
  inputSchema: {
811
- name: z3.string().describe("Folder name."),
812
- parentId: z3.string().optional().describe("Optional parent folder ID for nesting."),
813
- categoryId: z3.string().optional().describe("Optional category ID. Category keys are always rebound to their configured category.")
850
+ name: z4.string().describe("Folder name."),
851
+ parentId: z4.string().optional().describe("Optional parent folder ID for nesting."),
852
+ categoryId: z4.string().optional().describe("Optional category ID. Category keys are always rebound to their configured category.")
814
853
  }
815
854
  };
816
855
  var createHandler2 = (api) => async function createFolder(args) {
@@ -825,14 +864,14 @@ __export(exports_create_snapshot, {
825
864
  definition: () => definition3,
826
865
  handler: () => handler3
827
866
  });
828
- import { z as z4 } from "zod";
867
+ import { z as z5 } from "zod";
829
868
  var definition3 = {
830
869
  name: "create_snapshot",
831
870
  description: "Create a named snapshot (labelled version) of a document from its current content.",
832
871
  inputSchema: {
833
- documentId: z4.string().describe("Document ID to snapshot."),
834
- label: z4.string().describe("Short label for the snapshot (e.g. 'v1.0-release')."),
835
- description: z4.string().optional().describe("Optional longer description of the snapshot.")
872
+ documentId: z5.string().describe("Document ID to snapshot."),
873
+ label: z5.string().describe("Short label for the snapshot (e.g. 'v1.0-release')."),
874
+ description: z5.string().optional().describe("Optional longer description of the snapshot.")
836
875
  }
837
876
  };
838
877
  var createHandler3 = (api) => async function createSnapshot(args) {
@@ -848,12 +887,12 @@ __export(exports_export_document, {
848
887
  definition: () => definition4,
849
888
  handler: () => handler4
850
889
  });
851
- import { z as z5 } from "zod";
890
+ import { z as z6 } from "zod";
852
891
  var definition4 = {
853
892
  name: "export_document",
854
- description: "Export a document as markdown. Returns the rendered markdown content.",
893
+ description: "Render one readable document as portable Markdown and return a markdown string in the response object. Does not modify the document or include its complete metadata; use get_document to inspect the editable document.",
855
894
  inputSchema: {
856
- id: z5.string().describe("Document ID to export.")
895
+ id: z6.string().describe("Document ID to export.")
857
896
  }
858
897
  };
859
898
  var createHandler4 = (api) => async function exportDocument(args) {
@@ -868,12 +907,12 @@ __export(exports_get_document, {
868
907
  definition: () => definition5,
869
908
  handler: () => handler5
870
909
  });
871
- import { z as z6 } from "zod";
910
+ import { z as z7 } from "zod";
872
911
  var definition5 = {
873
912
  name: "get_document",
874
- description: "Fetch a single document by ID. Returns full content, metadata, and tags.",
913
+ description: "Read one document with its content, metadata, and tags before editing or citing it. Requires read access. Use export_document when you only need portable Markdown.",
875
914
  inputSchema: {
876
- id: z6.string().describe("Document ID.")
915
+ id: z7.string().describe("Document ID.")
877
916
  }
878
917
  };
879
918
  var createHandler5 = (api) => async function getDocument(args) {
@@ -888,15 +927,15 @@ __export(exports_list_documents, {
888
927
  definition: () => definition6,
889
928
  handler: () => handler6
890
929
  });
891
- import { z as z7 } from "zod";
930
+ import { z as z8 } from "zod";
892
931
  var definition6 = {
893
932
  name: "list_documents",
894
933
  description: "List documents with pagination, optionally filtered by folder or tag.",
895
934
  inputSchema: {
896
- folderId: z7.string().optional().describe("Optional folder ID to filter by."),
897
- tag: z7.string().optional().describe("Optional tag ID to filter by."),
898
- page: z7.number().int().positive().optional().describe("Page number (1-indexed, default 1)."),
899
- limit: z7.number().int().positive().max(100).optional().describe("Items per page (default 20, max 100).")
935
+ folderId: z8.string().optional().describe("Optional folder ID to filter by."),
936
+ tag: z8.string().optional().describe("Optional tag ID to filter by."),
937
+ page: z8.number().int().positive().optional().describe("Page number (1-indexed, default 1)."),
938
+ limit: z8.number().int().positive().max(100).optional().describe("Items per page (default 20, max 100).")
900
939
  }
901
940
  };
902
941
  var createHandler6 = (api) => async function listDocuments(args) {
@@ -911,12 +950,12 @@ __export(exports_list_folders, {
911
950
  definition: () => definition7,
912
951
  handler: () => handler7
913
952
  });
914
- import { z as z8 } from "zod";
953
+ import { z as z9 } from "zod";
915
954
  var definition7 = {
916
955
  name: "list_folders",
917
956
  description: "List folders, optionally scoped to a parent folder. Returns a flat list of immediate children.",
918
957
  inputSchema: {
919
- parentId: z8.string().optional().describe("Optional parent folder ID. Omit to list top-level (root) folders.")
958
+ parentId: z9.string().optional().describe("Optional parent folder ID. Omit to list top-level (root) folders.")
920
959
  }
921
960
  };
922
961
  var createHandler7 = (api) => async function listFolders(args) {
@@ -931,15 +970,15 @@ __export(exports_search, {
931
970
  definition: () => definition8,
932
971
  handler: () => handler8
933
972
  });
934
- import { z as z9 } from "zod";
973
+ import { z as z10 } from "zod";
935
974
  var definition8 = {
936
975
  name: "search_documents",
937
976
  description: "Hybrid search across documents (full-text + semantic). Supports filtering by folder and tags.",
938
977
  inputSchema: {
939
- query: z9.string().describe("Search query string."),
940
- folder: z9.string().optional().describe("Optional folder ID to scope the search to."),
941
- tags: z9.array(z9.string()).optional().describe("Optional tag IDs to filter by."),
942
- limit: z9.number().int().positive().optional().describe("Maximum number of results to return (default 20).")
978
+ query: z10.string().describe("Search query string."),
979
+ folder: z10.string().optional().describe("Optional folder ID to scope the search to."),
980
+ tags: z10.array(z10.string()).optional().describe("Optional tag IDs to filter by."),
981
+ limit: z10.number().int().positive().optional().describe("Maximum number of results to return (default 20).")
943
982
  }
944
983
  };
945
984
  var createHandler8 = (api) => async function searchDocuments(args) {
@@ -959,16 +998,16 @@ __export(exports_update_document, {
959
998
  definition: () => definition9,
960
999
  handler: () => handler9
961
1000
  });
962
- import { z as z10 } from "zod";
1001
+ import { z as z11 } from "zod";
963
1002
  var definition9 = {
964
1003
  name: "update_document",
965
1004
  description: "Update an existing document's title and/or content. The server creates a new version on each update.",
966
1005
  inputSchema: {
967
- id: z10.string().describe("Document ID to update."),
968
- title: z10.string().optional().describe("New title for the document."),
969
- content: z10.string().optional().describe("New markdown content for the document."),
970
- folderId: z10.string().nullable().optional().describe("Move the document to a folder."),
971
- categoryId: z10.string().nullable().optional().describe("Move the document to a category. Category keys cannot escape their configured category.")
1006
+ id: z11.string().describe("Document ID to update."),
1007
+ title: z11.string().optional().describe("New title for the document."),
1008
+ content: z11.string().optional().describe("New markdown content for the document."),
1009
+ folderId: z11.string().nullable().optional().describe("Move the document to a folder."),
1010
+ categoryId: z11.string().nullable().optional().describe("Move the document to a category. Category keys cannot escape their configured category.")
972
1011
  }
973
1012
  };
974
1013
  var createHandler9 = (api) => async function updateDocument(args) {
@@ -984,13 +1023,13 @@ __export(exports_version_history, {
984
1023
  definition: () => definition10,
985
1024
  handler: () => handler10
986
1025
  });
987
- import { z as z11 } from "zod";
1026
+ import { z as z12 } from "zod";
988
1027
  var definition10 = {
989
1028
  name: "get_version_history",
990
1029
  description: "List the version history for a document. Optionally restrict to named snapshots.",
991
1030
  inputSchema: {
992
- documentId: z11.string().describe("Document ID whose versions should be listed."),
993
- onlySnapshots: z11.boolean().optional().describe("When true, only return named snapshots (skip auto-saved revisions).")
1031
+ documentId: z12.string().describe("Document ID whose versions should be listed."),
1032
+ onlySnapshots: z12.boolean().optional().describe("When true, only return named snapshots (skip auto-saved revisions).")
994
1033
  }
995
1034
  };
996
1035
  var createHandler10 = (api) => async function getVersionHistory(args) {
@@ -1032,7 +1071,12 @@ function wrapHandler(name, handler11) {
1032
1071
  }
1033
1072
  function registerDocsmintMcpCapabilities(server, client2) {
1034
1073
  const register = (name, description, inputSchema, handler11) => {
1035
- server.registerTool(name, { description, inputSchema: z12.object(inputSchema) }, wrapHandler(name, handler11));
1074
+ server.registerTool(name, { description, inputSchema: z13.object(inputSchema), annotations: {
1075
+ readOnlyHint: !["create_document", "update_document", "create_folder", "create_snapshot"].includes(name),
1076
+ destructiveHint: name === "update_document",
1077
+ idempotentHint: !["create_document", "update_document", "create_folder", "create_snapshot"].includes(name),
1078
+ openWorldHint: false
1079
+ } }, wrapHandler(name, handler11));
1036
1080
  };
1037
1081
  const tools = [
1038
1082
  exports_search,
@@ -1050,9 +1094,10 @@ function registerDocsmintMcpCapabilities(server, client2) {
1050
1094
  register(tool.definition.name, tool.definition.description, tool.definition.inputSchema, tool.createHandler(client2));
1051
1095
  }
1052
1096
  registerExtendedCapabilities(server, client2, (handler11) => wrapHandler("extended", handler11));
1097
+ registerLifecycleCapabilities(server, client2, (handler11) => wrapHandler("lifecycle", handler11));
1053
1098
  }
1054
1099
  function createDocsmintMcpServer(options = {}) {
1055
- const server = new McpServer({ name: "docsmint", version: "0.8.6" });
1100
+ const server = new McpServer({ name: "docsmint", version: "0.8.8" });
1056
1101
  const client2 = options.docsClient ? createMcpDocsClient(options.docsClient, options.requestContext) : options.client ?? createMcpDocsClient(createDefaultDocsClient(), options.requestContext);
1057
1102
  registerDocsmintMcpCapabilities(server, client2);
1058
1103
  return server;
@@ -3,6 +3,10 @@ import type { McpServer } from "@modelcontextprotocol/server";
3
3
  import type { DocsClient, DocsRequestContext } from "./index.js";
4
4
 
5
5
  export interface HiaiDocsClient {
6
+ deleteDocument?(id: string): Promise<void>;
7
+ deleteFolder?(id: string): Promise<void>;
8
+ deleteCategory?(id: string): Promise<void>;
9
+ restoreDocumentVersion?(documentId: string, versionId: string): Promise<unknown>;
6
10
  search(params: { query: string; folder?: string; tags?: string[]; limit?: number }): Promise<unknown>;
7
11
  getDocument(id: string): Promise<unknown>;
8
12
  createDocument(input: { title: string; content?: string; folderId?: string | null; categoryId?: string | null }): Promise<unknown>;