@moorchehai/mcp 1.3.1 → 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,69 +1,69 @@
1
- {
2
- "name": "@moorchehai/mcp",
3
- "version": "1.3.1",
4
- "description": "Moorcheh MCP Server with completable functionality for AI-powered search and answer operations",
5
- "main": "src/server/index.js",
6
- "type": "module",
7
- "bin": {
8
- "moorcheh-mcp": "bin/cli.js",
9
- "mcp": "bin/cli.js"
10
- },
11
- "preferGlobal": true,
12
- "publishConfig": {
13
- "access": "public"
14
- },
15
- "scripts": {
16
- "start": "node src/server/index.js",
17
- "dev": "node --watch src/server/index.js",
18
- "test": "node src/test/test-mcp.js",
19
- "build": "echo 'No build step required'",
20
- "lint": "echo \"No linting configured\"",
21
- "format": "echo \"No formatting configured\""
22
- },
23
- "keywords": [
24
- "mcp",
25
- "moorcheh",
26
- "ai",
27
- "search",
28
- "bedrock",
29
- "vector",
30
- "semantic",
31
- "completable",
32
- "model-context-protocol",
33
- "knowledge-base",
34
- "document-search"
35
- ],
36
- "author": "Moorcheh Team",
37
- "license": "Apache-2.0",
38
- "dependencies": {
39
- "@modelcontextprotocol/sdk": "^1.25.2",
40
- "axios": "^1.12.0",
41
- "form-data": "^4.0.5",
42
- "zod": "^3.22.4"
43
- },
44
- "devDependencies": {
45
- "node": ">=18.0.0"
46
- },
47
- "engines": {
48
- "node": ">=18.0.0"
49
- },
50
- "repository": {
51
- "type": "git",
52
- "url": "git+https://github.com/moorcheh-ai/moorcheh-mcp.git"
53
- },
54
- "bugs": {
55
- "url": "https://github.com/moorcheh-ai/moorcheh-mcp/issues"
56
- },
57
- "homepage": "https://github.com/moorcheh-ai/moorcheh-mcp#readme",
58
- "files": [
59
- "src/",
60
- "bin/",
61
- "assets/",
62
- "env.example",
63
- "README.md",
64
- "LICENSE"
65
- ],
66
- "directories": {
67
- "lib": "src/server"
68
- }
69
- }
1
+ {
2
+ "name": "@moorchehai/mcp",
3
+ "version": "1.3.2",
4
+ "description": "Moorcheh MCP Server with completable functionality for AI-powered search and answer operations",
5
+ "main": "src/server/index.js",
6
+ "type": "module",
7
+ "bin": {
8
+ "moorcheh-mcp": "bin/cli.js",
9
+ "mcp": "bin/cli.js"
10
+ },
11
+ "preferGlobal": true,
12
+ "publishConfig": {
13
+ "access": "public"
14
+ },
15
+ "scripts": {
16
+ "start": "node src/server/index.js",
17
+ "dev": "node --watch src/server/index.js",
18
+ "test": "node src/test/test-mcp.js",
19
+ "build": "echo 'No build step required'",
20
+ "lint": "echo \"No linting configured\"",
21
+ "format": "echo \"No formatting configured\""
22
+ },
23
+ "keywords": [
24
+ "mcp",
25
+ "moorcheh",
26
+ "ai",
27
+ "search",
28
+ "bedrock",
29
+ "vector",
30
+ "semantic",
31
+ "completable",
32
+ "model-context-protocol",
33
+ "knowledge-base",
34
+ "document-search"
35
+ ],
36
+ "author": "Moorcheh Team",
37
+ "license": "Apache-2.0",
38
+ "dependencies": {
39
+ "@modelcontextprotocol/sdk": "^1.25.2",
40
+ "axios": "^1.12.0",
41
+ "form-data": "^4.0.5",
42
+ "zod": "^3.22.4"
43
+ },
44
+ "devDependencies": {
45
+ "node": ">=18.0.0"
46
+ },
47
+ "engines": {
48
+ "node": ">=18.0.0"
49
+ },
50
+ "repository": {
51
+ "type": "git",
52
+ "url": "git+https://github.com/moorcheh-ai/moorcheh-mcp.git"
53
+ },
54
+ "bugs": {
55
+ "url": "https://github.com/moorcheh-ai/moorcheh-mcp/issues"
56
+ },
57
+ "homepage": "https://github.com/moorcheh-ai/moorcheh-mcp#readme",
58
+ "files": [
59
+ "src/",
60
+ "bin/",
61
+ "assets/",
62
+ "env.example",
63
+ "README.md",
64
+ "LICENSE"
65
+ ],
66
+ "directories": {
67
+ "lib": "src/server"
68
+ }
69
+ }
@@ -2,7 +2,6 @@ import axios from 'axios';
2
2
  import { readFileSync, createReadStream, statSync } from 'fs';
3
3
  import { fileURLToPath } from 'url';
4
4
  import { dirname, join, basename } from 'path';
5
- import FormData from 'form-data';
6
5
 
7
6
  const __filename = fileURLToPath(import.meta.url);
8
7
  const __dirname = dirname(__filename);
@@ -88,42 +87,50 @@ async function makeApiRequest(method, url, data = null) {
88
87
  }
89
88
  }
90
89
 
91
- // Helper function to upload files (multipart/form-data)
90
+ // Helper function to upload files using pre-signed S3 URL flow
92
91
  async function uploadFile(namespace_name, filePath) {
93
92
  try {
94
93
  // Check if file exists
95
94
  const stats = statSync(filePath);
96
95
  const fileSizeInMB = stats.size / (1024 * 1024);
97
96
 
98
- // Check file size (max 10MB)
99
- if (fileSizeInMB > 10) {
100
- throw new Error(`File size (${fileSizeInMB.toFixed(2)}MB) exceeds maximum allowed size of 10MB`);
101
- }
102
-
103
97
  // Check file extension
104
98
  const allowedExtensions = ['.pdf', '.docx', '.xlsx', '.json', '.txt', '.csv', '.md'];
105
99
  const fileExtension = filePath.toLowerCase().substring(filePath.lastIndexOf('.'));
106
100
  if (!allowedExtensions.includes(fileExtension)) {
107
101
  throw new Error(`File type '${fileExtension}' is not supported. Allowed types: ${allowedExtensions.join(', ')}`);
108
102
  }
109
-
110
- // Create FormData
111
- const formData = new FormData();
112
- const fileName = basename(filePath);
113
- formData.append('file', createReadStream(filePath), fileName);
114
-
115
- // Make the request
116
- const url = `${API_ENDPOINTS.namespaces}/${namespace_name}/upload-file`;
117
- const response = await axios.post(url, formData, {
103
+
104
+ const file_name = basename(filePath);
105
+ const uploadUrlPayload = await makeApiRequest(
106
+ 'POST',
107
+ `${API_ENDPOINTS.namespaces}/${namespace_name}/upload-url`,
108
+ { file_name }
109
+ );
110
+
111
+ if (!uploadUrlPayload?.upload_url || !uploadUrlPayload?.content_type) {
112
+ throw new Error(`Invalid upload URL response: ${JSON.stringify(uploadUrlPayload)}`);
113
+ }
114
+
115
+ await axios.put(uploadUrlPayload.upload_url, createReadStream(filePath), {
118
116
  headers: {
119
- 'x-api-key': MOORCHEH_API_KEY,
120
- ...formData.getHeaders(),
117
+ 'Content-Type': uploadUrlPayload.content_type,
118
+ 'Content-Length': stats.size,
121
119
  },
122
120
  maxContentLength: Infinity,
123
121
  maxBodyLength: Infinity,
124
122
  });
125
-
126
- return response.data;
123
+
124
+ return {
125
+ status: 'success',
126
+ namespace_name,
127
+ file_name,
128
+ file_size_mb: Number(fileSizeInMB.toFixed(2)),
129
+ key: uploadUrlPayload.key,
130
+ content_type: uploadUrlPayload.content_type,
131
+ expires_in: uploadUrlPayload.expires_in,
132
+ message: 'File uploaded to storage successfully and queued for processing.',
133
+ };
127
134
  } catch (error) {
128
135
  if (error.response) {
129
136
  const status = error.response.status;
@@ -10,13 +10,20 @@ export const uploadTextTool = {
10
10
  documents: z.array(z.object({
11
11
  id: z.string().describe("Unique identifier for the document"),
12
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"),
13
+ metadata: z.record(z.string(), z.any()).optional().describe("Optional metadata for the document. Will be flattened as top-level fields to match API format."),
14
14
  })).describe("Array of documents to upload"),
15
15
  },
16
16
  handler: async ({ namespace_name, documents }) => {
17
17
  try {
18
+ const normalizedDocuments = documents.map((doc) => {
19
+ const { metadata, ...rest } = doc;
20
+ return metadata && typeof metadata === 'object'
21
+ ? { ...rest, ...metadata }
22
+ : rest;
23
+ });
24
+
18
25
  const data = await makeApiRequest('POST', `${API_ENDPOINTS.namespaces}/${namespace_name}/documents`, {
19
- documents,
26
+ documents: normalizedDocuments,
20
27
  });
21
28
 
22
29
  const resultText = `Successfully uploaded ${documents.length} document(s) to namespace "${namespace_name}":\n${JSON.stringify(data, null, 2)}`;
@@ -51,13 +58,21 @@ export const uploadVectorsTool = {
51
58
  vectors: z.array(z.object({
52
59
  id: z.string().describe("Unique identifier for the vector"),
53
60
  vector: z.array(z.number()).describe("Vector values"),
54
- metadata: z.record(z.string(), z.any()).optional().describe("Optional metadata for the vector"),
61
+ text: z.string().optional().describe("Optional original text for the vector"),
62
+ metadata: z.record(z.string(), z.any()).optional().describe("Optional metadata for the vector. Will be flattened as top-level fields to match API format."),
55
63
  })).describe("Array of vectors to upload"),
56
64
  },
57
65
  handler: async ({ namespace_name, vectors }) => {
58
66
  try {
67
+ const normalizedVectors = vectors.map((vectorItem) => {
68
+ const { metadata, ...rest } = vectorItem;
69
+ return metadata && typeof metadata === 'object'
70
+ ? { ...rest, ...metadata }
71
+ : rest;
72
+ });
73
+
59
74
  const data = await makeApiRequest('POST', `${API_ENDPOINTS.namespaces}/${namespace_name}/vectors`, {
60
- vectors,
75
+ vectors: normalizedVectors,
61
76
  });
62
77
 
63
78
  const resultText = `Successfully uploaded ${vectors.length} vector(s) to namespace "${namespace_name}":\n${JSON.stringify(data, null, 2)}`;
@@ -89,15 +104,16 @@ export const deleteDataTool = {
89
104
  description: "Delete specific data items from a namespace in Moorcheh",
90
105
  parameters: {
91
106
  namespace_name: z.string().describe("Name of the namespace to delete from"),
107
+ data_type: z.enum(["documents", "vectors"]).optional().describe("Data type to delete from. Use 'documents' for text namespaces and 'vectors' for vector namespaces. Default is 'documents'."),
92
108
  ids: z.array(z.string()).describe("Array of document/vector IDs to delete"),
93
109
  },
94
- handler: async ({ namespace_name, ids }) => {
110
+ handler: async ({ namespace_name, data_type = "documents", ids }) => {
95
111
  try {
96
- const data = await makeApiRequest('POST', `${API_ENDPOINTS.namespaces}/${namespace_name}/documents/delete`, {
112
+ const data = await makeApiRequest('POST', `${API_ENDPOINTS.namespaces}/${namespace_name}/${data_type}/delete`, {
97
113
  ids,
98
114
  });
99
115
 
100
- const resultText = `Successfully deleted ${ids.length} item(s) from namespace "${namespace_name}":\n${JSON.stringify(data, null, 2)}`;
116
+ const resultText = `Successfully processed deletion of ${ids.length} ${data_type} item(s) in namespace "${namespace_name}":\n${JSON.stringify(data, null, 2)}`;
101
117
 
102
118
  return {
103
119
  content: [
@@ -160,16 +176,17 @@ export const getDataTool = {
160
176
  // Upload file tool
161
177
  export const uploadFileTool = {
162
178
  name: "upload-file",
163
- description: "Upload a file directly to a text-type namespace for processing and indexing. Files are queued for ingestion and will be available for search once processed. Supported file types: .pdf, .docx, .xlsx, .json, .txt, .csv, .md (max 10MB)",
179
+ description: "Upload a file to a text namespace using pre-signed URL flow. The tool requests an upload URL, uploads the file directly to storage, then the file is queued for processing and indexing.",
164
180
  parameters: {
165
181
  namespace_name: z.string().describe("Name of the text namespace to upload the file to"),
166
- file_path: z.string().describe("Path to the file to upload (max 10MB). Must be one of: .pdf, .docx, .xlsx, .json, .txt, .csv, .md"),
182
+ file_path: z.string().describe("Absolute path to the file to upload. Must be one of: .pdf, .docx, .xlsx, .json, .txt, .csv, .md"),
167
183
  },
168
184
  handler: async ({ namespace_name, file_path }) => {
169
185
  try {
170
186
  const data = await uploadFile(namespace_name, file_path);
171
187
 
172
- const resultText = `Successfully uploaded file "${data.fileName}" to namespace "${namespace_name}":\n${JSON.stringify(data, null, 2)}`;
188
+ const uploaded_file_name = data.file_name || 'uploaded file';
189
+ const resultText = `Successfully uploaded file "${uploaded_file_name}" to namespace "${namespace_name}":\n${JSON.stringify(data, null, 2)}`;
173
190
 
174
191
  return {
175
192
  content: [
@@ -26,8 +26,8 @@ export const listNamespacesTool = {
26
26
  `Namespace: ${ns.namespace_name}`,
27
27
  `Type: ${ns.type}`,
28
28
  `Vector Dimension: ${ns.vector_dimension || 'N/A'}`,
29
- `Created: ${ns.createdAt}`,
30
- `Items: ${ns.itemCount}`,
29
+ `Created: ${ns.created_at || 'N/A'}`,
30
+ `Items: ${ns.item_count || 'N/A'}`,
31
31
  "---",
32
32
  ].join("\n")
33
33
  );