@datanimbus/dnio-mcp 1.0.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.
Files changed (59) hide show
  1. package/Dockerfile +20 -0
  2. package/docs/README.md +35 -0
  3. package/docs/architecture.md +171 -0
  4. package/docs/authentication.md +74 -0
  5. package/docs/tools/apps.md +59 -0
  6. package/docs/tools/connectors.md +76 -0
  7. package/docs/tools/data-pipes.md +286 -0
  8. package/docs/tools/data-services.md +105 -0
  9. package/docs/tools/deployment-groups.md +152 -0
  10. package/docs/tools/plugins.md +94 -0
  11. package/docs/tools/records.md +97 -0
  12. package/docs/workflows.md +195 -0
  13. package/env.example +16 -0
  14. package/package.json +43 -0
  15. package/readme.md +144 -0
  16. package/src/clients/api-keys.js +10 -0
  17. package/src/clients/apps.js +13 -0
  18. package/src/clients/base-client.js +78 -0
  19. package/src/clients/bots.js +10 -0
  20. package/src/clients/connectors.js +30 -0
  21. package/src/clients/data-formats.js +40 -0
  22. package/src/clients/data-pipes.js +33 -0
  23. package/src/clients/deployment-groups.js +59 -0
  24. package/src/clients/formulas.js +10 -0
  25. package/src/clients/functions.js +10 -0
  26. package/src/clients/plugins.js +39 -0
  27. package/src/clients/records.js +51 -0
  28. package/src/clients/services.js +63 -0
  29. package/src/clients/user-groups.js +10 -0
  30. package/src/clients/users.js +10 -0
  31. package/src/examples/ai-sdk-client.js +165 -0
  32. package/src/examples/claude_desktop_config.json +34 -0
  33. package/src/examples/express-integration.js +181 -0
  34. package/src/index.js +283 -0
  35. package/src/schemas/schema-converter.js +179 -0
  36. package/src/services/auth-manager.js +277 -0
  37. package/src/services/dnio-client.js +40 -0
  38. package/src/services/service-registry.js +150 -0
  39. package/src/services/session-manager.js +161 -0
  40. package/src/stdio-bridge.js +185 -0
  41. package/src/tools/_helpers.js +32 -0
  42. package/src/tools/api-keys.js +5 -0
  43. package/src/tools/apps.js +185 -0
  44. package/src/tools/bots.js +5 -0
  45. package/src/tools/connectors.js +165 -0
  46. package/src/tools/data-formats.js +806 -0
  47. package/src/tools/data-pipes.js +1305 -0
  48. package/src/tools/data-service-registry.js +500 -0
  49. package/src/tools/deployment-groups.js +511 -0
  50. package/src/tools/formulas.js +5 -0
  51. package/src/tools/functions.js +5 -0
  52. package/src/tools/mcp-tools-registry.js +38 -0
  53. package/src/tools/plugins.js +250 -0
  54. package/src/tools/records.js +217 -0
  55. package/src/tools/services.js +476 -0
  56. package/src/tools/user-groups.js +5 -0
  57. package/src/tools/users.js +5 -0
  58. package/src/utils/constants.js +135 -0
  59. package/src/utils/logger.js +63 -0
@@ -0,0 +1,250 @@
1
+ 'use strict';
2
+
3
+ const {z} = require('zod');
4
+ const {toolError} = require('./_helpers');
5
+
6
+ // Default select strings: minimal for browsing, full for getting one plugin's details.
7
+ const SUMMARY_SELECT = 'label,type,version,icon';
8
+ const DETAIL_SELECT = '_id,app,nodeId,version,category,group,type,label,icon,connectorType,inputSchema,outputSchema,errorSchema,code';
9
+
10
+ module.exports = function registerPluginsTools({server, dnioClient, registry, userContext}) {
11
+ server.registerTool(
12
+ 'list_marketplace_plugins',
13
+ {
14
+ title: 'List Marketplace Plugins',
15
+ description: `List plugins (workflow nodes) available in the DataNimbus marketplace. Plugins are reusable code blocks used as nodes in data pipes — they aren't usable in this app until installed via 'install_plugins'.
16
+
17
+ Each item shows the marketplace _id (use as 'marketIds' entry for install_plugins), label, type, version, and icon. Call this BEFORE install_plugins.
18
+
19
+ Args:
20
+ - search (optional): Substring match against label or type. Omit for all.`,
21
+ inputSchema: {
22
+ search: z.string().optional().describe('Substring match against label or type.')
23
+ },
24
+ annotations: {readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false}
25
+ },
26
+ async (params) => {
27
+ if (!registry.selectedApp) {
28
+ return {content: [{type: 'text', text: 'No app selected. Use list_apps → select_app first.'}], isError: true};
29
+ }
30
+ try {
31
+ dnioClient.setToken(userContext.token);
32
+ const result = await dnioClient.plugins.listMarketplace(registry.selectedApp);
33
+ let items = Array.isArray(result) ? result : [];
34
+ if (params.search) {
35
+ const q = params.search.toLowerCase();
36
+ items = items.filter(i =>
37
+ (i.label || '').toLowerCase().includes(q) ||
38
+ (i.type || '').toLowerCase().includes(q)
39
+ );
40
+ }
41
+ const summary = items.map(i => ({
42
+ marketId: i._id,
43
+ type: i.type,
44
+ label: i.label,
45
+ version: i.version,
46
+ icon: i.icon
47
+ }));
48
+ return {
49
+ content: [{
50
+ type: 'text',
51
+ text: JSON.stringify({
52
+ app: registry.selectedApp,
53
+ count: summary.length,
54
+ plugins: summary,
55
+ usage: "Pass an array of 'marketId' values to install_plugins to install them into this app."
56
+ }, null, 2)
57
+ }]
58
+ };
59
+ } catch (error) {
60
+ return toolError('Failed to list marketplace plugins', error);
61
+ }
62
+ }
63
+ );
64
+
65
+ server.registerTool(
66
+ 'install_plugins',
67
+ {
68
+ title: 'Install Plugins From Marketplace',
69
+ description: `Install one or more marketplace plugins into the currently selected app. After install, the plugins are available for use in data pipes built within this app.
70
+
71
+ Call list_marketplace_plugins first to get the marketIds. The 'app' is auto-injected from the selected app context.
72
+
73
+ Args:
74
+ - marketIds (required): Array of marketplace _id values to install (pass even a single one as an array).`,
75
+ inputSchema: {
76
+ marketIds: z.array(z.string().min(1)).min(1).describe('Array of marketplace plugin _id values to install')
77
+ },
78
+ annotations: {destructiveHint: false, idempotentHint: true}
79
+ },
80
+ async (params) => {
81
+ if (!registry.selectedApp) {
82
+ return {content: [{type: 'text', text: 'No app selected. Use list_apps → select_app first.'}], isError: true};
83
+ }
84
+ try {
85
+ dnioClient.setToken(userContext.token);
86
+ const result = await dnioClient.plugins.install(registry.selectedApp, params.marketIds);
87
+ const items = Array.isArray(result) ? result : [];
88
+ const installed = items.filter(i => i.statusCode === 200 || i.statusCode === 201);
89
+ const failed = items.filter(i => i.statusCode && i.statusCode >= 400);
90
+ return {
91
+ content: [{
92
+ type: 'text',
93
+ text: JSON.stringify({
94
+ app: registry.selectedApp,
95
+ requested: params.marketIds.length,
96
+ installed: installed.length,
97
+ failed: failed.length,
98
+ results: items
99
+ }, null, 2)
100
+ }]
101
+ };
102
+ } catch (error) {
103
+ return toolError('Failed to install plugins', error);
104
+ }
105
+ }
106
+ );
107
+
108
+ server.registerTool(
109
+ 'list_installed_plugins',
110
+ {
111
+ title: 'List or Get Installed Plugins',
112
+ description: `List plugins installed in the currently selected app, or fetch full details for a specific one.
113
+
114
+ Behaviour:
115
+ - No args → returns a summary list of all installed plugins (label, type, version, icon).
116
+ - 'pluginId' set → returns just that plugin (single object). Combined with 'details: true' fetches full schema (inputSchema/outputSchema/errorSchema/code).
117
+ - 'details: true' (no pluginId) → returns the full schema for every installed plugin (potentially large).
118
+ - 'search' → substring filter on label/type.
119
+
120
+ Args:
121
+ - pluginId (optional): Installed plugin _id (the long hex from install_plugins or list_installed_plugins).
122
+ - details (optional, default false): If true, include inputSchema, outputSchema, errorSchema, and code.
123
+ - search (optional): Substring match against label or type.`,
124
+ inputSchema: {
125
+ pluginId: z.string().optional().describe('Installed plugin _id; omit to list all.'),
126
+ details: z.boolean().optional().describe('If true, fetch full schemas + code. Default: false.'),
127
+ search: z.string().optional().describe('Substring match against label or type.')
128
+ },
129
+ annotations: {readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false}
130
+ },
131
+ async (params) => {
132
+ if (!registry.selectedApp) {
133
+ return {content: [{type: 'text', text: 'No app selected. Use list_apps → select_app first.'}], isError: true};
134
+ }
135
+ try {
136
+ dnioClient.setToken(userContext.token);
137
+ const select = params.details ? DETAIL_SELECT : SUMMARY_SELECT;
138
+ const filter = params.pluginId ? {_id: params.pluginId} : undefined;
139
+ const result = await dnioClient.plugins.listInstalled(registry.selectedApp, {select, filter});
140
+ let items = Array.isArray(result) ? result : [];
141
+
142
+ if (params.search) {
143
+ const q = params.search.toLowerCase();
144
+ items = items.filter(i =>
145
+ (i.label || '').toLowerCase().includes(q) ||
146
+ (i.type || '').toLowerCase().includes(q)
147
+ );
148
+ }
149
+
150
+ if (params.pluginId) {
151
+ const plugin = items[0];
152
+ if (!plugin) {
153
+ return {content: [{type: 'text', text: `Plugin '${params.pluginId}' not installed in ${registry.selectedApp}.`}], isError: true};
154
+ }
155
+ return {content: [{type: 'text', text: JSON.stringify({app: registry.selectedApp, plugin}, null, 2)}]};
156
+ }
157
+
158
+ return {
159
+ content: [{
160
+ type: 'text',
161
+ text: JSON.stringify({
162
+ app: registry.selectedApp,
163
+ count: items.length,
164
+ details: !!params.details,
165
+ plugins: items
166
+ }, null, 2)
167
+ }]
168
+ };
169
+ } catch (error) {
170
+ return toolError('Failed to list installed plugins', error);
171
+ }
172
+ }
173
+ );
174
+
175
+ server.registerTool(
176
+ 'update_plugins',
177
+ {
178
+ title: 'Refresh / Update Installed Plugins',
179
+ description: `Refresh installed plugins to the latest version from the marketplace. Use the installed plugin _id (the long hex from list_installed_plugins), NOT the marketplace _id.
180
+
181
+ The 'app' field in the request body is auto-injected from the selected app.
182
+
183
+ Args:
184
+ - ids (required): Array of installed plugin _id values to refresh.`,
185
+ inputSchema: {
186
+ ids: z.array(z.string().min(1)).min(1).describe('Array of installed plugin _id values to refresh')
187
+ },
188
+ annotations: {destructiveHint: false, idempotentHint: true}
189
+ },
190
+ async (params) => {
191
+ if (!registry.selectedApp) {
192
+ return {content: [{type: 'text', text: 'No app selected. Use list_apps → select_app first.'}], isError: true};
193
+ }
194
+ try {
195
+ dnioClient.setToken(userContext.token);
196
+ const result = await dnioClient.plugins.update(registry.selectedApp, params.ids);
197
+ return {
198
+ content: [{
199
+ type: 'text',
200
+ text: JSON.stringify({
201
+ app: registry.selectedApp,
202
+ requested: params.ids.length,
203
+ result
204
+ }, null, 2)
205
+ }]
206
+ };
207
+ } catch (error) {
208
+ return toolError('Failed to update plugins', error);
209
+ }
210
+ }
211
+ );
212
+
213
+ server.registerTool(
214
+ 'uninstall_plugins',
215
+ {
216
+ title: 'Uninstall Plugins From App',
217
+ description: `⚠️ Destructive. Uninstall plugins from the currently selected app. Data pipes that use uninstalled plugins may break.
218
+
219
+ Use the installed plugin _id (from list_installed_plugins), NOT the marketplace _id. The 'app' field is auto-injected from the selected app.
220
+
221
+ Args:
222
+ - ids (required): Array of installed plugin _id values to uninstall.`,
223
+ inputSchema: {
224
+ ids: z.array(z.string().min(1)).min(1).describe('Array of installed plugin _id values to uninstall')
225
+ },
226
+ annotations: {destructiveHint: true, idempotentHint: false}
227
+ },
228
+ async (params) => {
229
+ if (!registry.selectedApp) {
230
+ return {content: [{type: 'text', text: 'No app selected. Use list_apps → select_app first.'}], isError: true};
231
+ }
232
+ try {
233
+ dnioClient.setToken(userContext.token);
234
+ const result = await dnioClient.plugins.uninstall(registry.selectedApp, params.ids);
235
+ return {
236
+ content: [{
237
+ type: 'text',
238
+ text: JSON.stringify({
239
+ app: registry.selectedApp,
240
+ requested: params.ids.length,
241
+ result
242
+ }, null, 2)
243
+ }]
244
+ };
245
+ } catch (error) {
246
+ return toolError('Failed to uninstall plugins', error);
247
+ }
248
+ }
249
+ );
250
+ };
@@ -0,0 +1,217 @@
1
+ 'use strict';
2
+
3
+ const {z} = require('zod');
4
+ const {toolError, resolveServiceOrError} = require('./_helpers');
5
+
6
+ module.exports = function registerRecordsTools({server, dnioClient, registry, userContext}) {
7
+ server.registerTool(
8
+ 'list_records',
9
+ {
10
+ title: 'List Records',
11
+ description: `List/search records from a data service. Supports filtering, sorting, pagination.
12
+ Args: serviceName (required), filter (optional, MongoDB JSON), sort, select, page, count`,
13
+ inputSchema: {
14
+ serviceName: z.string().min(1).describe('Service name'),
15
+ filter: z.string().optional().describe('MongoDB-style JSON filter'),
16
+ sort: z.string().optional().describe('Sort field (prefix - for desc)'),
17
+ select: z.string().optional().describe('Comma-separated fields'),
18
+ page: z.number().int().min(1).default(1).optional().describe('Page (default: 1)'),
19
+ count: z.number().int().min(1).max(100).default(20).optional().describe('Per page (default: 20)')
20
+ },
21
+ annotations: {readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false}
22
+ },
23
+ async (params) => {
24
+ const svc = resolveServiceOrError(registry, params.serviceName);
25
+ if (svc.isError) return svc;
26
+ try {
27
+ dnioClient.setToken(userContext.token);
28
+ const filter = params.filter ? JSON.parse(params.filter) : undefined;
29
+ const result = await dnioClient.records.list(registry.selectedApp, svc.servicePath, {
30
+ filter, sort: params.sort, select: params.select, page: params.page || 1, count: params.count || 20
31
+ });
32
+ const records = Array.isArray(result) ? result : (result.records || [result]);
33
+ return {
34
+ content: [{
35
+ type: 'text',
36
+ text: JSON.stringify({
37
+ service: svc.name,
38
+ count: records.length,
39
+ page: params.page || 1,
40
+ records
41
+ }, null, 2)
42
+ }]
43
+ };
44
+ } catch (error) {
45
+ return toolError(`Failed to list ${svc.name} records`, error);
46
+ }
47
+ }
48
+ );
49
+
50
+ server.registerTool(
51
+ 'get_record',
52
+ {
53
+ title: 'Get Record',
54
+ description: 'Get a single record by ID.\nArgs: serviceName, recordId, expand (optional boolean)',
55
+ inputSchema: {
56
+ serviceName: z.string().min(1).describe('Service name'),
57
+ recordId: z.string().min(1).describe('Record ID'),
58
+ expand: z.boolean().default(false).optional().describe('Expand related records')
59
+ },
60
+ annotations: {readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false}
61
+ },
62
+ async (params) => {
63
+ const svc = resolveServiceOrError(registry, params.serviceName);
64
+ if (svc.isError) return svc;
65
+ try {
66
+ dnioClient.setToken(userContext.token);
67
+ const result = await dnioClient.records.get(registry.selectedApp, svc.servicePath, params.recordId, {expand: params.expand});
68
+ return {content: [{type: 'text', text: JSON.stringify({service: svc.name, record: result}, null, 2)}]};
69
+ } catch (error) {
70
+ if (error.status === 404) return {
71
+ content: [{
72
+ type: 'text',
73
+ text: `Record '${params.recordId}' not found in ${svc.name}`
74
+ }]
75
+ };
76
+ return toolError(`Failed to get record from ${svc.name}`, error);
77
+ }
78
+ }
79
+ );
80
+
81
+ server.registerTool(
82
+ 'create_record',
83
+ {
84
+ title: 'Create Record',
85
+ description: 'Create a new record. Use get_service_schema first.\nArgs: serviceName, data (JSON string)',
86
+ inputSchema: {
87
+ serviceName: z.string().min(1).describe('Service name'),
88
+ data: z.string().min(2).describe('JSON string of record data')
89
+ },
90
+ annotations: {readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false}
91
+ },
92
+ async (params) => {
93
+ const svc = resolveServiceOrError(registry, params.serviceName);
94
+ if (svc.isError) return svc;
95
+ try {
96
+ dnioClient.setToken(userContext.token);
97
+ const data = JSON.parse(params.data);
98
+ const result = await dnioClient.records.create(registry.selectedApp, svc.servicePath, data);
99
+ return {
100
+ content: [{
101
+ type: 'text',
102
+ text: JSON.stringify({service: svc.name, action: 'created', record: result}, null, 2)
103
+ }]
104
+ };
105
+ } catch (error) {
106
+ return toolError(`Failed to create record in ${svc.name}`, error);
107
+ }
108
+ }
109
+ );
110
+
111
+ server.registerTool(
112
+ 'update_record',
113
+ {
114
+ title: 'Update Record',
115
+ description: 'Update an existing record.\nArgs: serviceName, recordId, data (JSON string)',
116
+ inputSchema: {
117
+ serviceName: z.string().min(1).describe('Service name'),
118
+ recordId: z.string().min(1).describe('Record ID'),
119
+ data: z.string().min(2).describe('JSON string of fields to update')
120
+ },
121
+ annotations: {readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false}
122
+ },
123
+ async (params) => {
124
+ const svc = resolveServiceOrError(registry, params.serviceName);
125
+ if (svc.isError) return svc;
126
+ try {
127
+ dnioClient.setToken(userContext.token);
128
+ const data = JSON.parse(params.data);
129
+ const result = await dnioClient.records.update(registry.selectedApp, svc.servicePath, params.recordId, data);
130
+ return {
131
+ content: [{
132
+ type: 'text',
133
+ text: JSON.stringify({
134
+ service: svc.name,
135
+ action: 'updated',
136
+ recordId: params.recordId,
137
+ record: result
138
+ }, null, 2)
139
+ }]
140
+ };
141
+ } catch (error) {
142
+ if (error.status === 404) return {
143
+ content: [{
144
+ type: 'text',
145
+ text: `Record '${params.recordId}' not found in ${svc.name}`
146
+ }]
147
+ };
148
+ return toolError(`Failed to update record in ${svc.name}`, error);
149
+ }
150
+ }
151
+ );
152
+
153
+ server.registerTool(
154
+ 'delete_record',
155
+ {
156
+ title: 'Delete Record',
157
+ description: '⚠️ Destructive. Delete a record by ID.\nArgs: serviceName, recordId',
158
+ inputSchema: {
159
+ serviceName: z.string().min(1).describe('Service name'),
160
+ recordId: z.string().min(1).describe('Record ID to delete')
161
+ },
162
+ annotations: {readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false}
163
+ },
164
+ async (params) => {
165
+ const svc = resolveServiceOrError(registry, params.serviceName);
166
+ if (svc.isError) return svc;
167
+ try {
168
+ dnioClient.setToken(userContext.token);
169
+ const result = await dnioClient.records.delete(registry.selectedApp, svc.servicePath, params.recordId);
170
+ return {
171
+ content: [{
172
+ type: 'text',
173
+ text: JSON.stringify({
174
+ service: svc.name,
175
+ action: 'deleted',
176
+ recordId: params.recordId,
177
+ result
178
+ }, null, 2)
179
+ }]
180
+ };
181
+ } catch (error) {
182
+ if (error.status === 404) return {
183
+ content: [{
184
+ type: 'text',
185
+ text: `Record '${params.recordId}' not found in ${svc.name}`
186
+ }]
187
+ };
188
+ return toolError(`Failed to delete record from ${svc.name}`, error);
189
+ }
190
+ }
191
+ );
192
+
193
+ server.registerTool(
194
+ 'count_records',
195
+ {
196
+ title: 'Count Records',
197
+ description: 'Count records in a service, with optional filter.\nArgs: serviceName, filter (optional JSON)',
198
+ inputSchema: {
199
+ serviceName: z.string().min(1).describe('Service name'),
200
+ filter: z.string().optional().describe('MongoDB-style JSON filter')
201
+ },
202
+ annotations: {readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false}
203
+ },
204
+ async (params) => {
205
+ const svc = resolveServiceOrError(registry, params.serviceName);
206
+ if (svc.isError) return svc;
207
+ try {
208
+ dnioClient.setToken(userContext.token);
209
+ const filter = params.filter ? JSON.parse(params.filter) : undefined;
210
+ const result = await dnioClient.records.count(registry.selectedApp, svc.servicePath, filter);
211
+ return {content: [{type: 'text', text: JSON.stringify({service: svc.name, count: result}, null, 2)}]};
212
+ } catch (error) {
213
+ return toolError(`Failed to count ${svc.name} records`, error);
214
+ }
215
+ }
216
+ );
217
+ };