@vibetools/dokploy-mcp 2.0.1 → 2.1.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.
@@ -16,32 +16,57 @@ function deepFreeze(value) {
16
16
  }
17
17
  return value;
18
18
  }
19
+ const ARROW_FN_RE = /^\s*async\s*\(/;
20
+ /**
21
+ * Auto-wraps raw code into an async function if the agent didn't provide one.
22
+ * Accepts all these forms:
23
+ * 1. async ({ dokploy }) => { ... } -- arrow with destructuring (original)
24
+ * 2. async (ctx) => { ... } -- arrow with param
25
+ * 3. async () => dokploy.project.all() -- arrow using globals
26
+ * 4. dokploy.project.all() -- raw expression (auto-wrapped)
27
+ * 5. const x = await dokploy.project.all() -- raw statements (auto-wrapped)
28
+ */
29
+ function wrapSandboxCode(code) {
30
+ const trimmed = code.trim();
31
+ if (ARROW_FN_RE.test(trimmed)) {
32
+ return trimmed;
33
+ }
34
+ // Raw code -- wrap in an async function that receives context but ignores it
35
+ // (context keys are already available as globals)
36
+ const hasReturn = /\breturn\b/.test(trimmed);
37
+ const body = hasReturn ? trimmed : `return (${trimmed})`;
38
+ return `async () => { ${body} }`;
39
+ }
19
40
  export async function runSandboxedFunction({ code, context, limits: providedLimits, }) {
20
41
  const limits = providedLimits ?? resolveSandboxLimits();
21
42
  const logs = [];
22
43
  let loggedBytes = 0;
23
44
  const frozenContext = deepFreeze(context);
24
- const sandbox = createContext({
25
- __context: frozenContext,
26
- console: {
27
- log: (...args) => {
28
- const line = args
29
- .map((arg) => {
30
- try {
31
- return typeof arg === 'string' ? arg : JSON.stringify(arg);
32
- }
33
- catch {
34
- return String(arg);
35
- }
36
- })
37
- .join(' ');
38
- loggedBytes += Buffer.byteLength(line, 'utf8');
39
- if (loggedBytes > limits.maxLogBytes) {
40
- throw new Error(`Sandbox logs exceeded ${limits.maxLogBytes} bytes.`);
45
+ const consoleProxy = {
46
+ log: (...args) => {
47
+ const line = args
48
+ .map((arg) => {
49
+ try {
50
+ return typeof arg === 'string' ? arg : JSON.stringify(arg);
51
+ }
52
+ catch {
53
+ return String(arg);
41
54
  }
42
- logs.push(line);
43
- },
55
+ })
56
+ .join(' ');
57
+ loggedBytes += Buffer.byteLength(line, 'utf8');
58
+ if (loggedBytes > limits.maxLogBytes) {
59
+ throw new Error(`Sandbox logs exceeded ${limits.maxLogBytes} bytes.`);
60
+ }
61
+ logs.push(line);
44
62
  },
63
+ };
64
+ const sandbox = createContext({
65
+ __context: frozenContext,
66
+ // Expose context keys as top-level globals so agents can write
67
+ // `dokploy.project.all()` or `catalog.searchText("x")` directly
68
+ ...frozenContext,
69
+ console: consoleProxy,
45
70
  process: undefined,
46
71
  fetch: undefined,
47
72
  require: undefined,
@@ -63,15 +88,13 @@ export async function runSandboxedFunction({ code, context, limits: providedLimi
63
88
  wasm: false,
64
89
  },
65
90
  });
91
+ const wrappedCode = wrapSandboxCode(code);
66
92
  const script = new Script(`
67
93
  (async () => {
68
- const __fn = (${code})
94
+ const __fn = (${wrappedCode})
69
95
  if (typeof __fn !== 'function') {
70
96
  throw new Error('Sandbox code must evaluate to a function.')
71
97
  }
72
- if (__fn.constructor?.name !== 'AsyncFunction') {
73
- throw new Error('Sandbox code must evaluate to an async function.')
74
- }
75
98
  return await __fn(__context)
76
99
  })()
77
100
  `, {
@@ -11,10 +11,11 @@ const executeSchema = z
11
11
  code: z
12
12
  .string()
13
13
  .min(1)
14
- .describe('An async arrow function receiving ({ dokploy, helpers }). ' +
15
- 'Example: async ({ dokploy }) => { const p = await dokploy.project.all(); return p }. ' +
14
+ .describe('JavaScript code. `dokploy` and `helpers` are globals -- do NOT wrap in a function. ' +
15
+ 'Simple: `await dokploy.project.all()`. ' +
16
+ 'Multi-step: `const app = await dokploy.application.one({ applicationId: "abc" }); return app.name`. ' +
16
17
  'dokploy.<module>.<method>(params) calls the Dokploy API. ' +
17
- 'helpers: sleep(ms), assert(cond, msg), pick(obj, keys), limit(arr, n), selectOne(arr, pred).'),
18
+ 'helpers: sleep(ms), assert(cond, msg), pick(obj, keys), limit(arr, n).'),
18
19
  })
19
20
  .strict();
20
21
  export function buildExecuteContext(executor, maxCalls = resolveSandboxLimits().maxCalls) {
@@ -48,13 +49,13 @@ export async function runExecuteWithHost(code, host) {
48
49
  export const executeTool = createTool({
49
50
  name: 'execute',
50
51
  title: 'Execute Dokploy Workflow',
51
- description: 'Execute a sandboxed Dokploy workflow. ' +
52
- 'The code parameter must be an async arrow function: async ({ dokploy, helpers }) => { ... }. ' +
53
- 'Use dokploy.<module>.<method>(params) to call any Dokploy API procedure ' +
54
- '(e.g. dokploy.application.one({ applicationId }), dokploy.project.all()). ' +
55
- 'Available modules: project, environment, application, compose, domain, postgres, mysql, mariadb, mongo, redis, ' +
56
- 'deployment, docker, server, settings, user, notification, backup, mounts, registry, certificates, and more. ' +
57
- 'Use search tool first to discover exact procedure names and required parameters.',
52
+ description: 'Run JavaScript code against the Dokploy API. ' +
53
+ 'IMPORTANT: Do NOT wrap code in a function -- `dokploy` and `helpers` are already globals. ' +
54
+ 'Write bare code: `await dokploy.project.all()` or `const x = await dokploy.application.one({ applicationId: "id" }); return x`. ' +
55
+ 'dokploy.<module>.<method>(params) calls the API. ' +
56
+ 'Modules: project, environment, application, compose, domain, postgres, mysql, mariadb, mongo, redis, ' +
57
+ 'deployment, docker, server, settings, user, notification, backup, mounts, registry, certificates, schedule, patch, sshKey, gitProvider, and more. ' +
58
+ 'Use search tool first to discover procedure names and required parameters.',
58
59
  schema: executeSchema,
59
60
  annotations: { openWorldHint: true },
60
61
  handler: async ({ input }) => {
@@ -9,10 +9,10 @@ const searchSchema = z
9
9
  code: z
10
10
  .string()
11
11
  .min(1)
12
- .describe('An async arrow function receiving ({ catalog }). ' +
13
- 'Example: async ({ catalog }) => catalog.searchText("application deploy"). ' +
14
- 'catalog methods: searchText(query), get(procedure), getByTag(tag), ' +
15
- 'endpoints (array of all procedures), byTag (grouped by module).'),
12
+ .describe('JavaScript code. `catalog` is a global -- do NOT wrap in a function. ' +
13
+ 'Examples: `catalog.searchText("application deploy")` | ' +
14
+ '`catalog.getByTag("compose")` | `catalog.get("application.one")`. ' +
15
+ 'Methods: searchText(query), get(procedure), getByTag(tag), endpoints, byTag.'),
16
16
  })
17
17
  .strict();
18
18
  const searchCatalog = createSearchCatalogView();
@@ -48,11 +48,9 @@ function trimArrayToBytes(items) {
48
48
  export const searchTool = createTool({
49
49
  name: 'search',
50
50
  title: 'Search Dokploy API',
51
- description: 'Search the Dokploy API catalog to discover procedures, parameters, and modules. ' +
52
- 'The code parameter must be an async arrow function: async ({ catalog }) => { ... }. ' +
53
- 'Use catalog.searchText("query") to find procedures by keyword, ' +
54
- 'catalog.getByTag("application") to list all procedures in a module, ' +
55
- 'catalog.get("application.one") to get details of a specific procedure. ' +
51
+ description: 'Search the Dokploy API catalog. ' +
52
+ 'IMPORTANT: Do NOT wrap code in a function -- `catalog` is already a global. ' +
53
+ 'Write bare code: `catalog.searchText("deploy")` or `catalog.getByTag("application")` or `catalog.get("application.one")`. ' +
56
54
  'Returns procedure names, required/optional parameters, and HTTP methods.',
57
55
  schema: searchSchema,
58
56
  annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibetools/dokploy-mcp",
3
- "version": "2.0.1",
3
+ "version": "2.1.1",
4
4
  "description": "Dokploy MCP server with Code Mode by default",
5
5
  "type": "module",
6
6
  "exports": {