@hiai-gg/docsmint 0.8.8 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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 z13 } from "zod";
23
+ import { z as z14 } from "zod";
24
24
 
25
25
  // dist/client.js
26
26
  var docsApiErrorBrand = Symbol.for("io.github.hiai-gg.docsmint.DocsApiError");
@@ -582,86 +582,372 @@ var RESOURCE_PERMISSIONS = new Set([
582
582
  "write"
583
583
  ]);
584
584
  // ../mcp-server/src/lifecycle.ts
585
+ import { z as z2 } from "zod";
586
+
587
+ // ../mcp-server/src/output-schemas.ts
585
588
  import { z } from "zod";
589
+ var tagSchema = z.looseObject({
590
+ id: z.string(),
591
+ name: z.string(),
592
+ color: z.string().nullable(),
593
+ createdAt: z.string().optional(),
594
+ documentCount: z.number().optional()
595
+ });
596
+ var documentSchema = z.looseObject({
597
+ id: z.string(),
598
+ ownerId: z.string(),
599
+ folderId: z.string().nullable(),
600
+ categoryId: z.string().nullable(),
601
+ title: z.string(),
602
+ content: z.string(),
603
+ contentJson: z.unknown().optional(),
604
+ metadata: z.unknown().optional(),
605
+ visibility: z.enum(["private", "shared", "public"]),
606
+ createdAt: z.string(),
607
+ updatedAt: z.string(),
608
+ tags: z.array(tagSchema).optional(),
609
+ folderName: z.string().nullable().optional()
610
+ });
611
+ var documentListItemSchema = z.looseObject({
612
+ id: z.string(),
613
+ title: z.string(),
614
+ content: z.string(),
615
+ folderId: z.string().nullable(),
616
+ folderName: z.string().nullable(),
617
+ categoryId: z.string().nullable(),
618
+ categoryName: z.string().nullable(),
619
+ createdAt: z.string(),
620
+ updatedAt: z.string(),
621
+ tags: z.array(tagSchema)
622
+ });
623
+ var searchChunkSchema = z.looseObject({
624
+ chunkIndex: z.number(),
625
+ chunkText: z.string(),
626
+ charStart: z.number(),
627
+ charEnd: z.number(),
628
+ score: z.number()
629
+ });
630
+ var searchResultSchema = z.looseObject({
631
+ id: z.string(),
632
+ title: z.string(),
633
+ snippet: z.string(),
634
+ score: z.number(),
635
+ folder_id: z.string().nullable(),
636
+ folder_name: z.string().nullable(),
637
+ created_at: z.string(),
638
+ updated_at: z.string(),
639
+ tags: z.array(tagSchema).optional(),
640
+ chunks: z.array(searchChunkSchema).optional()
641
+ });
642
+ var categoryBaseSchema = z.looseObject({
643
+ id: z.string(),
644
+ name: z.string(),
645
+ order: z.number(),
646
+ createdAt: z.string(),
647
+ updatedAt: z.string(),
648
+ documentCount: z.number().optional(),
649
+ folderCount: z.number().optional()
650
+ });
651
+ var categorySchema = categoryBaseSchema.extend({
652
+ apiMode: z.enum(["unavailable", "global", "category"]),
653
+ apiPermissionRead: z.boolean(),
654
+ apiPermissionEdit: z.boolean(),
655
+ apiPermissionWrite: z.boolean()
656
+ });
657
+ var categoryListItemSchema = z.union([
658
+ categorySchema,
659
+ categoryBaseSchema
660
+ ]);
661
+ var graphEntitySchema = z.looseObject({
662
+ name: z.string(),
663
+ type: z.string()
664
+ });
665
+ var pipelineStatusSchema = z.enum([
666
+ "pending",
667
+ "processing",
668
+ "ready",
669
+ "retrying",
670
+ "failed",
671
+ "ready_with_warnings",
672
+ "skipped",
673
+ "cancelled"
674
+ ]);
675
+ var pipelineSchema = z.looseObject({
676
+ documentId: z.string(),
677
+ generationId: z.string(),
678
+ status: pipelineStatusSchema,
679
+ revision: z.string(),
680
+ stages: z.looseObject({
681
+ prepare: pipelineStatusSchema,
682
+ embed: pipelineStatusSchema,
683
+ graph: pipelineStatusSchema,
684
+ summarize: pipelineStatusSchema,
685
+ finalize: pipelineStatusSchema
686
+ }),
687
+ batches: z.looseObject({
688
+ total: z.number(),
689
+ completed: z.number(),
690
+ failed: z.number()
691
+ }),
692
+ warnings: z.array(z.looseObject({
693
+ stage: z.enum(["graph", "summarize"]),
694
+ code: z.string(),
695
+ retryable: z.boolean()
696
+ })),
697
+ updatedAt: z.string()
698
+ });
699
+ var indexStatusSchema = z.looseObject({
700
+ documentId: z.string(),
701
+ embeddingStatus: z.enum(["pending", "processing", "ready", "failed", "stale"]),
702
+ activeGenerationId: z.string().nullable(),
703
+ pendingGenerationId: z.string().nullable(),
704
+ embeddingProfile: z.string().nullable(),
705
+ embeddingErrorCode: z.string().nullable(),
706
+ embeddingUpdatedAt: z.string().nullable(),
707
+ searchable: z.boolean(),
708
+ pipeline: pipelineSchema.nullable()
709
+ });
710
+ var versionSchema = z.looseObject({
711
+ id: z.string(),
712
+ documentId: z.string(),
713
+ content: z.string(),
714
+ contentJson: z.unknown().optional(),
715
+ createdBy: z.string(),
716
+ createdAt: z.string(),
717
+ label: z.string().nullable().optional(),
718
+ description: z.string().nullable().optional(),
719
+ isSnapshot: z.boolean().optional(),
720
+ restoredFrom: z.string().nullable().optional()
721
+ });
722
+ var folderSchema = z.looseObject({
723
+ id: z.string(),
724
+ ownerId: z.string(),
725
+ parentId: z.string().nullable(),
726
+ name: z.string(),
727
+ createdAt: z.string(),
728
+ updatedAt: z.string(),
729
+ categoryId: z.string().nullable().optional(),
730
+ order: z.number().optional(),
731
+ documentCount: z.number().optional(),
732
+ subfolderCount: z.number().optional(),
733
+ children: z.array(z.lazy(() => folderSchema)).optional(),
734
+ documents: z.array(documentListItemSchema).optional()
735
+ });
736
+ var exportedDocumentSchema = z.looseObject({
737
+ markdown: z.string(),
738
+ filename: z.string().optional()
739
+ });
740
+ var relatedDocumentsSchema = z.looseObject({
741
+ related: z.array(z.looseObject({
742
+ docId: z.string(),
743
+ relationType: z.string(),
744
+ hopDistance: z.number()
745
+ }))
746
+ });
747
+ var graphSearchSchema = z.looseObject({
748
+ query: z.string().optional(),
749
+ entities: z.array(graphEntitySchema),
750
+ relatedDocs: z.array(z.looseObject({
751
+ docId: z.string(),
752
+ relationType: z.string(),
753
+ hopDistance: z.number(),
754
+ title: z.string(),
755
+ snippet: z.string()
756
+ }))
757
+ });
758
+ var deleteAcknowledgmentSchema = z.looseObject({
759
+ id: z.string().uuid(),
760
+ deleted: z.literal(true)
761
+ });
762
+ var toolOutputSchemas = {
763
+ search_documents: z.looseObject({
764
+ items: z.array(searchResultSchema),
765
+ total: z.number(),
766
+ page: z.number(),
767
+ limit: z.number(),
768
+ diagnostics: z.looseObject({
769
+ graphAttempted: z.boolean(),
770
+ graphFailed: z.boolean(),
771
+ graphContribution: z.boolean()
772
+ }).optional()
773
+ }),
774
+ get_document: documentSchema,
775
+ create_document: documentSchema,
776
+ update_document: documentSchema,
777
+ list_documents: z.looseObject({
778
+ items: z.array(documentListItemSchema),
779
+ total: z.number(),
780
+ page: z.number(),
781
+ limit: z.number()
782
+ }),
783
+ list_folders: z.array(folderSchema),
784
+ create_folder: folderSchema,
785
+ create_snapshot: versionSchema,
786
+ get_version_history: z.array(versionSchema),
787
+ export_document: exportedDocumentSchema,
788
+ list_categories: z.array(categoryListItemSchema),
789
+ create_category: categorySchema,
790
+ list_tags: z.array(tagSchema),
791
+ get_related_documents: relatedDocumentsSchema,
792
+ search_knowledge_graph: graphSearchSchema,
793
+ get_document_index_status: indexStatusSchema,
794
+ refresh_document_index: z.looseObject({
795
+ documentId: z.string(),
796
+ generationId: z.string(),
797
+ deduplicated: z.boolean()
798
+ }),
799
+ delete_document: deleteAcknowledgmentSchema,
800
+ delete_folder: deleteAcknowledgmentSchema,
801
+ delete_category: deleteAcknowledgmentSchema,
802
+ restore_document_version: documentSchema
803
+ };
804
+
805
+ // ../mcp-server/src/lifecycle.ts
586
806
  function unsupported(name) {
587
807
  throw new Error(`The injected legacy MCP client does not support ${name}; use a public DocsClient.`);
588
808
  }
589
809
  var annotations = { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false };
590
810
  function registerLifecycleCapabilities(server, client, wrap) {
591
811
  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." }
812
+ {
813
+ name: "delete_document",
814
+ description: "Move an existing document to trash; this is a soft delete and does not permanently purge its content or version history. Requires write access in the active workspace/category. Returns {id, deleted:true}. Use only when the user intends to delete it.",
815
+ action: (id) => client.deleteDocument ? client.deleteDocument(id) : unsupported("delete_document"),
816
+ idDescription: "UUID of the document to move to trash; obtain it from search_documents, list_documents, or get_document."
817
+ },
818
+ {
819
+ name: "delete_folder",
820
+ 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 active workspace/category. Returns {id, deleted:true}.",
821
+ action: (id) => client.deleteFolder ? client.deleteFolder(id) : unsupported("delete_folder"),
822
+ idDescription: "UUID of the folder to delete; obtain it from list_folders. This detaches, but does not delete, the documents inside it."
823
+ },
824
+ {
825
+ name: "delete_category",
826
+ description: "Delete a category and detach its folders and documents without deleting their content. Requires full workspace write access; category-scoped credentials cannot delete any category, including their configured one. Returns {id, deleted:true}.",
827
+ action: (id) => client.deleteCategory ? client.deleteCategory(id) : unsupported("delete_category"),
828
+ idDescription: "UUID of the category to delete; obtain it from list_categories using a workspace-scoped credential."
829
+ }
595
830
  ];
596
831
  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 }) => {
832
+ server.registerTool(tool.name, {
833
+ description: tool.description,
834
+ annotations,
835
+ inputSchema: z2.object({ id: z2.string().uuid().describe(tool.idDescription) }),
836
+ outputSchema: toolOutputSchemas[tool.name]
837
+ }, wrap(tool.name, async ({ id }) => {
598
838
  await tool.action(id);
599
839
  return { id, deleted: true };
600
840
  }));
601
841
  }
602
842
  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.",
843
+ description: "Restore content for an existing document from a version or named snapshot returned by get_version_history. Requires edit access. The server saves current content in history, records the restoration, and queues indexing. Returns the updated document; it does not recover a trashed document or change folder/category placement.",
604
844
  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")));
845
+ inputSchema: z2.object({
846
+ documentId: z2.string().uuid().describe("UUID of the document whose content should be restored; it must be visible in the active workspace or category."),
847
+ versionId: z2.string().uuid().describe("UUID of a version belonging to this document, from get_version_history. Use the version ID, not a snapshot label.")
848
+ }),
849
+ outputSchema: toolOutputSchemas.restore_document_version
850
+ }, wrap("restore_document_version", async ({ documentId, versionId }) => client.restoreDocumentVersion ? client.restoreDocumentVersion(documentId, versionId) : unsupported("restore_document_version")));
610
851
  }
611
852
 
612
853
  // ../mcp-server/src/capabilities.ts
613
- import { z as z2 } from "zod";
854
+ import { z as z3 } from "zod";
614
855
  function registerExtendedCapabilities(server, client, wrapHandler) {
615
856
  server.registerTool("list_categories", {
616
- annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
617
- description: "List categories visible to the API key. Category keys receive only their bound category.",
618
- inputSchema: z2.object({})
619
- }, wrapHandler(async () => client.listCategories()));
857
+ annotations: {
858
+ readOnlyHint: true,
859
+ destructiveHint: false,
860
+ idempotentHint: true,
861
+ openWorldHint: false
862
+ },
863
+ description: "List categories visible in the active workspace or category scope. Requires read access; a category-scoped credential sees only its configured category. Use this before create_document or create_folder when you need an existing category ID.",
864
+ inputSchema: z3.object({}),
865
+ outputSchema: toolOutputSchemas.list_categories
866
+ }, wrapHandler("list_categories", async () => client.listCategories()));
620
867
  server.registerTool("create_category", {
621
- annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
622
- description: "Create a category. Requires a workspace key with write access; category keys cannot mutate categories.",
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.")
626
- })
627
- }, wrapHandler(async (input) => client.createCategory(input)));
868
+ annotations: {
869
+ readOnlyHint: false,
870
+ destructiveHint: false,
871
+ idempotentHint: false,
872
+ openWorldHint: false
873
+ },
874
+ description: "Create a new category for the active workspace. Requires workspace-level write access; category-scoped credentials cannot create categories. Returns the created category. Use list_categories to inspect existing categories.",
875
+ inputSchema: z3.object({
876
+ name: z3.string().trim().min(1).max(255).describe("Non-empty display name, up to 255 characters, for example Project notes.")
877
+ }),
878
+ outputSchema: toolOutputSchemas.create_category
879
+ }, wrapHandler("create_category", async (input) => client.createCategory(input)));
628
880
  server.registerTool("list_tags", {
629
- annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
630
- description: "List tags visible in the workspace or bound category.",
631
- inputSchema: z2.object({})
632
- }, wrapHandler(async () => client.listTags()));
881
+ annotations: {
882
+ readOnlyHint: true,
883
+ destructiveHint: false,
884
+ idempotentHint: true,
885
+ openWorldHint: false
886
+ },
887
+ description: "List tags visible in the active workspace or configured category scope. Requires read access; use returned tag IDs to filter list_documents and returned tag names to filter search_documents.",
888
+ inputSchema: z3.object({}),
889
+ outputSchema: toolOutputSchemas.list_tags
890
+ }, wrapHandler("list_tags", async () => client.listTags()));
633
891
  server.registerTool("get_related_documents", {
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.")
639
- })
640
- }, wrapHandler(async ({ documentId, limit }) => client.getRelatedDocuments(documentId, limit)));
892
+ annotations: {
893
+ readOnlyHint: true,
894
+ destructiveHint: false,
895
+ idempotentHint: true,
896
+ openWorldHint: false
897
+ },
898
+ description: "Traverse graph relations from one readable document without a text query. Requires read access and returns related document IDs plus relation metadata. Use search_knowledge_graph to rank graph neighbors with query text, or search_documents for normal hybrid retrieval. Results stay in the active scope and may be empty when graph data is unavailable.",
899
+ inputSchema: z3.object({
900
+ documentId: z3.string().min(1).describe("Document ID returned by search_documents or list_documents; it must be readable in the active scope."),
901
+ limit: z3.number().int().min(1).max(100).optional().describe("Maximum related documents to return, from 1 to 100. Omit to use the server default.")
902
+ }),
903
+ outputSchema: toolOutputSchemas.get_related_documents
904
+ }, wrapHandler("get_related_documents", async ({ documentId, limit }) => client.getRelatedDocuments(documentId, limit)));
641
905
  server.registerTool("search_knowledge_graph", {
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.")
648
- })
649
- }, wrapHandler(async (input) => client.searchGraph(input)));
906
+ annotations: {
907
+ readOnlyHint: true,
908
+ destructiveHint: false,
909
+ idempotentHint: true,
910
+ openWorldHint: false
911
+ },
912
+ description: "Search graph relations from one or more readable seed documents, optionally using query text to filter and rank related documents. Requires read access and returns seed entities plus relatedDocs. Use search_documents first to obtain authorized seed IDs; use get_related_documents for one-seed neighbors without graph search. Results stay in the active scope and graph data may be empty when unavailable.",
913
+ inputSchema: z3.object({
914
+ query: z3.string().max(2000).optional().describe("Optional search text up to 2,000 characters to filter and rank graph-related documents; omit it to inspect relations without query filtering."),
915
+ docIds: z3.array(z3.string().min(1)).min(1).max(50).describe("Between 1 and 50 document IDs returned by search_documents or list_documents; each seed must be readable in the active scope."),
916
+ limit: z3.number().int().min(1).max(100).optional().describe("Maximum related documents to return, from 1 to 100. Omit to use the server default.")
917
+ }),
918
+ outputSchema: toolOutputSchemas.search_knowledge_graph
919
+ }, wrapHandler("search_knowledge_graph", async (input) => client.searchGraph(input)));
650
920
  server.registerTool("get_document_index_status", {
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.") })
654
- }, wrapHandler(async ({ documentId }) => client.getDocumentIndexStatus(documentId)));
921
+ annotations: {
922
+ readOnlyHint: true,
923
+ destructiveHint: false,
924
+ idempotentHint: true,
925
+ openWorldHint: false
926
+ },
927
+ description: "Read indexing and knowledge-pipeline status for an existing document without starting work. Requires read access. Use after a save or refresh_document_index to check progress or failures.",
928
+ inputSchema: z3.object({
929
+ documentId: z3.string().uuid().describe("UUID of a document returned by search_documents or list_documents; it must be readable in the active scope.")
930
+ }),
931
+ outputSchema: toolOutputSchemas.get_document_index_status
932
+ }, wrapHandler("get_document_index_status", async ({ documentId }) => client.getDocumentIndexStatus(documentId)));
655
933
  server.registerTool("refresh_document_index", {
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.") })
659
- }, wrapHandler(async ({ documentId }) => client.refreshDocumentIndex(documentId)));
934
+ annotations: {
935
+ readOnlyHint: false,
936
+ destructiveHint: false,
937
+ idempotentHint: false,
938
+ openWorldHint: false
939
+ },
940
+ description: "Queue an explicit reindex for an existing document; processing is asynchronous and the response acknowledges the generation. Requires write access. Normal content and placement changes schedule indexing when needed, so use get_document_index_status first and refresh only when a retry is intended.",
941
+ inputSchema: z3.object({
942
+ documentId: z3.string().uuid().describe("UUID of the document to refresh, obtained from search_documents or list_documents and visible in the active scope.")
943
+ }),
944
+ outputSchema: toolOutputSchemas.refresh_document_index
945
+ }, wrapHandler("refresh_document_index", async ({ documentId }) => client.refreshDocumentIndex(documentId)));
660
946
  server.registerPrompt("organize_workspace", {
661
947
  description: "Plan safe document organization using DocsMint categories and folders.",
662
- argsSchema: z2.object({
663
- objective: z2.string(),
664
- language: z2.string().optional()
948
+ argsSchema: z3.object({
949
+ objective: z3.string(),
950
+ language: z3.string().optional()
665
951
  })
666
952
  }, ({ objective, language }) => ({
667
953
  messages: [
@@ -676,9 +962,9 @@ function registerExtendedCapabilities(server, client, wrapHandler) {
676
962
  }));
677
963
  server.registerPrompt("research_workspace", {
678
964
  description: "Research a question with hybrid search and GraphRAG while citing DocsMint document IDs.",
679
- argsSchema: z2.object({
680
- question: z2.string(),
681
- language: z2.string().optional()
965
+ argsSchema: z3.object({
966
+ question: z3.string(),
967
+ language: z3.string().optional()
682
968
  })
683
969
  }, ({ question, language }) => ({
684
970
  messages: [
@@ -819,15 +1105,15 @@ __export(exports_create_document, {
819
1105
  definition: () => definition,
820
1106
  handler: () => handler
821
1107
  });
822
- import { z as z3 } from "zod";
1108
+ import { z as z4 } from "zod";
823
1109
  var definition = {
824
1110
  name: "create_document",
825
- description: "Create a new document. Optionally provide initial markdown content and a folder ID.",
1111
+ description: "Create a new DocsMint document when no existing document should be modified. Optionally set a title, initial Markdown content, and folder or category placement; an omitted title defaults to \u201CUntitled\u201D. Requires write access in the target scope. A category-scoped credential must create inside its configured category, by selecting that category or a folder within it. Normal creation schedules indexing asynchronously and returns the created document. Use update_document for an existing document.",
826
1112
  inputSchema: {
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.")
1113
+ title: z4.string().min(1).max(500).optional().describe("Optional title from 1 to 500 characters; omit to use the API default \u201CUntitled\u201D."),
1114
+ content: z4.string().optional().describe("Optional initial Markdown content; omit to create an empty document."),
1115
+ folderId: z4.string().uuid().optional().describe("Optional folder UUID from list_folders; the folder must be writable in the active workspace or category."),
1116
+ categoryId: z4.string().uuid().nullable().optional().describe("Optional category UUID from list_categories; omit or pass null to leave the explicit category unset. A categorized folder still supplies the document\u2019s effective category; without one the document remains uncategorized. Category-scoped credentials stay bound to their configured category.")
831
1117
  }
832
1118
  };
833
1119
  var createHandler = (api) => async function createDocument(args) {
@@ -842,14 +1128,14 @@ __export(exports_create_folder, {
842
1128
  definition: () => definition2,
843
1129
  handler: () => handler2
844
1130
  });
845
- import { z as z4 } from "zod";
1131
+ import { z as z5 } from "zod";
846
1132
  var definition2 = {
847
1133
  name: "create_folder",
848
- description: "Create a new folder, optionally nested under a parent folder.",
1134
+ description: "Create a new folder in the active workspace or category scope. Requires write access and returns the created folder. A nested folder inherits its parent category; for a root folder, pass categoryId when using a category-scoped credential. Category-scoped credentials cannot create outside their configured category.",
849
1135
  inputSchema: {
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.")
1136
+ name: z5.string().min(1).max(255).describe("Non-empty folder name up to 255 characters."),
1137
+ parentId: z5.string().uuid().nullable().optional().describe("Optional parent folder UUID from list_folders; omit or pass null to create a root-level folder."),
1138
+ categoryId: z5.string().uuid().nullable().optional().describe("Optional category UUID from list_categories for a root-level folder. A nested folder inherits its parent category; category-scoped credentials cannot escape their configured category.")
853
1139
  }
854
1140
  };
855
1141
  var createHandler2 = (api) => async function createFolder(args) {
@@ -864,14 +1150,14 @@ __export(exports_create_snapshot, {
864
1150
  definition: () => definition3,
865
1151
  handler: () => handler3
866
1152
  });
867
- import { z as z5 } from "zod";
1153
+ import { z as z6 } from "zod";
868
1154
  var definition3 = {
869
1155
  name: "create_snapshot",
870
- description: "Create a named snapshot (labelled version) of a document from its current content.",
1156
+ description: "Save a named, retained snapshot of an existing document\u2019s current content before a planned change. Requires edit access and adds an entry to get_version_history without changing the document itself. Use restore_document_version with the returned version ID to restore its content later.",
871
1157
  inputSchema: {
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.")
1158
+ documentId: z6.string().uuid().describe("UUID of the existing document, obtained from search_documents or list_documents and visible in the active scope."),
1159
+ label: z6.string().min(1).max(200).describe("Non-empty snapshot label up to 200 characters, for example 'v1.0-release'."),
1160
+ description: z6.string().max(1000).optional().describe("Optional snapshot note up to 1,000 characters; omit if not needed.")
875
1161
  }
876
1162
  };
877
1163
  var createHandler3 = (api) => async function createSnapshot(args) {
@@ -887,12 +1173,12 @@ __export(exports_export_document, {
887
1173
  definition: () => definition4,
888
1174
  handler: () => handler4
889
1175
  });
890
- import { z as z6 } from "zod";
1176
+ import { z as z7 } from "zod";
891
1177
  var definition4 = {
892
1178
  name: "export_document",
893
1179
  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.",
894
1180
  inputSchema: {
895
- id: z6.string().describe("Document ID to export.")
1181
+ id: z7.string().uuid().describe("UUID of the readable document to export, obtained from search_documents or list_documents.")
896
1182
  }
897
1183
  };
898
1184
  var createHandler4 = (api) => async function exportDocument(args) {
@@ -907,12 +1193,12 @@ __export(exports_get_document, {
907
1193
  definition: () => definition5,
908
1194
  handler: () => handler5
909
1195
  });
910
- import { z as z7 } from "zod";
1196
+ import { z as z8 } from "zod";
911
1197
  var definition5 = {
912
1198
  name: "get_document",
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.",
1199
+ description: "Read an existing document by UUID with its content, metadata, and tags before editing or citing it. Requires read access in the active workspace/category. Use export_document when you only need the portable Markdown body.",
914
1200
  inputSchema: {
915
- id: z7.string().describe("Document ID.")
1201
+ id: z8.string().uuid().describe("UUID of the document, obtained from search_documents or list_documents, and visible in the active scope.")
916
1202
  }
917
1203
  };
918
1204
  var createHandler5 = (api) => async function getDocument(args) {
@@ -927,15 +1213,15 @@ __export(exports_list_documents, {
927
1213
  definition: () => definition6,
928
1214
  handler: () => handler6
929
1215
  });
930
- import { z as z8 } from "zod";
1216
+ import { z as z9 } from "zod";
931
1217
  var definition6 = {
932
1218
  name: "list_documents",
933
- description: "List documents with pagination, optionally filtered by folder or tag.",
1219
+ description: "List readable documents in the active workspace or category with page-based results. Optionally filter by folder or tag; page defaults to 1 and limit to 20 (maximum 1,000). Use search_documents for text or semantic retrieval.",
934
1220
  inputSchema: {
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).")
1221
+ folderId: z9.string().uuid().optional().describe("Optional folder UUID from list_folders to limit the listing."),
1222
+ tag: z9.string().uuid().optional().describe("Optional tag UUID from list_tags to filter the documents."),
1223
+ page: z9.number().int().min(1).optional().describe("1-indexed result page; defaults to 1."),
1224
+ limit: z9.number().int().min(1).max(1000).optional().describe("Number of documents per page, from 1 to 1,000; defaults to 20.")
939
1225
  }
940
1226
  };
941
1227
  var createHandler6 = (api) => async function listDocuments(args) {
@@ -950,12 +1236,12 @@ __export(exports_list_folders, {
950
1236
  definition: () => definition7,
951
1237
  handler: () => handler7
952
1238
  });
953
- import { z as z9 } from "zod";
1239
+ import { z as z10 } from "zod";
954
1240
  var definition7 = {
955
1241
  name: "list_folders",
956
- description: "List folders, optionally scoped to a parent folder. Returns a flat list of immediate children.",
1242
+ description: "List folders readable in the active workspace/category. Omit parentId to list root folders, or supply a parent UUID to list its immediate children as a flat list. Use create_folder to add a folder.",
957
1243
  inputSchema: {
958
- parentId: z9.string().optional().describe("Optional parent folder ID. Omit to list top-level (root) folders.")
1244
+ parentId: z10.string().uuid().optional().describe("Optional parent folder UUID from list_folders; omit to list root folders.")
959
1245
  }
960
1246
  };
961
1247
  var createHandler7 = (api) => async function listFolders(args) {
@@ -970,15 +1256,15 @@ __export(exports_search, {
970
1256
  definition: () => definition8,
971
1257
  handler: () => handler8
972
1258
  });
973
- import { z as z10 } from "zod";
1259
+ import { z as z11 } from "zod";
974
1260
  var definition8 = {
975
1261
  name: "search_documents",
976
- description: "Hybrid search across documents (full-text + semantic). Supports filtering by folder and tags.",
1262
+ description: "Search readable DocsMint documents with hybrid full-text and semantic retrieval, optionally filtered by folder and tag names. Requires read access and stays within the active workspace/category. Use search_knowledge_graph for graph relations from seed documents or get_related_documents for neighbors without a text query.",
977
1263
  inputSchema: {
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).")
1264
+ query: z11.string().describe("Text to search for; preserve the language and terms relevant to the user request."),
1265
+ folder: z11.string().optional().describe("Optional folder UUID from list_folders to scope retrieval."),
1266
+ tags: z11.array(z11.string()).optional().describe("Optional tag names as shown by list_tags; documents match when they have any supplied tag name."),
1267
+ limit: z11.number().int().positive().max(100).optional().describe("Maximum result count, from 1 to 100; defaults to 20.")
982
1268
  }
983
1269
  };
984
1270
  var createHandler8 = (api) => async function searchDocuments(args) {
@@ -998,16 +1284,16 @@ __export(exports_update_document, {
998
1284
  definition: () => definition9,
999
1285
  handler: () => handler9
1000
1286
  });
1001
- import { z as z11 } from "zod";
1287
+ import { z as z12 } from "zod";
1002
1288
  var definition9 = {
1003
1289
  name: "update_document",
1004
- description: "Update an existing document's title and/or content. The server creates a new version on each update.",
1290
+ description: "Modify an existing document by UUID after reading its current state. Omitted fields remain unchanged; null folderId removes folder placement and null categoryId clears the explicit category, while a categorized folder can still supply the effective category. Changing title or Markdown content requires edit access, while moving placement requires write access in the active workspace/category. The server stores the prior content in version history and queues indexing when content or placement changes. Returns the updated document. Use create_document for new content.",
1005
1291
  inputSchema: {
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.")
1292
+ id: z12.string().uuid().describe("UUID of the existing document, obtained from search_documents, list_documents, or get_document."),
1293
+ title: z12.string().min(1).max(500).optional().describe("Optional replacement title, 1 to 500 characters; omit to leave the current title unchanged."),
1294
+ content: z12.string().optional().describe("New markdown content for the document."),
1295
+ folderId: z12.string().uuid().nullable().optional().describe("Optional destination folder UUID from list_folders; omit to keep current placement or pass null to remove folder placement."),
1296
+ categoryId: z12.string().uuid().nullable().optional().describe("Optional destination category UUID from list_categories; omit to keep the explicit category or pass null to clear it. A categorized folder may still supply the effective category. Category-scoped credentials cannot move outside their configured category.")
1011
1297
  }
1012
1298
  };
1013
1299
  var createHandler9 = (api) => async function updateDocument(args) {
@@ -1023,13 +1309,13 @@ __export(exports_version_history, {
1023
1309
  definition: () => definition10,
1024
1310
  handler: () => handler10
1025
1311
  });
1026
- import { z as z12 } from "zod";
1312
+ import { z as z13 } from "zod";
1027
1313
  var definition10 = {
1028
1314
  name: "get_version_history",
1029
- description: "List the version history for a document. Optionally restrict to named snapshots.",
1315
+ description: "Read versions for an existing document, including saved snapshots. Requires read access and returns version IDs with content and timestamps; pass a version ID to restore_document_version when the user asks to restore content. This is read-only and does not change the document.",
1030
1316
  inputSchema: {
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).")
1317
+ documentId: z13.string().uuid().describe("UUID of the readable document whose version history you need; obtain it from search_documents or list_documents."),
1318
+ onlySnapshots: z13.boolean().optional().describe("When true, return only named snapshots and omit auto-saved revisions; omit or use false for the full history.")
1033
1319
  }
1034
1320
  };
1035
1321
  var createHandler10 = (api) => async function getVersionHistory(args) {
@@ -1038,11 +1324,17 @@ var createHandler10 = (api) => async function getVersionHistory(args) {
1038
1324
  var handler10 = createHandler10(client);
1039
1325
 
1040
1326
  // ../mcp-server/src/server.ts
1041
- function wrapHandler(name, handler11) {
1327
+ function wrapHandler(name, handler11, outputSchema) {
1042
1328
  return async (args) => {
1043
1329
  try {
1330
+ const output = await handler11(args);
1331
+ const parsedOutput = outputSchema ? await outputSchema.safeParseAsync(output) : undefined;
1332
+ if (parsedOutput && !parsedOutput.success) {
1333
+ throw new Error(`Tool '${name}' returned a result outside its published output schema`);
1334
+ }
1044
1335
  return {
1045
- content: [{ type: "text", text: JSON.stringify(await handler11(args), null, 2) }]
1336
+ content: [{ type: "text", text: JSON.stringify(output, null, 2) }],
1337
+ ...parsedOutput?.success ? { structuredContent: parsedOutput.data } : {}
1046
1338
  };
1047
1339
  } catch (error) {
1048
1340
  if (isDocsApiError(error) || error instanceof HiaiDocsError) {
@@ -1071,12 +1363,27 @@ function wrapHandler(name, handler11) {
1071
1363
  }
1072
1364
  function registerDocsmintMcpCapabilities(server, client2) {
1073
1365
  const register = (name, description, inputSchema, 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));
1366
+ server.registerTool(name, {
1367
+ description,
1368
+ inputSchema: z14.object(inputSchema),
1369
+ annotations: {
1370
+ readOnlyHint: ![
1371
+ "create_document",
1372
+ "update_document",
1373
+ "create_folder",
1374
+ "create_snapshot"
1375
+ ].includes(name),
1376
+ destructiveHint: name === "update_document",
1377
+ idempotentHint: ![
1378
+ "create_document",
1379
+ "update_document",
1380
+ "create_folder",
1381
+ "create_snapshot"
1382
+ ].includes(name),
1383
+ openWorldHint: false
1384
+ },
1385
+ outputSchema: toolOutputSchemas[name]
1386
+ }, wrapHandler(name, handler11, toolOutputSchemas[name]));
1080
1387
  };
1081
1388
  const tools = [
1082
1389
  exports_search,
@@ -1093,11 +1400,11 @@ function registerDocsmintMcpCapabilities(server, client2) {
1093
1400
  for (const tool of tools) {
1094
1401
  register(tool.definition.name, tool.definition.description, tool.definition.inputSchema, tool.createHandler(client2));
1095
1402
  }
1096
- registerExtendedCapabilities(server, client2, (handler11) => wrapHandler("extended", handler11));
1097
- registerLifecycleCapabilities(server, client2, (handler11) => wrapHandler("lifecycle", handler11));
1403
+ registerExtendedCapabilities(server, client2, (name, handler11) => wrapHandler(name, handler11, toolOutputSchemas[name]));
1404
+ registerLifecycleCapabilities(server, client2, (name, handler11) => wrapHandler(name, handler11, toolOutputSchemas[name]));
1098
1405
  }
1099
1406
  function createDocsmintMcpServer(options = {}) {
1100
- const server = new McpServer({ name: "docsmint", version: "0.8.8" });
1407
+ const server = new McpServer({ name: "docsmint", version: "0.9.0" });
1101
1408
  const client2 = options.docsClient ? createMcpDocsClient(options.docsClient, options.requestContext) : options.client ?? createMcpDocsClient(createDefaultDocsClient(), options.requestContext);
1102
1409
  registerDocsmintMcpCapabilities(server, client2);
1103
1410
  return server;