bimp-mcp 0.2.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/LICENSE +21 -0
- package/README.md +112 -0
- package/bimp-api.json +22409 -0
- package/dist/client.d.ts +23 -0
- package/dist/client.js +160 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +186 -0
- package/dist/nomenclatures-extended.d.ts +6 -0
- package/dist/nomenclatures-extended.js +118 -0
- package/dist/prompts.d.ts +25 -0
- package/dist/prompts.js +342 -0
- package/dist/tool-generator.d.ts +44 -0
- package/dist/tool-generator.js +105 -0
- package/dist/utilities.d.ts +13 -0
- package/dist/utilities.js +270 -0
- package/package.json +51 -0
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
const PAGE_SIZE = 100;
|
|
2
|
+
/**
|
|
3
|
+
* Derive the "read" tool name from a "readList" tool name.
|
|
4
|
+
* bimp_foo_readList -> bimp_foo_read
|
|
5
|
+
* bimp_foo_readList_cursor -> bimp_foo_read
|
|
6
|
+
*/
|
|
7
|
+
function deriveReadToolName(listToolName) {
|
|
8
|
+
return listToolName.replace(/_readList(?:_cursor)?$/, "_read");
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Run promises in batches of `concurrency`, collecting all settled results.
|
|
12
|
+
*/
|
|
13
|
+
async function runInBatches(tasks, concurrency) {
|
|
14
|
+
const results = [];
|
|
15
|
+
for (let i = 0; i < tasks.length; i += concurrency) {
|
|
16
|
+
const batch = tasks.slice(i, i + concurrency);
|
|
17
|
+
const settled = await Promise.allSettled(batch.map((fn) => fn()));
|
|
18
|
+
results.push(...settled);
|
|
19
|
+
}
|
|
20
|
+
return results;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Internal batch-read logic shared by bimp_fetch_all (enrich) and bimp_batch_read.
|
|
24
|
+
*/
|
|
25
|
+
async function batchReadUuids(client, toolDef, uuids, concurrency) {
|
|
26
|
+
const items = [];
|
|
27
|
+
const errors = [];
|
|
28
|
+
const tasks = uuids.map((uuid) => async () => {
|
|
29
|
+
const response = (await client.request(toolDef.metadata.method, toolDef.metadata.path, { uuid }));
|
|
30
|
+
return { uuid, data: response.data };
|
|
31
|
+
});
|
|
32
|
+
const results = await runInBatches(tasks, concurrency);
|
|
33
|
+
for (let i = 0; i < results.length; i++) {
|
|
34
|
+
const result = results[i];
|
|
35
|
+
if (result.status === "fulfilled") {
|
|
36
|
+
items.push(result.value.data);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
errors.push({
|
|
40
|
+
uuid: uuids[i],
|
|
41
|
+
error: result.reason instanceof Error ? result.reason.message : String(result.reason),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return { items, errors };
|
|
46
|
+
}
|
|
47
|
+
function createFetchAllTool(client, toolMap) {
|
|
48
|
+
return {
|
|
49
|
+
name: "bimp_fetch_all",
|
|
50
|
+
description: "Auto-paginate any readList endpoint to fetch all items. " +
|
|
51
|
+
"Supports offset, cursor, page, and none pagination types. " +
|
|
52
|
+
"Use enrich=true to call the corresponding read endpoint for full details on each item.",
|
|
53
|
+
inputSchema: {
|
|
54
|
+
type: "object",
|
|
55
|
+
properties: {
|
|
56
|
+
tool: {
|
|
57
|
+
type: "string",
|
|
58
|
+
description: "Name of a readList tool to paginate (e.g. bimp_nomenclature_readList)",
|
|
59
|
+
},
|
|
60
|
+
limit: {
|
|
61
|
+
type: "number",
|
|
62
|
+
description: "Maximum number of items to return (default: unlimited)",
|
|
63
|
+
},
|
|
64
|
+
enrich: {
|
|
65
|
+
type: "boolean",
|
|
66
|
+
description: "If true, fetch full details for each item via the corresponding read endpoint",
|
|
67
|
+
},
|
|
68
|
+
filters: {
|
|
69
|
+
type: "object",
|
|
70
|
+
description: "Additional filter parameters to pass through to the API",
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
required: ["tool"],
|
|
74
|
+
},
|
|
75
|
+
handler: async (params) => {
|
|
76
|
+
const toolName = params.tool;
|
|
77
|
+
const limit = params.limit;
|
|
78
|
+
const enrich = params.enrich;
|
|
79
|
+
const filters = (params.filters ?? {});
|
|
80
|
+
const toolDef = toolMap.get(toolName);
|
|
81
|
+
if (!toolDef) {
|
|
82
|
+
throw new Error(`Tool not found: ${toolName}`);
|
|
83
|
+
}
|
|
84
|
+
let allItems = [];
|
|
85
|
+
const paginationType = toolDef.metadata.paginationType;
|
|
86
|
+
if (paginationType === "offset") {
|
|
87
|
+
let offset = 0;
|
|
88
|
+
while (true) {
|
|
89
|
+
const requestParams = {
|
|
90
|
+
...filters,
|
|
91
|
+
pagination: { offset, count: PAGE_SIZE },
|
|
92
|
+
};
|
|
93
|
+
const response = (await client.request(toolDef.metadata.method, toolDef.metadata.path, requestParams));
|
|
94
|
+
const page = response.data ?? [];
|
|
95
|
+
allItems.push(...page);
|
|
96
|
+
if (limit && allItems.length >= limit) {
|
|
97
|
+
allItems = allItems.slice(0, limit);
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
if (page.length < PAGE_SIZE) {
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
offset += PAGE_SIZE;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
else if (paginationType === "cursor") {
|
|
107
|
+
let cursor;
|
|
108
|
+
while (true) {
|
|
109
|
+
const requestParams = {
|
|
110
|
+
...filters,
|
|
111
|
+
...(cursor ? { cursor } : {}),
|
|
112
|
+
count: PAGE_SIZE,
|
|
113
|
+
};
|
|
114
|
+
const response = (await client.request(toolDef.metadata.method, toolDef.metadata.path, requestParams));
|
|
115
|
+
const page = response.data ?? [];
|
|
116
|
+
allItems.push(...page);
|
|
117
|
+
if (limit && allItems.length >= limit) {
|
|
118
|
+
allItems = allItems.slice(0, limit);
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
cursor = response.cursor;
|
|
122
|
+
if (!cursor || page.length < PAGE_SIZE) {
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
else if (paginationType === "page") {
|
|
128
|
+
let page = 1;
|
|
129
|
+
while (true) {
|
|
130
|
+
const requestParams = {
|
|
131
|
+
...filters,
|
|
132
|
+
page,
|
|
133
|
+
pageSize: PAGE_SIZE,
|
|
134
|
+
};
|
|
135
|
+
const response = (await client.request(toolDef.metadata.method, toolDef.metadata.path, requestParams));
|
|
136
|
+
const items = response.data ?? [];
|
|
137
|
+
allItems.push(...items);
|
|
138
|
+
if (limit && allItems.length >= limit) {
|
|
139
|
+
allItems = allItems.slice(0, limit);
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
if (items.length < PAGE_SIZE) {
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
page++;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
// paginationType === "none" — single request
|
|
150
|
+
const requestParams = { ...filters };
|
|
151
|
+
const response = (await client.request(toolDef.metadata.method, toolDef.metadata.path, requestParams));
|
|
152
|
+
const items = response.data ?? [];
|
|
153
|
+
allItems.push(...items);
|
|
154
|
+
if (limit && allItems.length > limit) {
|
|
155
|
+
allItems = allItems.slice(0, limit);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// Enrich: call the read endpoint for each item
|
|
159
|
+
if (enrich && allItems.length > 0) {
|
|
160
|
+
const readToolName = deriveReadToolName(toolName);
|
|
161
|
+
const readToolDef = toolMap.get(readToolName);
|
|
162
|
+
if (!readToolDef) {
|
|
163
|
+
throw new Error(`Cannot enrich: read tool not found (expected ${readToolName})`);
|
|
164
|
+
}
|
|
165
|
+
const uuids = allItems
|
|
166
|
+
.map((item) => item.uuid)
|
|
167
|
+
.filter(Boolean);
|
|
168
|
+
const { items: enrichedItems } = await batchReadUuids(client, readToolDef, uuids, 10);
|
|
169
|
+
return { items: enrichedItems, count: enrichedItems.length };
|
|
170
|
+
}
|
|
171
|
+
return { items: allItems, count: allItems.length };
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
function createBatchReadTool(client, toolMap) {
|
|
176
|
+
return {
|
|
177
|
+
name: "bimp_batch_read",
|
|
178
|
+
description: "Read multiple items by UUID in parallel. Provide the read tool name and an array of UUIDs.",
|
|
179
|
+
inputSchema: {
|
|
180
|
+
type: "object",
|
|
181
|
+
properties: {
|
|
182
|
+
tool: {
|
|
183
|
+
type: "string",
|
|
184
|
+
description: "Name of a read tool (e.g. bimp_nomenclature_read)",
|
|
185
|
+
},
|
|
186
|
+
uuids: {
|
|
187
|
+
type: "array",
|
|
188
|
+
items: { type: "string" },
|
|
189
|
+
description: "Array of UUIDs to read",
|
|
190
|
+
},
|
|
191
|
+
concurrency: {
|
|
192
|
+
type: "number",
|
|
193
|
+
description: "Number of parallel requests (default: 10)",
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
required: ["tool", "uuids"],
|
|
197
|
+
},
|
|
198
|
+
handler: async (params) => {
|
|
199
|
+
const toolName = params.tool;
|
|
200
|
+
const uuids = params.uuids;
|
|
201
|
+
const concurrency = params.concurrency ?? 10;
|
|
202
|
+
const toolDef = toolMap.get(toolName);
|
|
203
|
+
if (!toolDef) {
|
|
204
|
+
throw new Error(`Tool not found: ${toolName}`);
|
|
205
|
+
}
|
|
206
|
+
return batchReadUuids(client, toolDef, uuids, concurrency);
|
|
207
|
+
},
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
function createBulkUpdateTool(client, toolMap) {
|
|
211
|
+
return {
|
|
212
|
+
name: "bimp_bulk_update",
|
|
213
|
+
description: "Update multiple items in parallel. Provide the update tool name and an array of items (each must include uuid).",
|
|
214
|
+
inputSchema: {
|
|
215
|
+
type: "object",
|
|
216
|
+
properties: {
|
|
217
|
+
tool: {
|
|
218
|
+
type: "string",
|
|
219
|
+
description: "Name of an update tool (e.g. bimp_nomenclature_update)",
|
|
220
|
+
},
|
|
221
|
+
items: {
|
|
222
|
+
type: "array",
|
|
223
|
+
items: { type: "object" },
|
|
224
|
+
description: "Array of objects to update (each must contain uuid)",
|
|
225
|
+
},
|
|
226
|
+
concurrency: {
|
|
227
|
+
type: "number",
|
|
228
|
+
description: "Number of parallel requests (default: 5)",
|
|
229
|
+
},
|
|
230
|
+
},
|
|
231
|
+
required: ["tool", "items"],
|
|
232
|
+
},
|
|
233
|
+
handler: async (params) => {
|
|
234
|
+
const toolName = params.tool;
|
|
235
|
+
const items = params.items;
|
|
236
|
+
const concurrency = params.concurrency ?? 5;
|
|
237
|
+
const toolDef = toolMap.get(toolName);
|
|
238
|
+
if (!toolDef) {
|
|
239
|
+
throw new Error(`Tool not found: ${toolName}`);
|
|
240
|
+
}
|
|
241
|
+
let updated = 0;
|
|
242
|
+
const errors = [];
|
|
243
|
+
const tasks = items.map((item) => async () => {
|
|
244
|
+
await client.request(toolDef.metadata.method, toolDef.metadata.path, item);
|
|
245
|
+
return item.uuid;
|
|
246
|
+
});
|
|
247
|
+
const results = await runInBatches(tasks, concurrency);
|
|
248
|
+
for (let i = 0; i < results.length; i++) {
|
|
249
|
+
const result = results[i];
|
|
250
|
+
if (result.status === "fulfilled") {
|
|
251
|
+
updated++;
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
errors.push({
|
|
255
|
+
uuid: items[i].uuid ?? `item[${i}]`,
|
|
256
|
+
error: result.reason instanceof Error ? result.reason.message : String(result.reason),
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return { updated, errors };
|
|
261
|
+
},
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
export function createUtilityTools(client, toolMap) {
|
|
265
|
+
return [
|
|
266
|
+
createFetchAllTool(client, toolMap),
|
|
267
|
+
createBatchReadTool(client, toolMap),
|
|
268
|
+
createBulkUpdateTool(client, toolMap),
|
|
269
|
+
];
|
|
270
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "bimp-mcp",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "MCP server for BIMP ERP API — ~140 tools dynamically generated from OpenAPI spec, plus planning/accounting fields and bulk operations",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"bimp-mcp": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"bimp-api.json"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"start": "tsx src/index.ts",
|
|
16
|
+
"dev": "tsx watch src/index.ts",
|
|
17
|
+
"build": "tsc -p tsconfig.build.json",
|
|
18
|
+
"prepublishOnly": "npm run build",
|
|
19
|
+
"test": "vitest run --project unit",
|
|
20
|
+
"test:integration": "vitest run --project integration",
|
|
21
|
+
"test:functional": "vitest run --project functional",
|
|
22
|
+
"test:all": "vitest run",
|
|
23
|
+
"test:watch": "vitest --project unit",
|
|
24
|
+
"demo:nomenclatures": "tsx scripts/demo-nomenclatures.ts"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"mcp",
|
|
28
|
+
"model-context-protocol",
|
|
29
|
+
"bimp",
|
|
30
|
+
"erp",
|
|
31
|
+
"bimpsoft",
|
|
32
|
+
"claude",
|
|
33
|
+
"ai-tools"
|
|
34
|
+
],
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "https://github.com/dutchakdev/bimp-mcp.git"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
42
|
+
"zod": "^3.25.0"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@types/node": "^22.0.0",
|
|
46
|
+
"dotenv": "^16.5.0",
|
|
47
|
+
"tsx": "^4.19.0",
|
|
48
|
+
"typescript": "^5.8.0",
|
|
49
|
+
"vitest": "^3.2.0"
|
|
50
|
+
}
|
|
51
|
+
}
|