flipped-aurora/gin-vue-admin · error

MCP 健康检查失败: %s

Error message

MCP 健康检查失败: %s

What it means

checkMCPHealth reports failure when the health-check HTTP request to the standalone MCP process succeeded at transport level but returned a non-2xx status code; the error carries the raw resp.Status (e.g. '503 Service Unavailable'). 2xx (200-299) is treated as healthy.

Source

Thrown at server/mcp/standalone_manager.go:266

	timeoutCtx, cancel := context.WithTimeout(ctx, mcpHealthCheckTimeout)
	defer cancel()

	req, err := http.NewRequestWithContext(timeoutCtx, http.MethodGet, ResolveMCPHealthURL(), nil)
	if err != nil {
		return false, err
	}

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return false, err
	}
	defer resp.Body.Close()

	if resp.StatusCode >= http.StatusOK && resp.StatusCode < http.StatusMultipleChoices {
		return true, nil
	}

	return false, fmt.Errorf("MCP 健康检查失败: %s", resp.Status)
}

func waitForManagedProcess(ctx context.Context, meta *managedProcessMeta) (ManagedStandaloneStatus, error) {
	deadline := time.NewTimer(mcpStartWaitTimeout)
	ticker := time.NewTicker(300 * time.Millisecond)
	defer deadline.Stop()
	defer ticker.Stop()

	for {
		select {
		case <-ctx.Done():
			return GetManagedStandaloneStatus(context.Background()), ctx.Err()
		case <-deadline.C:
			return GetManagedStandaloneStatus(context.Background()), fmt.Errorf("等待 MCP 独立服务启动超时,请查看日志: %s", meta.LogPath)
		case <-ticker.C:
			current := GetManagedStandaloneStatus(context.Background())
			if current.Reachable {
				return current, nil

View on GitHub (pinned to 3136500ef3)

Solutions

  1. Wait briefly and retry — transient 503 during startup is common; the caller waitForManagedProcess already retries on a 300ms ticker.
  2. Compare resp.Status in the error: 401/403 -> fix auth token; 404 -> health endpoint path changed; 5xx -> inspect the MCP log file.
  3. Confirm the health URL/port in GVA_MCP_CONFIG matches the standalone server's actual listener.
  4. If 5xx persists, check MCP logs for panics or failed dependency init.

Example fix

// defensive polling wrapper
func healthyEventually(url string, within time.Duration) error {
    deadline := time.Now().Add(within)
    for time.Now().Before(deadline) {
        ok, err := checkMCPHealth(url)
        if err == nil && ok { return nil }
        time.Sleep(300 * time.Millisecond)
    }
    return fmt.Errorf("health check still failing after %s", within)
}
Defensive patterns

Strategy: retry

Validate before calling

// pre-validate the health endpoint manually
resp, err := http.Get(healthURL)
if err != nil { return err }
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
    return fmt.Errorf("health endpoint returned %s", resp.Status)
}

Try / catch

ok, err := checkMCPHealth(url)
if err != nil && strings.Contains(err.Error(), "503") {
    time.Sleep(500 * time.Millisecond) // still initializing; retry
    ok, err = checkMCPHealth(url)
}

Prevention

When it happens

Trigger: GET to the MCP standalone health endpoint returns 401/404/429/5xx: process still initializing, auth token mismatch, wrong health route, or the process is overloaded/degraded.

Common situations: Polling during startup before the HTTP server is fully ready; health endpoint path changed between versions; reverse proxy in front returning 502/503; rate limiting returning 429.

Related errors


AI-assisted analysis of flipped-aurora/gin-vue-admin@3136500ef3 (2026-08-31). Data as JSON: /api/errors/b9bbde8ecab07cbd. Report an issue: GitHub.