@lovinka/deployik-mcp 0.1.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 (70) hide show
  1. package/README.md +59 -0
  2. package/dist/client/errors.js +51 -0
  3. package/dist/client/errors.js.map +1 -0
  4. package/dist/client/http.js +106 -0
  5. package/dist/client/http.js.map +1 -0
  6. package/dist/client/types.js +5 -0
  7. package/dist/client/types.js.map +1 -0
  8. package/dist/config/audit.js +43 -0
  9. package/dist/config/audit.js.map +1 -0
  10. package/dist/config/binding.js +44 -0
  11. package/dist/config/binding.js.map +1 -0
  12. package/dist/config/cache.js +44 -0
  13. package/dist/config/cache.js.map +1 -0
  14. package/dist/config/env.js +40 -0
  15. package/dist/config/env.js.map +1 -0
  16. package/dist/index.js +22 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/knowledge/index.js +116 -0
  19. package/dist/knowledge/index.js.map +1 -0
  20. package/dist/knowledge/prompts.js +20 -0
  21. package/dist/knowledge/prompts.js.map +1 -0
  22. package/dist/knowledge/recipes.generated.js +17 -0
  23. package/dist/knowledge/recipes.generated.js.map +1 -0
  24. package/dist/lib/format.js +96 -0
  25. package/dist/lib/format.js.map +1 -0
  26. package/dist/lib/logs.js +46 -0
  27. package/dist/lib/logs.js.map +1 -0
  28. package/dist/lib/poll.js +20 -0
  29. package/dist/lib/poll.js.map +1 -0
  30. package/dist/lib/safety.js +38 -0
  31. package/dist/lib/safety.js.map +1 -0
  32. package/dist/resolve/project.js +130 -0
  33. package/dist/resolve/project.js.map +1 -0
  34. package/dist/server.js +32 -0
  35. package/dist/server.js.map +1 -0
  36. package/dist/tools/_helpers.js +76 -0
  37. package/dist/tools/_helpers.js.map +1 -0
  38. package/dist/tools/analytics.js +46 -0
  39. package/dist/tools/analytics.js.map +1 -0
  40. package/dist/tools/auto_build.js +84 -0
  41. package/dist/tools/auto_build.js.map +1 -0
  42. package/dist/tools/deployments.js +120 -0
  43. package/dist/tools/deployments.js.map +1 -0
  44. package/dist/tools/domains.js +104 -0
  45. package/dist/tools/domains.js.map +1 -0
  46. package/dist/tools/email.js +90 -0
  47. package/dist/tools/email.js.map +1 -0
  48. package/dist/tools/env.js +116 -0
  49. package/dist/tools/env.js.map +1 -0
  50. package/dist/tools/github.js +59 -0
  51. package/dist/tools/github.js.map +1 -0
  52. package/dist/tools/help.js +34 -0
  53. package/dist/tools/help.js.map +1 -0
  54. package/dist/tools/index.js +33 -0
  55. package/dist/tools/index.js.map +1 -0
  56. package/dist/tools/projects.js +164 -0
  57. package/dist/tools/projects.js.map +1 -0
  58. package/dist/tools/protection.js +88 -0
  59. package/dist/tools/protection.js.map +1 -0
  60. package/dist/tools/secrets.js +115 -0
  61. package/dist/tools/secrets.js.map +1 -0
  62. package/dist/tools/tokens.js +60 -0
  63. package/dist/tools/tokens.js.map +1 -0
  64. package/dist/tools/volumes.js +77 -0
  65. package/dist/tools/volumes.js.map +1 -0
  66. package/dist/tools/workflows.js +350 -0
  67. package/dist/tools/workflows.js.map +1 -0
  68. package/dist/tools/workspaces.js +48 -0
  69. package/dist/tools/workspaces.js.map +1 -0
  70. package/package.json +50 -0
package/README.md ADDED
@@ -0,0 +1,59 @@
1
+ # @lovinka/deployik-mcp
2
+
3
+ MCP server for [Deployik](https://github.com/lefteq/lovinka-deployik). Lets any MCP-aware AI drive Deployik end-to-end — create projects, set secrets, trigger deployments, debug failed builds, manage domains — without touching the dashboard.
4
+
5
+ ## Install (one-shot config)
6
+
7
+ Add to your MCP client config (Claude Desktop, Claude Code, etc.):
8
+
9
+ ```json
10
+ {
11
+ "mcpServers": {
12
+ "deployik": {
13
+ "command": "npx",
14
+ "args": ["-y", "@lovinka/deployik-mcp"],
15
+ "env": {
16
+ "DEPLOYIK_URL": "https://deployik.lovinka.com",
17
+ "DEPLOYIK_TOKEN": "dpk_..."
18
+ }
19
+ }
20
+ }
21
+ }
22
+ ```
23
+
24
+ Get a token at **Account → Access tokens** in Deployik. The token is shown once on creation; copy it then.
25
+
26
+ For a VPN-only deployment, point `DEPLOYIK_URL` at any reachable host (`http://10.x.x.x:8080`, `https://deployik.internal`, etc.).
27
+
28
+ ## What it does
29
+
30
+ - **~32 thin tools** — one per Deployik HTTP endpoint (projects, deployments, env vars, secrets, domains, auto-build, password protection, volumes, analytics, email, workspaces, tokens, GitHub).
31
+ - **9 workflow tools** — `setup_project_from_repo`, `deploy_project`, `set_secret`, `tail_latest_logs`, `debug_failed_deployment`, `get_project_health`, `init_in_repo`, and more.
32
+ - **Bundled knowledge** — Deployik's how-to recipes ship as MCP prompts (`deployik_recipe_*`) and a `get_recipe(topic)` tool, so a fresh AI session can self-onboard.
33
+ - **Repo binding** — `init_in_repo` writes a `.deployik/binding.json` (gitignored) so the AI knows which Deployik project this folder maps to without asking.
34
+ - **Tiered safety** — destructive operations require `confirm: true`; production-touching operations also require `confirm_name: <project>`. Every destructive call is logged to `.deployik/audit.log`.
35
+
36
+ ## Local development
37
+
38
+ ```bash
39
+ cd mcp
40
+ bun install
41
+ bun run build
42
+ bun run inspect # opens MCP Inspector against the local binary
43
+ ```
44
+
45
+ Test against a local Deployik dev server with `DEPLOYIK_URL=http://127.0.0.1:8080` and a dev-mode PAT.
46
+
47
+ ## Files written on the host
48
+
49
+ When the AI calls `init_in_repo` from inside a repo:
50
+
51
+ ```
52
+ .deployik/
53
+ ├── binding.json project + workspace mapping for this repo
54
+ ├── cache.json cached project/workspace list (1h TTL)
55
+ ├── token optional token fallback (mode 0600)
56
+ └── audit.log destructive-call ledger
57
+ ```
58
+
59
+ `.gitignore` is auto-appended with `.deployik/` if missing.
@@ -0,0 +1,51 @@
1
+ export class ApiError extends Error {
2
+ status;
3
+ endpoint;
4
+ body;
5
+ hint;
6
+ constructor(opts) {
7
+ super(opts.message);
8
+ this.name = "ApiError";
9
+ this.status = opts.status;
10
+ this.endpoint = opts.endpoint;
11
+ this.body = opts.body;
12
+ this.hint = opts.hint;
13
+ }
14
+ }
15
+ export function hintForStatus(status, endpoint) {
16
+ if (status === 401) {
17
+ return "Token is missing, expired, or revoked. Set DEPLOYIK_TOKEN to a fresh `dpk_…` token from Account → Access tokens.";
18
+ }
19
+ if (status === 403) {
20
+ return "Token is valid but the owner cannot access this resource. Check the project's workspace membership.";
21
+ }
22
+ if (status === 404) {
23
+ if (endpoint.includes("/projects/")) {
24
+ return "Project or deployment not found. Call `list_projects` to see what this token can reach, or run `init_in_repo` to bind this folder to an existing project.";
25
+ }
26
+ return "Resource not found at this URL.";
27
+ }
28
+ if (status === 409) {
29
+ return "Conflict — the resource is in a state that blocks this operation (e.g. a volume in use by a running container, or a duplicate key).";
30
+ }
31
+ if (status === 429) {
32
+ return "Rate limited. Back off and retry in a few seconds.";
33
+ }
34
+ if (status >= 500) {
35
+ return "Deployik server error. Retry; if it persists, check the deployik server logs.";
36
+ }
37
+ return undefined;
38
+ }
39
+ export function asString(err) {
40
+ if (err instanceof Error)
41
+ return err.message;
42
+ if (typeof err === "string")
43
+ return err;
44
+ try {
45
+ return JSON.stringify(err);
46
+ }
47
+ catch {
48
+ return String(err);
49
+ }
50
+ }
51
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/client/errors.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,QAAS,SAAQ,KAAK;IACxB,MAAM,CAAS;IACf,QAAQ,CAAS;IACjB,IAAI,CAAU;IACd,IAAI,CAAU;IAEvB,YAAY,IAAyF;QACnG,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpB,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;QACvB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC1B,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC9B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACtB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;IACxB,CAAC;CACF;AAED,MAAM,UAAU,aAAa,CAAC,MAAc,EAAE,QAAgB;IAC5D,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;QACnB,OAAO,kHAAkH,CAAC;IAC5H,CAAC;IACD,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;QACnB,OAAO,qGAAqG,CAAC;IAC/G,CAAC;IACD,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;QACnB,IAAI,QAAQ,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;YACpC,OAAO,2JAA2J,CAAC;QACrK,CAAC;QACD,OAAO,iCAAiC,CAAC;IAC3C,CAAC;IACD,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;QACnB,OAAO,qIAAqI,CAAC;IAC/I,CAAC;IACD,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;QACnB,OAAO,oDAAoD,CAAC;IAC9D,CAAC;IACD,IAAI,MAAM,IAAI,GAAG,EAAE,CAAC;QAClB,OAAO,+EAA+E,CAAC;IACzF,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,GAAY;IACnC,IAAI,GAAG,YAAY,KAAK;QAAE,OAAO,GAAG,CAAC,OAAO,CAAC;IAC7C,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,GAAG,CAAC;IACxC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;IACrB,CAAC;AACH,CAAC"}
@@ -0,0 +1,106 @@
1
+ import { ApiError, hintForStatus } from "./errors.js";
2
+ export class DeployikClient {
3
+ baseUrl;
4
+ token;
5
+ timeoutMs;
6
+ fetchImpl;
7
+ userAgent;
8
+ constructor(opts) {
9
+ this.baseUrl = opts.baseUrl.replace(/\/+$/, "");
10
+ this.token = opts.token;
11
+ this.timeoutMs = opts.timeoutMs ?? 30_000;
12
+ this.fetchImpl = opts.fetchImpl ?? fetch;
13
+ this.userAgent = opts.userAgent ?? "deployik-mcp/0.1.0";
14
+ }
15
+ async request(path, opts = {}) {
16
+ const url = this.buildUrl(path, opts.query);
17
+ const headers = {
18
+ Authorization: `Bearer ${this.token}`,
19
+ Accept: "application/json",
20
+ "User-Agent": this.userAgent,
21
+ };
22
+ const init = {
23
+ method: opts.method ?? "GET",
24
+ headers,
25
+ };
26
+ if (opts.body !== undefined) {
27
+ headers["Content-Type"] = "application/json";
28
+ init.body = JSON.stringify(opts.body);
29
+ }
30
+ const controller = new AbortController();
31
+ const timeout = setTimeout(() => controller.abort(), this.timeoutMs);
32
+ init.signal = controller.signal;
33
+ let response;
34
+ try {
35
+ response = await this.fetchImpl(url, init);
36
+ }
37
+ catch (err) {
38
+ clearTimeout(timeout);
39
+ if (err instanceof Error && err.name === "AbortError") {
40
+ throw new ApiError({
41
+ status: 0,
42
+ endpoint: path,
43
+ body: null,
44
+ message: `Request timed out after ${this.timeoutMs}ms: ${path}`,
45
+ hint: "Set DEPLOYIK_TIMEOUT_MS to a larger value, or check that DEPLOYIK_URL is reachable from this host (VPN connected?).",
46
+ });
47
+ }
48
+ throw new ApiError({
49
+ status: 0,
50
+ endpoint: path,
51
+ body: null,
52
+ message: `Network error calling ${path}: ${err.message}`,
53
+ hint: "Check DEPLOYIK_URL and your network connection (VPN may not be active).",
54
+ });
55
+ }
56
+ clearTimeout(timeout);
57
+ const text = await response.text();
58
+ let parsed = null;
59
+ if (text) {
60
+ try {
61
+ parsed = JSON.parse(text);
62
+ }
63
+ catch {
64
+ parsed = text;
65
+ }
66
+ }
67
+ if (!response.ok) {
68
+ if (opts.rawError) {
69
+ // caller wants the body as-is; surface as a typed throw anyway
70
+ }
71
+ const errMsg = (parsed && typeof parsed === "object" && parsed.error) ||
72
+ (parsed && typeof parsed === "object" && parsed.message) ||
73
+ text ||
74
+ `Request failed (${response.status})`;
75
+ throw new ApiError({
76
+ status: response.status,
77
+ endpoint: path,
78
+ body: parsed,
79
+ message: String(errMsg),
80
+ hint: hintForStatus(response.status, path),
81
+ });
82
+ }
83
+ if (parsed === null)
84
+ return undefined;
85
+ return parsed;
86
+ }
87
+ buildUrl(path, query) {
88
+ const normalized = path.startsWith("/") ? path : `/${path}`;
89
+ const base = `${this.baseUrl}${normalized.startsWith("/api/") ? normalized : `/api${normalized}`}`;
90
+ if (!query)
91
+ return base;
92
+ const params = new URLSearchParams();
93
+ for (const [k, v] of Object.entries(query)) {
94
+ if (v === undefined || v === null)
95
+ continue;
96
+ params.set(k, String(v));
97
+ }
98
+ const qs = params.toString();
99
+ return qs ? `${base}?${qs}` : base;
100
+ }
101
+ /** Build a screenshot URL (no fetch — just the URL). */
102
+ screenshotUrl(deploymentId) {
103
+ return `${this.baseUrl}/api/deployments/${encodeURIComponent(deploymentId)}/screenshot`;
104
+ }
105
+ }
106
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/client/http.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ // Types mirrored from web/src/types/api.ts. Kept manual to avoid coupling the
2
+ // published package to the frontend build. Keep in sync when the backend models
3
+ // move.
4
+ export {};
5
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/client/types.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,gFAAgF;AAChF,QAAQ"}
@@ -0,0 +1,43 @@
1
+ import { appendFileSync, mkdirSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ const AUDIT_FILE = "audit.log";
4
+ const SECRET_KEYS = new Set([
5
+ "value",
6
+ "secret",
7
+ "password",
8
+ "token",
9
+ "recaptcha_secret_key",
10
+ "smtp_password",
11
+ "DEPLOYIK_TOKEN",
12
+ ]);
13
+ export function redact(args) {
14
+ const out = {};
15
+ for (const [k, v] of Object.entries(args)) {
16
+ if (SECRET_KEYS.has(k)) {
17
+ if (typeof v === "string" && v.length > 0) {
18
+ out[k] = `***${v.slice(-4)}`;
19
+ }
20
+ else if (v !== undefined) {
21
+ out[k] = "***";
22
+ }
23
+ continue;
24
+ }
25
+ if (v && typeof v === "object" && !Array.isArray(v)) {
26
+ out[k] = redact(v);
27
+ }
28
+ else {
29
+ out[k] = v;
30
+ }
31
+ }
32
+ return out;
33
+ }
34
+ export function appendAudit(stateDir, entry) {
35
+ try {
36
+ mkdirSync(stateDir, { recursive: true });
37
+ appendFileSync(join(stateDir, AUDIT_FILE), `${JSON.stringify(entry)}\n`, "utf8");
38
+ }
39
+ catch {
40
+ // audit is best-effort; never throw out of a tool call
41
+ }
42
+ }
43
+ //# sourceMappingURL=audit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/config/audit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAYjC,MAAM,UAAU,GAAG,WAAW,CAAC;AAE/B,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC;IAC1B,OAAO;IACP,QAAQ;IACR,UAAU;IACV,OAAO;IACP,sBAAsB;IACtB,eAAe;IACf,gBAAgB;CACjB,CAAC,CAAC;AAEH,MAAM,UAAU,MAAM,CAAC,IAA6B;IAClD,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1C,IAAI,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YACvB,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC1C,GAAG,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC/B,CAAC;iBAAM,IAAI,CAAC,KAAK,SAAS,EAAE,CAAC;gBAC3B,GAAG,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC;YACjB,CAAC;YACD,SAAS;QACX,CAAC;QACD,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;YACpD,GAAG,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAA4B,CAAC,CAAC;QAChD,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QACb,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,QAAgB,EAAE,KAAiB;IAC7D,IAAI,CAAC;QACH,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACzC,cAAc,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACnF,CAAC;IAAC,MAAM,CAAC;QACP,uDAAuD;IACzD,CAAC;AACH,CAAC"}
@@ -0,0 +1,44 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, appendFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ const BINDING_FILE = "binding.json";
4
+ const GITIGNORE_LINE = ".deployik/";
5
+ export function bindingPath(stateDir) {
6
+ return join(stateDir, BINDING_FILE);
7
+ }
8
+ export function readBinding(stateDir) {
9
+ const path = bindingPath(stateDir);
10
+ if (!existsSync(path))
11
+ return undefined;
12
+ try {
13
+ const raw = readFileSync(path, "utf8");
14
+ const parsed = JSON.parse(raw);
15
+ if (!parsed || typeof parsed.project !== "string")
16
+ return undefined;
17
+ return parsed;
18
+ }
19
+ catch {
20
+ return undefined;
21
+ }
22
+ }
23
+ export function writeBinding(stateDir, binding) {
24
+ mkdirSync(stateDir, { recursive: true });
25
+ writeFileSync(bindingPath(stateDir), `${JSON.stringify(binding, null, 2)}\n`, "utf8");
26
+ }
27
+ /** Append `.deployik/` to the consuming repo's .gitignore if absent. Returns true if it added the line. */
28
+ export function ensureGitignore(cwd) {
29
+ const gitignorePath = join(cwd, ".gitignore");
30
+ if (!existsSync(dirname(gitignorePath)))
31
+ return false;
32
+ let existing = "";
33
+ if (existsSync(gitignorePath)) {
34
+ existing = readFileSync(gitignorePath, "utf8");
35
+ const lines = existing.split(/\r?\n/);
36
+ if (lines.some((l) => l.trim() === GITIGNORE_LINE || l.trim() === ".deployik")) {
37
+ return false;
38
+ }
39
+ }
40
+ const prefix = existing.length === 0 || existing.endsWith("\n") ? "" : "\n";
41
+ appendFileSync(gitignorePath, `${prefix}${GITIGNORE_LINE}\n`, "utf8");
42
+ return true;
43
+ }
44
+ //# sourceMappingURL=binding.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"binding.js","sourceRoot":"","sources":["../../src/config/binding.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC7F,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAQ1C,MAAM,YAAY,GAAG,cAAc,CAAC;AACpC,MAAM,cAAc,GAAG,YAAY,CAAC;AAEpC,MAAM,UAAU,WAAW,CAAC,QAAgB;IAC1C,OAAO,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;AACtC,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,QAAgB;IAC1C,MAAM,IAAI,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;IACnC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IACxC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACvC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAC;QACpE,OAAO,MAAqB,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,QAAgB,EAAE,OAAoB;IACjE,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,aAAa,CAAC,WAAW,CAAC,QAAQ,CAAC,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AACxF,CAAC;AAED,2GAA2G;AAC3G,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;IAC9C,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IACtD,IAAI,QAAQ,GAAG,EAAE,CAAC;IAClB,IAAI,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QAC9B,QAAQ,GAAG,YAAY,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;QAC/C,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACtC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,cAAc,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,WAAW,CAAC,EAAE,CAAC;YAC/E,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IACD,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAC5E,cAAc,CAAC,aAAa,EAAE,GAAG,MAAM,GAAG,cAAc,IAAI,EAAE,MAAM,CAAC,CAAC;IACtE,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,44 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ const CACHE_FILE = "cache.json";
4
+ const DEFAULT_TTL_SECONDS = 60 * 60; // 1h
5
+ export function cachePath(stateDir) {
6
+ return join(stateDir, CACHE_FILE);
7
+ }
8
+ export function readCache(stateDir) {
9
+ const path = cachePath(stateDir);
10
+ if (!existsSync(path))
11
+ return undefined;
12
+ try {
13
+ const raw = readFileSync(path, "utf8");
14
+ const parsed = JSON.parse(raw);
15
+ if (parsed.version !== 1)
16
+ return undefined;
17
+ return parsed;
18
+ }
19
+ catch {
20
+ return undefined;
21
+ }
22
+ }
23
+ export function writeCache(stateDir, data) {
24
+ mkdirSync(stateDir, { recursive: true });
25
+ const full = {
26
+ version: 1,
27
+ fetchedAt: new Date().toISOString(),
28
+ ttlSeconds: data.ttlSeconds ?? DEFAULT_TTL_SECONDS,
29
+ projects: data.projects,
30
+ workspaces: data.workspaces,
31
+ platform: data.platform,
32
+ };
33
+ writeFileSync(cachePath(stateDir), `${JSON.stringify(full, null, 2)}\n`, "utf8");
34
+ return full;
35
+ }
36
+ export function isCacheFresh(cache) {
37
+ if (!cache)
38
+ return false;
39
+ const fetched = Date.parse(cache.fetchedAt);
40
+ if (Number.isNaN(fetched))
41
+ return false;
42
+ return Date.now() - fetched < cache.ttlSeconds * 1000;
43
+ }
44
+ //# sourceMappingURL=cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/config/cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAyBjC,MAAM,UAAU,GAAG,YAAY,CAAC;AAChC,MAAM,mBAAmB,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,KAAK;AAE1C,MAAM,UAAU,SAAS,CAAC,QAAgB;IACxC,OAAO,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,QAAgB;IACxC,MAAM,IAAI,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;IACjC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IACxC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACvC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAc,CAAC;QAC5C,IAAI,MAAM,CAAC,OAAO,KAAK,CAAC;YAAE,OAAO,SAAS,CAAC;QAC3C,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,QAAgB,EAAE,IAAuF;IAClI,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,MAAM,IAAI,GAAc;QACtB,OAAO,EAAE,CAAC;QACV,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,mBAAmB;QAClD,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,QAAQ,EAAE,IAAI,CAAC,QAAQ;KACxB,CAAC;IACF,aAAa,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACjF,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAA4B;IACvD,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IACzB,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IAC5C,IAAI,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC;QAAE,OAAO,KAAK,CAAC;IACxC,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,GAAG,KAAK,CAAC,UAAU,GAAG,IAAI,CAAC;AACxD,CAAC"}
@@ -0,0 +1,40 @@
1
+ import { readFileSync, existsSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ export class ConfigError extends Error {
4
+ constructor(message) {
5
+ super(message);
6
+ this.name = "ConfigError";
7
+ }
8
+ }
9
+ function readTokenFile(stateDir) {
10
+ const path = join(stateDir, "token");
11
+ if (!existsSync(path))
12
+ return undefined;
13
+ try {
14
+ const raw = readFileSync(path, "utf8").trim();
15
+ return raw || undefined;
16
+ }
17
+ catch {
18
+ return undefined;
19
+ }
20
+ }
21
+ export function loadEnv(env = process.env, cwd = process.cwd()) {
22
+ const baseUrl = env.DEPLOYIK_URL?.trim();
23
+ if (!baseUrl) {
24
+ throw new ConfigError("DEPLOYIK_URL is not set. Add it to your MCP server config, e.g. \"DEPLOYIK_URL\": \"https://deployik.lovinka.com\".");
25
+ }
26
+ const stateDir = join(cwd, ".deployik");
27
+ const token = env.DEPLOYIK_TOKEN?.trim() || readTokenFile(stateDir);
28
+ if (!token) {
29
+ throw new ConfigError("DEPLOYIK_TOKEN is not set and no .deployik/token file was found. Create a token at Account → Access tokens and pass it as DEPLOYIK_TOKEN in the MCP server env.");
30
+ }
31
+ const timeoutMs = parseInt(env.DEPLOYIK_TIMEOUT_MS ?? "30000", 10);
32
+ return {
33
+ baseUrl,
34
+ token,
35
+ cwd,
36
+ stateDir,
37
+ timeoutMs: Number.isFinite(timeoutMs) && timeoutMs > 0 ? timeoutMs : 30_000,
38
+ };
39
+ }
40
+ //# sourceMappingURL=env.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/config/env.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAYjC,MAAM,OAAO,WAAY,SAAQ,KAAK;IACpC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;IAC5B,CAAC;CACF;AAED,SAAS,aAAa,CAAC,QAAgB;IACrC,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACrC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IACxC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;QAC9C,OAAO,GAAG,IAAI,SAAS,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,MAAyB,OAAO,CAAC,GAAG,EAAE,MAAc,OAAO,CAAC,GAAG,EAAE;IACvF,MAAM,OAAO,GAAG,GAAG,CAAC,YAAY,EAAE,IAAI,EAAE,CAAC;IACzC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,WAAW,CACnB,qHAAqH,CACtH,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;IACxC,MAAM,KAAK,GAAG,GAAG,CAAC,cAAc,EAAE,IAAI,EAAE,IAAI,aAAa,CAAC,QAAQ,CAAC,CAAC;IACpE,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,WAAW,CACnB,iKAAiK,CAClK,CAAC;IACJ,CAAC;IAED,MAAM,SAAS,GAAG,QAAQ,CAAC,GAAG,CAAC,mBAAmB,IAAI,OAAO,EAAE,EAAE,CAAC,CAAC;IAEnE,OAAO;QACL,OAAO;QACP,KAAK;QACL,GAAG;QACH,QAAQ;QACR,SAAS,EAAE,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM;KAC5E,CAAC;AACJ,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env node
2
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
+ import { buildServer } from "./server.js";
4
+ import { ConfigError } from "./config/env.js";
5
+ async function main() {
6
+ try {
7
+ const { server } = buildServer();
8
+ const transport = new StdioServerTransport();
9
+ await server.connect(transport);
10
+ process.stderr.write("deployik-mcp ready (stdio)\n");
11
+ }
12
+ catch (err) {
13
+ if (err instanceof ConfigError) {
14
+ process.stderr.write(`deployik-mcp: ${err.message}\n`);
15
+ process.exit(2);
16
+ }
17
+ process.stderr.write(`deployik-mcp: ${err.message}\n`);
18
+ process.exit(1);
19
+ }
20
+ }
21
+ main();
22
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE9C,KAAK,UAAU,IAAI;IACjB,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,WAAW,EAAE,CAAC;QACjC,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;QAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAChC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,8BAA8B,CAAC,CAAC;IACvD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,WAAW,EAAE,CAAC;YAC/B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,iBAAiB,GAAG,CAAC,OAAO,IAAI,CAAC,CAAC;YACvD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,iBAAkB,GAAa,CAAC,OAAO,IAAI,CAAC,CAAC;QAClE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC"}
@@ -0,0 +1,116 @@
1
+ import { RECIPE_FILES } from "./recipes.generated.js";
2
+ const TOPIC_TITLES = {
3
+ overview: "Deployik overview",
4
+ "create-project": "Connect a GitHub repo and deploy it",
5
+ "custom-domain": "Set up a custom domain with SSL",
6
+ "env-vars": "Add environment variables and secrets",
7
+ "auto-deploy": "Configure auto-deploy from GitHub",
8
+ "password-protection": "Password-protect a preview or production site",
9
+ "contact-form-email": "Wire up a contact form with email + reCAPTCHA",
10
+ rollback: "Roll back a deployment",
11
+ };
12
+ const TOPIC_SUMMARIES = {
13
+ overview: "What Deployik is and the dashboard's anatomy.",
14
+ "create-project": "From 'I have a GitHub repo' to 'it's deployed' in seven clicks.",
15
+ "custom-domain": "DNS verification, SSL provisioning, and primary-domain selection.",
16
+ "env-vars": "Shared, preview, and production scopes — when to use which.",
17
+ "auto-deploy": "GitHub webhooks, preview/production branch matching, opt-in production fan-out.",
18
+ "password-protection": "Generate a per-environment password and share the URL.",
19
+ "contact-form-email": "Webglobe SMTP + reCAPTCHA v3 + the AI-install prompt for Next.js routes.",
20
+ rollback: "Promote a previous successful deployment back to live.",
21
+ };
22
+ const cached = null;
23
+ let recipesCache = cached;
24
+ function buildRecipes() {
25
+ if (recipesCache)
26
+ return recipesCache;
27
+ const fileContent = {};
28
+ for (const f of RECIPE_FILES) {
29
+ fileContent[f.file] = f.content;
30
+ }
31
+ const clickPaths = fileContent["click-paths.md"] ?? "";
32
+ const apiActions = fileContent["api-actions.md"] ?? "";
33
+ const skill = fileContent["SKILL.md"] ?? "";
34
+ const clickSections = splitSections(clickPaths);
35
+ const apiSections = splitSections(apiActions);
36
+ const recipes = [
37
+ {
38
+ topic: "overview",
39
+ title: TOPIC_TITLES.overview,
40
+ summary: TOPIC_SUMMARIES.overview,
41
+ body: skill,
42
+ },
43
+ ];
44
+ const topicHeaders = [
45
+ { topic: "create-project", header: "create-project" },
46
+ { topic: "custom-domain", header: "custom-domain" },
47
+ { topic: "env-vars", header: "env-vars" },
48
+ { topic: "auto-deploy", header: "auto-deploy" },
49
+ { topic: "password-protection", header: "password-protection" },
50
+ { topic: "contact-form-email", header: "contact-form-email" },
51
+ { topic: "rollback", header: "rollback" },
52
+ ];
53
+ for (const { topic, header } of topicHeaders) {
54
+ const click = clickSections[header];
55
+ const api = apiSections[header];
56
+ const parts = [];
57
+ if (click)
58
+ parts.push(`# ${TOPIC_TITLES[topic]}\n\n## Where to click\n\n${click.trim()}`);
59
+ if (api)
60
+ parts.push(`## API actions\n\n${api.trim()}`);
61
+ if (parts.length === 0)
62
+ continue;
63
+ recipes.push({
64
+ topic,
65
+ title: TOPIC_TITLES[topic],
66
+ summary: TOPIC_SUMMARIES[topic],
67
+ body: parts.join("\n\n"),
68
+ });
69
+ }
70
+ recipesCache = recipes;
71
+ return recipes;
72
+ }
73
+ function splitSections(markdown) {
74
+ // Splits a markdown doc by `## <slug>` headers; section body is everything until the next `## ` or EOF.
75
+ const sections = {};
76
+ if (!markdown)
77
+ return sections;
78
+ const lines = markdown.split(/\r?\n/);
79
+ let currentSlug = null;
80
+ let buf = [];
81
+ const flush = () => {
82
+ if (currentSlug !== null) {
83
+ sections[currentSlug] = buf.join("\n");
84
+ }
85
+ };
86
+ for (const line of lines) {
87
+ const m = /^##\s+(\S+)\s*$/.exec(line);
88
+ if (m) {
89
+ flush();
90
+ currentSlug = m[1];
91
+ buf = [];
92
+ continue;
93
+ }
94
+ if (currentSlug !== null)
95
+ buf.push(line);
96
+ }
97
+ flush();
98
+ return sections;
99
+ }
100
+ export function listRecipes() {
101
+ return buildRecipes();
102
+ }
103
+ export function getRecipe(topic) {
104
+ return buildRecipes().find((r) => r.topic === topic);
105
+ }
106
+ export const RECIPE_TOPICS = [
107
+ "overview",
108
+ "create-project",
109
+ "custom-domain",
110
+ "env-vars",
111
+ "auto-deploy",
112
+ "password-protection",
113
+ "contact-form-email",
114
+ "rollback",
115
+ ];
116
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/knowledge/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import { listRecipes } from "./index.js";
2
+ export function registerKnowledgePrompts(server) {
3
+ for (const recipe of listRecipes()) {
4
+ server.registerPrompt(`deployik_recipe_${recipe.topic.replace(/-/g, "_")}`, {
5
+ title: recipe.title,
6
+ description: recipe.summary,
7
+ }, async () => ({
8
+ messages: [
9
+ {
10
+ role: "user",
11
+ content: {
12
+ type: "text",
13
+ text: recipe.body,
14
+ },
15
+ },
16
+ ],
17
+ }));
18
+ }
19
+ }
20
+ //# sourceMappingURL=prompts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompts.js","sourceRoot":"","sources":["../../src/knowledge/prompts.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,MAAM,UAAU,wBAAwB,CAAC,MAAiB;IACxD,KAAK,MAAM,MAAM,IAAI,WAAW,EAAE,EAAE,CAAC;QACnC,MAAM,CAAC,cAAc,CACnB,mBAAmB,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,EACpD;YACE,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,WAAW,EAAE,MAAM,CAAC,OAAO;SAC5B,EACD,KAAK,IAAI,EAAE,CAAC,CAAC;YACX,QAAQ,EAAE;gBACR;oBACE,IAAI,EAAE,MAAM;oBACZ,OAAO,EAAE;wBACP,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,MAAM,CAAC,IAAI;qBAClB;iBACF;aACF;SACF,CAAC,CACH,CAAC;IACJ,CAAC;AACH,CAAC"}