@moorchehai/mcp 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,158 @@
1
+ import { z } from 'zod';
2
+ import { makeApiRequest, API_ENDPOINTS } from '../config/api.js';
3
+
4
+ // Upload text documents tool
5
+ export const uploadTextTool = {
6
+ name: "upload-text",
7
+ description: "Upload text documents to a namespace in Moorcheh",
8
+ parameters: {
9
+ namespace_name: z.string().describe("Name of the namespace to upload to"),
10
+ documents: z.array(z.object({
11
+ id: z.string().describe("Unique identifier for the document"),
12
+ text: z.string().describe("Text content of the document"),
13
+ metadata: z.record(z.string(), z.any()).optional().describe("Optional metadata for the document"),
14
+ })).describe("Array of documents to upload"),
15
+ },
16
+ handler: async ({ namespace_name, documents }) => {
17
+ try {
18
+ const data = await makeApiRequest('POST', `${API_ENDPOINTS.namespaces}/${namespace_name}/documents`, {
19
+ documents,
20
+ });
21
+
22
+ const resultText = `Successfully uploaded ${documents.length} document(s) to namespace "${namespace_name}":\n${JSON.stringify(data, null, 2)}`;
23
+
24
+ return {
25
+ content: [
26
+ {
27
+ type: "text",
28
+ text: resultText,
29
+ },
30
+ ],
31
+ };
32
+ } catch (error) {
33
+ return {
34
+ content: [
35
+ {
36
+ type: "text",
37
+ text: `Error uploading text documents: ${error.message}`,
38
+ },
39
+ ],
40
+ };
41
+ }
42
+ },
43
+ };
44
+
45
+ // Upload vectors tool
46
+ export const uploadVectorsTool = {
47
+ name: "upload-vectors",
48
+ description: "Upload vector data to a namespace in Moorcheh",
49
+ parameters: {
50
+ namespace_name: z.string().describe("Name of the namespace to upload to"),
51
+ vectors: z.array(z.object({
52
+ id: z.string().describe("Unique identifier for the vector"),
53
+ vector: z.array(z.number()).describe("Vector values"),
54
+ metadata: z.record(z.string(), z.any()).optional().describe("Optional metadata for the vector"),
55
+ })).describe("Array of vectors to upload"),
56
+ },
57
+ handler: async ({ namespace_name, vectors }) => {
58
+ try {
59
+ const data = await makeApiRequest('POST', `${API_ENDPOINTS.namespaces}/${namespace_name}/vectors`, {
60
+ vectors,
61
+ });
62
+
63
+ const resultText = `Successfully uploaded ${vectors.length} vector(s) to namespace "${namespace_name}":\n${JSON.stringify(data, null, 2)}`;
64
+
65
+ return {
66
+ content: [
67
+ {
68
+ type: "text",
69
+ text: resultText,
70
+ },
71
+ ],
72
+ };
73
+ } catch (error) {
74
+ return {
75
+ content: [
76
+ {
77
+ type: "text",
78
+ text: `Error uploading vectors: ${error.message}`,
79
+ },
80
+ ],
81
+ };
82
+ }
83
+ },
84
+ };
85
+
86
+ // Delete data tool
87
+ export const deleteDataTool = {
88
+ name: "delete-data",
89
+ description: "Delete specific data items from a namespace in Moorcheh",
90
+ parameters: {
91
+ namespace_name: z.string().describe("Name of the namespace to delete from"),
92
+ ids: z.array(z.string()).describe("Array of document/vector IDs to delete"),
93
+ },
94
+ handler: async ({ namespace_name, ids }) => {
95
+ try {
96
+ const data = await makeApiRequest('POST', `${API_ENDPOINTS.namespaces}/${namespace_name}/documents/delete`, {
97
+ ids,
98
+ });
99
+
100
+ const resultText = `Successfully deleted ${ids.length} item(s) from namespace "${namespace_name}":\n${JSON.stringify(data, null, 2)}`;
101
+
102
+ return {
103
+ content: [
104
+ {
105
+ type: "text",
106
+ text: resultText,
107
+ },
108
+ ],
109
+ };
110
+ } catch (error) {
111
+ return {
112
+ content: [
113
+ {
114
+ type: "text",
115
+ text: `Error deleting data: ${error.message}`,
116
+ },
117
+ ],
118
+ };
119
+ }
120
+ },
121
+ };
122
+
123
+ // Get data by IDs tool (for text namespaces)
124
+ export const getDataTool = {
125
+ name: "get-data",
126
+ description: "Get specific data items by ID from a text namespace in Moorcheh",
127
+ parameters: {
128
+ namespace_name: z.string().describe("Name of the text namespace to read from"),
129
+ ids: z.array(z.string()).describe("Array of document IDs to retrieve"),
130
+ },
131
+ handler: async ({ namespace_name, ids }) => {
132
+ try {
133
+ const data = await makeApiRequest('POST', `${API_ENDPOINTS.namespaces}/${namespace_name}/documents/get`, {
134
+ ids,
135
+ });
136
+
137
+ const resultText = `Fetched ${ids.length} item(s) from namespace "${namespace_name}":\n${JSON.stringify(data, null, 2)}`;
138
+
139
+ return {
140
+ content: [
141
+ {
142
+ type: "text",
143
+ text: resultText,
144
+ },
145
+ ],
146
+ };
147
+ } catch (error) {
148
+ return {
149
+ content: [
150
+ {
151
+ type: "text",
152
+ text: `Error fetching data: ${error.message}`,
153
+ },
154
+ ],
155
+ };
156
+ }
157
+ },
158
+ };
@@ -0,0 +1,130 @@
1
+ import { z } from 'zod';
2
+ import { makeApiRequest, API_ENDPOINTS } from '../config/api.js';
3
+
4
+ // Namespace listing tool
5
+ export const listNamespacesTool = {
6
+ name: "list-namespaces",
7
+ description: "List all available namespaces in Moorcheh",
8
+ parameters: {},
9
+ handler: async () => {
10
+ try {
11
+ const data = await makeApiRequest('GET', API_ENDPOINTS.namespaces);
12
+
13
+ if (!data.namespaces || data.namespaces.length === 0) {
14
+ return {
15
+ content: [
16
+ {
17
+ type: "text",
18
+ text: "No namespaces found",
19
+ },
20
+ ],
21
+ };
22
+ }
23
+
24
+ const formattedNamespaces = data.namespaces.map((ns) =>
25
+ [
26
+ `Namespace: ${ns.namespace_name}`,
27
+ `Type: ${ns.type}`,
28
+ `Vector Dimension: ${ns.vector_dimension || 'N/A'}`,
29
+ `Created: ${ns.createdAt}`,
30
+ `Items: ${ns.itemCount}`,
31
+ "---",
32
+ ].join("\n")
33
+ );
34
+
35
+ const namespacesText = `Available namespaces:\n\n${formattedNamespaces.join("\n")}`;
36
+
37
+ return {
38
+ content: [
39
+ {
40
+ type: "text",
41
+ text: namespacesText,
42
+ },
43
+ ],
44
+ };
45
+ } catch (error) {
46
+ return {
47
+ content: [
48
+ {
49
+ type: "text",
50
+ text: `Error listing namespaces: ${error.message}`,
51
+ },
52
+ ],
53
+ };
54
+ }
55
+ },
56
+ };
57
+
58
+ // Create namespace tool
59
+ export const createNamespaceTool = {
60
+ name: "create-namespace",
61
+ description: "Create a new namespace for document storage in Moorcheh",
62
+ parameters: {
63
+ namespace_name: z.string().describe("Name of the namespace to create"),
64
+ type: z.string().optional().describe("Type of namespace (text, vector, etc.)"),
65
+ vector_dimension: z.number().optional().describe("Vector dimension for vector namespaces"),
66
+ },
67
+ handler: async ({ namespace_name, type = "text", vector_dimension }) => {
68
+ try {
69
+ const data = await makeApiRequest('POST', API_ENDPOINTS.namespaces, {
70
+ namespace_name,
71
+ type,
72
+ vector_dimension,
73
+ });
74
+
75
+ const resultText = `Successfully created namespace "${namespace_name}":\n${JSON.stringify(data, null, 2)}`;
76
+
77
+ return {
78
+ content: [
79
+ {
80
+ type: "text",
81
+ text: resultText,
82
+ },
83
+ ],
84
+ };
85
+ } catch (error) {
86
+ return {
87
+ content: [
88
+ {
89
+ type: "text",
90
+ text: `Error creating namespace: ${error.message}`,
91
+ },
92
+ ],
93
+ };
94
+ }
95
+ },
96
+ };
97
+
98
+ // Delete namespace tool
99
+ export const deleteNamespaceTool = {
100
+ name: "delete-namespace",
101
+ description: "Delete a namespace and all its contents from Moorcheh",
102
+ parameters: {
103
+ namespace_name: z.string().describe("Name of the namespace to delete"),
104
+ },
105
+ handler: async ({ namespace_name }) => {
106
+ try {
107
+ const data = await makeApiRequest('DELETE', `${API_ENDPOINTS.namespaces}/${namespace_name}`);
108
+
109
+ const resultText = `Successfully deleted namespace "${namespace_name}":\n${JSON.stringify(data, null, 2)}`;
110
+
111
+ return {
112
+ content: [
113
+ {
114
+ type: "text",
115
+ text: resultText,
116
+ },
117
+ ],
118
+ };
119
+ } catch (error) {
120
+ return {
121
+ content: [
122
+ {
123
+ type: "text",
124
+ text: `Error deleting namespace: ${error.message}`,
125
+ },
126
+ ],
127
+ };
128
+ }
129
+ },
130
+ };
@@ -0,0 +1,230 @@
1
+ import { z } from 'zod';
2
+ import { makeApiRequest, API_ENDPOINTS } from '../config/api.js';
3
+
4
+ // Search tool
5
+ export const searchTool = {
6
+ name: "search",
7
+ description: "Search for data in a namespace using semantic search or vector similarity. This tool provides powerful search capabilities across your namespaces, supporting both text-based semantic search and vector-based similarity search. For text search, you can use natural language queries to find relevant documents based on meaning rather than just keywords. For vector search, you can find similar content by comparing vector embeddings. The tool supports advanced features like result filtering, similarity thresholds, and kiosk mode for production environments. This is ideal for building intelligent search interfaces, recommendation systems, or content discovery features.",
8
+ parameters: {
9
+ namespaces: z.array(z.string().min(1)).min(1).describe("Namespaces to search in. Provide an array of namespace names where you want to search for content. You can search across multiple namespaces simultaneously. All namespaces must be accessible with your API key."),
10
+ query: z.union([z.string().min(1), z.array(z.number())]).describe("Search query. For text search: provide a natural language query string (e.g., 'tell me about the company?', 'how to configure authentication?'). For vector search: provide an array of numbers representing a vector embedding (e.g., [0.1, 0.2, 0.3, ..., 0.768]). The query type will be automatically detected based on the input format. DO NOT USE QUOTES IN THE QUERY FOR VECTOR SEARCH."),
11
+ query_type: z.enum(['text', 'vector']).optional().describe("Type of query to perform. 'text' for semantic search using natural language queries. 'vector' for similarity search using vector embeddings. If not specified, the type will be automatically detected based on the query format (string for text, array for vector)."),
12
+ top_k: z.number().int().positive().optional().describe("Number of top results to return. Controls how many search results are returned, with higher values providing more comprehensive results. Default is 10. Use lower values (3-5) for focused results, higher values (10-20) for broader exploration."),
13
+ threshold: z.number().min(0).max(1).optional().describe("Similarity threshold for results. A value between 0 and 1 that filters results based on similarity score. Higher values (0.7-0.9) return only highly similar results, lower values (0.3-0.5) return more comprehensive results. Required when kiosk_mode is true."),
14
+ kiosk_mode: z.boolean().optional().describe("Kiosk mode for restricted search. When true, search is restricted to specific namespaces with threshold filtering, providing more controlled results suitable for production environments. When false, search across all specified namespaces without strict filtering."),
15
+ },
16
+ handler: async ({ namespaces, query, query_type, top_k = 10, threshold, kiosk_mode = false }) => {
17
+ try {
18
+ // Determine query type if not explicitly provided
19
+ let finalQueryType = query_type;
20
+ let finalQuery = query;
21
+
22
+ if (!finalQueryType) {
23
+ if (typeof query === 'string') {
24
+ // Check if it's a string representation of a vector array
25
+ if (query.startsWith('[') && query.endsWith(']')) {
26
+ try {
27
+ const parsedArray = JSON.parse(query);
28
+ if (Array.isArray(parsedArray) && parsedArray.every(item => typeof item === 'number')) {
29
+ finalQuery = parsedArray;
30
+ finalQueryType = 'vector';
31
+ } else {
32
+ finalQueryType = 'text';
33
+ }
34
+ } catch (e) {
35
+ finalQueryType = 'text';
36
+ }
37
+ } else {
38
+ finalQueryType = 'text';
39
+ }
40
+ } else if (Array.isArray(query) && query.every(item => typeof item === 'number')) {
41
+ finalQueryType = 'vector';
42
+ } else {
43
+ return {
44
+ content: [
45
+ {
46
+ type: "text",
47
+ text: 'Error: Unable to determine query type. Please specify query_type parameter or provide a valid string (for text) or number array (for vector).',
48
+ },
49
+ ],
50
+ };
51
+ }
52
+ }
53
+
54
+ // Handle vector query type with string input
55
+ if (finalQueryType === 'vector' && typeof query === 'string') {
56
+ try {
57
+ const parsedArray = JSON.parse(query);
58
+ if (Array.isArray(parsedArray) && parsedArray.every(item => typeof item === 'number')) {
59
+ finalQuery = parsedArray;
60
+ } else {
61
+ return {
62
+ content: [
63
+ {
64
+ type: "text",
65
+ text: 'Error: Vector query type requires an array of numbers',
66
+ },
67
+ ],
68
+ };
69
+ }
70
+ } catch (e) {
71
+ return {
72
+ content: [
73
+ {
74
+ type: "text",
75
+ text: 'Error: Vector query type requires an array of numbers',
76
+ },
77
+ ],
78
+ };
79
+ }
80
+ }
81
+
82
+ // Validate query format matches query type
83
+ if (finalQueryType === 'text' && typeof finalQuery !== 'string') {
84
+ return {
85
+ content: [
86
+ {
87
+ type: "text",
88
+ text: 'Error: Text query type requires a string query. Example: "your search text here"',
89
+ },
90
+ ],
91
+ };
92
+ }
93
+ if (finalQueryType === 'vector' && (!Array.isArray(finalQuery) || !finalQuery.every(item => typeof item === 'number'))) {
94
+ return {
95
+ content: [
96
+ {
97
+ type: "text",
98
+ text: 'Error: Vector query type requires an array of numbers. Example: [0.1, 0.2, 0.3, 0.4, 0.5] for 5-dimensional namespace',
99
+ },
100
+ ],
101
+ };
102
+ }
103
+
104
+ const requestBody = {
105
+ namespaces,
106
+ query: finalQuery,
107
+ top_k,
108
+ kiosk_mode,
109
+ };
110
+ if (threshold !== undefined) {
111
+ requestBody.threshold = threshold;
112
+ }
113
+
114
+ const data = await makeApiRequest('POST', API_ENDPOINTS.search, requestBody);
115
+
116
+ if (!data.results || data.results.length === 0) {
117
+ return {
118
+ content: [
119
+ {
120
+ type: "text",
121
+ text: `No results found for query: "${query}"`,
122
+ },
123
+ ],
124
+ };
125
+ }
126
+
127
+ const formattedResults = data.results.map((result, index) =>
128
+ [
129
+ `Result ${index + 1}:`,
130
+ `Content: ${result.content}`,
131
+ `Score: ${result.score || 'N/A'}`,
132
+ `Metadata: ${JSON.stringify(result.metadata || {}, null, 2)}`,
133
+ "---",
134
+ ].join("\n")
135
+ );
136
+
137
+ const searchText = `Search results for "${query}" in namespaces [${namespaces.join(', ')}]:\n\n${formattedResults.join("\n")}\n\nTotal results: ${data.total || data.results.length}`;
138
+
139
+ return {
140
+ content: [
141
+ {
142
+ type: "text",
143
+ text: searchText,
144
+ },
145
+ ],
146
+ };
147
+ } catch (error) {
148
+ return {
149
+ content: [
150
+ {
151
+ type: "text",
152
+ text: `Error searching: ${error.message}`,
153
+ },
154
+ ],
155
+ };
156
+ }
157
+ },
158
+ };
159
+
160
+ // Answer tool
161
+ export const answerTool = {
162
+ name: "answer",
163
+ description: "Get AI-generated answers based on data in a namespace using text queries. This tool provides intelligent, context-aware responses by searching through your stored text documents and generating comprehensive answers using advanced language models.",
164
+ parameters: {
165
+ namespace: z.string().min(1).describe("Namespace to answer questions from. Must be a text namespace containing the documents that will be searched to generate answers. The namespace should contain relevant content that can answer the types of questions you expect to ask."),
166
+ query: z.string().min(1).describe("Text query for AI answer generation. Provide a natural language question or prompt that you want the AI to answer. The AI will search through your namespace content and generate a comprehensive response based on the relevant information found."),
167
+ top_k: z.number().int().positive().optional().describe("Number of top results to return. Controls how many relevant documents the AI considers when generating an answer. Default is 5. Use lower values (3-5) for focused answers, higher values (8-10) for comprehensive responses that consider more context."),
168
+ threshold: z.number().min(0).max(1).optional().describe("Similarity threshold for results. A value between 0 and 1 that filters documents based on relevance before generating the answer. Higher values (0.7-0.9) ensure only highly relevant content is used, lower values (0.3-0.5) include more context. Required when kiosk_mode is true."),
169
+ kiosk_mode: z.boolean().optional().describe("Kiosk mode for restricted search. When true, search is restricted to specific namespaces with threshold filtering, providing more controlled and focused answers suitable for production environments."),
170
+ aiModel: z.string().optional().describe("AI model to use for answer generation. Different models may have different capabilities, response styles, and performance characteristics. Supported AI models include: 'anthropic.claude-3-7-sonnet-20250219-v1:0' (Claude 3.7 Sonnet), 'anthropic.claude-sonnet-4-20250514-v1:0' (Claude Sonnet 4), 'meta.llama4-maverick-17b-instruct-v1:0' (Llama 4 Maverick), 'meta.llama3-3-70b-instruct-v1:0' (Llama 3.3 70B), 'deepseek.r1-v1:0' (DeepSeek R1). If not specified, defaults to Claude 3.7 Sonnet."),
171
+ chatHistory: z.array(z.object({
172
+ role: z.string().describe("Role of the message in the conversation. Use 'user' for user messages and 'assistant' for AI responses. This helps maintain conversation context and allows the AI to reference previous exchanges."),
173
+ content: z.string()
174
+ })).optional().describe("Chat history for AI answer generation. Provide previous conversation context to help the AI maintain continuity and reference earlier parts of the conversation. This enables more coherent multi-turn conversations."),
175
+ headerPrompt: z.string().optional().describe("Header prompt for AI answer generation. Custom instructions that define the AI's role, style, and behavior. Use this to create specialized assistants (e.g., technical support, friendly helper, formal advisor) or set specific guidelines for response generation."),
176
+ footerPrompt: z.string().optional().describe("Footer prompt for AI answer generation. Additional instructions that are applied after the main response generation. Useful for formatting requirements, citation styles, or specific response patterns that should be consistently applied."),
177
+ temperature: z.number().min(0).max(2.0).optional().describe("Temperature for AI answer generation. Controls the creativity and randomness of responses. Lower values (0.1-0.3) produce more focused, deterministic answers. Higher values (0.7-1.0) produce more creative, varied responses. Default is 0.7."),
178
+ },
179
+ handler: async ({ namespace, query, top_k = 5, threshold, kiosk_mode = false, aiModel, chatHistory = [], headerPrompt, footerPrompt, temperature = 0.7 }) => {
180
+ try {
181
+ const requestBody = {
182
+ namespace,
183
+ query,
184
+ top_k,
185
+ kiosk_mode,
186
+ };
187
+
188
+ if (threshold !== undefined) {
189
+ requestBody.threshold = threshold;
190
+ }
191
+ if (aiModel) {
192
+ requestBody.aiModel = aiModel;
193
+ }
194
+ if (chatHistory && chatHistory.length > 0) {
195
+ requestBody.chatHistory = chatHistory;
196
+ }
197
+ if (headerPrompt) {
198
+ requestBody.headerPrompt = headerPrompt;
199
+ }
200
+ if (footerPrompt) {
201
+ requestBody.footerPrompt = footerPrompt;
202
+ }
203
+ if (temperature !== undefined) {
204
+ requestBody.temperature = temperature;
205
+ }
206
+
207
+ const data = await makeApiRequest('POST', API_ENDPOINTS.answer, requestBody);
208
+
209
+ const resultText = `AI Answer for "${query}" in namespace "${namespace}":\n\n${data.answer || data.response || JSON.stringify(data, null, 2)}`;
210
+
211
+ return {
212
+ content: [
213
+ {
214
+ type: "text",
215
+ text: resultText,
216
+ },
217
+ ],
218
+ };
219
+ } catch (error) {
220
+ return {
221
+ content: [
222
+ {
223
+ type: "text",
224
+ text: `Error getting AI answer: ${error.message}`,
225
+ },
226
+ ],
227
+ };
228
+ }
229
+ },
230
+ };
@@ -0,0 +1,77 @@
1
+ import { z } from 'zod';
2
+
3
+ // ========== COMPLETABLE FUNCTIONALITY ==========
4
+
5
+ // Enum for MCP Zod Type Kind
6
+ const McpZodTypeKind = {
7
+ Completable: "McpCompletable"
8
+ };
9
+
10
+ // Main Completable class
11
+ class Completable extends z.ZodType {
12
+ constructor(def) {
13
+ super(def);
14
+ this._def = def;
15
+ }
16
+
17
+ _parse(input) {
18
+ const { ctx } = this._processInputParams(input);
19
+ const data = ctx.data;
20
+ return this._def.type._parse({
21
+ data,
22
+ path: ctx.path,
23
+ parent: ctx,
24
+ });
25
+ }
26
+
27
+ unwrap() {
28
+ return this._def.type;
29
+ }
30
+
31
+ static create(type, params) {
32
+ return new Completable({
33
+ type,
34
+ typeName: McpZodTypeKind.Completable,
35
+ complete: params.complete,
36
+ ...processCreateParams(params),
37
+ });
38
+ }
39
+ }
40
+
41
+ // Helper function to process create parameters
42
+ function processCreateParams(params) {
43
+ if (!params) return {};
44
+ const { errorMap, invalid_type_error, required_error, description } = params;
45
+
46
+ if (errorMap && (invalid_type_error || required_error)) {
47
+ throw new Error(
48
+ `Can't use "invalid_type_error" or "required_error" in conjunction with custom error map.`,
49
+ );
50
+ }
51
+
52
+ if (errorMap) return { errorMap: errorMap, description };
53
+
54
+ const customMap = (iss, ctx) => {
55
+ const { message } = params;
56
+
57
+ if (iss.code === "invalid_enum_value") {
58
+ return { message: message ?? ctx.defaultError };
59
+ }
60
+ if (typeof ctx.data === "undefined") {
61
+ return { message: message ?? required_error ?? ctx.defaultError };
62
+ }
63
+ if (iss.code !== "invalid_type") return { message: ctx.defaultError };
64
+ return { message: message ?? invalid_type_error ?? ctx.defaultError };
65
+ };
66
+
67
+ return { errorMap: customMap, description };
68
+ }
69
+
70
+ /**
71
+ * Wraps a Zod type to provide autocompletion capabilities. Useful for, e.g., prompt arguments in MCP.
72
+ */
73
+ function completable(schema, complete) {
74
+ return Completable.create(schema, { ...schema._def, complete });
75
+ }
76
+
77
+ export { Completable, completable, McpZodTypeKind };