@vibetools/dokploy-mcp 1.0.0 → 1.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.
Files changed (47) hide show
  1. package/README.md +20 -14
  2. package/dist/api/client.d.ts +2 -0
  3. package/dist/api/client.js +44 -11
  4. package/dist/tools/_database.js +25 -6
  5. package/dist/tools/_factory.d.ts +2 -0
  6. package/dist/tools/_factory.js +21 -4
  7. package/dist/tools/application.js +219 -82
  8. package/dist/tools/backup.js +30 -0
  9. package/dist/tools/compose.js +273 -35
  10. package/dist/tools/deployment.js +82 -2
  11. package/dist/tools/docker.js +62 -2
  12. package/dist/tools/domain.js +15 -2
  13. package/dist/tools/environment.d.ts +2 -0
  14. package/dist/tools/environment.js +104 -0
  15. package/dist/tools/git-provider.d.ts +2 -0
  16. package/dist/tools/git-provider.js +22 -0
  17. package/dist/tools/github.d.ts +2 -0
  18. package/dist/tools/github.js +66 -0
  19. package/dist/tools/gitlab.d.ts +2 -0
  20. package/dist/tools/gitlab.js +98 -0
  21. package/dist/tools/index.js +24 -0
  22. package/dist/tools/mariadb.js +1 -1
  23. package/dist/tools/mongo.js +2 -1
  24. package/dist/tools/mounts.js +53 -9
  25. package/dist/tools/mysql.js +1 -1
  26. package/dist/tools/notification.d.ts +2 -0
  27. package/dist/tools/notification.js +559 -0
  28. package/dist/tools/patch.d.ts +2 -0
  29. package/dist/tools/patch.js +179 -0
  30. package/dist/tools/postgres.js +1 -1
  31. package/dist/tools/preview-deployment.d.ts +2 -0
  32. package/dist/tools/preview-deployment.js +50 -0
  33. package/dist/tools/project.js +32 -1
  34. package/dist/tools/redis.js +1 -1
  35. package/dist/tools/rollback.d.ts +2 -0
  36. package/dist/tools/rollback.js +28 -0
  37. package/dist/tools/schedule.d.ts +2 -0
  38. package/dist/tools/schedule.js +92 -0
  39. package/dist/tools/server.d.ts +2 -0
  40. package/dist/tools/server.js +192 -0
  41. package/dist/tools/settings.js +251 -0
  42. package/dist/tools/ssh-key.d.ts +2 -0
  43. package/dist/tools/ssh-key.js +74 -0
  44. package/dist/tools/user.js +75 -2
  45. package/dist/tools/volume-backups.d.ts +2 -0
  46. package/dist/tools/volume-backups.js +96 -0
  47. package/package.json +1 -1
package/README.md CHANGED
@@ -4,9 +4,9 @@
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
5
5
  [![Node >= 22](https://img.shields.io/badge/node-%3E%3D22-brightgreen)](https://nodejs.org/)
6
6
 
7
- MCP server for the Dokploy API. 196 tools across 23 modules. Your AI agent can now deploy apps, manage databases, configure domains, and handle backups -- without you touching a dashboard.
7
+ MCP server for the Dokploy API. 377 tools across 35 modules. Your AI agent can now deploy apps, manage databases, configure domains, and handle backups -- without you touching a dashboard.
8
8
 
9
- Forked from [Dokploy/mcp](https://github.com/Dokploy/mcp) and rebuilt with expanded API coverage, tool annotations, Zod v4 schemas, lazy config loading, and a setup wizard. The original had 67 tools. This one has 196. Standing on shoulders, etc.
9
+ Forked from [Dokploy/mcp](https://github.com/Dokploy/mcp) and rebuilt with expanded API coverage, tool annotations, Zod v4 schemas, lazy config loading, and a setup wizard. The original had 67 tools. This one has 377. Standing on shoulders, etc.
10
10
 
11
11
  ## Quick Start
12
12
 
@@ -45,7 +45,7 @@ If you already have the [Dokploy CLI](https://github.com/Dokploy/cli) installed
45
45
 
46
46
  ## Features
47
47
 
48
- - **196 tools, 23 modules** -- applications, compose, databases (Postgres/MySQL/MariaDB/MongoDB/Redis), domains, backups, Docker, settings, and more
48
+ - **377 tools, 35 modules** -- applications, compose, environments, servers, Git providers, notifications, databases (Postgres/MySQL/MariaDB/MongoDB/Redis), domains, backups, deployment queues, rollback, patching, Docker, settings, preview deployments, schedules, and more
49
49
  - **Tool annotations** -- `readOnlyHint`, `destructiveHint`, `idempotentHint` so clients can warn before you nuke something
50
50
  - **Type-safe schemas** -- Zod v4 validation on every parameter
51
51
  - **Lazy config loading** -- validates credentials on first API call, not at startup
@@ -136,18 +136,24 @@ Already ran `setup` or have Dokploy CLI authenticated? Drop the `env` block enti
136
136
 
137
137
  | Module | Tools | Module | Tools |
138
138
  |--------|-------|--------|-------|
139
- | Project | 6 | Deployment | 2 |
140
- | Application | 26 | Docker | 4 |
141
- | Compose | 14 | Certificates | 4 |
142
- | Domain | 8 | Registry | 6 |
143
- | PostgreSQL | 13 | Destination | 6 |
144
- | MySQL | 13 | Backup | 8 |
145
- | MariaDB | 13 | Mounts | 4 |
146
- | MongoDB | 13 | Port | 4 |
147
- | Redis | 13 | Redirects | 4 |
139
+ | Project | 8 | Deployment | 8 |
140
+ | Environment | 7 | Docker | 7 |
141
+ | Application | 29 | Server | 16 |
142
+ | Compose | 28 | Certificates | 4 |
143
+ | Domain | 9 | Registry | 6 |
144
+ | Patch | 12 | SSH Key | 6 |
145
+ | Git Provider | 2 | GitHub | 6 |
146
+ | GitLab | 7 | PostgreSQL | 14 |
147
+ | Notification | 38 | MySQL | 14 |
148
+ | Destination | 6 | MariaDB | 14 |
149
+ | Backup | 11 | MongoDB | 14 |
150
+ | Mounts | 6 | Redis | 14 |
151
+ | Port | 4 | Volume Backups | 6 |
152
+ | Redirects | 4 | Rollback | 2 |
153
+ | Preview Deployment | 4 | Schedule | 6 |
148
154
  | Security | 4 | Cluster | 4 |
149
- | Settings | 25 | Admin | 1 |
150
- | User | 1 | | |
155
+ | Settings | 49 | Admin | 1 |
156
+ | User | 7 | | |
151
157
 
152
158
  Full reference with parameters and descriptions: **[docs/tools.md](docs/tools.md)**
153
159
 
@@ -1,3 +1,4 @@
1
+ export declare function unwrapTrpcResponse(data: unknown): unknown;
1
2
  export declare class ApiError extends Error {
2
3
  readonly status: number;
3
4
  readonly statusText: string;
@@ -5,6 +6,7 @@ export declare class ApiError extends Error {
5
6
  readonly endpoint: string;
6
7
  constructor(status: number, statusText: string, body: unknown, endpoint: string);
7
8
  }
9
+ export declare function buildQueryString(body: unknown): string;
8
10
  export declare const api: {
9
11
  get: <T = unknown>(path: string, params?: Record<string, unknown>) => Promise<T>;
10
12
  post: <T = unknown>(path: string, body?: unknown) => Promise<T>;
@@ -1,5 +1,38 @@
1
1
  import { resolveConfig } from '../config/resolver.js';
2
2
  const DEFAULT_TIMEOUT = 30_000;
3
+ function getErrorMessage(body, statusText) {
4
+ if (typeof body !== 'object' || body === null) {
5
+ return statusText;
6
+ }
7
+ if ('message' in body && typeof body.message === 'string') {
8
+ return body.message;
9
+ }
10
+ if ('error' in body && typeof body.error === 'object' && body.error !== null) {
11
+ const error = body.error;
12
+ if ('message' in error && typeof error.message === 'string') {
13
+ return error.message;
14
+ }
15
+ if ('json' in error && typeof error.json === 'object' && error.json !== null) {
16
+ const json = error.json;
17
+ if ('message' in json && typeof json.message === 'string') {
18
+ return json.message;
19
+ }
20
+ }
21
+ }
22
+ return statusText;
23
+ }
24
+ export function unwrapTrpcResponse(data) {
25
+ if (typeof data !== 'object' || data === null)
26
+ return data;
27
+ const outer = data;
28
+ if (typeof outer.result !== 'object' || outer.result === null)
29
+ return data;
30
+ const result = outer.result;
31
+ if (typeof result.data !== 'object' || result.data === null)
32
+ return data;
33
+ const inner = result.data;
34
+ return 'json' in inner ? inner.json : data;
35
+ }
3
36
  function getConfig() {
4
37
  const resolved = resolveConfig();
5
38
  if (!resolved) {
@@ -29,9 +62,7 @@ export class ApiError extends Error {
29
62
  body;
30
63
  endpoint;
31
64
  constructor(status, statusText, body, endpoint) {
32
- const msg = typeof body === 'object' && body !== null && 'message' in body
33
- ? body.message
34
- : statusText;
65
+ const msg = getErrorMessage(body, statusText);
35
66
  super(`Dokploy API error (${status}): ${msg}`);
36
67
  this.status = status;
37
68
  this.statusText = statusText;
@@ -40,13 +71,15 @@ export class ApiError extends Error {
40
71
  this.name = 'ApiError';
41
72
  }
42
73
  }
43
- function buildQueryString(body) {
44
- if (!body || typeof body !== 'object')
74
+ export function buildQueryString(body) {
75
+ if (body == null)
76
+ return '';
77
+ if (typeof body !== 'object')
45
78
  return '';
46
- const entries = Object.entries(body)
47
- .filter(([, v]) => v != null)
48
- .map(([k, v]) => [k, String(v)]);
49
- return entries.length > 0 ? new URLSearchParams(entries).toString() : '';
79
+ const params = Object.fromEntries(Object.entries(body).filter(([, value]) => value != null));
80
+ return new URLSearchParams({
81
+ input: JSON.stringify({ json: params }),
82
+ }).toString();
50
83
  }
51
84
  /**
52
85
  * Checks whether an error was caused by an aborted fetch.
@@ -70,7 +103,7 @@ async function request(method, path, body) {
70
103
  Accept: 'application/json',
71
104
  'x-api-key': apiKey,
72
105
  },
73
- body: method === 'POST' && body ? JSON.stringify(body) : undefined,
106
+ body: method === 'POST' && body ? JSON.stringify({ json: body }) : undefined,
74
107
  signal: controller.signal,
75
108
  });
76
109
  const text = await response.text();
@@ -84,7 +117,7 @@ async function request(method, path, body) {
84
117
  if (!response.ok) {
85
118
  throw new ApiError(response.status, response.statusText, data, path);
86
119
  }
87
- return data;
120
+ return unwrapTrpcResponse(data);
88
121
  }
89
122
  catch (error) {
90
123
  if (error instanceof ApiError) {
@@ -6,7 +6,7 @@ export function createDatabaseTools(config) {
6
6
  .object({ [idField]: z.string().min(1).describe(`Unique ${displayName} database ID`) })
7
7
  .strict();
8
8
  function tool(action, title, description, schema, opts = {}) {
9
- const endpoint = `${type}.${action}`;
9
+ const endpoint = `/${type}.${action}`;
10
10
  const name = `dokploy_${type}_${action.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`)}`;
11
11
  if (opts.get) {
12
12
  return getTool({ name, title, description, schema, endpoint, annotations: opts.annotations });
@@ -14,12 +14,18 @@ export function createDatabaseTools(config) {
14
14
  return postTool({ name, title, description, schema, endpoint, annotations: opts.annotations });
15
15
  }
16
16
  const one = tool('one', `Get ${displayName} Details`, `Retrieve detailed information about a specific ${displayName} database managed by Dokploy. Returns the full configuration including connection settings, resource limits, environment variables, and current status. Requires the unique ${displayName} database ID.`, idSchema, { get: true });
17
- const create = tool('create', `Create ${displayName} Database`, `Create a new ${displayName} database instance inside a Dokploy project. Requires a display name, app-level identifier, and the target project ID. Optionally specify a Docker image, description, or remote server. Returns the newly created database record.`, z
17
+ const create = tool('create', `Create ${displayName} Database`, `Create a new ${displayName} database instance inside a Dokploy environment. Requires a display name and the target environment ID. Optionally specify an app-level identifier, Docker image, description, or remote server. Returns the newly created database record.`, z
18
18
  .object({
19
19
  name: z.string().min(1).describe('Display name for the database'),
20
- appName: z.string().min(1).describe('Unique app-level identifier'),
20
+ appName: z
21
+ .string()
22
+ .min(1)
23
+ .max(63)
24
+ .regex(/^[a-zA-Z0-9._-]+$/)
25
+ .optional()
26
+ .describe('Unique app-level identifier'),
21
27
  ...createFields.shape,
22
- projectId: z.string().min(1).describe('Project ID to create the database in'),
28
+ environmentId: z.string().min(1).describe('Environment ID to create the database in'),
23
29
  dockerImage: z.string().optional().describe(`Docker image (default: ${defaultImage})`),
24
30
  description: z.string().nullable().optional().describe('Optional description'),
25
31
  serverId: z.string().nullable().optional().describe('Target server ID (null for local)'),
@@ -42,10 +48,10 @@ export function createDatabaseTools(config) {
42
48
  })
43
49
  .strict());
44
50
  const remove = tool('remove', `Remove ${displayName} Database`, `Permanently delete a ${displayName} database from Dokploy. This action removes the database container, its data, and all associated configuration. Requires the ${displayName} database ID. This operation is destructive and cannot be undone.`, idSchema, { annotations: { destructiveHint: true } });
45
- const move = tool('move', `Move ${displayName} Database`, `Move a ${displayName} database from its current project to a different project within Dokploy. Requires the ${displayName} database ID and the destination project ID. The database configuration and data are preserved during the move.`, z
51
+ const move = tool('move', `Move ${displayName} Database`, `Move a ${displayName} database from its current environment to a different environment within Dokploy. Requires the ${displayName} database ID and the destination environment ID. The database configuration and data are preserved during the move.`, z
46
52
  .object({
47
53
  [idField]: z.string().min(1).describe(`Unique ${displayName} database ID`),
48
- targetProjectId: z.string().min(1).describe('Destination project ID'),
54
+ targetEnvironmentId: z.string().min(1).describe('Destination environment ID'),
49
55
  })
50
56
  .strict());
51
57
  const deploy = tool('deploy', `Deploy ${displayName} Database`, `Deploy a ${displayName} database container in Dokploy. Triggers the build and start process for the specified database. Requires the ${displayName} database ID. Returns the deployment status.`, idSchema);
@@ -78,6 +84,18 @@ export function createDatabaseTools(config) {
78
84
  env: z.string().nullable().optional().describe('Environment variables as a string'),
79
85
  })
80
86
  .strict());
87
+ const search = tool('search', `Search ${displayName} Databases`, `Search ${displayName} databases in Dokploy by free text or field-specific filters. Supports pagination through limit and offset.`, z
88
+ .object({
89
+ q: z.string().optional().describe('Free-text query'),
90
+ name: z.string().optional().describe('Display name'),
91
+ appName: z.string().optional().describe('App-level identifier'),
92
+ description: z.string().optional().describe('Description'),
93
+ projectId: z.string().optional().describe('Project ID'),
94
+ environmentId: z.string().optional().describe('Environment ID'),
95
+ limit: z.number().min(1).max(100).optional().describe('Maximum number of results'),
96
+ offset: z.number().min(0).optional().describe('Number of results to skip'),
97
+ })
98
+ .strict(), { get: true });
81
99
  return [
82
100
  one,
83
101
  create,
@@ -92,5 +110,6 @@ export function createDatabaseTools(config) {
92
110
  changeStatus,
93
111
  saveExternalPort,
94
112
  saveEnvironment,
113
+ search,
95
114
  ];
96
115
  }
@@ -12,6 +12,8 @@ export interface ToolDefinition {
12
12
  name: string;
13
13
  title: string;
14
14
  description: string;
15
+ endpoint?: string;
16
+ method?: 'GET' | 'POST';
15
17
  schema: AnyZodObject;
16
18
  annotations: ToolAnnotations;
17
19
  handler: (input: Record<string, unknown>) => Promise<{
@@ -1,8 +1,15 @@
1
1
  import { ApiError, api } from '../api/client.js';
2
+ function wrapStructured(data) {
3
+ if (Array.isArray(data))
4
+ return { items: data };
5
+ if (data === null || data === undefined || typeof data !== 'object')
6
+ return { value: data };
7
+ return data;
8
+ }
2
9
  function success(data) {
3
10
  return {
4
11
  content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
5
- structuredContent: data,
12
+ structuredContent: wrapStructured(data),
6
13
  };
7
14
  }
8
15
  function error(message, details) {
@@ -55,7 +62,7 @@ export function createTool(def) {
55
62
  };
56
63
  }
57
64
  export function postTool(opts) {
58
- return createTool({
65
+ const tool = createTool({
59
66
  name: opts.name,
60
67
  title: opts.title,
61
68
  description: opts.description,
@@ -63,9 +70,14 @@ export function postTool(opts) {
63
70
  annotations: opts.annotations,
64
71
  handler: async ({ input, api }) => api.post(opts.endpoint, input),
65
72
  });
73
+ return {
74
+ ...tool,
75
+ endpoint: opts.endpoint,
76
+ method: 'POST',
77
+ };
66
78
  }
67
79
  export function getTool(opts) {
68
- return createTool({
80
+ const tool = createTool({
69
81
  name: opts.name,
70
82
  title: opts.title,
71
83
  description: opts.description,
@@ -82,7 +94,12 @@ export function getTool(opts) {
82
94
  params[k] = v;
83
95
  }
84
96
  }
85
- return api.get(opts.endpoint, Object.keys(params).length > 0 ? params : undefined);
97
+ return api.get(opts.endpoint, params);
86
98
  },
87
99
  });
100
+ return {
101
+ ...tool,
102
+ endpoint: opts.endpoint,
103
+ method: 'GET',
104
+ };
88
105
  }