diff --git a/server/internal/orchestrator/swarm/mcp_config_resource.go b/server/internal/orchestrator/swarm/mcp_config_resource.go index bcb2620a..fda628c5 100644 --- a/server/internal/orchestrator/swarm/mcp_config_resource.go +++ b/server/internal/orchestrator/swarm/mcp_config_resource.go @@ -254,6 +254,21 @@ func (r *MCPConfigResource) writeUserFileIfNeeded(fs afero.Fs, usersPath string) // return nil — the config file is already correct on disk and will be // picked up on the next container restart. Injector failures are returned // as errors since they indicate a systemic problem. +// PostDeploy is called by ServiceInstanceResource after the container is confirmed +// running. It sends SIGHUP to trigger a config reload, handling VirtioFS +// bind-mount propagation delays on initial provisioning. +func (r *MCPConfigResource) PostDeploy(ctx context.Context, rc *resource.Context) { + logger, err := do.Invoke[zerolog.Logger](rc.Injector) + if err != nil { + return + } + if err := r.signalConfigReload(ctx, rc); err != nil { + logger.Warn().Err(err). + Str("service_instance_id", r.ServiceInstanceID). + Msg("post-deploy config reload signal failed") + } +} + func (r *MCPConfigResource) signalConfigReload(ctx context.Context, rc *resource.Context) error { dockerClient, err := do.Invoke[*docker.Docker](rc.Injector) if err != nil { diff --git a/server/internal/orchestrator/swarm/service_instance.go b/server/internal/orchestrator/swarm/service_instance.go index a7775074..25786566 100644 --- a/server/internal/orchestrator/swarm/service_instance.go +++ b/server/internal/orchestrator/swarm/service_instance.go @@ -203,6 +203,8 @@ func (s *ServiceInstanceResource) deploy(ctx context.Context, rc *resource.Conte Str("service_id", res.ServiceID). Msg("service instance started successfully") + spec.PostDeploy(ctx, rc) + // Transition state to "running" immediately after successful deployment. // This mirrors the database instance pattern where state is set // deterministically within the resource activity, not deferred to the monitor. diff --git a/server/internal/orchestrator/swarm/service_instance_spec.go b/server/internal/orchestrator/swarm/service_instance_spec.go index 2b46793f..8bb310bb 100644 --- a/server/internal/orchestrator/swarm/service_instance_spec.go +++ b/server/internal/orchestrator/swarm/service_instance_spec.go @@ -90,6 +90,22 @@ func (s *ServiceInstanceSpecResource) TypeDependencies() []resource.Type { return nil } +// PostDeploy is called by ServiceInstanceResource after the container is confirmed +// running. Each service type can trigger any necessary post-start actions here. +func (s *ServiceInstanceSpecResource) PostDeploy(ctx context.Context, rc *resource.Context) { + switch s.ServiceSpec.ServiceType { + case "mcp": + mcpCfg, err := resource.FromContext[*MCPConfigResource](rc, MCPConfigResourceIdentifier(s.ServiceInstanceID)) + if err != nil { + log.Warn().Err(err). + Str("service_instance_id", s.ServiceInstanceID). + Msg("skipping MCP post-deploy hook: MCP config resource unavailable") + return + } + mcpCfg.PostDeploy(ctx, rc) + } +} + func (s *ServiceInstanceSpecResource) Refresh(ctx context.Context, rc *resource.Context) error { network, err := resource.FromContext[*Network](rc, NetworkResourceIdentifier(s.DatabaseNetworkID)) if err != nil {