How the Summarize Chrome Extension Communicates with the Local Daemon

The Summarize Chrome extension communicates with its local daemon via HTTP requests to http://127.0.0.1:8787, using REST endpoints for health checks and content extraction, and Server-Sent Events (SSE) for streaming summaries.

The steipete/summarize repository implements a Chrome extension that relies on a locally running daemon for content processing. Understanding how the Chrome extension communicates with the local daemon reveals a clean HTTP-based architecture that avoids native messaging in favor of standard web protocols. This implementation uses standard fetch requests against localhost to handle authentication, content extraction, and real-time summarization.

HTTP-Based Communication Architecture

The extension’s background script (apps/chrome-extension/src/entrypoints/background.ts) initiates all communication over standard HTTP to http://127.0.0.1:8787. Unlike extensions that use Chrome's native messaging API, this architecture relies entirely on REST endpoints and Server-Sent Events, keeping the communication within the browser process while targeting the local daemon.

All requests originate from the background service worker and are directed at three primary endpoints:

  • GET /health – Liveness checks
  • GET /v1/ping – Authentication validation
  • POST /v1/summarize – Content extraction and summarization

Health Checks and Authentication

Before processing any content, the extension verifies daemon availability and validates authentication tokens.

Verifying Daemon Availability

The extension calls daemonHealth() to send a GET request to /health. This checks whether the daemon is running and responsive.

// apps/chrome-extension/src/entrypoints/background.ts
const health = await daemonHealth();  // GET http://127.0.0.1:8787/health

if (!health.ok) {
  // UI shows "Daemon unreachable" state
}

Token Authentication

When a bearer token is configured, the extension performs an additional validation via daemonPing(), which sends a GET request to /v1/ping with the Authorization: Bearer header.

// apps/chrome-extension/src/entrypoints/background.ts
const auth = token ? await daemonPing(token) : Promise.resolve({ ok: false });
// GET /v1/ping with Authorization: Bearer <token>

if (!auth.ok) {
  // Handle authentication failure
}

Content Extraction Requests

For pages requiring transcript fetching or full-page scraping, the extension sends a POST request to /v1/summarize with extractOnly: true. The buildSummarizeRequestBody() function in apps/chrome-extension/src/lib/daemon-payload.ts constructs these payloads.

// apps/chrome-extension/src/lib/daemon-payload.ts
import { buildSummarizeRequestBody } from "../lib/daemon-payload";

const body = buildSummarizeRequestBody({
  extracted,          // Page or video data collected earlier
  settings,           // User-chosen model, length, token, etc.
  noCache: false,
  inputMode: "video",
  timestamps: true,
  slides: { 
    enabled: true, 
    ocr: true, 
    maxSlides: 20, 
    minDurationSeconds: 5 
  },
});

This payload structure allows the daemon to handle various content types—whether extracting YouTube transcripts, scraping article text, or processing local files—before returning structured data to the extension.

Streaming Summarization with Server-Sent Events

When generating summaries, the extension posts to the same /v1/summarize endpoint with a richer payload including model selection, length preferences, and slide settings. The daemon returns a Server-Sent Events (SSE) stream rather than a single JSON response.

The parseSseStream() generator in apps/chrome-extension/src/lib/sse.ts consumes this stream, parsing each event:data chunk and forwarding incremental updates to the side-panel UI.

// apps/chrome-extension/src/entrypoints/background.ts
const res = await fetch("http://127.0.0.1:8787/v1/summarize", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${settings.token.trim()}`,
    "content-type": "application/json",
  },
  body: JSON.stringify(body),
  signal: controller.signal,
});

// Parse SSE stream from daemon
for await (const { event, data } of parseSseStream(res.body!)) {
  if (event === "message") {
    // Forward partial summary to side-panel UI
    send(session, { 
      type: "agent:chunk", 
      requestId, 
      text: data 
    });
  }
}

The SSE implementation allows the UI to display summary text as it is generated, rather than waiting for the entire completion, creating a responsive user experience for long-form content.

Recovery and Retry Logic

Network failures or daemon unavailability are handled by apps/chrome-extension/src/lib/daemon-recovery.ts. The createDaemonRecovery() helper records failed URLs and evaluates whether conditions are right for automatic retry when the daemon becomes healthy again.

// apps/chrome-extension/src/lib/daemon-recovery.ts
import { createDaemonRecovery } from "../lib/daemon-recovery";

const recovery = createDaemonRecovery();
recovery.recordFailure(url);  // Called when request errors

if (recovery.maybeRecover({ 
  isReady, 
  currentUrlMatches, 
  isIdle 
})) {
  // Automatically retry summarization
}

This recovery mechanism ensures that transient daemon restarts or network hiccups do not require manual user intervention to resume pending summarization tasks.

Summary

  • HTTP Protocol: The extension uses standard fetch requests to http://127.0.0.1:8787 rather than Chrome's native messaging API
  • Health Verification: daemonHealth() checks /health and daemonPing() validates tokens via /v1/ping before processing
  • Payload Construction: buildSummarizeRequestBody() in daemon-payload.ts assembles request bodies for both extraction and summarization modes
  • Streaming Responses: The daemon returns SSE streams consumed by parseSseStream() in lib/sse.ts for real-time UI updates
  • Fault Tolerance: createDaemonRecovery() tracks failures and enables automatic retry when the daemon returns to a healthy state

Frequently Asked Questions

What port does the Summarize daemon use?

The daemon listens on port 8787 by default. The extension hardcodes http://127.0.0.1:8787 as the base URL for all daemon communication in the background script.

Does the extension use Chrome's native messaging API?

No. According to the source code in apps/chrome-extension/src/entrypoints/background.ts, all communication uses standard HTTP fetch requests to localhost. This design choice simplifies the architecture by treating the local daemon as a standard web service rather than using Chrome's native messaging protocol.

How does the extension handle streaming responses?

The extension consumes Server-Sent Events (SSE) using the parseSseStream() generator function defined in apps/chrome-extension/src/lib/sse.ts. This async generator yields parsed events from the daemon's response stream, allowing the background script to forward incremental summary chunks to the side-panel UI as they are generated.

What happens if the daemon is offline when I request a summary?

If the daemon is unreachable, the recordFailure() method in apps/chrome-extension/src/lib/daemon-recovery.ts logs the failed request. When the daemon becomes available again (verified by subsequent health checks), the maybeRecover() function evaluates whether to automatically retry the summarization based on the current UI state and URL matching.

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 →