Skip to content

add-a-simple-health-endpoint-to-the-api: add GET /health endpoint#617

Open
happyforever006 wants to merge 1 commit intobreaking-brake:mainfrom
happyforever006:feat/add-a-simple-health-endpoint-to-the-api
Open

add-a-simple-health-endpoint-to-the-api: add GET /health endpoint#617
happyforever006 wants to merge 1 commit intobreaking-brake:mainfrom
happyforever006:feat/add-a-simple-health-endpoint-to-the-api

Conversation

@happyforever006
Copy link

@happyforever006 happyforever006 commented Feb 28, 2026

Summary

  • Added HealthServerService (src/extension/services/health-server-service.ts) that starts a lightweight Node.js HTTP server on port 3456 when the extension activates
  • GET /health returns HTTP 200 with a JSON body: { status, name, version, timestamp }
  • All other paths return HTTP 404
  • The server is started in extension.ts after all commands are registered (fire-and-forget, non-fatal on port conflicts)
  • The server is stopped cleanly in deactivate()

Example response

```json
{
"status": "ok",
"name": "cc-wf-studio",
"version": "3.12.3",
"timestamp": "2026-02-28T10:00:00.000Z"
}
```

Test plan

  • Activate the extension and run curl http://127.0.0.1:3456/health — expect 200 OK with JSON body
  • Request any other path (e.g. curl http://127.0.0.1:3456/) — expect 404 Not Found
  • Deactivate the extension — server should stop and port should be released
  • Verify the extension still activates normally if port 3456 is already in use (warning logged, activation continues)
  • Build passes: npm run format && npm run lint && npm run check && npm run build:extension

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Extension now provides a health monitoring endpoint for observability and CI/CD integration, exposing extension status, name, version, and timestamp information. The endpoint operates on a configurable port (default 3456) to facilitate integration with monitoring systems.

- Added HealthServerService that starts a lightweight HTTP server on port 3456
- GET /health returns 200 with JSON: status, name, version, timestamp
- Server starts on extension activation and stops on deactivation
- Non-fatal: port conflicts are logged as warnings without blocking activation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link

coderabbitai bot commented Feb 28, 2026

📝 Walkthrough

Walkthrough

A new HealthServerService is introduced to expose a lightweight HTTP /health endpoint returning extension status, name, version, and timestamp. The service is integrated into the extension's activation and deactivation lifecycle, starting on activation and stopping on deactivation with graceful error handling for non-fatal port conflicts.

Changes

Cohort / File(s) Summary
Health Server Service
src/extension/services/health-server-service.ts
New module implementing HealthServerService class and HealthResponse interface. Exposes /health endpoint returning JSON with extension metadata; all other routes return 404. Includes start/stop lifecycle methods and request handling with logging.
Extension Lifecycle Integration
src/extension/extension.ts, package.json
Imports and instantiates HealthServerService; starts server on extension activation (non-fatal if port in use) and stops it on deactivation. Adds logging for health server startup/shutdown events alongside existing cleanup.

Sequence Diagram

sequenceDiagram
    participant Ext as Extension<br/>(Activation)
    participant HS as HealthServerService
    participant Server as HTTP Server
    participant Client as Health Check<br/>Client

    Ext->>HS: new HealthServerService(name, version, port)
    Ext->>HS: start()
    HS->>Server: http.createServer()
    HS->>Server: listen(port)
    Server->>HS: listening event
    HS->>Ext: Promise resolved
    
    Client->>Server: GET /health
    Server->>HS: handleRequest()
    HS->>HS: handleHealth()
    HS->>Client: 200 JSON {status, name, version, timestamp}
    
    Ext->>HS: stop()
    HS->>Server: close()
    Server->>HS: closed
    HS->>Ext: Promise resolved
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A health check hops into place,
Monitoring the extension's pace,
With heartbeat steady, status clear,
The server whispers: "I am here!"
~/health/ rings out with glee,
As rabbits ensure all runs free! 🏥✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change—adding a GET /health endpoint to the extension via a new HealthServerService, which aligns with the changeset's primary objective.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/extension/services/health-server-service.ts (1)

125-141: Consider reducing log verbosity for health checks.

Line 140 logs every health check request at INFO level. If monitoring tools poll frequently (e.g., every 5-10 seconds), this will generate significant log noise.

Consider either removing this log, changing it to DEBUG level (if available), or rate-limiting the log output.

🔧 Suggested fix to reduce log verbosity
     res.end(json);
-
-    log('INFO', 'Health check request served');
   }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/extension/services/health-server-service.ts` around lines 125 - 141, The
health-check handler handleHealth currently logs every request with log('INFO',
...) causing noise; reduce verbosity by either removing that log call or
lowering it to a debug level (e.g., log('DEBUG', ...)) and, if your logger
supports it, wrap it with a guard like isDebugEnabled() before calling;
alternatively implement simple rate-limiting/throttling around the log
invocation (track lastLogged timestamp inside the HealthServerService) so
handleHealth only emits a log at most once per configurable interval.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/extension/extension.ts`:
- Around line 244-254: The deactivate() function currently returns void while
calling the async healthServer?.stop() fire-and-forget; change deactivate to
return a Thenable<void> (or async Promise<void>) so you can await
healthServer.stop() before disposing resources: call and await (or return)
healthServer.stop() (handling rejection and logging the error with the
still-open outputChannel), only then call outputChannel?.dispose() and null
assignments; reference the deactivate function and healthServer.stop() and
outputChannel.dispose() when making the change and ensure you propagate the
stop() promise (or use Promise.resolve/Promise.all if adding other async
cleanup) so VS Code can wait for clean shutdown.

---

Nitpick comments:
In `@src/extension/services/health-server-service.ts`:
- Around line 125-141: The health-check handler handleHealth currently logs
every request with log('INFO', ...) causing noise; reduce verbosity by either
removing that log call or lowering it to a debug level (e.g., log('DEBUG', ...))
and, if your logger supports it, wrap it with a guard like isDebugEnabled()
before calling; alternatively implement simple rate-limiting/throttling around
the log invocation (track lastLogged timestamp inside the HealthServerService)
so handleHealth only emits a log at most once per configurable interval.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 36f5998 and e895382.

📒 Files selected for processing (2)
  • src/extension/extension.ts
  • src/extension/services/health-server-service.ts

Comment on lines 244 to 254
export function deactivate(): void {
log('INFO', 'Claude Code Workflow Studio is now deactivated');

// Stop health server if running
healthServer?.stop().catch((err: Error) => {
log('WARN', 'Health server failed to stop cleanly', { error: err.message });
});
healthServer = null;

outputChannel?.dispose();
outputChannel = null;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Async cleanup may not complete before extension fully deactivates.

The deactivate() function returns void but healthServer?.stop() is async. VS Code supports returning Thenable<void> from deactivate() to allow proper async cleanup. Currently:

  1. The stop() promise is fire-and-forget
  2. outputChannel?.dispose() may execute before the server finishes stopping
  3. Any logs from stop() could fail if outputChannel is already disposed

Consider returning the promise to ensure clean shutdown.

🔧 Suggested fix for proper async cleanup
 /**
  * Extension deactivation function
  * Called when the extension is deactivated
  */
-export function deactivate(): void {
+export async function deactivate(): Promise<void> {
   log('INFO', 'Claude Code Workflow Studio is now deactivated');

   // Stop health server if running
-  healthServer?.stop().catch((err: Error) => {
-    log('WARN', 'Health server failed to stop cleanly', { error: err.message });
-  });
+  if (healthServer) {
+    try {
+      await healthServer.stop();
+    } catch (err) {
+      log('WARN', 'Health server failed to stop cleanly', { 
+        error: err instanceof Error ? err.message : String(err) 
+      });
+    }
+  }
   healthServer = null;

   outputChannel?.dispose();
   outputChannel = null;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/extension/extension.ts` around lines 244 - 254, The deactivate() function
currently returns void while calling the async healthServer?.stop()
fire-and-forget; change deactivate to return a Thenable<void> (or async
Promise<void>) so you can await healthServer.stop() before disposing resources:
call and await (or return) healthServer.stop() (handling rejection and logging
the error with the still-open outputChannel), only then call
outputChannel?.dispose() and null assignments; reference the deactivate function
and healthServer.stop() and outputChannel.dispose() when making the change and
ensure you propagate the stop() promise (or use Promise.resolve/Promise.all if
adding other async cleanup) so VS Code can wait for clean shutdown.

@breaking-brake
Copy link
Owner

Thanks for the PR! Before diving into a full review, a couple of questions:

  1. What's the use case? Could you share the specific scenario where an HTTP health endpoint is needed for a VSCode extension? Typically, extension lifecycle is managed by VSCode itself, so it'd be helpful to understand who or what would be polling this endpoint.
  2. Security consideration: Opening an HTTP server on a fixed port (3456) increases the extension's attack surface (e.g., DNS rebinding). What are your thoughts on mitigating this risk?

Thanks!

@breaking-brake breaking-brake added enhancement New feature or request and removed enhancement New feature or request labels Mar 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants