@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,40 @@
1
+ 'use strict';
2
+
3
+ class DataFormatsClient {
4
+ constructor(http) {
5
+ this.http = http;
6
+ }
7
+
8
+ _base(appName) {
9
+ return `api/a/bm/${appName}/dataFormat`;
10
+ }
11
+
12
+ list(appName, {filter, select, count = -1, page = 1} = {}) {
13
+ const params = new URLSearchParams();
14
+ params.set('filter', JSON.stringify({app: appName, ...(filter || {})}));
15
+ params.set('count', String(count));
16
+ params.set('page', String(page));
17
+ if (select) params.set('select', select);
18
+ return this.http.get(`${this._base(appName)}?${params.toString()}`);
19
+ }
20
+
21
+ get(appName, id) {
22
+ return this.http.get(`${this._base(appName)}/${id}`);
23
+ }
24
+
25
+ // Step 1: POST shell with empty definition. Platform assigns _id.
26
+ create(appName, payload) {
27
+ return this.http.post(this._base(appName), payload);
28
+ }
29
+
30
+ // Step 2 (and all attribute mutations): full PUT with populated definition.
31
+ update(appName, id, payload) {
32
+ return this.http.put(`${this._base(appName)}/${id}`, payload);
33
+ }
34
+
35
+ delete(appName, id) {
36
+ return this.http.delete(`${this._base(appName)}/${id}`);
37
+ }
38
+ }
39
+
40
+ module.exports = DataFormatsClient;
@@ -0,0 +1,33 @@
1
+ 'use strict';
2
+
3
+ class DataPipesClient {
4
+ constructor(http) {
5
+ this.http = http;
6
+ }
7
+
8
+ list(appName, {filter, select, count = -1} = {}) {
9
+ const params = new URLSearchParams();
10
+ params.set('filter', JSON.stringify({app: appName, ...(filter || {})}));
11
+ params.set('count', String(count));
12
+ if (select) params.set('select', select);
13
+ return this.http.get(`api/a/bm/${appName}/flow?${params.toString()}`);
14
+ }
15
+
16
+ get(appName, flowId) {
17
+ return this.http.get(`api/a/bm/${appName}/flow/${flowId}`);
18
+ }
19
+
20
+ create(appName, payload) {
21
+ return this.http.post(`api/a/bm/${appName}/flow`, payload);
22
+ }
23
+
24
+ update(appName, flowId, payload) {
25
+ return this.http.put(`api/a/bm/${appName}/flow/${flowId}`, payload);
26
+ }
27
+
28
+ publish(appName, flowId) {
29
+ return this.http.put(`api/a/bm/${appName}/flow/utils/${flowId}/publish`, {app: appName});
30
+ }
31
+ }
32
+
33
+ module.exports = DataPipesClient;
@@ -0,0 +1,59 @@
1
+ 'use strict';
2
+
3
+ class DeploymentGroupsClient {
4
+ constructor(http) {
5
+ this.http = http;
6
+ }
7
+
8
+ _base(appName) {
9
+ return `api/a/bm/${appName}/deployment/group`;
10
+ }
11
+
12
+ listAvailableFlows(appName) {
13
+ const filter = encodeURIComponent(JSON.stringify({app: appName}));
14
+ return this.http.get(`${this._base(appName)}/utils/available/flows?filter=${filter}`);
15
+ }
16
+
17
+ list(appName, {filter, count = -1, select} = {}) {
18
+ const params = new URLSearchParams();
19
+ params.set('filter', JSON.stringify({app: appName, ...(filter || {})}));
20
+ params.set('count', String(count));
21
+ if (select) params.set('select', select);
22
+ return this.http.get(`${this._base(appName)}?${params.toString()}`);
23
+ }
24
+
25
+ get(appName, groupId) {
26
+ return this.http.get(`${this._base(appName)}/${groupId}`);
27
+ }
28
+
29
+ create(appName, payload) {
30
+ return this.http.post(this._base(appName), payload);
31
+ }
32
+
33
+ update(appName, groupId, payload) {
34
+ return this.http.put(`${this._base(appName)}/${groupId}`, payload);
35
+ }
36
+
37
+ start(appName, groupId) {
38
+ return this.http.put(`${this._base(appName)}/utils/${groupId}/start`, {});
39
+ }
40
+
41
+ stop(appName, groupId) {
42
+ return this.http.put(`${this._base(appName)}/utils/${groupId}/stop`, {});
43
+ }
44
+
45
+ sync(appName, groupId) {
46
+ return this.http.put(`${this._base(appName)}/utils/${groupId}/sync`, {});
47
+ }
48
+
49
+ delete(appName, groupId) {
50
+ return this.http.delete(`${this._base(appName)}/${groupId}`);
51
+ }
52
+
53
+ getYamls(appName, groupId) {
54
+ const filter = encodeURIComponent(JSON.stringify({app: appName}));
55
+ return this.http.get(`${this._base(appName)}/utils/${groupId}/yamls?filter=${filter}`);
56
+ }
57
+ }
58
+
59
+ module.exports = DeploymentGroupsClient;
@@ -0,0 +1,10 @@
1
+ 'use strict';
2
+
3
+ class FormulasClient {
4
+ constructor(http) {
5
+ this.http = http;
6
+ }
7
+ // TODO: implement DNIO formulas APIs
8
+ }
9
+
10
+ module.exports = FormulasClient;
@@ -0,0 +1,10 @@
1
+ 'use strict';
2
+
3
+ class FunctionsClient {
4
+ constructor(http) {
5
+ this.http = http;
6
+ }
7
+ // TODO: implement DNIO functions APIs
8
+ }
9
+
10
+ module.exports = FunctionsClient;
@@ -0,0 +1,39 @@
1
+ 'use strict';
2
+
3
+ class PluginsClient {
4
+ constructor(http) {
5
+ this.http = http;
6
+ }
7
+
8
+ listMarketplace(appName, {page = 1, count = -1, select, sort = 'label', filter} = {}) {
9
+ const params = new URLSearchParams();
10
+ params.set('page', String(page));
11
+ params.set('count', String(count));
12
+ params.set('select', select || 'label,type,version,icon');
13
+ params.set('sort', sort);
14
+ params.set('filter', JSON.stringify(filter || {}));
15
+ return this.http.get(`api/a/bm/${appName}/marketplace/node?${params.toString()}`);
16
+ }
17
+
18
+ listInstalled(appName, {count = -1, select, filter} = {}) {
19
+ const params = new URLSearchParams();
20
+ params.set('count', String(count));
21
+ params.set('select', select || 'label,type,version,icon');
22
+ params.set('filter', JSON.stringify({app: appName, ...(filter || {})}));
23
+ return this.http.get(`api/a/bm/${appName}/my-node?${params.toString()}`);
24
+ }
25
+
26
+ install(appName, marketIds) {
27
+ return this.http.post(`api/a/bm/${appName}/my-node/utils/install`, {marketIds});
28
+ }
29
+
30
+ update(appName, ids) {
31
+ return this.http.post(`api/a/bm/${appName}/my-node/utils/update`, {app: appName, ids});
32
+ }
33
+
34
+ uninstall(appName, ids) {
35
+ return this.http.post(`api/a/bm/${appName}/my-node/utils/uninstall`, {app: appName, ids});
36
+ }
37
+ }
38
+
39
+ module.exports = PluginsClient;
@@ -0,0 +1,51 @@
1
+ 'use strict';
2
+
3
+ class RecordsClient {
4
+ constructor(http) {
5
+ this.http = http;
6
+ }
7
+
8
+ _endpoint(appName, servicePath, recordId) {
9
+ const base = `api/c/${appName}/${servicePath}`;
10
+ return recordId ? `${base}/${recordId}` : base;
11
+ }
12
+
13
+ list(appName, servicePath, {filter, sort, select, page = 1, count = 20, expand = false} = {}) {
14
+ const endpoint = this._endpoint(appName, servicePath);
15
+ const params = new URLSearchParams();
16
+ if (filter) params.set('filter', JSON.stringify(filter));
17
+ if (sort) params.set('sort', sort);
18
+ if (select) params.set('select', select);
19
+ params.set('page', String(page));
20
+ params.set('count', String(count));
21
+ if (expand) params.set('expand', 'true');
22
+ const qs = params.toString();
23
+ return this.http.get(`${endpoint}${qs ? '?' + qs : ''}`);
24
+ }
25
+
26
+ get(appName, servicePath, recordId, {expand = false} = {}) {
27
+ let endpoint = this._endpoint(appName, servicePath, recordId);
28
+ if (expand) endpoint += '?expand=true';
29
+ return this.http.get(endpoint);
30
+ }
31
+
32
+ create(appName, servicePath, data) {
33
+ return this.http.post(this._endpoint(appName, servicePath), data);
34
+ }
35
+
36
+ update(appName, servicePath, recordId, data) {
37
+ return this.http.put(this._endpoint(appName, servicePath, recordId), data);
38
+ }
39
+
40
+ delete(appName, servicePath, recordId) {
41
+ return this.http.delete(this._endpoint(appName, servicePath, recordId));
42
+ }
43
+
44
+ count(appName, servicePath, filter) {
45
+ let endpoint = this._endpoint(appName, servicePath) + '/utils/count';
46
+ if (filter) endpoint += `?filter=${encodeURIComponent(JSON.stringify(filter))}`;
47
+ return this.http.get(endpoint, {timeout: {request: 1000}});
48
+ }
49
+ }
50
+
51
+ module.exports = RecordsClient;
@@ -0,0 +1,63 @@
1
+ 'use strict';
2
+
3
+ const logger = require('../utils/logger');
4
+
5
+ class ServicesClient {
6
+ constructor(http) {
7
+ this.http = http;
8
+ }
9
+
10
+ _endpoint(appName, serviceId) {
11
+ const base = `api/a/sm/${appName}/service`;
12
+ return serviceId ? `${base}/${serviceId}` : base;
13
+ }
14
+
15
+ list(appName, {filter, select, count = -1} = {}) {
16
+ const params = new URLSearchParams();
17
+ params.set('filter', JSON.stringify({app: appName, ...(filter || {})}));
18
+ params.set('count', String(count));
19
+ if (select) params.set('select', select);
20
+ return this.http.get(`${this._endpoint(appName)}?${params.toString()}`);
21
+ }
22
+
23
+ getSchema(appName, serviceId) {
24
+ return this.http.get(`api/a/sm/${appName}/service/${serviceId}`);
25
+ }
26
+
27
+ create(appName, data) {
28
+ return this.http.post(this._endpoint(appName), data);
29
+ }
30
+
31
+ update(appName, serviceId, data) {
32
+ return this.http.put(this._endpoint(appName, serviceId), data);
33
+ }
34
+
35
+ get(appName, serviceId, {draft = false, select} = {}) {
36
+ const params = new URLSearchParams();
37
+ params.set('filter', JSON.stringify({app: appName}));
38
+ if (draft) params.set('draft', 'true');
39
+ if (select) params.set('select', select);
40
+ return this.http.get(`${this._endpoint(appName, serviceId)}?${params.toString()}`);
41
+ }
42
+
43
+ deploy(appName, serviceId) {
44
+ return this.http.put(`api/a/sm/${appName}/service/utils/${serviceId}/deploy`, {app: appName});
45
+ }
46
+
47
+ startStop(appName, serviceId, {start = true} = {}) {
48
+ const action = start ? 'start' : 'stop';
49
+ return this.http.put(`api/a/sm/${appName}/service/utils/${serviceId}/${action}`, {app: appName});
50
+ }
51
+
52
+ async verifyPod(appName, servicePath) {
53
+ try {
54
+ await this.http.records.count(appName, servicePath);
55
+ return true;
56
+ } catch (error) {
57
+ logger.warn(`Pod verification failed for ${appName}/${servicePath}: ${error.message}`);
58
+ return false;
59
+ }
60
+ }
61
+ }
62
+
63
+ module.exports = ServicesClient;
@@ -0,0 +1,10 @@
1
+ 'use strict';
2
+
3
+ class UserGroupsClient {
4
+ constructor(http) {
5
+ this.http = http;
6
+ }
7
+ // TODO: implement DNIO user groups APIs
8
+ }
9
+
10
+ module.exports = UserGroupsClient;
@@ -0,0 +1,10 @@
1
+ 'use strict';
2
+
3
+ class UsersClient {
4
+ constructor(http) {
5
+ this.http = http;
6
+ }
7
+ // TODO: implement DNIO users APIs
8
+ }
9
+
10
+ module.exports = UsersClient;
@@ -0,0 +1,165 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * AI SDK Integration Example
5
+ *
6
+ * Shows how to connect the DNIO MCP server to AI models via Vercel AI SDK.
7
+ * Supports: Google Vertex AI, OpenAI, Local Ollama
8
+ *
9
+ * Usage:
10
+ * PROVIDER=vertex node src/examples/ai-sdk-client.js "List all contracts"
11
+ * PROVIDER=openai node src/examples/ai-sdk-client.js "Create a party named Acme Corp"
12
+ * PROVIDER=ollama node src/examples/ai-sdk-client.js "How many contracts exist?"
13
+ */
14
+
15
+ // ─── Provider Setup ──────────────────────────────────────────────────────────
16
+
17
+ function getModel() {
18
+ const provider = process.env.PROVIDER || 'vertex';
19
+
20
+ switch (provider) {
21
+ case 'vertex': {
22
+ const {createVertex} = require('@ai-sdk/google-vertex');
23
+ const vertex = createVertex({
24
+ project: process.env.GOOGLE_VERTEX_PROJECT || process.env.GOOGLE_CLOUD_PROJECT,
25
+ location: process.env.GOOGLE_VERTEX_LOCATION || 'us-central1',
26
+ ...(process.env.GOOGLE_CLIENT_EMAIL && {
27
+ googleAuthOptions: {
28
+ credentials: {
29
+ client_email: process.env.GOOGLE_CLIENT_EMAIL,
30
+ private_key: (process.env.GOOGLE_PRIVATE_KEY || '').replace(/\\n/g, '\n')
31
+ }
32
+ }
33
+ })
34
+ });
35
+ return vertex(process.env.AI_MODEL || 'gemini-2.5-flash');
36
+ }
37
+
38
+ case 'openai': {
39
+ const {createOpenAI} = require('@ai-sdk/openai');
40
+ const openai = createOpenAI({
41
+ apiKey: process.env.OPENAI_API_KEY,
42
+ ...(process.env.OPENAI_BASE_URL && {baseURL: process.env.OPENAI_BASE_URL})
43
+ });
44
+ return openai(process.env.AI_MODEL || 'gpt-4o');
45
+ }
46
+
47
+ case 'ollama': {
48
+ // Ollama is OpenAI-compatible, so we use the OpenAI provider with custom base URL
49
+ const {createOpenAI} = require('@ai-sdk/openai');
50
+ const ollama = createOpenAI({
51
+ baseURL: process.env.OLLAMA_BASE_URL || 'http://localhost:11434/v1',
52
+ apiKey: 'ollama' // Ollama doesn't need a real key
53
+ });
54
+ return ollama(process.env.AI_MODEL || 'llama3.1');
55
+ }
56
+
57
+ default:
58
+ throw new Error(`Unknown provider: ${provider}. Use 'vertex', 'openai', or 'ollama'`);
59
+ }
60
+ }
61
+
62
+ // ─── MCP Client Connection ───────────────────────────────────────────────────
63
+
64
+ async function connectMCPClient() {
65
+ const {createMCPClient} = require('@ai-sdk/mcp');
66
+
67
+ const mcpTransport = process.env.MCP_TRANSPORT || 'stdio';
68
+
69
+ if (mcpTransport === 'http') {
70
+ // Connect to remote MCP server via HTTP
71
+ const mcpUrl = process.env.MCP_SERVER_URL || 'http://localhost:3100/mcp';
72
+ console.error(`Connecting to MCP server at ${mcpUrl}`);
73
+
74
+ return createMCPClient({
75
+ transport: {
76
+ type: 'http',
77
+ url: mcpUrl
78
+ }
79
+ });
80
+ } else {
81
+ // Spawn MCP server as subprocess (stdio)
82
+ const {Experimental_StdioMCPTransport} = require('@ai-sdk/mcp/mcp-stdio');
83
+
84
+ console.error('Starting MCP server via stdio...');
85
+ return createMCPClient({
86
+ transport: new Experimental_StdioMCPTransport({
87
+ command: 'node',
88
+ args: [require('path').resolve(__dirname, '../index.js')],
89
+ env: {
90
+ ...process.env,
91
+ TRANSPORT: 'stdio'
92
+ }
93
+ })
94
+ });
95
+ }
96
+ }
97
+
98
+ // ─── Main Agent Loop ─────────────────────────────────────────────────────────
99
+
100
+ async function main() {
101
+ const prompt = process.argv[2];
102
+ if (!prompt) {
103
+ console.error('Usage: node ai-sdk-client.js "your question here"');
104
+ console.error('');
105
+ console.error('Environment variables:');
106
+ console.error(' PROVIDER=vertex|openai|ollama');
107
+ console.error(' MCP_TRANSPORT=stdio|http');
108
+ console.error(' MCP_SERVER_URL=http://localhost:3100/mcp (for http transport)');
109
+ console.error(' DNIO_BASE_URL, DNIO_TOKEN, DNIO_APP_NAME');
110
+ process.exit(1);
111
+ }
112
+
113
+ const {generateText} = require('ai');
114
+
115
+ let mcpClient;
116
+ try {
117
+ // 1. Get AI model
118
+ const model = getModel();
119
+ console.error(`Using model: ${process.env.AI_MODEL || 'default'} via ${process.env.PROVIDER || 'vertex'}`);
120
+
121
+ // 2. Connect to MCP server
122
+ mcpClient = await connectMCPClient();
123
+
124
+ // 3. Get tools from MCP server
125
+ const tools = await mcpClient.tools();
126
+ console.error(`Loaded ${Object.keys(tools).length} tools from MCP server`);
127
+
128
+ // 4. Run agent with tools
129
+ const result = await generateText({
130
+ model,
131
+ tools,
132
+ maxSteps: 10,
133
+ system: `You are a helpful assistant that manages data on the DataNimbus BaaS platform.
134
+ You have access to tools for CRUD operations on various data services.
135
+
136
+ Start by listing available data services if you need to understand what's available.
137
+ Use the appropriate tools to fulfill user requests.
138
+ Always confirm destructive operations before executing them.
139
+
140
+ When creating records, make sure to follow the schema structure for each service.`,
141
+ messages: [{role: 'user', content: prompt}]
142
+ });
143
+
144
+ console.log('\n' + result.text);
145
+
146
+ // Show tool usage summary
147
+ if (result.steps?.length > 1) {
148
+ console.error(`\n--- Completed in ${result.steps.length} steps ---`);
149
+ for (const step of result.steps) {
150
+ if (step.toolCalls) {
151
+ for (const call of step.toolCalls) {
152
+ console.error(` Tool: ${call.toolName}(${JSON.stringify(call.args).substring(0, 80)}...)`);
153
+ }
154
+ }
155
+ }
156
+ }
157
+
158
+ } catch (error) {
159
+ console.error('Error:', error.message);
160
+ if (error.cause) console.error('Cause:', error.cause);
161
+ process.exit(1);
162
+ } finally {
163
+ if (mcpClient) await mcpClient.close();
164
+ }
165
+ }
@@ -0,0 +1,34 @@
1
+ {
2
+ "mcpServers": {
3
+ "dnio": {
4
+ "command": "node",
5
+ "args": [
6
+ "<ABSOLUTE_PATH_TO>/dnio-mcp-server/src/index.js"
7
+ ],
8
+ "env": {
9
+ "DNIO_BASE_URL": "<YOUR_DNIO_BASE_URL>",
10
+ "DNIO_USERNAME": "<DNIO_ADMIN_USERNAME>",
11
+ "DNIO_PASSWORD": "<DNIO_ADMIN_PASSWORD>",
12
+ "MCP_USER_EMAIL": "<MCP_USER_ACCOUNT_EMAIL>",
13
+ "MCP_USER_PASSWORD": "<MCP_USER_ACCOUNT_PASSWORD>",
14
+ "DNIO_TOKEN_TTL": "1800",
15
+ "DNIO_NAMESPACE": "DNIO",
16
+ "TRANSPORT": "stdio",
17
+ "LOG_LEVEL": "info"
18
+ }
19
+ },
20
+ "dnio-bridge": {
21
+ "command": "node",
22
+ "args": [
23
+ "<ABSOLUTE_PATH_TO>/dnio-mcp-server/src/stdio-bridge.js"
24
+ ],
25
+ "env": {
26
+ "MCP_REMOTE_URL": "http://localhost:3100/mcp"
27
+ }
28
+ },
29
+ "dnio-remote": {
30
+ "type": "streamable-http",
31
+ "url": "<YOUR_DEPLOYED_MCP_SERVER_URL>/mcp"
32
+ }
33
+ }
34
+ }