@vibetools/dokploy-mcp 2.2.0 → 2.2.1

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/README.md CHANGED
@@ -94,6 +94,26 @@ await dokploy.application.one({
94
94
 
95
95
  Without shaping params, behavior is identical to the raw Dokploy API -- fully backward compatible.
96
96
 
97
+ ## Secret redaction
98
+
99
+ Git provider credentials (GitHub App private keys, client secrets, webhook secrets, Gitea/GitLab/Bitbucket tokens) are **automatically redacted** from all responses. Your AI agent sees `[REDACTED]` instead of the real values -- because leaking your private key into a context window is the kind of mistake you only make once.
100
+
101
+ Affected procedures: `application.one`, `application.many`, `github.one`, `gitea.one`, `gitlab.one`, `bitbucket.one`, `github.githubProviders`, `gitProvider.getAll`.
102
+
103
+ If you actually need the raw secrets (rotation scripts, migration, etc.), opt in explicitly:
104
+
105
+ ```js
106
+ await dokploy.application.one({
107
+ applicationId: "id",
108
+ includeSecrets: true // you asked for it
109
+ })
110
+
111
+ await dokploy.github.one({
112
+ githubId: "id",
113
+ includeSecrets: true
114
+ })
115
+ ```
116
+
97
117
  ## Virtual helpers
98
118
 
99
119
  Code Mode includes MCP-side helpers for common multi-call patterns. They run inside `execute`, fan out to real Dokploy API calls, and charge every underlying call against the sandbox budget honestly.
@@ -1,5 +1,77 @@
1
1
  import { procedureSchemas } from '../../generated/dokploy-schemas.js';
2
- const applicationOneMcpOnlyKeys = new Set(['select', 'includeDeployments', 'deploymentLimit']);
2
+ // Keys that hold credentials in git-provider objects (github, gitea, gitlab, bitbucket).
3
+ // Redacted by default — callers must pass includeSecrets: true to receive them.
4
+ const gitProviderSecretKeys = new Set([
5
+ // GitHub App
6
+ 'githubClientSecret',
7
+ 'githubPrivateKey',
8
+ 'githubWebhookSecret',
9
+ // Gitea
10
+ 'clientSecret',
11
+ 'accessToken',
12
+ 'refreshToken',
13
+ // GitLab
14
+ 'secret',
15
+ // Bitbucket
16
+ 'appPassword',
17
+ 'apiToken',
18
+ // SSH / generic
19
+ 'privateKey',
20
+ 'privateKeyPass',
21
+ ]);
22
+ // Top-level keys on an application object that contain nested git-provider data
23
+ const gitProviderNestingKeys = new Set(['github', 'gitea', 'gitlab', 'bitbucket']);
24
+ function redactRecord(value) {
25
+ const redacted = {};
26
+ for (const [key, val] of Object.entries(value)) {
27
+ if (gitProviderSecretKeys.has(key)) {
28
+ redacted[key] = '[REDACTED]';
29
+ }
30
+ else if (isRecord(val)) {
31
+ redacted[key] = redactRecord(val);
32
+ }
33
+ else {
34
+ redacted[key] = val;
35
+ }
36
+ }
37
+ return redacted;
38
+ }
39
+ function redactGitProviderSecrets(data) {
40
+ if (!isRecord(data)) {
41
+ return data;
42
+ }
43
+ if (Array.isArray(data)) {
44
+ return data;
45
+ }
46
+ let changed = false;
47
+ const result = {};
48
+ for (const [key, value] of Object.entries(data)) {
49
+ if (gitProviderSecretKeys.has(key)) {
50
+ result[key] = '[REDACTED]';
51
+ changed = true;
52
+ }
53
+ else if (gitProviderNestingKeys.has(key) && isRecord(value)) {
54
+ result[key] = redactRecord(value);
55
+ changed = true;
56
+ }
57
+ else {
58
+ result[key] = value;
59
+ }
60
+ }
61
+ return changed ? result : data;
62
+ }
63
+ function redactGitProviderArray(data) {
64
+ if (!Array.isArray(data)) {
65
+ return redactGitProviderSecrets(data);
66
+ }
67
+ return data.map((item) => redactGitProviderSecrets(item));
68
+ }
69
+ const applicationOneMcpOnlyKeys = new Set([
70
+ 'select',
71
+ 'includeDeployments',
72
+ 'deploymentLimit',
73
+ 'includeSecrets',
74
+ ]);
3
75
  const applicationOneInputSchema = {
4
76
  type: 'object',
5
77
  properties: {
@@ -19,6 +91,9 @@ const applicationOneInputSchema = {
19
91
  deploymentLimit: {
20
92
  type: 'integer',
21
93
  },
94
+ includeSecrets: {
95
+ type: 'boolean',
96
+ },
22
97
  },
23
98
  required: ['applicationId'],
24
99
  additionalProperties: false,
@@ -99,7 +174,28 @@ function transformApplicationOneResponse(data, input) {
99
174
  return data;
100
175
  }
101
176
  const selected = pickSelectedFields(data, input.select);
102
- return applyDeploymentControls(selected, input);
177
+ const shaped = applyDeploymentControls(selected, input);
178
+ return input.includeSecrets === true ? shaped : redactGitProviderSecrets(shaped);
179
+ }
180
+ const includeSecretsMcpOnlyKeys = new Set(['includeSecrets']);
181
+ function mapIncludeSecretsInput(input) {
182
+ return Object.fromEntries(Object.entries(input).filter(([key]) => !includeSecretsMcpOnlyKeys.has(key)));
183
+ }
184
+ function transformWithSecretGate(data, input) {
185
+ return input.includeSecrets === true ? data : redactGitProviderSecrets(data);
186
+ }
187
+ function transformArrayWithSecretGate(data, input) {
188
+ return input.includeSecrets === true ? data : redactGitProviderArray(data);
189
+ }
190
+ function withIncludeSecrets(schema) {
191
+ const properties = (schema.properties ?? {});
192
+ return {
193
+ ...schema,
194
+ properties: {
195
+ ...properties,
196
+ includeSecrets: { type: 'boolean' },
197
+ },
198
+ };
103
199
  }
104
200
  const procedureOverrides = {
105
201
  'application.one': {
@@ -108,6 +204,52 @@ const procedureOverrides = {
108
204
  validateInput: validateApplicationOneInput,
109
205
  transformResponse: transformApplicationOneResponse,
110
206
  },
207
+ 'github.one': {
208
+ inputSchema: withIncludeSecrets({
209
+ type: 'object',
210
+ properties: { githubId: { type: 'string', minLength: 1 } },
211
+ required: ['githubId'],
212
+ additionalProperties: false,
213
+ }),
214
+ mapInput: mapIncludeSecretsInput,
215
+ transformResponse: transformWithSecretGate,
216
+ },
217
+ 'github.githubProviders': {
218
+ transformResponse: transformArrayWithSecretGate,
219
+ },
220
+ 'gitea.one': {
221
+ inputSchema: withIncludeSecrets({
222
+ type: 'object',
223
+ properties: { giteaId: { type: 'string', minLength: 1 } },
224
+ required: ['giteaId'],
225
+ additionalProperties: false,
226
+ }),
227
+ mapInput: mapIncludeSecretsInput,
228
+ transformResponse: transformWithSecretGate,
229
+ },
230
+ 'gitlab.one': {
231
+ inputSchema: withIncludeSecrets({
232
+ type: 'object',
233
+ properties: { gitlabId: { type: 'string', minLength: 1 } },
234
+ required: ['gitlabId'],
235
+ additionalProperties: false,
236
+ }),
237
+ mapInput: mapIncludeSecretsInput,
238
+ transformResponse: transformWithSecretGate,
239
+ },
240
+ 'bitbucket.one': {
241
+ inputSchema: withIncludeSecrets({
242
+ type: 'object',
243
+ properties: { bitbucketId: { type: 'string', minLength: 1 } },
244
+ required: ['bitbucketId'],
245
+ additionalProperties: false,
246
+ }),
247
+ mapInput: mapIncludeSecretsInput,
248
+ transformResponse: transformWithSecretGate,
249
+ },
250
+ 'gitProvider.getAll': {
251
+ transformResponse: transformArrayWithSecretGate,
252
+ },
111
253
  };
112
254
  function getGeneratedProcedureSchema(procedure) {
113
255
  return procedureSchemas[procedure];
@@ -20,6 +20,9 @@ function createApplicationManyInputSchema() {
20
20
  deploymentLimit: {
21
21
  type: 'integer',
22
22
  },
23
+ includeSecrets: {
24
+ type: 'boolean',
25
+ },
23
26
  },
24
27
  required: ['applicationIds'],
25
28
  additionalProperties: false,
@@ -93,6 +96,9 @@ function buildApplicationOneInput(applicationId, input) {
93
96
  if ('deploymentLimit' in input) {
94
97
  nextInput.deploymentLimit = input.deploymentLimit;
95
98
  }
99
+ if ('includeSecrets' in input) {
100
+ nextInput.includeSecrets = input.includeSecrets;
101
+ }
96
102
  return nextInput;
97
103
  }
98
104
  async function executeApplicationMany(input, context) {
@@ -257,7 +263,7 @@ const virtualProcedureDefinitions = {
257
263
  description: 'MCP-only virtual helper that fans out to application.one while preserving input order and execute call budgeting.',
258
264
  inputKind: 'body',
259
265
  requiredInputs: ['applicationIds'],
260
- optionalInputs: ['select', 'includeDeployments', 'deploymentLimit'],
266
+ optionalInputs: ['select', 'includeDeployments', 'deploymentLimit', 'includeSecrets'],
261
267
  response: {
262
268
  type: 'object',
263
269
  keys: ['items', 'total'],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibetools/dokploy-mcp",
3
- "version": "2.2.0",
3
+ "version": "2.2.1",
4
4
  "description": "Dokploy MCP server with Code Mode by default",
5
5
  "type": "module",
6
6
  "exports": {