From 37b4a8b4e71c4f8b3bc55d3e579c49aeeed80423 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Tue, 3 Nov 2026 14:00:17 +0100 Subject: [PATCH] fix(mcp): keep the deployment source on additional servers and audit MCP calls as MCP --- CHANGELOG.md | 2 +- app/Jobs/ApplicationDeploymentJob.php | 3 +++ app/Models/ApplicationDeploymentQueue.php | 8 ++------ bootstrap/helpers/audit.php | 5 +++++ 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d7b23301f..d601dcb7d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), ### Fixed - Handle an existing Cloudflare DNS record with a different IP when Coolify adds the DNS record of a new domain automatically. Coolify now replaces the record when it created the record and no other resource uses it. Otherwise it opens the **Configure DNS** dialog, where you can replace the record, and the domain shows the conflict instead of "Not checked" -- Save deployments started through the MCP server as MCP deployments, not as API deployments. The deployment list shows them with the **MCP** source and filter, and the API and MCP deployment data have a new `is_mcp` field +- Save deployments started through the MCP server as MCP deployments, not as API deployments. The deployment list shows them with the **MCP** source and filter, and the API and MCP deployment data have a new `is_mcp` field. The deployments to additional servers keep the source of the main deployment, and the audit log records actions that MCP tools do through the API as MCP events ## [v4.6.0] - 2026-11-02 diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index f95a85a87a..7b438c524d 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -3297,12 +3297,15 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue deployment_uuid: $deployment_uuid, application: $this->application, commit: $this->commit, + is_webhook: (bool) $this->application_deployment_queue->is_webhook, + is_api: (bool) $this->application_deployment_queue->is_api, no_questions_asked: true, server: $server, destination: $destination, rollback: $this->rollback, docker_registry_image_tag: $this->application_deployment_queue->docker_registry_image_tag, parent_deployment_uuid: $this->deployment_uuid, + is_mcp: (bool) $this->application_deployment_queue->is_mcp, ); } catch (Throwable $e) { \Log::warning("Deployment {$this->deployment_uuid} could not queue the deployment to {$server->name}: {$e->getMessage()}"); diff --git a/app/Models/ApplicationDeploymentQueue.php b/app/Models/ApplicationDeploymentQueue.php index bf4ad54945..83ea45f569 100644 --- a/app/Models/ApplicationDeploymentQueue.php +++ b/app/Models/ApplicationDeploymentQueue.php @@ -112,14 +112,10 @@ class ApplicationDeploymentQueue extends Model return; } + // The API and MCP cancel paths do not fire model events and record their own audit event. $application = $deployment->application; - $source = match (true) { - (bool) $deployment->is_mcp => 'mcp', - (bool) $deployment->is_api => 'api', - default => 'ui', - }; - auditLog("{$source}.deployment.cancelled", [ + auditLog('ui.deployment.cancelled', [ 'team_id' => $application?->team()?->id, 'application_uuid' => $application?->uuid, 'application_name' => $application?->name, diff --git a/bootstrap/helpers/audit.php b/bootstrap/helpers/audit.php index 46ec4ba2b4..482a535897 100644 --- a/bootstrap/helpers/audit.php +++ b/bootstrap/helpers/audit.php @@ -16,6 +16,11 @@ if (! function_exists('auditLog')) { */ function auditLog(string $event, array $context = [], string $level = 'info'): void { + // MCP tools also call the REST API controllers through an internal request. + if (str_starts_with($event, 'api.') && app()->bound('request') && request()->attributes->get('mcp_rest_bridge') === true) { + $event = 'mcp.'.substr($event, 4); + } + if (! shouldRecordAuditEvent($event, $context)) { return; }