Skip to content

Commit c6f10cb

Browse files
authored
chore: release v2.5.0 - type safety improvements (#85)
* refactor: eliminate all `as any` casts in MCP tool handlers (#81) Application handlers (create_public, create_github, create_key, create_dockerimage) and service create handler now use explicit typed objects instead of `as any` casts. Changes: - Replace all 5 `as any` casts with explicit object construction - Import BuildPack type for proper typing - Fix CreateApplicationPrivateGHRequest and CreateApplicationPrivateKeyRequest types: - build_pack and ports_exposes are optional (verified against Coolify API) - Remove `eslint-disable @typescript-eslint/no-explicit-any` directive - Add tested Coolify version to README (v4.0.0-beta.460) This prevents bugs like #76 where MCP-internal fields leaked to the API. Closes #81 Stu Mason + AI <me@stumason.dev> * fix: address PR review feedback for #81 - Change build_pack from z.string() to z.enum() for type safety - Move apiData destructuring inside update cases only (fixes unused variable) - Remove as BuildPack casts (Zod enum handles typing) - Remove unused BuildPack import Stu Mason + AI <me@stumason.dev> * chore: bump version to 2.5.0 and finalize type safety (#81) - Reverted build_pack from Zod enum to z.string() for safety (let Coolify API validate - tested against real API) - Updated version to 2.5.0 - Updated CHANGELOG with release notes Verified against Coolify v4.0.0-beta.460: - nixpacks, dockerfile, static, dockercompose all work - Invalid values return clear error from Coolify API Stu Mason + AI <me@stumason.dev>
1 parent a08d29c commit c6f10cb

File tree

6 files changed

+100
-21
lines changed

6 files changed

+100
-21
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [2.5.0] - 2026-01-15
11+
1012
### Added
1113

1214
- **Codecov Test Analytics** - Enable test result tracking (#84):
@@ -22,6 +24,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2224
- Fix: Default to last 200 lines AND cap at 50K characters
2325
- Added `max_chars` parameter for customization
2426

27+
- **Type Safety** - Eliminate all `as any` casts in MCP tool handlers (#81):
28+
- `application` handlers (create_public, create_github, create_key, create_dockerimage) now use explicit typed objects
29+
- `service` create handler uses explicit typed object
30+
- Removed `eslint-disable @typescript-eslint/no-explicit-any` directive
31+
- Fixed type definitions: `build_pack` and `ports_exposes` now optional for GitHub/Key deploys
32+
- Verified against Coolify v4.0.0-beta.460
33+
2534
## [2.4.0] - 2026-01-15
2635

2736
### Added

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ The server uses **85% fewer tokens** than a naive implementation (6,600 vs 43,00
4343
### Prerequisites
4444

4545
- Node.js >= 18
46-
- A running Coolify instance
46+
- A running Coolify instance (tested with v4.0.0-beta.460)
4747
- Coolify API access token (generate in Coolify Settings > API)
4848

4949
### Claude Desktop

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@masonator/coolify-mcp",
33
"scope": "@masonator",
4-
"version": "2.4.0",
4+
"version": "2.5.0",
55
"description": "MCP server implementation for Coolify",
66
"type": "module",
77
"main": "./dist/index.js",

src/lib/mcp-server.ts

Lines changed: 75 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
* Coolify MCP Server v2.4.0
33
* Consolidated tools for efficient token usage
44
*/
5-
/* eslint-disable @typescript-eslint/no-explicit-any */
65

76
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
87
import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
@@ -16,9 +15,9 @@ import {
1615
type ServiceSummary,
1716
type GitHubAppSummary,
1817
} from './coolify-client.js';
19-
import type { CoolifyConfig, GitHubApp } from '../types/coolify.js';
18+
import type { CoolifyConfig, GitHubApp, BuildPack } from '../types/coolify.js';
2019

21-
const VERSION = '2.4.0';
20+
const VERSION = '2.5.0';
2221

2322
/** Wrap handler with error handling */
2423
function wrap<T>(
@@ -330,8 +329,7 @@ export class CoolifyMcpServer extends McpServer {
330329
delete_volumes: z.boolean().optional(),
331330
},
332331
async (args) => {
333-
// Strip MCP-internal fields before passing to API (fixes #76)
334-
const { action, uuid, delete_volumes, ...apiData } = args;
332+
const { action, uuid, delete_volumes } = args;
335333
switch (action) {
336334
case 'create_public':
337335
if (
@@ -351,7 +349,21 @@ export class CoolifyMcpServer extends McpServer {
351349
],
352350
};
353351
}
354-
return wrap(() => this.client.createApplicationPublic(apiData as any));
352+
return wrap(() =>
353+
this.client.createApplicationPublic({
354+
project_uuid: args.project_uuid!,
355+
server_uuid: args.server_uuid!,
356+
git_repository: args.git_repository!,
357+
git_branch: args.git_branch!,
358+
build_pack: args.build_pack! as BuildPack,
359+
ports_exposes: args.ports_exposes!,
360+
environment_name: args.environment_name,
361+
environment_uuid: args.environment_uuid,
362+
name: args.name,
363+
description: args.description,
364+
fqdn: args.fqdn,
365+
}),
366+
);
355367
case 'create_github':
356368
if (
357369
!args.project_uuid ||
@@ -369,7 +381,22 @@ export class CoolifyMcpServer extends McpServer {
369381
],
370382
};
371383
}
372-
return wrap(() => this.client.createApplicationPrivateGH(apiData as any));
384+
return wrap(() =>
385+
this.client.createApplicationPrivateGH({
386+
project_uuid: args.project_uuid!,
387+
server_uuid: args.server_uuid!,
388+
github_app_uuid: args.github_app_uuid!,
389+
git_repository: args.git_repository!,
390+
git_branch: args.git_branch!,
391+
build_pack: args.build_pack as BuildPack | undefined,
392+
ports_exposes: args.ports_exposes,
393+
environment_name: args.environment_name,
394+
environment_uuid: args.environment_uuid,
395+
name: args.name,
396+
description: args.description,
397+
fqdn: args.fqdn,
398+
}),
399+
);
373400
case 'create_key':
374401
if (
375402
!args.project_uuid ||
@@ -387,7 +414,22 @@ export class CoolifyMcpServer extends McpServer {
387414
],
388415
};
389416
}
390-
return wrap(() => this.client.createApplicationPrivateKey(apiData as any));
417+
return wrap(() =>
418+
this.client.createApplicationPrivateKey({
419+
project_uuid: args.project_uuid!,
420+
server_uuid: args.server_uuid!,
421+
private_key_uuid: args.private_key_uuid!,
422+
git_repository: args.git_repository!,
423+
git_branch: args.git_branch!,
424+
build_pack: args.build_pack as BuildPack | undefined,
425+
ports_exposes: args.ports_exposes,
426+
environment_name: args.environment_name,
427+
environment_uuid: args.environment_uuid,
428+
name: args.name,
429+
description: args.description,
430+
fqdn: args.fqdn,
431+
}),
432+
);
391433
case 'create_dockerimage':
392434
if (
393435
!args.project_uuid ||
@@ -404,11 +446,27 @@ export class CoolifyMcpServer extends McpServer {
404446
],
405447
};
406448
}
407-
return wrap(() => this.client.createApplicationDockerImage(apiData as any));
408-
case 'update':
449+
return wrap(() =>
450+
this.client.createApplicationDockerImage({
451+
project_uuid: args.project_uuid!,
452+
server_uuid: args.server_uuid!,
453+
docker_registry_image_name: args.docker_registry_image_name!,
454+
ports_exposes: args.ports_exposes!,
455+
docker_registry_image_tag: args.docker_registry_image_tag,
456+
environment_name: args.environment_name,
457+
environment_uuid: args.environment_uuid,
458+
name: args.name,
459+
description: args.description,
460+
fqdn: args.fqdn,
461+
}),
462+
);
463+
case 'update': {
409464
if (!uuid)
410465
return { content: [{ type: 'text' as const, text: 'Error: uuid required' }] };
411-
return wrap(() => this.client.updateApplication(uuid, apiData));
466+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
467+
const { action: _, uuid: __, delete_volumes: ___, ...updateData } = args;
468+
return wrap(() => this.client.updateApplication(uuid, updateData));
469+
}
412470
case 'delete':
413471
if (!uuid)
414472
return { content: [{ type: 'text' as const, text: 'Error: uuid required' }] };
@@ -550,8 +608,7 @@ export class CoolifyMcpServer extends McpServer {
550608
delete_volumes: z.boolean().optional(),
551609
},
552610
async (args) => {
553-
// Strip MCP-internal fields before passing to API (fixes #76)
554-
const { action, uuid, delete_volumes, ...apiData } = args;
611+
const { action, uuid, delete_volumes } = args;
555612
switch (action) {
556613
case 'create':
557614
if (!args.server_uuid || !args.project_uuid) {
@@ -573,10 +630,13 @@ export class CoolifyMcpServer extends McpServer {
573630
docker_compose_raw: args.docker_compose_raw,
574631
}),
575632
);
576-
case 'update':
633+
case 'update': {
577634
if (!uuid)
578635
return { content: [{ type: 'text' as const, text: 'Error: uuid required' }] };
579-
return wrap(() => this.client.updateService(uuid, apiData));
636+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
637+
const { action: _, uuid: __, delete_volumes: ___, ...updateData } = args;
638+
return wrap(() => this.client.updateService(uuid, updateData));
639+
}
580640
case 'delete':
581641
if (!uuid)
582642
return { content: [{ type: 'text' as const, text: 'Error: uuid required' }] };

src/types/coolify.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -269,12 +269,22 @@ export interface CreateApplicationPublicRequest {
269269
instant_deploy?: boolean;
270270
}
271271

272-
export interface CreateApplicationPrivateGHRequest extends CreateApplicationPublicRequest {
272+
export interface CreateApplicationPrivateGHRequest extends Omit<
273+
CreateApplicationPublicRequest,
274+
'build_pack' | 'ports_exposes'
275+
> {
273276
github_app_uuid: string;
277+
build_pack?: BuildPack; // Optional for GitHub app deploys
278+
ports_exposes?: string; // Optional for GitHub app deploys
274279
}
275280

276-
export interface CreateApplicationPrivateKeyRequest extends CreateApplicationPublicRequest {
281+
export interface CreateApplicationPrivateKeyRequest extends Omit<
282+
CreateApplicationPublicRequest,
283+
'build_pack' | 'ports_exposes'
284+
> {
277285
private_key_uuid: string;
286+
build_pack?: BuildPack; // Optional for private key deploys
287+
ports_exposes?: string; // Optional for private key deploys
278288
}
279289

280290
export interface CreateApplicationDockerfileRequest {

0 commit comments

Comments
 (0)