@masonator/coolify-mcp 2.11.0 → 2.12.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/README.md +2 -0
- package/dist/__tests__/coolify-client.test.js +111 -5
- package/dist/__tests__/mcp-server.test.js +16 -0
- package/dist/lib/coolify-client.d.ts +20 -2
- package/dist/lib/coolify-client.js +73 -2
- package/dist/lib/mcp-server.js +6 -2
- package/dist/types/coolify.d.ts +8 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
|
|
12
12
|
> **The most comprehensive MCP server for Coolify** - 42 optimized tools, smart diagnostics, documentation search, and batch operations for managing your self-hosted PaaS through AI assistants.
|
|
13
13
|
|
|
14
|
+
📖 **Docs**: [**coolify-mcp.stumason.dev**](https://coolify-mcp.stumason.dev) — install guide, quickstart, full tools reference, MCP primer, Coolify API gotchas, contributing guide, and the public v3 roadmap.
|
|
15
|
+
|
|
14
16
|
A Model Context Protocol (MCP) server for [Coolify](https://coolify.io/), enabling AI assistants to manage and debug your Coolify instances through natural language.
|
|
15
17
|
|
|
16
18
|
## Features
|
|
@@ -3198,7 +3198,7 @@ describe('CoolifyClient', () => {
|
|
|
3198
3198
|
uuid: 'env-1',
|
|
3199
3199
|
key: 'DB_HOST',
|
|
3200
3200
|
value: 'localhost',
|
|
3201
|
-
|
|
3201
|
+
is_buildtime: false,
|
|
3202
3202
|
is_literal: false,
|
|
3203
3203
|
is_multiline: false,
|
|
3204
3204
|
is_preview: false,
|
|
@@ -3532,13 +3532,119 @@ describe('CoolifyClient', () => {
|
|
|
3532
3532
|
// Resources endpoint
|
|
3533
3533
|
// ===========================================================================
|
|
3534
3534
|
describe('listResources', () => {
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3535
|
+
const fluffyResource = {
|
|
3536
|
+
uuid: 'r1',
|
|
3537
|
+
name: 'my-app',
|
|
3538
|
+
type: 'application',
|
|
3539
|
+
status: 'running:healthy',
|
|
3540
|
+
// ---- fluff that the essential projection should drop ----
|
|
3541
|
+
id: 6,
|
|
3542
|
+
config_hash: 'd1b84af30038c5902cced139b19e5f6f',
|
|
3543
|
+
build_pack: 'dockerfile',
|
|
3544
|
+
health_check_path: '/',
|
|
3545
|
+
ports_exposes: '3500',
|
|
3546
|
+
dockerfile_location: '/Dockerfile',
|
|
3547
|
+
custom_labels: 'dHJhZWZpa...',
|
|
3548
|
+
docker_compose: null,
|
|
3549
|
+
};
|
|
3550
|
+
it('returns essential projection by default (uuid/name/type/status only)', async () => {
|
|
3551
|
+
mockFetch.mockResolvedValueOnce(mockResponse([fluffyResource]));
|
|
3538
3552
|
const result = await client.listResources();
|
|
3539
|
-
expect(result).toEqual(
|
|
3553
|
+
expect(result).toEqual([
|
|
3554
|
+
{ uuid: 'r1', name: 'my-app', type: 'application', status: 'running:healthy' },
|
|
3555
|
+
]);
|
|
3556
|
+
const [first] = result;
|
|
3557
|
+
expect(Object.keys(first).sort()).toEqual(['name', 'status', 'type', 'uuid']);
|
|
3540
3558
|
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/resources', expect.any(Object));
|
|
3541
3559
|
});
|
|
3560
|
+
it('omits status when the resource has none', async () => {
|
|
3561
|
+
const noStatus = { ...fluffyResource };
|
|
3562
|
+
delete noStatus.status;
|
|
3563
|
+
mockFetch.mockResolvedValueOnce(mockResponse([noStatus]));
|
|
3564
|
+
const result = await client.listResources();
|
|
3565
|
+
expect(result).toEqual([{ uuid: 'r1', name: 'my-app', type: 'application' }]);
|
|
3566
|
+
expect('status' in result[0]).toBe(false);
|
|
3567
|
+
});
|
|
3568
|
+
it('returns the raw Coolify payload when include_full is true', async () => {
|
|
3569
|
+
mockFetch.mockResolvedValueOnce(mockResponse([fluffyResource]));
|
|
3570
|
+
const result = await client.listResources({ include_full: true });
|
|
3571
|
+
expect(result).toEqual([fluffyResource]);
|
|
3572
|
+
const [first] = result;
|
|
3573
|
+
expect(first.config_hash).toBe(fluffyResource.config_hash);
|
|
3574
|
+
expect(first.custom_labels).toBe(fluffyResource.custom_labels);
|
|
3575
|
+
});
|
|
3576
|
+
it('treats include_full=false as the default essential projection', async () => {
|
|
3577
|
+
mockFetch.mockResolvedValueOnce(mockResponse([fluffyResource]));
|
|
3578
|
+
const result = await client.listResources({ include_full: false });
|
|
3579
|
+
expect(Object.keys(result[0]).sort()).toEqual(['name', 'status', 'type', 'uuid']);
|
|
3580
|
+
});
|
|
3581
|
+
it('returns [] on an empty payload (default projection)', async () => {
|
|
3582
|
+
mockFetch.mockResolvedValueOnce(mockResponse([]));
|
|
3583
|
+
const result = await client.listResources();
|
|
3584
|
+
expect(result).toEqual([]);
|
|
3585
|
+
});
|
|
3586
|
+
it('returns [] on an empty payload (include_full=true, mask path)', async () => {
|
|
3587
|
+
mockFetch.mockResolvedValueOnce(mockResponse([]));
|
|
3588
|
+
const result = await client.listResources({ include_full: true });
|
|
3589
|
+
expect(result).toEqual([]);
|
|
3590
|
+
});
|
|
3591
|
+
it('drops status when Coolify returns a non-string shape (defensive)', async () => {
|
|
3592
|
+
// Hypothetical future Coolify divergence: status emitted as an object/null.
|
|
3593
|
+
// The essential projection must not propagate a non-string into a `string`-typed field.
|
|
3594
|
+
const objStatus = { ...fluffyResource, status: { state: 'running', healthy: true } };
|
|
3595
|
+
mockFetch.mockResolvedValueOnce(mockResponse([objStatus]));
|
|
3596
|
+
const result = await client.listResources();
|
|
3597
|
+
expect(result).toEqual([{ uuid: 'r1', name: 'my-app', type: 'application' }]);
|
|
3598
|
+
expect('status' in result[0]).toBe(false);
|
|
3599
|
+
});
|
|
3600
|
+
describe('sensitive-field masking on include_full', () => {
|
|
3601
|
+
const sensitiveResource = {
|
|
3602
|
+
...fluffyResource,
|
|
3603
|
+
manual_webhook_secret_github: 'real-github-secret',
|
|
3604
|
+
manual_webhook_secret_gitlab: 'real-gitlab-secret',
|
|
3605
|
+
manual_webhook_secret_gitea: 'real-gitea-secret',
|
|
3606
|
+
manual_webhook_secret_bitbucket: 'real-bitbucket-secret',
|
|
3607
|
+
http_basic_auth_password: 'real-basic-auth-pwd',
|
|
3608
|
+
};
|
|
3609
|
+
it('masks webhook secrets and basic-auth password by default on include_full=true', async () => {
|
|
3610
|
+
mockFetch.mockResolvedValueOnce(mockResponse([sensitiveResource]));
|
|
3611
|
+
const [first] = (await client.listResources({ include_full: true }));
|
|
3612
|
+
expect(first.manual_webhook_secret_github).toBe('***');
|
|
3613
|
+
expect(first.manual_webhook_secret_gitlab).toBe('***');
|
|
3614
|
+
expect(first.manual_webhook_secret_gitea).toBe('***');
|
|
3615
|
+
expect(first.manual_webhook_secret_bitbucket).toBe('***');
|
|
3616
|
+
expect(first.http_basic_auth_password).toBe('***');
|
|
3617
|
+
expect(first.config_hash).toBe(sensitiveResource.config_hash);
|
|
3618
|
+
});
|
|
3619
|
+
it('round-trips plaintext when reveal=true', async () => {
|
|
3620
|
+
mockFetch.mockResolvedValueOnce(mockResponse([sensitiveResource]));
|
|
3621
|
+
const [first] = (await client.listResources({
|
|
3622
|
+
include_full: true,
|
|
3623
|
+
reveal: true,
|
|
3624
|
+
}));
|
|
3625
|
+
expect(first.manual_webhook_secret_github).toBe('real-github-secret');
|
|
3626
|
+
expect(first.http_basic_auth_password).toBe('real-basic-auth-pwd');
|
|
3627
|
+
});
|
|
3628
|
+
it('leaves null secret values untouched (Coolify uses null when unset)', async () => {
|
|
3629
|
+
const noSecrets = {
|
|
3630
|
+
...fluffyResource,
|
|
3631
|
+
manual_webhook_secret_github: null,
|
|
3632
|
+
manual_webhook_secret_gitlab: null,
|
|
3633
|
+
manual_webhook_secret_gitea: null,
|
|
3634
|
+
manual_webhook_secret_bitbucket: null,
|
|
3635
|
+
http_basic_auth_password: null,
|
|
3636
|
+
};
|
|
3637
|
+
mockFetch.mockResolvedValueOnce(mockResponse([noSecrets]));
|
|
3638
|
+
const [first] = (await client.listResources({ include_full: true }));
|
|
3639
|
+
expect(first.manual_webhook_secret_github).toBeNull();
|
|
3640
|
+
expect(first.http_basic_auth_password).toBeNull();
|
|
3641
|
+
});
|
|
3642
|
+
it('reveal=true on the default projection is a no-op (no fluff to reveal)', async () => {
|
|
3643
|
+
mockFetch.mockResolvedValueOnce(mockResponse([sensitiveResource]));
|
|
3644
|
+
const result = await client.listResources({ reveal: true });
|
|
3645
|
+
expect(Object.keys(result[0]).sort()).toEqual(['name', 'status', 'type', 'uuid']);
|
|
3646
|
+
});
|
|
3647
|
+
});
|
|
3542
3648
|
});
|
|
3543
3649
|
// ===========================================================================
|
|
3544
3650
|
// Health endpoint
|
|
@@ -300,6 +300,22 @@ describe('CoolifyMcpServer v2', () => {
|
|
|
300
300
|
expect(result.content[0].text).toContain('key, value required');
|
|
301
301
|
});
|
|
302
302
|
});
|
|
303
|
+
describe('system tool handler', () => {
|
|
304
|
+
const callSystem = async (srv, args) => {
|
|
305
|
+
const tool = srv._registeredTools['system'];
|
|
306
|
+
return tool.handler(args, {});
|
|
307
|
+
};
|
|
308
|
+
it('forwards include_full and reveal to listResources', async () => {
|
|
309
|
+
const spy = jest.spyOn(server['client'], 'listResources').mockResolvedValue([]);
|
|
310
|
+
await callSystem(server, { action: 'list_resources', include_full: true, reveal: true });
|
|
311
|
+
expect(spy).toHaveBeenCalledWith({ include_full: true, reveal: true });
|
|
312
|
+
});
|
|
313
|
+
it('calls listResources with undefined flags when neither is passed', async () => {
|
|
314
|
+
const spy = jest.spyOn(server['client'], 'listResources').mockResolvedValue([]);
|
|
315
|
+
await callSystem(server, { action: 'list_resources' });
|
|
316
|
+
expect(spy).toHaveBeenCalledWith({ include_full: undefined, reveal: undefined });
|
|
317
|
+
});
|
|
318
|
+
});
|
|
303
319
|
describe('bulk_env_update tool handler', () => {
|
|
304
320
|
it('forwards is_buildtime/is_runtime to bulkEnvUpdate', async () => {
|
|
305
321
|
const spy = jest.spyOn(server['client'], 'bulkEnvUpdate').mockResolvedValue({
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Coolify API Client
|
|
3
3
|
* Complete HTTP client for the Coolify API v1
|
|
4
4
|
*/
|
|
5
|
-
import type { CoolifyConfig, DeleteOptions, MessageResponse, UuidResponse, Server, ServerResource, ServerDomain, ServerValidation, CreateServerRequest, UpdateServerRequest, Project, CreateProjectRequest, UpdateProjectRequest, Environment, CreateEnvironmentRequest, Application, CreateApplicationPublicRequest, CreateApplicationPrivateGHRequest, CreateApplicationPrivateKeyRequest, CreateApplicationDockerfileRequest, CreateApplicationDockerImageRequest, CreateApplicationDockerComposeRequest, UpdateApplicationRequest, ApplicationActionResponse, EnvironmentVariable, EnvVarSummary, CreateEnvVarRequest, UpdateEnvVarRequest, BulkUpdateEnvVarsRequest, Database, UpdateDatabaseRequest, CreatePostgresqlRequest, CreateMysqlRequest, CreateMariadbRequest, CreateMongodbRequest, CreateRedisRequest, CreateKeydbRequest, CreateClickhouseRequest, CreateDragonflyRequest, CreateDatabaseResponse, DatabaseBackup, BackupExecution, CreateDatabaseBackupRequest, UpdateDatabaseBackupRequest, Service, CreateServiceRequest, UpdateServiceRequest, ServiceCreateResponse, Deployment, DeploymentEssential, Team, TeamMember, PrivateKey, CreatePrivateKeyRequest, UpdatePrivateKeyRequest, GitHubApp, CreateGitHubAppRequest, UpdateGitHubAppRequest, GitHubAppUpdateResponse, CloudToken, CreateCloudTokenRequest, UpdateCloudTokenRequest, CloudTokenValidation, Version, StorageListResponse, CreateStorageRequest, UpdateStorageRequest, ScheduledTask, ScheduledTaskExecution, CreateScheduledTaskRequest, UpdateScheduledTaskRequest, HetznerLocation, HetznerServerType, HetznerImage, HetznerSSHKey, CreateHetznerServerRequest, CreateHetznerServerResponse, GitHubRepository, GitHubBranch, ApplicationDiagnostic, ServerDiagnostic, InfrastructureIssuesReport, BatchOperationResult, ResourceListItem } from '../types/coolify.js';
|
|
5
|
+
import type { CoolifyConfig, DeleteOptions, MessageResponse, UuidResponse, Server, ServerResource, ServerDomain, ServerValidation, CreateServerRequest, UpdateServerRequest, Project, CreateProjectRequest, UpdateProjectRequest, Environment, CreateEnvironmentRequest, Application, CreateApplicationPublicRequest, CreateApplicationPrivateGHRequest, CreateApplicationPrivateKeyRequest, CreateApplicationDockerfileRequest, CreateApplicationDockerImageRequest, CreateApplicationDockerComposeRequest, UpdateApplicationRequest, ApplicationActionResponse, EnvironmentVariable, EnvVarSummary, CreateEnvVarRequest, UpdateEnvVarRequest, BulkUpdateEnvVarsRequest, Database, UpdateDatabaseRequest, CreatePostgresqlRequest, CreateMysqlRequest, CreateMariadbRequest, CreateMongodbRequest, CreateRedisRequest, CreateKeydbRequest, CreateClickhouseRequest, CreateDragonflyRequest, CreateDatabaseResponse, DatabaseBackup, BackupExecution, CreateDatabaseBackupRequest, UpdateDatabaseBackupRequest, Service, CreateServiceRequest, UpdateServiceRequest, ServiceCreateResponse, Deployment, DeploymentEssential, Team, TeamMember, PrivateKey, CreatePrivateKeyRequest, UpdatePrivateKeyRequest, GitHubApp, CreateGitHubAppRequest, UpdateGitHubAppRequest, GitHubAppUpdateResponse, CloudToken, CreateCloudTokenRequest, UpdateCloudTokenRequest, CloudTokenValidation, Version, StorageListResponse, CreateStorageRequest, UpdateStorageRequest, ScheduledTask, ScheduledTaskExecution, CreateScheduledTaskRequest, UpdateScheduledTaskRequest, HetznerLocation, HetznerServerType, HetznerImage, HetznerSSHKey, CreateHetznerServerRequest, CreateHetznerServerResponse, GitHubRepository, GitHubBranch, ApplicationDiagnostic, ServerDiagnostic, InfrastructureIssuesReport, BatchOperationResult, ResourceListItem, ResourceListItemFull } from '../types/coolify.js';
|
|
6
6
|
export interface ListOptions {
|
|
7
7
|
page?: number;
|
|
8
8
|
per_page?: number;
|
|
@@ -262,7 +262,25 @@ export declare class CoolifyClient {
|
|
|
262
262
|
createHetznerServer(data: CreateHetznerServerRequest): Promise<CreateHetznerServerResponse>;
|
|
263
263
|
listGitHubAppRepositories(githubAppId: number): Promise<GitHubRepository[]>;
|
|
264
264
|
listGitHubAppBranches(githubAppId: number, owner: string, repo: string): Promise<GitHubBranch[]>;
|
|
265
|
-
|
|
265
|
+
/**
|
|
266
|
+
* List every resource on the Coolify instance.
|
|
267
|
+
*
|
|
268
|
+
* Defaults to an essential projection ({@link ResourceListItem}: uuid, name,
|
|
269
|
+
* type, optional status) — Coolify's `/api/v1/resources` endpoint actually
|
|
270
|
+
* returns ~95 fields per row including the full build/healthcheck/limits
|
|
271
|
+
* config, which on a moderate instance can exceed 500 KB on a single call
|
|
272
|
+
* and blow MCP/LLM context budgets. Set `include_full: true` to opt back
|
|
273
|
+
* into the raw response shape ({@link ResourceListItemFull}).
|
|
274
|
+
*
|
|
275
|
+
* When `include_full: true`, sensitive fields ({@link SENSITIVE_RESOURCE_FIELDS}:
|
|
276
|
+
* webhook HMAC secrets + basic-auth password) are replaced with `'***'`
|
|
277
|
+
* unless the caller also passes `reveal: true`. Mirrors the v2.9.0 env_vars
|
|
278
|
+
* masking posture.
|
|
279
|
+
*/
|
|
280
|
+
listResources(options?: {
|
|
281
|
+
include_full?: boolean;
|
|
282
|
+
reveal?: boolean;
|
|
283
|
+
}): Promise<ResourceListItem[] | ResourceListItemFull[]>;
|
|
266
284
|
getHealth(): Promise<MessageResponse>;
|
|
267
285
|
enableApi(): Promise<MessageResponse>;
|
|
268
286
|
disableApi(): Promise<MessageResponse>;
|
|
@@ -180,6 +180,58 @@ function maskEnvVarSummary(envVar) {
|
|
|
180
180
|
value: MASKED_VALUE,
|
|
181
181
|
};
|
|
182
182
|
}
|
|
183
|
+
/**
|
|
184
|
+
* Project a full Coolify resource row down to {@link ResourceListItem} — the
|
|
185
|
+
* four fields callers actually need for enumeration (uuid, name, type, status).
|
|
186
|
+
* Drops the ~90 extra fields Coolify returns by default to keep MCP token
|
|
187
|
+
* budgets sane.
|
|
188
|
+
*/
|
|
189
|
+
function toResourceListItemEssential(item) {
|
|
190
|
+
const essential = {
|
|
191
|
+
uuid: item.uuid,
|
|
192
|
+
name: item.name,
|
|
193
|
+
type: item.type,
|
|
194
|
+
};
|
|
195
|
+
if (typeof item.status === 'string') {
|
|
196
|
+
essential.status = item.status;
|
|
197
|
+
}
|
|
198
|
+
return essential;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Per-resource sensitive fields returned by Coolify's `/api/v1/resources`
|
|
202
|
+
* endpoint that are masked by default in {@link CoolifyClient.listResources}
|
|
203
|
+
* when `include_full: true` is passed. Mirrors the v2.9.0 env-var masking
|
|
204
|
+
* posture: the underlying API exposes these via the same access token, but
|
|
205
|
+
* the MCP layer narrows the trust boundary so an LLM client that was granted
|
|
206
|
+
* "list resources" doesn't silently exfiltrate webhook HMAC secrets or
|
|
207
|
+
* basic-auth credentials.
|
|
208
|
+
*
|
|
209
|
+
* The `manual_webhook_secret_*` fields are HMAC signing keys for inbound
|
|
210
|
+
* deploy webhooks — anyone with one can forge deploys for that repo
|
|
211
|
+
* independently of the Coolify API token. `http_basic_auth_password` is the
|
|
212
|
+
* password gating front-of-app access.
|
|
213
|
+
*/
|
|
214
|
+
const SENSITIVE_RESOURCE_FIELDS = [
|
|
215
|
+
'manual_webhook_secret_github',
|
|
216
|
+
'manual_webhook_secret_gitlab',
|
|
217
|
+
'manual_webhook_secret_gitea',
|
|
218
|
+
'manual_webhook_secret_bitbucket',
|
|
219
|
+
'http_basic_auth_password',
|
|
220
|
+
];
|
|
221
|
+
/**
|
|
222
|
+
* Replace each {@link SENSITIVE_RESOURCE_FIELDS} entry with `'***'` on a full
|
|
223
|
+
* resource row. Null/undefined values are preserved (since `null` conveys
|
|
224
|
+
* "no secret set" and matters to callers); only populated values get masked.
|
|
225
|
+
*/
|
|
226
|
+
function maskResourceItemFull(item) {
|
|
227
|
+
const masked = { ...item };
|
|
228
|
+
for (const field of SENSITIVE_RESOURCE_FIELDS) {
|
|
229
|
+
if (masked[field] != null) {
|
|
230
|
+
masked[field] = MASKED_VALUE;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return masked;
|
|
234
|
+
}
|
|
183
235
|
/**
|
|
184
236
|
* HTTP client for the Coolify API
|
|
185
237
|
*/
|
|
@@ -1128,8 +1180,27 @@ export class CoolifyClient {
|
|
|
1128
1180
|
// ===========================================================================
|
|
1129
1181
|
// Resources endpoint
|
|
1130
1182
|
// ===========================================================================
|
|
1131
|
-
|
|
1132
|
-
|
|
1183
|
+
/**
|
|
1184
|
+
* List every resource on the Coolify instance.
|
|
1185
|
+
*
|
|
1186
|
+
* Defaults to an essential projection ({@link ResourceListItem}: uuid, name,
|
|
1187
|
+
* type, optional status) — Coolify's `/api/v1/resources` endpoint actually
|
|
1188
|
+
* returns ~95 fields per row including the full build/healthcheck/limits
|
|
1189
|
+
* config, which on a moderate instance can exceed 500 KB on a single call
|
|
1190
|
+
* and blow MCP/LLM context budgets. Set `include_full: true` to opt back
|
|
1191
|
+
* into the raw response shape ({@link ResourceListItemFull}).
|
|
1192
|
+
*
|
|
1193
|
+
* When `include_full: true`, sensitive fields ({@link SENSITIVE_RESOURCE_FIELDS}:
|
|
1194
|
+
* webhook HMAC secrets + basic-auth password) are replaced with `'***'`
|
|
1195
|
+
* unless the caller also passes `reveal: true`. Mirrors the v2.9.0 env_vars
|
|
1196
|
+
* masking posture.
|
|
1197
|
+
*/
|
|
1198
|
+
async listResources(options) {
|
|
1199
|
+
const full = await this.request('/resources');
|
|
1200
|
+
if (options?.include_full !== true) {
|
|
1201
|
+
return full.map(toResourceListItemEssential);
|
|
1202
|
+
}
|
|
1203
|
+
return options.reveal === true ? full : full.map(maskResourceItemFull);
|
|
1133
1204
|
}
|
|
1134
1205
|
// ===========================================================================
|
|
1135
1206
|
// Health endpoint
|
package/dist/lib/mcp-server.js
CHANGED
|
@@ -1469,12 +1469,16 @@ export class CoolifyMcpServer extends McpServer {
|
|
|
1469
1469
|
// =========================================================================
|
|
1470
1470
|
// System (1 tool - health/list_resources/api_control consolidated)
|
|
1471
1471
|
// =========================================================================
|
|
1472
|
-
this.tool('system', 'System operations: health/list_resources/enable_api/disable_api
|
|
1472
|
+
this.tool('system', 'System operations: health/list_resources/enable_api/disable_api. `list_resources` defaults to an essential projection (uuid/name/type/status) to keep token budgets sane on instances with many resources; pass `include_full: true` for the raw Coolify payload. When `include_full: true`, webhook HMAC secrets and basic-auth password are masked unless `reveal: true` is also set (matches the `env_vars` `reveal` ergonomics).', {
|
|
1473
|
+
action: z.enum(['health', 'list_resources', 'enable_api', 'disable_api']),
|
|
1474
|
+
include_full: z.boolean().optional(),
|
|
1475
|
+
reveal: z.boolean().optional(),
|
|
1476
|
+
}, async ({ action, include_full, reveal }) => {
|
|
1473
1477
|
switch (action) {
|
|
1474
1478
|
case 'health':
|
|
1475
1479
|
return wrap(() => this.client.getHealth());
|
|
1476
1480
|
case 'list_resources':
|
|
1477
|
-
return wrap(() => this.client.listResources());
|
|
1481
|
+
return wrap(() => this.client.listResources({ include_full, reveal }));
|
|
1478
1482
|
case 'enable_api':
|
|
1479
1483
|
return wrap(() => this.client.enableApi());
|
|
1480
1484
|
case 'disable_api':
|
package/dist/types/coolify.d.ts
CHANGED
|
@@ -1093,6 +1093,14 @@ export interface ResourceListItem {
|
|
|
1093
1093
|
type: 'server' | 'application' | 'database' | 'service' | string;
|
|
1094
1094
|
status?: string;
|
|
1095
1095
|
}
|
|
1096
|
+
/**
|
|
1097
|
+
* Full Coolify `/api/v1/resources` row — the typed essentials plus every other
|
|
1098
|
+
* field Coolify returns (build/healthcheck/limits/git/docker-compose config,
|
|
1099
|
+
* etc.). Only surfaced when the caller passes `include_full: true`; the default
|
|
1100
|
+
* `listResources()` response uses {@link ResourceListItem} to keep MCP token
|
|
1101
|
+
* budgets sane on instances with many resources.
|
|
1102
|
+
*/
|
|
1103
|
+
export type ResourceListItemFull = ResourceListItem & Record<string, unknown>;
|
|
1096
1104
|
export interface ResponseAction {
|
|
1097
1105
|
tool: string;
|
|
1098
1106
|
args: Record<string, string | number | boolean>;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@masonator/coolify-mcp",
|
|
3
3
|
"scope": "@masonator",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.12.0",
|
|
5
5
|
"mcpName": "io.github.StuMason/coolify",
|
|
6
6
|
"description": "MCP server for Coolify — 42 optimized tools for infrastructure management, diagnostics, and documentation search",
|
|
7
7
|
"type": "module",
|