Coverage for src/qdrant_loader_mcp_server/mcp/schemas.py: 100%
35 statements
« prev ^ index » next coverage.py v7.10.3, created at 2025-08-13 09:20 +0000
« prev ^ index » next coverage.py v7.10.3, created at 2025-08-13 09:20 +0000
1"""MCP Tool Schema Definitions."""
3from typing import Any
6class MCPSchemas:
7 """Tool schema definitions for MCP server."""
9 @staticmethod
10 def get_search_tool_schema() -> dict[str, Any]:
11 """Get the basic search tool schema."""
12 return {
13 "name": "search",
14 "description": "Perform semantic search across multiple data sources",
15 "annotations": {"read-only": True},
16 "inputSchema": {
17 "type": "object",
18 "properties": {
19 "query": {
20 "type": "string",
21 "description": "The search query in natural language",
22 },
23 "source_types": {
24 "type": "array",
25 "items": {
26 "type": "string",
27 "enum": [
28 "git",
29 "confluence",
30 "jira",
31 "documentation",
32 "localfile",
33 ],
34 },
35 "description": "Optional list of source types to filter results",
36 },
37 "project_ids": {
38 "type": "array",
39 "items": {
40 "type": "string",
41 },
42 "description": "Optional list of project IDs to filter results",
43 },
44 "limit": {
45 "type": "integer",
46 "description": "Maximum number of results to return",
47 "default": 5,
48 },
49 },
50 "required": ["query"],
51 },
52 "outputSchema": {
53 "type": "object",
54 "properties": {
55 "results": {
56 "type": "array",
57 "items": {
58 "type": "object",
59 "properties": {
60 "score": {"type": "number"},
61 "title": {"type": "string"},
62 "content": {"type": "string"},
63 "source_type": {"type": "string"},
64 "metadata": {
65 "type": "object",
66 "properties": {
67 "file_path": {"type": "string"},
68 "project_id": {"type": "string"},
69 "created_at": {"type": "string"},
70 "last_modified": {"type": "string"},
71 },
72 },
73 },
74 },
75 },
76 "total_found": {"type": "integer"},
77 "query_context": {
78 "type": "object",
79 "properties": {
80 "original_query": {"type": "string"},
81 "source_types_filtered": {
82 "type": "array",
83 "items": {"type": "string"},
84 },
85 "project_ids_filtered": {
86 "type": "array",
87 "items": {"type": "string"},
88 },
89 },
90 },
91 },
92 },
93 }
95 @staticmethod
96 def get_hierarchy_search_tool_schema() -> dict[str, Any]:
97 """Get the hierarchy search tool schema."""
98 return {
99 "name": "hierarchy_search",
100 "description": "Search Confluence documents with hierarchy-aware filtering and organization",
101 "annotations": {"read-only": True},
102 "inputSchema": {
103 "type": "object",
104 "properties": {
105 "query": {
106 "type": "string",
107 "description": "The search query in natural language",
108 },
109 "hierarchy_filter": {
110 "type": "object",
111 "properties": {
112 "depth": {
113 "type": "integer",
114 "description": "Filter by specific hierarchy depth (0 = root pages)",
115 },
116 "parent_title": {
117 "type": "string",
118 "description": "Filter by parent page title",
119 },
120 "root_only": {
121 "type": "boolean",
122 "description": "Show only root pages (no parent)",
123 },
124 "has_children": {
125 "type": "boolean",
126 "description": "Filter by whether pages have children",
127 },
128 },
129 },
130 "organize_by_hierarchy": {
131 "type": "boolean",
132 "description": "Group results by hierarchy structure",
133 "default": False,
134 },
135 "limit": {
136 "type": "integer",
137 "description": "Maximum number of results to return",
138 "default": 10,
139 },
140 },
141 "required": ["query"],
142 },
143 "outputSchema": {
144 "type": "object",
145 "properties": {
146 "results": {
147 "type": "array",
148 "items": {
149 "type": "object",
150 "properties": {
151 "score": {"type": "number"},
152 "title": {"type": "string"},
153 "content": {"type": "string"},
154 "hierarchy_path": {"type": "string"},
155 "parent_title": {"type": "string"},
156 "metadata": {
157 "type": "object",
158 "properties": {
159 "space_key": {"type": "string"},
160 "project_id": {"type": "string"},
161 "page_id": {"type": "string"},
162 "hierarchy_level": {"type": "integer"},
163 },
164 },
165 },
166 },
167 },
168 "total_found": {"type": "integer"},
169 "hierarchy_organization": {
170 "type": "object",
171 "properties": {
172 "organized_by_hierarchy": {"type": "boolean"},
173 "hierarchy_groups": {
174 "type": "array",
175 "items": {"type": "object"},
176 },
177 },
178 },
179 },
180 },
181 }
183 @staticmethod
184 def get_attachment_search_tool_schema() -> dict[str, Any]:
185 """Get the attachment search tool schema."""
186 return {
187 "name": "attachment_search",
188 "description": "Search for file attachments and their parent documents across Confluence, Jira, and other sources",
189 "annotations": {"read-only": True},
190 "inputSchema": {
191 "type": "object",
192 "properties": {
193 "query": {
194 "type": "string",
195 "description": "The search query in natural language",
196 },
197 "attachment_filter": {
198 "type": "object",
199 "properties": {
200 "attachments_only": {
201 "type": "boolean",
202 "description": "Show only file attachments",
203 },
204 "parent_document_title": {
205 "type": "string",
206 "description": "Filter by parent document title",
207 },
208 "file_type": {
209 "type": "string",
210 "description": "Filter by file type (e.g., 'pdf', 'xlsx', 'png')",
211 },
212 "file_size_min": {
213 "type": "integer",
214 "description": "Minimum file size in bytes",
215 },
216 "file_size_max": {
217 "type": "integer",
218 "description": "Maximum file size in bytes",
219 },
220 "author": {
221 "type": "string",
222 "description": "Filter by attachment author",
223 },
224 },
225 },
226 "include_parent_context": {
227 "type": "boolean",
228 "description": "Include parent document information in results",
229 "default": True,
230 },
231 "limit": {
232 "type": "integer",
233 "description": "Maximum number of results to return",
234 "default": 10,
235 },
236 },
237 "required": ["query"],
238 },
239 "outputSchema": {
240 "type": "object",
241 "properties": {
242 "results": {
243 "type": "array",
244 "items": {
245 "type": "object",
246 "properties": {
247 "score": {"type": "number"},
248 "title": {"type": "string"},
249 "content": {"type": "string"},
250 "attachment_info": {
251 "type": "object",
252 "properties": {
253 "filename": {"type": "string"},
254 "file_type": {"type": "string"},
255 "file_size": {"type": "integer"},
256 "parent_document": {"type": "string"},
257 },
258 },
259 "metadata": {
260 "type": "object",
261 "properties": {
262 "file_path": {"type": "string"},
263 "project_id": {"type": "string"},
264 "upload_date": {"type": "string"},
265 "author": {"type": "string"},
266 },
267 },
268 },
269 },
270 },
271 "total_found": {"type": "integer"},
272 "attachment_summary": {
273 "type": "object",
274 "properties": {
275 "total_attachments": {"type": "integer"},
276 "file_types": {
277 "type": "array",
278 "items": {"type": "string"},
279 },
280 "attachments_only": {"type": "boolean"},
281 },
282 },
283 },
284 },
285 }
287 @staticmethod
288 def get_analyze_relationships_tool_schema() -> dict[str, Any]:
289 """Get the analyze document relationships tool schema."""
290 return {
291 "name": "analyze_relationships",
292 "description": "Analyze relationships between documents",
293 "annotations": {"read-only": True},
294 "inputSchema": {
295 "type": "object",
296 "properties": {
297 "query": {
298 "type": "string",
299 "description": "Search query to get documents for analysis",
300 },
301 "limit": {
302 "type": "integer",
303 "description": "Maximum number of documents to analyze",
304 "default": 20,
305 },
306 "source_types": {
307 "type": "array",
308 "items": {"type": "string"},
309 "description": "Optional list of source types to filter by",
310 },
311 "project_ids": {
312 "type": "array",
313 "items": {"type": "string"},
314 "description": "Optional list of project IDs to filter by",
315 },
316 "use_llm": {
317 "type": "boolean",
318 "description": "Enable LLM validation for top pairs (budgeted)"
319 },
320 "max_llm_pairs": {
321 "type": "integer",
322 "description": "Maximum number of pairs to analyze with LLM"
323 },
324 "overall_timeout_s": {
325 "type": "number",
326 "description": "Overall analysis budget in seconds"
327 },
328 "max_pairs_total": {
329 "type": "integer",
330 "description": "Maximum candidate pairs to analyze after tiering"
331 },
332 "text_window_chars": {
333 "type": "integer",
334 "description": "Per-document text window size for lexical analysis"
335 },
336 },
337 "required": ["query"],
338 },
339 "outputSchema": {
340 "type": "object",
341 "properties": {
342 "relationships": {
343 "type": "array",
344 "items": {
345 "type": "object",
346 "properties": {
347 "document_1": {"type": "string"},
348 "document_2": {"type": "string"},
349 "relationship_type": {"type": "string"},
350 "score": {"type": "number"},
351 "description": {"type": "string"},
352 },
353 },
354 },
355 "total_analyzed": {"type": "integer"},
356 "summary": {"type": "string"},
357 },
358 },
359 }
361 @staticmethod
362 def get_find_similar_tool_schema() -> dict[str, Any]:
363 """Get the find similar documents tool schema."""
364 return {
365 "name": "find_similar_documents",
366 "description": "Find documents similar to a target document using multiple similarity metrics",
367 "annotations": {"read-only": True},
368 "inputSchema": {
369 "type": "object",
370 "properties": {
371 "target_query": {
372 "type": "string",
373 "description": "Query to find the target document",
374 },
375 "comparison_query": {
376 "type": "string",
377 "description": "Query to get documents to compare against",
378 },
379 "similarity_metrics": {
380 "type": "array",
381 "items": {
382 "type": "string",
383 "enum": [
384 "entity_overlap",
385 "topic_overlap",
386 "semantic_similarity",
387 "metadata_similarity",
388 "hierarchical_distance",
389 "content_features",
390 ],
391 },
392 "description": "Similarity metrics to use",
393 },
394 "max_similar": {
395 "type": "integer",
396 "description": "Maximum number of similar documents to return",
397 "default": 5,
398 },
399 "source_types": {
400 "type": "array",
401 "items": {"type": "string"},
402 "description": "Optional list of source types to filter by",
403 },
404 "project_ids": {
405 "type": "array",
406 "items": {"type": "string"},
407 "description": "Optional list of project IDs to filter by",
408 },
409 },
410 "required": ["target_query", "comparison_query"],
411 },
412 "outputSchema": {
413 "type": "object",
414 "properties": {
415 "similar_documents": {
416 "type": "array",
417 "items": {
418 "type": "object",
419 "properties": {
420 "document_id": {"type": "string"},
421 "title": {"type": "string"},
422 "similarity_score": {"type": "number"},
423 "similarity_metrics": {
424 "type": "object",
425 "properties": {
426 "entity_overlap": {"type": "number"},
427 "topic_overlap": {"type": "number"},
428 "semantic_similarity": {"type": "number"},
429 "metadata_similarity": {"type": "number"},
430 },
431 },
432 "similarity_reason": {"type": "string"},
433 "content_preview": {"type": "string"},
434 },
435 },
436 },
437 "target_document": {
438 "type": "object",
439 "properties": {
440 "title": {"type": "string"},
441 "content_preview": {"type": "string"},
442 "source_type": {"type": "string"},
443 },
444 },
445 "similarity_summary": {
446 "type": "object",
447 "properties": {
448 "total_compared": {"type": "integer"},
449 "similar_found": {"type": "integer"},
450 "highest_similarity": {"type": "number"},
451 "metrics_used": {
452 "type": "array",
453 "items": {"type": "string"},
454 },
455 },
456 },
457 },
458 },
459 }
461 @staticmethod
462 def get_detect_conflicts_tool_schema() -> dict[str, Any]:
463 """Get the detect conflicts tool schema."""
464 return {
465 "name": "detect_document_conflicts",
466 "description": "Detect conflicts and contradictions between documents",
467 "annotations": {"read-only": True, "compute-intensive": True},
468 "inputSchema": {
469 "type": "object",
470 "properties": {
471 "query": {
472 "type": "string",
473 "description": "Search query to get documents for conflict analysis",
474 },
475 "limit": {
476 "type": "integer",
477 "description": "Maximum number of documents to analyze",
478 "default": 10,
479 },
480 "source_types": {
481 "type": "array",
482 "items": {"type": "string"},
483 "description": "Optional list of source types to filter by",
484 },
485 "project_ids": {
486 "type": "array",
487 "items": {"type": "string"},
488 "description": "Optional list of project IDs to filter by",
489 },
490 },
491 "required": ["query"],
492 },
493 "outputSchema": {
494 "type": "object",
495 "properties": {
496 "conflicts_detected": {
497 "type": "array",
498 "items": {
499 "type": "object",
500 "properties": {
501 "conflict_id": {"type": "string"},
502 "document_1": {
503 "type": "object",
504 "properties": {
505 "title": {"type": "string"},
506 "content_preview": {"type": "string"},
507 "source_type": {"type": "string"},
508 },
509 },
510 "document_2": {
511 "type": "object",
512 "properties": {
513 "title": {"type": "string"},
514 "content_preview": {"type": "string"},
515 "source_type": {"type": "string"},
516 },
517 },
518 "conflict_type": {"type": "string"},
519 "conflict_score": {"type": "number"},
520 "conflict_description": {"type": "string"},
521 "conflicting_statements": {
522 "type": "array",
523 "items": {
524 "type": "object",
525 "properties": {
526 "from_doc1": {"type": "string"},
527 "from_doc2": {"type": "string"},
528 },
529 },
530 },
531 },
532 },
533 },
534 "conflict_summary": {
535 "type": "object",
536 "properties": {
537 "total_documents_analyzed": {"type": "integer"},
538 "conflicts_found": {"type": "integer"},
539 "conflict_types": {
540 "type": "array",
541 "items": {"type": "string"},
542 },
543 "highest_conflict_score": {"type": "number"},
544 },
545 },
546 "analysis_metadata": {
547 "type": "object",
548 "properties": {
549 "query_used": {"type": "string"},
550 "analysis_date": {"type": "string"},
551 "processing_time_ms": {"type": "number"},
552 },
553 },
554 },
555 },
556 }
558 @staticmethod
559 def get_find_complementary_tool_schema() -> dict[str, Any]:
560 """Get the find complementary content tool schema."""
561 return {
562 "name": "find_complementary_content",
563 "description": "Find content that complements a target document",
564 "annotations": {"read-only": True},
565 "inputSchema": {
566 "type": "object",
567 "properties": {
568 "target_query": {
569 "type": "string",
570 "description": "Query to find the target document",
571 },
572 "context_query": {
573 "type": "string",
574 "description": "Query to get contextual documents",
575 },
576 "max_recommendations": {
577 "type": "integer",
578 "description": "Maximum number of recommendations",
579 "default": 5,
580 },
581 "source_types": {
582 "type": "array",
583 "items": {"type": "string"},
584 "description": "Optional list of source types to filter by",
585 },
586 "project_ids": {
587 "type": "array",
588 "items": {"type": "string"},
589 "description": "Optional list of project IDs to filter by",
590 },
591 },
592 "required": ["target_query", "context_query"],
593 },
594 "outputSchema": {
595 "type": "object",
596 "properties": {
597 "complementary_content": {
598 "type": "array",
599 "items": {
600 "type": "object",
601 "properties": {
602 "document_id": {"type": "string"},
603 "title": {"type": "string"},
604 "content_preview": {"type": "string"},
605 "complementary_score": {"type": "number"},
606 "complementary_reason": {"type": "string"},
607 "relationship_type": {"type": "string"},
608 "source_type": {"type": "string"},
609 "metadata": {
610 "type": "object",
611 "properties": {
612 "project_id": {"type": "string"},
613 "created_date": {"type": "string"},
614 "author": {"type": "string"},
615 },
616 },
617 },
618 },
619 },
620 "target_document": {
621 "type": "object",
622 "properties": {
623 "title": {"type": "string"},
624 "content_preview": {"type": "string"},
625 "source_type": {"type": "string"},
626 },
627 },
628 "complementary_summary": {
629 "type": "object",
630 "properties": {
631 "total_analyzed": {"type": "integer"},
632 "complementary_found": {"type": "integer"},
633 "highest_score": {"type": "number"},
634 "relationship_types": {
635 "type": "array",
636 "items": {"type": "string"},
637 },
638 },
639 },
640 },
641 },
642 }
644 @staticmethod
645 def get_cluster_documents_tool_schema() -> dict[str, Any]:
646 """Get the cluster documents tool schema."""
647 return {
648 "name": "cluster_documents",
649 "description": "Cluster documents based on similarity and relationships",
650 "annotations": {"read-only": True, "compute-intensive": True},
651 "inputSchema": {
652 "type": "object",
653 "properties": {
654 "query": {
655 "type": "string",
656 "description": "Search query to get documents for clustering",
657 },
658 "strategy": {
659 "type": "string",
660 "enum": [
661 "mixed_features",
662 "entity_based",
663 "topic_based",
664 "project_based",
665 "hierarchical",
666 "adaptive",
667 ],
668 "description": "Clustering strategy to use (adaptive automatically selects the best strategy)",
669 "default": "mixed_features",
670 },
671 "max_clusters": {
672 "type": "integer",
673 "description": "Maximum number of clusters to create",
674 "default": 10,
675 },
676 "min_cluster_size": {
677 "type": "integer",
678 "description": "Minimum size for a cluster",
679 "default": 2,
680 },
681 "limit": {
682 "type": "integer",
683 "description": "Maximum number of documents to cluster",
684 "default": 25,
685 },
686 "source_types": {
687 "type": "array",
688 "items": {"type": "string"},
689 "description": "Optional list of source types to filter by",
690 },
691 "project_ids": {
692 "type": "array",
693 "items": {"type": "string"},
694 "description": "Optional list of project IDs to filter by",
695 },
696 },
697 "required": ["query"],
698 },
699 "outputSchema": {
700 "type": "object",
701 "properties": {
702 "clusters": {
703 "type": "array",
704 "items": {
705 "type": "object",
706 "properties": {
707 "cluster_id": {"type": "string"},
708 "cluster_name": {"type": "string"},
709 "cluster_theme": {"type": "string"},
710 "document_count": {"type": "integer"},
711 "cohesion_score": {"type": "number"},
712 "documents": {
713 "type": "array",
714 "items": {
715 "type": "object",
716 "properties": {
717 "document_id": {"type": "string"},
718 "title": {"type": "string"},
719 "content_preview": {"type": "string"},
720 "source_type": {"type": "string"},
721 "cluster_relevance": {"type": "number"},
722 },
723 },
724 },
725 "cluster_keywords": {
726 "type": "array",
727 "items": {"type": "string"},
728 },
729 "cluster_summary": {"type": "string"},
730 },
731 },
732 },
733 "clustering_metadata": {
734 "type": "object",
735 "properties": {
736 "total_documents": {"type": "integer"},
737 "clusters_created": {"type": "integer"},
738 "strategy": {"type": "string"},
739 "unclustered_documents": {"type": "integer"},
740 "clustering_quality": {"type": "number"},
741 "processing_time_ms": {"type": "number"},
742 },
743 },
744 "cluster_relationships": {
745 "type": "array",
746 "items": {
747 "type": "object",
748 "properties": {
749 "cluster_1": {"type": "string"},
750 "cluster_2": {"type": "string"},
751 "relationship_type": {"type": "string"},
752 "relationship_strength": {"type": "number"},
753 },
754 },
755 },
756 },
757 },
758 }
760 @staticmethod
761 def get_expand_cluster_tool_schema() -> dict[str, Any]:
762 """Get the expand cluster tool schema for lazy loading cluster documents."""
763 return {
764 "name": "expand_cluster",
765 "description": "Retrieve all documents from a specific cluster for lazy loading",
766 "annotations": {"read-only": True},
767 "inputSchema": {
768 "type": "object",
769 "properties": {
770 "cluster_id": {
771 "type": "string",
772 "description": "The ID of the cluster to expand and retrieve all documents",
773 },
774 "limit": {
775 "type": "integer",
776 "description": "Maximum number of documents to return from cluster (default: 20)",
777 "default": 20,
778 },
779 "offset": {
780 "type": "integer",
781 "description": "Number of documents to skip for pagination (default: 0)",
782 "default": 0,
783 },
784 "include_metadata": {
785 "type": "boolean",
786 "description": "Include detailed metadata for each document (default: true)",
787 "default": True,
788 },
789 },
790 "required": ["cluster_id"],
791 },
792 "outputSchema": {
793 "type": "object",
794 "properties": {
795 "cluster_id": {
796 "type": "string",
797 "description": "The expanded cluster ID",
798 },
799 "cluster_info": {
800 "type": "object",
801 "description": "Detailed cluster information",
802 },
803 "documents": {
804 "type": "array",
805 "description": "Full list of documents in the cluster",
806 },
807 "pagination": {
808 "type": "object",
809 "description": "Pagination information",
810 },
811 },
812 },
813 }
815 @staticmethod
816 def get_expand_document_tool_schema() -> dict[str, Any]:
817 """Get the expand document tool schema for lazy loading - uses same format as search."""
818 return {
819 "name": "expand_document",
820 "description": "Retrieve full document content by document ID for lazy loading",
821 "annotations": {"read-only": True},
822 "inputSchema": {
823 "type": "object",
824 "properties": {
825 "document_id": {
826 "type": "string",
827 "description": "The ID of the document to expand and retrieve full content",
828 },
829 "include_metadata": {
830 "type": "boolean",
831 "description": "Include detailed metadata (default: true)",
832 "default": True,
833 },
834 "include_hierarchy": {
835 "type": "boolean",
836 "description": "Include hierarchy information for Confluence documents (default: true)",
837 "default": True,
838 },
839 "include_attachments": {
840 "type": "boolean",
841 "description": "Include attachment information if available (default: true)",
842 "default": True,
843 },
844 },
845 "required": ["document_id"],
846 },
847 "outputSchema": {
848 "type": "object",
849 "properties": {
850 "results": {
851 "type": "array",
852 "items": {
853 "type": "object",
854 "properties": {
855 "score": {"type": "number"},
856 "title": {"type": "string"},
857 "content": {"type": "string"},
858 "source_type": {"type": "string"},
859 "metadata": {
860 "type": "object",
861 "properties": {
862 "file_path": {"type": "string"},
863 "project_id": {"type": "string"},
864 "created_at": {"type": "string"},
865 "last_modified": {"type": "string"},
866 },
867 },
868 },
869 },
870 },
871 "total_found": {"type": "integer"},
872 "query_context": {
873 "type": "object",
874 "properties": {
875 "original_query": {"type": "string"},
876 "source_types_filtered": {
877 "type": "array",
878 "items": {"type": "string"},
879 },
880 "project_ids_filtered": {
881 "type": "array",
882 "items": {"type": "string"},
883 },
884 },
885 },
886 },
887 },
888 }
890 @classmethod
891 def get_all_tool_schemas(cls) -> list[dict[str, Any]]:
892 """Get all tool schemas."""
893 return [
894 cls.get_search_tool_schema(),
895 cls.get_hierarchy_search_tool_schema(),
896 cls.get_attachment_search_tool_schema(),
897 cls.get_analyze_relationships_tool_schema(),
898 cls.get_find_similar_tool_schema(),
899 cls.get_detect_conflicts_tool_schema(),
900 cls.get_find_complementary_tool_schema(),
901 cls.get_cluster_documents_tool_schema(),
902 cls.get_expand_document_tool_schema(), # ✅ Add expand_document tool
903 cls.get_expand_cluster_tool_schema(), # ✅ Add expand_cluster tool
904 ]