@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.
- package/Dockerfile +20 -0
- package/docs/README.md +35 -0
- package/docs/architecture.md +171 -0
- package/docs/authentication.md +74 -0
- package/docs/tools/apps.md +59 -0
- package/docs/tools/connectors.md +76 -0
- package/docs/tools/data-pipes.md +286 -0
- package/docs/tools/data-services.md +105 -0
- package/docs/tools/deployment-groups.md +152 -0
- package/docs/tools/plugins.md +94 -0
- package/docs/tools/records.md +97 -0
- package/docs/workflows.md +195 -0
- package/env.example +16 -0
- package/package.json +43 -0
- package/readme.md +144 -0
- package/src/clients/api-keys.js +10 -0
- package/src/clients/apps.js +13 -0
- package/src/clients/base-client.js +78 -0
- package/src/clients/bots.js +10 -0
- package/src/clients/connectors.js +30 -0
- package/src/clients/data-formats.js +40 -0
- package/src/clients/data-pipes.js +33 -0
- package/src/clients/deployment-groups.js +59 -0
- package/src/clients/formulas.js +10 -0
- package/src/clients/functions.js +10 -0
- package/src/clients/plugins.js +39 -0
- package/src/clients/records.js +51 -0
- package/src/clients/services.js +63 -0
- package/src/clients/user-groups.js +10 -0
- package/src/clients/users.js +10 -0
- package/src/examples/ai-sdk-client.js +165 -0
- package/src/examples/claude_desktop_config.json +34 -0
- package/src/examples/express-integration.js +181 -0
- package/src/index.js +283 -0
- package/src/schemas/schema-converter.js +179 -0
- package/src/services/auth-manager.js +277 -0
- package/src/services/dnio-client.js +40 -0
- package/src/services/service-registry.js +150 -0
- package/src/services/session-manager.js +161 -0
- package/src/stdio-bridge.js +185 -0
- package/src/tools/_helpers.js +32 -0
- package/src/tools/api-keys.js +5 -0
- package/src/tools/apps.js +185 -0
- package/src/tools/bots.js +5 -0
- package/src/tools/connectors.js +165 -0
- package/src/tools/data-formats.js +806 -0
- package/src/tools/data-pipes.js +1305 -0
- package/src/tools/data-service-registry.js +500 -0
- package/src/tools/deployment-groups.js +511 -0
- package/src/tools/formulas.js +5 -0
- package/src/tools/functions.js +5 -0
- package/src/tools/mcp-tools-registry.js +38 -0
- package/src/tools/plugins.js +250 -0
- package/src/tools/records.js +217 -0
- package/src/tools/services.js +476 -0
- package/src/tools/user-groups.js +5 -0
- package/src/tools/users.js +5 -0
- package/src/utils/constants.js +135 -0
- package/src/utils/logger.js +63 -0
package/Dockerfile
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
FROM node:22-alpine
|
|
2
|
+
|
|
3
|
+
RUN apk update && apk upgrade && apk add --no-cache dumb-init
|
|
4
|
+
|
|
5
|
+
WORKDIR /app
|
|
6
|
+
|
|
7
|
+
COPY package.json package-lock.json ./
|
|
8
|
+
|
|
9
|
+
RUN npm ci --omit=dev
|
|
10
|
+
|
|
11
|
+
COPY src/ src/
|
|
12
|
+
|
|
13
|
+
ENV NODE_ENV=production
|
|
14
|
+
ENV TRANSPORT=http
|
|
15
|
+
ENV MCP_PORT=3100
|
|
16
|
+
ENV LOG_LEVEL=info
|
|
17
|
+
|
|
18
|
+
EXPOSE 3100
|
|
19
|
+
|
|
20
|
+
CMD ["dumb-init", "node", "src/index.js"]
|
package/docs/README.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# DNIO MCP Server — Documentation
|
|
2
|
+
|
|
3
|
+
This directory documents the DNIO MCP server in depth. The top-level [`readme.md`](../readme.md) is the entry point for users; these docs are for anyone extending the server, debugging tool behaviour, or needing the full surface reference.
|
|
4
|
+
|
|
5
|
+
## Index
|
|
6
|
+
|
|
7
|
+
### Overview
|
|
8
|
+
- [`architecture.md`](architecture.md) — codebase organization, master-client + per-domain pattern, file layout.
|
|
9
|
+
- [`authentication.md`](authentication.md) — admin + MCP-user dual-token model, refresh lifecycle, app provisioning.
|
|
10
|
+
|
|
11
|
+
### Tool reference (one file per domain)
|
|
12
|
+
- [`tools/apps.md`](tools/apps.md) — app & service discovery (5 tools)
|
|
13
|
+
- [`tools/records.md`](tools/records.md) — record CRUD, generic + per-service typed tools (6 + dynamic)
|
|
14
|
+
- [`tools/data-services.md`](tools/data-services.md) — data-service definitions (7 tools)
|
|
15
|
+
- [`tools/connectors.md`](tools/connectors.md) — connector instances + marketplace (3 tools)
|
|
16
|
+
- [`tools/plugins.md`](tools/plugins.md) — workflow-node plugins from marketplace (5 tools)
|
|
17
|
+
- [`tools/data-pipes.md`](tools/data-pipes.md) — flows: triggers, processes, mappings, branching (10 tools)
|
|
18
|
+
- [`tools/deployment-groups.md`](tools/deployment-groups.md) — K8s deployment lifecycle (12 tools)
|
|
19
|
+
|
|
20
|
+
### Recipes
|
|
21
|
+
- [`workflows.md`](workflows.md) — end-to-end flows that combine multiple tools.
|
|
22
|
+
|
|
23
|
+
### Design history
|
|
24
|
+
- [`superpowers/specs/`](superpowers/specs/) — design docs for past restructures.
|
|
25
|
+
|
|
26
|
+
## How the docs are organized
|
|
27
|
+
|
|
28
|
+
Each `tools/<domain>.md` file lists every tool in that domain with:
|
|
29
|
+
- the tool name as registered with the MCP server,
|
|
30
|
+
- a one-line purpose,
|
|
31
|
+
- the inputs (parameters & types),
|
|
32
|
+
- behaviour notes (validation, auto-injection, server-side defaults),
|
|
33
|
+
- the underlying HTTP endpoint hit on the DNIO platform.
|
|
34
|
+
|
|
35
|
+
The docs intentionally describe **only what is in the code today**. Stub domains (functions, formulas, users, user-groups, api-keys, bots, data-formats) are not documented because no tools are registered for them yet.
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
## Two parallel trees
|
|
4
|
+
|
|
5
|
+
The codebase splits around two parallel concerns:
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
src/
|
|
9
|
+
├── clients/ # HTTP layer, one file per product domain
|
|
10
|
+
│ ├── base-client.js # shared got + token mgmt
|
|
11
|
+
│ ├── apps.js
|
|
12
|
+
│ ├── services.js
|
|
13
|
+
│ ├── records.js
|
|
14
|
+
│ ├── connectors.js
|
|
15
|
+
│ ├── plugins.js
|
|
16
|
+
│ ├── data-pipes.js
|
|
17
|
+
│ ├── deployment-groups.js
|
|
18
|
+
│ └── <stubs>.js # data-formats, formulas, functions, users, …
|
|
19
|
+
└── tools/ # MCP tool registrations, one file per product domain
|
|
20
|
+
├── _helpers.js # toolError + resolveServiceOrError
|
|
21
|
+
├── mcp-tools-registry.js # master: imports every domain registrar
|
|
22
|
+
├── apps.js
|
|
23
|
+
├── services.js
|
|
24
|
+
├── records.js
|
|
25
|
+
├── connectors.js
|
|
26
|
+
├── plugins.js
|
|
27
|
+
├── data-pipes.js
|
|
28
|
+
├── deployment-groups.js
|
|
29
|
+
├── data-service-registry.js # generates per-service typed tools dynamically
|
|
30
|
+
└── <stubs>.js
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Each domain owns:
|
|
34
|
+
- a **client class** in `src/clients/<domain>.js` that defines the HTTP methods,
|
|
35
|
+
- a **tool registrar** in `src/tools/<domain>.js` that exports a `(ctx) => void` function registering MCP tools.
|
|
36
|
+
|
|
37
|
+
The masters (`src/services/dnio-client.js` and `src/tools/mcp-tools-registry.js`) just compose the per-domain modules.
|
|
38
|
+
|
|
39
|
+
## Master client
|
|
40
|
+
|
|
41
|
+
`src/services/dnio-client.js`:
|
|
42
|
+
|
|
43
|
+
```js
|
|
44
|
+
class DNIOClient extends BaseClient {
|
|
45
|
+
constructor(opts) {
|
|
46
|
+
super(opts);
|
|
47
|
+
this.apps = new AppsClient(this);
|
|
48
|
+
this.services = new ServicesClient(this);
|
|
49
|
+
this.records = new RecordsClient(this);
|
|
50
|
+
this.connectors = new ConnectorsClient(this);
|
|
51
|
+
this.plugins = new PluginsClient(this);
|
|
52
|
+
this.dataPipes = new DataPipesClient(this);
|
|
53
|
+
this.deploymentGroups = new DeploymentGroupsClient(this);
|
|
54
|
+
// stubs
|
|
55
|
+
this.functions = new FunctionsClient(this);
|
|
56
|
+
this.dataFormats = new DataFormatsClient(this);
|
|
57
|
+
this.formulas = new FormulasClient(this);
|
|
58
|
+
this.users = new UsersClient(this);
|
|
59
|
+
this.userGroups = new UserGroupsClient(this);
|
|
60
|
+
this.apiKeys = new ApiKeysClient(this);
|
|
61
|
+
this.bots = new BotsClient(this);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
`BaseClient` (in `src/clients/base-client.js`) owns:
|
|
67
|
+
- the `got` instance (with `prefixUrl`, timeout, default headers),
|
|
68
|
+
- `setToken(token)` — rebuilds the `got` instance with the new `Authorization` header,
|
|
69
|
+
- `_request(method, path, options)` — internal request helper with error normalization,
|
|
70
|
+
- `get` / `post` / `put` / `delete` — exposed HTTP verbs,
|
|
71
|
+
- `_logRequest(method, url, options)` — emits one `logger.debug` line per outbound request, with method, decoded query params, and JSON-stringified body. Lives in one place so every domain client gets it for free.
|
|
72
|
+
|
|
73
|
+
Each domain client receives the master in its constructor and uses `this.http.get/post/...` for HTTP. Domain clients are stateless w.r.t. auth — `dnioClient.setToken(...)` mutates the master and every domain call automatically picks up the new token.
|
|
74
|
+
|
|
75
|
+
Cross-domain calls go through the master. Example: `services.verifyPod(app, path)` calls `this.http.records.count(app, path)` to test whether the K8s pod for a service is up.
|
|
76
|
+
|
|
77
|
+
## Master tool registrar
|
|
78
|
+
|
|
79
|
+
`src/tools/mcp-tools-registry.js`:
|
|
80
|
+
|
|
81
|
+
```js
|
|
82
|
+
function registerAllTools(server, dnioClient, authManager, registry, userContext) {
|
|
83
|
+
const ctx = {server, dnioClient, authManager, registry, userContext};
|
|
84
|
+
|
|
85
|
+
registerAppsTools(ctx);
|
|
86
|
+
registerServicesTools(ctx);
|
|
87
|
+
registerRecordsTools(ctx);
|
|
88
|
+
registerConnectorsTools(ctx);
|
|
89
|
+
registerPluginsTools(ctx);
|
|
90
|
+
registerDataPipesTools(ctx);
|
|
91
|
+
registerDeploymentGroupsTools(ctx);
|
|
92
|
+
// stubs (no-op until those domains get tools):
|
|
93
|
+
registerFunctionsTools(ctx);
|
|
94
|
+
registerDataFormatsTools(ctx);
|
|
95
|
+
registerFormulasTools(ctx);
|
|
96
|
+
registerUsersTools(ctx);
|
|
97
|
+
registerUserGroupsTools(ctx);
|
|
98
|
+
registerApiKeysTools(ctx);
|
|
99
|
+
registerBotsTools(ctx);
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Each domain registrar destructures from the same `ctx`, so they all share the same `registry` instance — when `select_app` mutates `registry.selectedApp`, every other tool sees it.
|
|
104
|
+
|
|
105
|
+
## App-state model
|
|
106
|
+
|
|
107
|
+
The `ServiceRegistry` (in `src/services/service-registry.js`) is the single source of truth for the currently selected app:
|
|
108
|
+
|
|
109
|
+
- `registry.selectedApp` — the app name (`null` until `select_app` succeeds).
|
|
110
|
+
- `registry.services` — `Map<serviceId, ServiceEntry>` of every service discovered in that app.
|
|
111
|
+
- `registry.prefixToServiceId`, `registry.nameToServiceId` — fast lookups for `resolveService(identifier)`.
|
|
112
|
+
|
|
113
|
+
`select_app` triggers `registry.loadApp(appName, adminToken, userToken)`, which:
|
|
114
|
+
1. Lists active services via `client.services.list(appName, {filter: {status: 'Active'}, …})`.
|
|
115
|
+
2. For each service, fetches its full schema if the list response didn't include it.
|
|
116
|
+
3. Computes a `toolPrefix` (slugified service name).
|
|
117
|
+
4. Stores everything in the registry maps.
|
|
118
|
+
5. Triggers `data-service-registry.js` to register per-service typed CRUD tools.
|
|
119
|
+
|
|
120
|
+
`refresh_services` re-runs the same logic without restarting.
|
|
121
|
+
|
|
122
|
+
## Static vs. dynamic tools
|
|
123
|
+
|
|
124
|
+
There are two layers of record CRUD tools:
|
|
125
|
+
|
|
126
|
+
- **Static, generic** — `list_records`, `get_record`, `create_record`, `update_record`, `delete_record`, `count_records` (in `tools/records.js`). They take a `serviceName` argument and resolve it via `resolveServiceOrError(registry, serviceName)`.
|
|
127
|
+
- **Dynamic, typed** — registered by `tools/data-service-registry.js` after `select_app`. Each service gets six tools named `<toolPrefix>_list/_get/_create/_update/_delete/_count`. Their input schemas are converted from the service's `definition` array via `src/schemas/schema-converter.js`.
|
|
128
|
+
|
|
129
|
+
Both layers exist so the agent has the option of working generically against any service (when it doesn't know the schema yet) or with strongly-typed inputs once the service is known.
|
|
130
|
+
|
|
131
|
+
## Transports
|
|
132
|
+
|
|
133
|
+
`src/index.js` supports two transports:
|
|
134
|
+
|
|
135
|
+
### stdio (`TRANSPORT=stdio`, default)
|
|
136
|
+
- Single shared `DNIOClient`, `ServiceRegistry`, and MCP server for the lifetime of the process.
|
|
137
|
+
- Used by Claude Desktop's local MCP integration and Cursor's MCP config.
|
|
138
|
+
|
|
139
|
+
### HTTP (`TRANSPORT=http`)
|
|
140
|
+
- An Express server at `MCP_PORT` (default 3100).
|
|
141
|
+
- Per-session `DNIOClient` + `ServiceRegistry` + MCP server, keyed by `Mcp-Session-Id` header.
|
|
142
|
+
- `SessionManager` (in `src/services/session-manager.js`) handles session creation, expiry (`SESSION_TTL`, default 2h), and cleanup.
|
|
143
|
+
- Routes:
|
|
144
|
+
- `POST /mcp` — tool calls / MCP requests; creates a new session if no header present.
|
|
145
|
+
- `GET /mcp` — SSE notifications (requires session).
|
|
146
|
+
- `DELETE /mcp` — close session.
|
|
147
|
+
- `GET /health` — health + active session count.
|
|
148
|
+
- `GET /sessions?key=<admin_pass>` — debug listing of active sessions.
|
|
149
|
+
- `GET /.well-known/oauth-*` — return 404; the server is authless.
|
|
150
|
+
|
|
151
|
+
## Authentication
|
|
152
|
+
|
|
153
|
+
All MCP-server-to-DNIO API calls use a JWT in the `Authorization: JWT <token>` header. The token is set on the master client via `setToken`. There are two tokens, both managed by `src/services/auth-manager.js`:
|
|
154
|
+
|
|
155
|
+
- **Admin token** — for management endpoints (listing apps, fetching schemas, creating data services, deploying flows).
|
|
156
|
+
- **MCP-user token** — for data plane (record CRUD).
|
|
157
|
+
|
|
158
|
+
See [`authentication.md`](authentication.md) for the lifecycle.
|
|
159
|
+
|
|
160
|
+
## Where things live (cheat sheet)
|
|
161
|
+
|
|
162
|
+
| Concern | File |
|
|
163
|
+
|---|---|
|
|
164
|
+
| Add a new domain client | `src/clients/<domain>.js` (plus mount in `services/dnio-client.js`) |
|
|
165
|
+
| Add a new domain tool file | `src/tools/<domain>.js` (plus require + call in `tools/mcp-tools-registry.js`) |
|
|
166
|
+
| HTTP plumbing / token lifecycle | `src/clients/base-client.js`, `src/services/auth-manager.js` |
|
|
167
|
+
| App / service discovery state | `src/services/service-registry.js` |
|
|
168
|
+
| Per-service typed tools | `src/tools/data-service-registry.js` |
|
|
169
|
+
| HTTP-transport sessions | `src/services/session-manager.js`, `src/index.js` |
|
|
170
|
+
| Shared tool helpers | `src/tools/_helpers.js` (`toolError`, `resolveServiceOrError`) |
|
|
171
|
+
| Spec / docs surfaced to the LLM | `src/utils/constants.js` (data-service creation spec, connector/role docs) |
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Authentication
|
|
2
|
+
|
|
3
|
+
The MCP server manages two separate JWTs against the DNIO platform and switches between them based on the operation. Tokens auto-refresh; consumers of the MCP tools never see them.
|
|
4
|
+
|
|
5
|
+
## Two tokens
|
|
6
|
+
|
|
7
|
+
| Token | Used for | Provisioned by |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| **Admin token** | Management / control plane: `list_apps`, `get_service_schema`, anything that lists apps or fetches data-service definitions across apps. | `DNIO_USERNAME` / `DNIO_PASSWORD` env vars. |
|
|
10
|
+
| **MCP-user token** | Data plane: every record CRUD, plus most domain-specific operations (creating connectors, designing flows, managing deployment groups). | `MCP_USER_EMAIL` / `MCP_USER_PASSWORD` env vars — the server looks up this account; if it doesn't exist on the platform, it creates it on first boot. |
|
|
11
|
+
|
|
12
|
+
`auth-manager.js` (`src/services/auth-manager.js`) owns both lifecycles. It exits the process at startup if `MCP_USER_EMAIL` or `MCP_USER_PASSWORD` is unset; `src/index.js` does the same for `DNIO_BASE_URL` / `DNIO_USERNAME` / `DNIO_PASSWORD`. There are no hardcoded credentials anywhere in the code.
|
|
13
|
+
|
|
14
|
+
## Boot sequence
|
|
15
|
+
|
|
16
|
+
1. **Login as admin.** POST `/api/a/rbac/auth/login` with `{username, password, isSuperAdmin: true}`. Stores the admin token + computes its expiry from `DNIO_TOKEN_TTL`.
|
|
17
|
+
2. **Look up MCP user.** GET `/api/a/rbac/admin/user?filter={"email":"mcp-user@dnio.com"}` (with admin token). If found, reuse; otherwise:
|
|
18
|
+
3. **Create MCP user.** POST `/api/a/rbac/${DNIO_NAMESPACE}/user` with the MCP user payload. Records the user's `_id`.
|
|
19
|
+
4. **Add MCP user to all apps.** GET `/api/a/rbac/admin/app?count=-1&select=_id` (admin token), then PUT `/api/a/rbac/admin/user/utils/addToApps/mcp-user@dnio.com` with the full app list. This is what gives the MCP user data-plane access across the platform.
|
|
20
|
+
5. **Login as MCP user.** Same `/auth/login` endpoint with the MCP user's stored credentials. Stores the MCP-user token + expiry.
|
|
21
|
+
|
|
22
|
+
After step 5, the server is ready; both tokens are warm.
|
|
23
|
+
|
|
24
|
+
## Per-tool token switching
|
|
25
|
+
|
|
26
|
+
Tools call `dnioClient.setToken(token)` before each HTTP request to point the master client at the right JWT for that operation.
|
|
27
|
+
|
|
28
|
+
- `tools/apps.js` `list_apps` — uses admin token, then resets to MCP-user token before returning.
|
|
29
|
+
- `tools/apps.js` `select_app` — uses both: admin to list services + fetch schemas, MCP-user for pod verification.
|
|
30
|
+
- `tools/apps.js` `get_service_schema` — admin token (then resets).
|
|
31
|
+
- Almost every other tool — MCP-user token.
|
|
32
|
+
|
|
33
|
+
The pattern is always `dnioClient.setToken(...)` immediately before the API call inside the tool handler. Because every domain client shares the same `BaseClient` `got` instance, one `setToken` updates the auth header for every subsequent call.
|
|
34
|
+
|
|
35
|
+
## Refresh
|
|
36
|
+
|
|
37
|
+
`AuthManager` schedules a refresh just before each token expires (`DNIO_TOKEN_TTL`, default 1800 s):
|
|
38
|
+
|
|
39
|
+
- For the admin token: re-run the admin login.
|
|
40
|
+
- For the MCP-user token: re-run the MCP-user login.
|
|
41
|
+
|
|
42
|
+
If a tool is mid-flight when refresh fires, the in-flight call uses whatever token the master client had at the moment of the request. Subsequent calls pick up the refreshed token because `setToken` rebuilds the `got` instance with the new header.
|
|
43
|
+
|
|
44
|
+
If the admin password changes externally, restart the server — there's no UI for re-prompting credentials.
|
|
45
|
+
|
|
46
|
+
## HTTP-transport sessions
|
|
47
|
+
|
|
48
|
+
In HTTP mode (`TRANSPORT=http`), every Mcp-Session creates its own `DNIOClient` initialized with the current MCP-user token, and that per-session client lives inside `SessionManager`. Token refresh on `AuthManager` does not propagate into already-issued session clients — a session that lives for hours will keep using its initial token until either (a) a tool inside that session calls `setToken` again with the latest from `authManager.getUserToken()`, or (b) the session expires (`SESSION_TTL`, default 7200 s).
|
|
49
|
+
|
|
50
|
+
Practically every tool handler does call `dnioClient.setToken(userContext.token)` and `userContext.token` is refreshed on `select_app`, so this rarely surfaces. If you see auth failures after a session has been idle for >30 minutes, the `SESSION_TTL` cleanup is the simplest fix.
|
|
51
|
+
|
|
52
|
+
## stdio-transport
|
|
53
|
+
|
|
54
|
+
In stdio mode there's a single `DNIOClient`, `ServiceRegistry`, and MCP server for the process. Token refresh on `AuthManager` is enough — every tool handler calls `setToken` against that single client.
|
|
55
|
+
|
|
56
|
+
## Endpoints used
|
|
57
|
+
|
|
58
|
+
All against `${DNIO_BASE_URL}`:
|
|
59
|
+
|
|
60
|
+
| Method | Path | Purpose |
|
|
61
|
+
|---|---|---|
|
|
62
|
+
| POST | `/api/a/rbac/auth/login` | Admin and MCP-user login |
|
|
63
|
+
| GET | `/api/a/rbac/admin/user?filter=…` | Find the MCP user |
|
|
64
|
+
| POST | `/api/a/rbac/${namespace}/user` | Create MCP user |
|
|
65
|
+
| GET | `/api/a/rbac/admin/app?count=-1&select=_id` | List apps for provisioning |
|
|
66
|
+
| PUT | `/api/a/rbac/admin/user/utils/addToApps/${email}` | Grant MCP user app access |
|
|
67
|
+
|
|
68
|
+
The MCP user's password is generated on first creation and persisted by the platform; the server doesn't store it locally beyond memory.
|
|
69
|
+
|
|
70
|
+
## Security notes
|
|
71
|
+
|
|
72
|
+
- The MCP server's HTTP transport is **authless** — anyone with the URL can connect. If you deploy it externally, put it behind a network policy or auth proxy.
|
|
73
|
+
- `GET /sessions?key=<admin_pass>` is a debug endpoint that uses the admin password as a shared secret. Disable it in production by removing the route or wrapping it in middleware.
|
|
74
|
+
- Tokens are kept in memory only. They never hit disk and aren't logged (the request logger logs path/query/body but never headers).
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Apps & Service Discovery
|
|
2
|
+
|
|
3
|
+
Tools registered by `src/tools/apps.js`. They drive the entry-point flow (`list_apps` → `select_app`) and provide read-only inspection of services in the selected app.
|
|
4
|
+
|
|
5
|
+
Backed by `AppsClient` (`src/clients/apps.js`) and `ServicesClient.getSchema` (`src/clients/services.js`).
|
|
6
|
+
|
|
7
|
+
## `list_apps`
|
|
8
|
+
|
|
9
|
+
| | |
|
|
10
|
+
|---|---|
|
|
11
|
+
| **Purpose** | List every app on the platform. The first tool to run in any session. |
|
|
12
|
+
| **Inputs** | (none) |
|
|
13
|
+
| **Endpoint** | `GET /api/a/rbac/admin/app?count=-1&select=_id,description` (admin token) |
|
|
14
|
+
| **Auth** | Admin token, then resets to MCP-user token before returning. |
|
|
15
|
+
| **Returns** | `{ platform, user, selectedApp, apps: [{ appName, description }] }` |
|
|
16
|
+
|
|
17
|
+
## `select_app`
|
|
18
|
+
|
|
19
|
+
| | |
|
|
20
|
+
|---|---|
|
|
21
|
+
| **Purpose** | Switch the active app. Discovers every active data service in the app, verifies pods, loads schemas, and registers per-service typed CRUD tools. Required before any data-plane tool. |
|
|
22
|
+
| **Inputs** | `appName` (string, required) |
|
|
23
|
+
| **Behaviour** | 1. Calls `authManager.ensureUserAppAccess(appName)` to make sure the MCP user has access. 2. Refreshes `userContext.token`. 3. Calls `registry.loadApp(appName, adminToken, userToken)`, which lists active services, fetches each service's `definition`, and stores them in the registry. 4. Triggers per-service tool registration via `data-service-registry.js`. |
|
|
24
|
+
| **Endpoints** | `GET /api/a/sm/${app}/service?filter={"app":"${app}","status":"Active"}&count=-1&select=…` (admin) for listing; `GET /api/a/sm/${app}/service/${serviceId}` (admin) per service that needs schema fetch. |
|
|
25
|
+
| **Returns** | `{ action: 'app_selected', appName, services: [...], skipped: [...]?, totalLoaded, usage }` |
|
|
26
|
+
|
|
27
|
+
## `list_services`
|
|
28
|
+
|
|
29
|
+
| | |
|
|
30
|
+
|---|---|
|
|
31
|
+
| **Purpose** | Show every loaded data service for the currently selected app, including the parsed schema description used for tool generation. Read-only — uses the in-memory `ServiceRegistry`. |
|
|
32
|
+
| **Inputs** | (none) |
|
|
33
|
+
| **Endpoint** | (none — reads `registry.getServiceList()`) |
|
|
34
|
+
| **Returns** | `{ appName, services: [{ name, serviceId, apiPath, schema }] }` |
|
|
35
|
+
|
|
36
|
+
## `refresh_services`
|
|
37
|
+
|
|
38
|
+
| | |
|
|
39
|
+
|---|---|
|
|
40
|
+
| **Purpose** | Re-scan the selected app for new/changed services without restarting the server. Useful after creating a new data service in the same session. |
|
|
41
|
+
| **Inputs** | (none) |
|
|
42
|
+
| **Behaviour** | Re-runs `registry.loadApp` for the currently selected app. |
|
|
43
|
+
| **Returns** | `{ action: 'refreshed', appName, loaded: [name…], skipped: [...] }` |
|
|
44
|
+
|
|
45
|
+
## `get_service_schema`
|
|
46
|
+
|
|
47
|
+
| | |
|
|
48
|
+
|---|---|
|
|
49
|
+
| **Purpose** | Fetch the full schema of a single service — useful before constructing a record payload for `create_record`. |
|
|
50
|
+
| **Inputs** | `serviceName` (string, required) — name, slug prefix, or `_id`. |
|
|
51
|
+
| **Behaviour** | Resolves `serviceName` via `registry.resolveService` (matches by `_id`, lowercase name, or slugified prefix). If not found, returns the available list. |
|
|
52
|
+
| **Endpoint** | `GET /api/a/sm/${app}/service/${serviceId}` (admin token) |
|
|
53
|
+
| **Returns** | `{ serviceId, name, api, attributeCount, status, definition }` |
|
|
54
|
+
|
|
55
|
+
## Notes
|
|
56
|
+
|
|
57
|
+
- `select_app` is the only tool in this domain that mutates server-side state — it sets `registry.selectedApp` and rebuilds the per-service typed tool set.
|
|
58
|
+
- `list_services` and `get_service_schema` will return errors when no app is selected.
|
|
59
|
+
- `select_app` fetches schemas individually only when the list response did not already include `definition`. The platform's list endpoint generally returns it; the per-service GET is a safety net.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Connectors
|
|
2
|
+
|
|
3
|
+
Tools registered by `src/tools/connectors.js`. A **connector** is a configured backing system (a database, an S3 bucket, an SFTP server, an SMTP relay, an ActiveMQ queue, …) that data services and flow nodes bind to.
|
|
4
|
+
|
|
5
|
+
The platform separates **connector types** (templates from a marketplace catalog) from **connector instances** (concrete configured ones in the selected app).
|
|
6
|
+
|
|
7
|
+
Backed by `ConnectorsClient` (`src/clients/connectors.js`).
|
|
8
|
+
|
|
9
|
+
## Where connectors come from
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
list_connector_types → marketplace catalog (templates)
|
|
13
|
+
│
|
|
14
|
+
▼
|
|
15
|
+
create_connector → POST /api/a/bm/${app}/connector
|
|
16
|
+
│ creates an instance in this app
|
|
17
|
+
▼
|
|
18
|
+
list_connectors → GET /api/a/rbac/${app}/connector
|
|
19
|
+
lists existing instances
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The instance API and the marketplace API live under different namespaces (`/api/a/rbac` vs `/api/a/bm`).
|
|
23
|
+
|
|
24
|
+
## Tool reference
|
|
25
|
+
|
|
26
|
+
### `list_connectors`
|
|
27
|
+
|
|
28
|
+
| | |
|
|
29
|
+
|---|---|
|
|
30
|
+
| **Purpose** | List existing connector instances in the selected app. Used by `create_data_service` (auto-fill) and as the source for connector IDs throughout the platform. |
|
|
31
|
+
| **Inputs** | `category` (optional, one of `DB` or `STORAGE`). Omit for all categories (`MESSAGING`, `STORAGE`, `DB`, etc.). |
|
|
32
|
+
| **Endpoint** | `GET /api/a/rbac/${app}/connector?filter={"app":"${app}"[, "category":"…"]}&count=-1&select=_id,name,category,subCategory,type,options,_metadata` |
|
|
33
|
+
| **Returns** | `{ app, count, connectors: [{ _id, name, category, subCategory, type, isDefault, isValid }], usage }` |
|
|
34
|
+
|
|
35
|
+
`isDefault` is true for the platform's pre-provisioned default connectors (a default MongoDB DB connector and a default GridFS storage connector). `create_data_service` auto-attaches these when the LLM doesn't specify a connector.
|
|
36
|
+
|
|
37
|
+
### `list_connector_types`
|
|
38
|
+
|
|
39
|
+
| | |
|
|
40
|
+
|---|---|
|
|
41
|
+
| **Purpose** | List connector **templates** in the marketplace. Each item shows the marketplace `_id` (used as `marketItemId` for `create_connector`), the `type` / `label` / `category`, and a `fields[]` schema describing what credentials each type needs. |
|
|
42
|
+
| **Inputs** | `category` (optional, e.g. `DB`, `STORAGE`, `MESSAGING`), `search` (optional, substring on label/type/tags). |
|
|
43
|
+
| **Endpoint** | `GET /api/a/bm/${app}/marketplace/connector?count=1000&select=label,thumbnail,type,fields,category,tags,version` |
|
|
44
|
+
| **Behaviour** | Strips the `thumbnail` SVG from the response to keep payloads small. Filtering is client-side. |
|
|
45
|
+
| **Returns** | `{ app, count, types: [{ marketItemId, type, label, category, tags, version, fields: [{ key, label, type, htmlInputType, required, encrypted }] }], usage }` |
|
|
46
|
+
|
|
47
|
+
The `fields[]` schema tells the agent exactly what to ask the user for. Examples seen in the wild:
|
|
48
|
+
|
|
49
|
+
| Type | Fields |
|
|
50
|
+
|---|---|
|
|
51
|
+
| `MONGODB` | `connectionString` (encrypted) |
|
|
52
|
+
| `S3` | `accessKeyId`, `secretAccessKey` (encrypted), `region`, `bucket` |
|
|
53
|
+
| `ACTIVEMQ` | `host`, `port`, `username`, `password` (encrypted) |
|
|
54
|
+
| `SFTP` / `SMTP` / etc. | various |
|
|
55
|
+
|
|
56
|
+
### `create_connector`
|
|
57
|
+
|
|
58
|
+
| | |
|
|
59
|
+
|---|---|
|
|
60
|
+
| **Purpose** | Create a new connector instance in the selected app. Always preceded by `list_connector_types`. |
|
|
61
|
+
| **Inputs** | `name` (required, display name), `marketItemId` (required, from `list_connector_types`), `values` (required, JSON string keyed by each field's `key`). |
|
|
62
|
+
| **Behaviour** | Builds payload `{ values, name, app: registry.selectedApp, marketItemId }` — the `app` field is server-injected from the selected app, regardless of what the LLM passes. Encrypted fields (e.g. passwords, secret keys) are sent as-is; the platform encrypts them at rest. |
|
|
63
|
+
| **Endpoint** | `POST /api/a/bm/${app}/connector` |
|
|
64
|
+
| **Returns** | The platform's response (the created connector). |
|
|
65
|
+
|
|
66
|
+
## Typical sequence
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
list_connector_types(category: 'DB', search: 'mongo')
|
|
70
|
+
→ user picks one (marketItemId, fields)
|
|
71
|
+
→ ask the user for each field's value (passwords for encrypted ones)
|
|
72
|
+
create_connector(name, marketItemId, values)
|
|
73
|
+
list_connectors # confirm it shows up
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The created instance can then be used as `connectors.data._id` (for DB) or `connectors.file._id` (for STORAGE) when calling `create_data_service`.
|