@jurislm/coolify-plugin 4.0.29 → 4.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.
- package/.codex-plugin/plugin.json +1 -1
- package/dist/client.d.ts +2 -1
- package/dist/client.js +26 -8
- package/dist/server.js +3 -3
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/skills/coolify/SKILL.md +1 -1
package/dist/client.d.ts
CHANGED
|
@@ -16,7 +16,8 @@ export interface BinaryEnvelope {
|
|
|
16
16
|
}
|
|
17
17
|
export type FetchLike = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
18
18
|
type Operation = Pick<GeneratedOperation, "method" | "path" | "parameters">;
|
|
19
|
-
export declare function
|
|
19
|
+
export declare function isEnvOperation(path: string): boolean;
|
|
20
|
+
export declare function redactSensitive<T>(value: T, tokens?: Array<string | undefined>, envResponse?: boolean): T;
|
|
20
21
|
export declare class CoolifyClient {
|
|
21
22
|
private readonly config;
|
|
22
23
|
private readonly fetchImpl;
|
package/dist/client.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { isAlias, isScalar, parseDocument, stringify, visit } from "yaml";
|
|
2
2
|
import { CoolifyApiError, redactErrorText } from "./errors.js";
|
|
3
3
|
const sensitiveKey = /(^value$|real_?value|private_?key|api_?key|user_?key|webhook_?url|token|secret|password|authorization|cookie)/iu;
|
|
4
|
+
const envValueKey = /^(value|real_value)$/u;
|
|
5
|
+
const envPath = /\/envs(\/|$)/u;
|
|
4
6
|
const composeKey = /^(docker_compose(_pr)?(_raw)?|configuration)$/u;
|
|
5
7
|
const composeSecretName = /(password|passwd|secret|token|private|credential|api_?key|access_?key|root_?user|authorization|cookie)/iu;
|
|
6
8
|
const composeSecretSegment = /(^|[_.-])(key|pass|pwd|auth)($|[_.-])/iu;
|
|
@@ -137,18 +139,34 @@ function redactComposeText(text) {
|
|
|
137
139
|
const decoded = decodeBase64Text(text);
|
|
138
140
|
return decoded === undefined ? redactComposeYaml(text) : Buffer.from(redactComposeYaml(decoded), "utf8").toString("base64");
|
|
139
141
|
}
|
|
140
|
-
|
|
142
|
+
function withoutTokens(text, tokens) {
|
|
143
|
+
return tokens.reduce((redacted, token) => token ? redacted.replaceAll(token, "[REDACTED]") : redacted, text);
|
|
144
|
+
}
|
|
145
|
+
export function isEnvOperation(path) {
|
|
146
|
+
return envPath.test(path);
|
|
147
|
+
}
|
|
148
|
+
export function redactSensitive(value, tokens = [], envResponse = false) {
|
|
141
149
|
if (typeof value === "string")
|
|
142
|
-
return redactUrlCredentials(value);
|
|
150
|
+
return withoutTokens(redactUrlCredentials(value), tokens);
|
|
143
151
|
if (Array.isArray(value))
|
|
144
|
-
return value.map(redactSensitive);
|
|
152
|
+
return value.map((item) => redactSensitive(item, tokens, envResponse));
|
|
145
153
|
if (!value || typeof value !== "object")
|
|
146
154
|
return value;
|
|
147
155
|
const binary = value.encoding === "base64" && typeof value.contentType === "string" && typeof value.value === "string";
|
|
148
|
-
return Object.fromEntries(Object.entries(value).map(([key, child]) => [
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
156
|
+
return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, redactField(key, child, tokens, envResponse, binary)]));
|
|
157
|
+
}
|
|
158
|
+
function redactField(key, child, tokens, envResponse, binary) {
|
|
159
|
+
if (binary && key === "value")
|
|
160
|
+
return child;
|
|
161
|
+
if (envResponse && envValueKey.test(key))
|
|
162
|
+
return typeof child === "string" ? withoutTokens(child, tokens) : child;
|
|
163
|
+
if (sensitiveKey.test(key))
|
|
164
|
+
return "[REDACTED]";
|
|
165
|
+
if (composeKey.test(key) && typeof child === "string")
|
|
166
|
+
return withoutTokens(redactComposeText(child), tokens);
|
|
167
|
+
if (key === "custom_docker_run_options" && typeof child === "string")
|
|
168
|
+
return withoutTokens(redactUrlCredentials(redactCommandLine(child)), tokens);
|
|
169
|
+
return redactSensitive(child, tokens, envResponse);
|
|
152
170
|
}
|
|
153
171
|
function pathValue(value, name) {
|
|
154
172
|
if (value === undefined || value === null)
|
|
@@ -246,7 +264,7 @@ export class CoolifyClient {
|
|
|
246
264
|
catch {
|
|
247
265
|
throw new Error("Coolify API returned an unreadable response body");
|
|
248
266
|
}
|
|
249
|
-
return redactSensitive({ data, status: response.status, request: { method: operation.method, path } });
|
|
267
|
+
return redactSensitive({ data, status: response.status, request: { method: operation.method, path } }, [token], isEnvOperation(operation.path));
|
|
250
268
|
}
|
|
251
269
|
}
|
|
252
270
|
export { CoolifyApiError };
|
package/dist/server.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import { CoolifyClient, redactSensitive } from "./client.js";
|
|
3
|
+
import { CoolifyClient, isEnvOperation, redactSensitive } from "./client.js";
|
|
4
4
|
import { formatToolError } from "./errors.js";
|
|
5
5
|
import { registerCapabilities } from "./capabilities.js";
|
|
6
6
|
import { operations } from "./generated/operations.js";
|
|
@@ -8,7 +8,7 @@ import { pluginVersion } from "./version.js";
|
|
|
8
8
|
const outputRequestSchema = z.object({ method: z.string(), path: z.string() });
|
|
9
9
|
export function createServer(config, fetchImpl) {
|
|
10
10
|
const client = new CoolifyClient(config, fetchImpl);
|
|
11
|
-
const server = new McpServer({ name: "coolify-plugin", version: pluginVersion }, { instructions: "Use read tools to identify exact Coolify resources before mutations. Never expose
|
|
11
|
+
const server = new McpServer({ name: "coolify-plugin", version: pluginVersion }, { instructions: "Use read tools to identify exact Coolify resources before mutations. Never expose the Coolify access token. Env tools return actual environment variable values; report them only when the user asks." });
|
|
12
12
|
for (const operation of operations) {
|
|
13
13
|
server.registerTool(operation.name, {
|
|
14
14
|
title: operation.title,
|
|
@@ -20,7 +20,7 @@ export function createServer(config, fetchImpl) {
|
|
|
20
20
|
try {
|
|
21
21
|
const envelope = await client.request(operation, input);
|
|
22
22
|
const data = operation.responseSchema.parse(envelope.data);
|
|
23
|
-
const structuredContent = redactSensitive({ data, status: envelope.status, request: envelope.request });
|
|
23
|
+
const structuredContent = redactSensitive({ data, status: envelope.status, request: envelope.request }, [config.token], isEnvOperation(operation.path));
|
|
24
24
|
return { structuredContent, content: [{ type: "text", text: JSON.stringify(structuredContent) }] };
|
|
25
25
|
}
|
|
26
26
|
catch (error) {
|
package/package.json
CHANGED
package/plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
|
3
3
|
"name": "coolify-plugin",
|
|
4
|
-
"version": "4.0
|
|
4
|
+
"version": "4.1.0",
|
|
5
5
|
"description": "Portable Coolify MCP plugin with local stdio tools generated from the official OpenAPI contract.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "JurisLM",
|
package/skills/coolify/SKILL.md
CHANGED
|
@@ -5,4 +5,4 @@ description: Manage a configured Coolify instance through generated local MCP to
|
|
|
5
5
|
|
|
6
6
|
# Coolify
|
|
7
7
|
|
|
8
|
-
Use `coolify_*` tools only after resolving the target resource. Read before mutations and do not expose `COOLIFY_ACCESS_TOKEN`
|
|
8
|
+
Use `coolify_*` tools only after resolving the target resource. Read before mutations and do not expose `COOLIFY_ACCESS_TOKEN`. Env tools return actual environment variable values, including `is_shown_once` ones; report them only when the user asks.
|