How OpenWork Manages Background Tasks: Diagnostic Tracking and Graceful Shutdown

OpenWork manages background tasks through a centralized diagnostic tracker that monitors async loop phases, classifies errors with structured metadata, and ensures graceful shutdown via abort signals and lifecycle hooks.

OpenWork, an open-source enterprise platform from different-ai/openwork, relies on long-running background processes like MCP diagnostic polling and server-side event listeners to maintain system health. These asynchronous operations are managed through a robust diagnostic tracker pattern implemented in the ExternalMcpDiagnosticTracker class, which provides observability and error resilience across the codebase.

The Three Pillars of Background Task Management

OpenWork's background task architecture rests on three core concepts that ensure reliability and observability.

Centralized Diagnostic Tracking with ExternalMcpDiagnosticTracker

At the heart of the system is the ExternalMcpDiagnosticTracker class, defined in packages/enterprise-mcp-client/src/capability-sources/external-mcp-diagnostics.ts. This tracker records the current phase of a background operation, captures any errors that occur, and stores a unique referenceId that can be displayed to users in UI error banners.

The tracker's captureBackgroundFailure method serves as the primary entry point for error handling. When a background task throws an exception, this method immediately classifies the error and stores a structured ExternalMcpDiagnosticError object that includes safe-cause chains, HTTP status codes, and provider-declared error evidence.

Graceful Lifecycle Hooks and Abort Signals

Background tasks are orchestrated through explicit lifecycle management in the Den API server. The system launches async loops during startServer and shuts them down cleanly via stopBackgroundLoops, located in ee/apps/den-api/src/server.ts.

The loops themselves are ordinary async functions that accept an AbortSignal and periodically check abortSignal.aborted to determine when to terminate. During shutdown, stopBackgroundLoops() iterates over all running loops, awaits their completion, and logs any failures via appLogger.error("background loop shutdown failed",…).

Structured Error Classification

Raw exceptions are transformed into user-friendly diagnostics through the classifyError function. This logic maps arbitrary errors into structured Classification objects that include phase context, error category, and retryability flags. The classification logic resides in the same diagnostic module as the tracker, ensuring consistent error handling across all background operations.

The Background Task Execution Flow

Background tasks in OpenWork follow a strict lifecycle that maximizes observability and minimizes unhandled rejections.

Initialization and Phase Declaration

When the server starts, it creates an ExternalMcpDiagnosticTracker instance for each logical background job, such as an "MCP tool-catalog poll". The tracker initializes in the CONFIGURATION phase and generates a unique referenceId. Before performing work, the task calls tracker.begin(phase) to announce the current sub-phase (e.g., MCP_TOOL_DISCOVERY) to the monitoring system.

Success Tracking and Health Monitoring

Upon successful completion of an operation, the task invokes tracker.passed(phase, healthLevel), passing a health level indicator such as "catalog_ready". This updates the diagnostic state and confirms the task is functioning correctly, allowing the UI to display healthy status indicators.

Failure Capture and User-Friendly Diagnostics

If the async work throws an exception, the catch-block immediately calls tracker.captureBackgroundFailure(phase, err). The tracker automatically classifies the error using the internal classifyError switch statement, creating an ExternalMcpDiagnosticError with structured metadata. This prevents raw stack traces from reaching users while preserving diagnostic detail for operators.

Implementation Example: Creating a Diagnostic-Tracked Polling Loop

The following pattern demonstrates how to implement a robust background polling loop using OpenWork's diagnostic infrastructure:

import { ExternalMcpDiagnosticTracker } from "@openwork/enterprise-mcp-client";

// Create a tracker for this background task
const pollTracker = new ExternalMcpDiagnosticTracker("poll-mcp-tools");

// The actual loop – runs until `abortSignal` is triggered
async function pollMcpTools(abortSignal: AbortSignal) {
  while (!abortSignal.aborted) {
    pollTracker.begin("MCP_TOOL_DISCOVERY");
    try {
      const catalog = await fetchToolCatalog();          // ← network request
      pollTracker.passed("MCP_TOOL_DISCOVERY", "catalog_ready");
    } catch (err) {
      // Any failure is captured and classified automatically
      pollTracker.captureBackgroundFailure("MCP_TOOL_DISCOVERY", err);
      // Optionally break out if the error is not retryable
      if (!isRetryable(err)) break;
    }
    await delay(30_000); // wait 30 s before the next poll
  }
}

// Register the loop with the server’s background‑loop manager
startBackgroundLoop(pollMcpTools);

The tracker automatically enriches the diagnostic object with HTTP status codes and provider-specific error evidence, making it available for UI rendering or automated retry logic.

Key Source Files in the OpenWork Repository

File Role
packages/enterprise-mcp-client/src/capability-sources/external-mcp-diagnostics.ts Core tracker class, classifyError logic, and background-failure helpers used by both client and server
ee/apps/den-api/src/capability-sources/external-mcp-diagnostics.ts Server-specific diagnostic module imports and implementations
ee/apps/den-api/src/server.ts Server bootstrap logic, startServer initialization, and stopBackgroundLoops shutdown orchestration

Summary

  • OpenWork treats every long-running process as a diagnostic-tracked background task using the ExternalMcpDiagnosticTracker class.
  • Phase-aware monitoring via begin() and passed() methods provides granular visibility into task execution states.
  • Automatic error classification transforms raw exceptions into structured ExternalMcpDiagnosticError objects suitable for UI display and operational logging.
  • Graceful shutdown is achieved through AbortSignal propagation and the stopBackgroundLoops() lifecycle hook in the Den API server.
  • File locations: Core logic resides in packages/enterprise-mcp-client/src/capability-sources/external-mcp-diagnostics.ts and server orchestration in ee/apps/den-api/src/server.ts.

Frequently Asked Questions

How does OpenWork prevent unhandled promise rejections in background tasks?

OpenWork wraps all background operations in try-catch blocks that route exceptions through tracker.captureBackgroundFailure(). This ensures every error is classified, stored with a referenceId, and surfaced to the UI as a structured diagnostic rather than an unhandled rejection or raw stack trace.

What information does the ExternalMcpDiagnosticTracker store?

The tracker stores the current operational phase, a unique referenceId for UI correlation, health level indicators, and structured error classifications. It automatically captures HTTP status codes, safe-cause chains, and provider-declared error evidence when failures occur.

How are background tasks shut down when the server receives SIGTERM?

The server invokes stopBackgroundLoops(), which iterates over all registered async loops and awaits their completion. Each loop monitors an AbortSignal, allowing cooperative cancellation. Any shutdown failures are logged via appLogger.error() before the process exits.

Where is the error classification logic defined?

The classifyError function and ExternalMcpDiagnosticError class are defined in packages/enterprise-mcp-client/src/capability-sources/external-mcp-diagnostics.ts. This module contains the switch-based classification logic that maps raw exceptions to structured diagnostic objects with retryability flags and category metadata.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →