Skip to content

feat: send structured logging messages to MCP clients - #510

Merged
mergify[bot] merged 2 commits into
mainfrom
feat/499-structured-client-logging
Jul 10, 2026
Merged

feat: send structured logging messages to MCP clients#510
mergify[bot] merged 2 commits into
mainfrom
feat/499-structured-client-logging

Conversation

@charliecreates

@charliecreates charliecreates Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Advertise MCP logging support during server initialization.
  • Inject a best-effort MCP client logger without coupling API operations to notification delivery.
  • Emit structured retry, rate-limit, API error, and exercise-template cache events with sanitized context.
  • Preserve existing stderr observability alongside client logging notifications.

Test plan

  • npm run check
  • npm run check:types
  • npm run build
  • npx vitest run --exclude tests/integration/** (21 files / 308 tests)
  • npm run check:changeset
  • git diff --check

Includes the non-empty minor changeset .changeset/calm-coins-log.md.

Resolves #499

✨ PR Description

Purpose: Implement structured logging infrastructure to send MCP client notifications for Hevy API request failures, retries, rate limits, and cache operations.

Main changes:

  • Created createMcpClientLogger utility providing fire-and-forget logging to MCP clients with connection state validation and error isolation
  • Instrumented hevyClientKubb to emit structured logs for API failures, retries, rate limits with endpoint redaction and categorized severity levels
  • Extended registerTemplateTools with optional logger parameter to emit structured logs on exercise template catalog refreshes with reason tracking

Generated by LinearB AI and added by gitStream.
AI-generated content may contain inaccuracies. Please verify before using.
💡 Tip: You can customize your AI Description using Guidelines Learn how

@codecov

codecov Bot commented Jul 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.98246% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 91.01%. Comparing base (6efb52b) to head (2f79244).

Files with missing lines Patch % Lines
src/utils/hevyClientKubb.ts 89.65% 0 Missing and 3 partials ⚠️
src/tools/templates.ts 83.33% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #510      +/-   ##
==========================================
+ Coverage   90.80%   91.01%   +0.20%     
==========================================
  Files          28       29       +1     
  Lines        1120     1168      +48     
  Branches      286      301      +15     
==========================================
+ Hits         1017     1063      +46     
  Misses         46       46              
- Partials       57       59       +2     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@gitstream-cm gitstream-cm Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✨ PR Review

The PR cleanly introduces structured MCP client logging with good separation of concerns, fire-and-forget semantics, and solid test coverage. One functional bug stands out where the logging logic inadvertently captures all mutation failures as error-level MCP log messages, which was clearly not the intent.

2 issues detected:

🐞 Bug - `!shouldRetryRequest` is true for ALL non-GET methods, so every POST/PUT/DELETE failure unconditionally emits an error-level MCP log, flooding clients with spurious errors for expected application responses like 400 or 409.

Details: The "Hevy API request failed without retry" error-level log is emitted for every failed request where shouldRetryRequest returns false. Since shouldRetryRequest returns false for all non-GET methods (POST, PUT, DELETE) regardless of error type, every mutation failure — including expected 400, 409 (conflict), and 422 validation errors — generates an "error" level MCP log notification to the client. This will produce misleading noise for normal application-level errors on write operations.

File: src/utils/hevyClientKubb.ts (333-344)

🐞 Bug - `refreshReason` is evaluated in the outer handler scope before `getOrFetch` is invoked, so concurrent calls or in-flight fetches can cause the logged reason to be computed from stale or irrelevant cache state.

Details: refreshReason is computed from exerciseTemplateCatalogCache.size === 0 before getOrFetch is called. If two concurrent search-exercise-templates calls arrive when the cache is empty, both compute refreshReason = "initial-load". Only one actually triggers the fetch callback (the cache deduplicates), but both closures capture the same label. More critically, if the cache is non-empty but TTL-expired and a refresh is in-flight from a previous call, a new call will compute refreshReason = "ttl-expired" even though its callback will never execute since getOrFetch returns the in-progress fetch. The reason logged will therefore not reflect the actual trigger for the specific refresh event.

File: src/tools/templates.ts (286-303)

Generated by LinearB AI and added by gitStream.
AI-generated content may contain inaccuracies. Please verify before using.
💡 Tip: You can customize your AI Review using Guidelines Learn how

@chrisdoc chrisdoc added the queue Add to Mergify merge queue label Jul 10, 2026
@chrisdoc

Copy link
Copy Markdown
Owner

@Mergifyio refresh

@mergify

mergify Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

refresh

✅ Pull request refreshed

@mergify mergify Bot added the queued label Jul 10, 2026
@mergify

mergify Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Merge Queue Status

  • Entered queue2026-07-10 10:32 UTC · Rule: default · triggered by rule Queue pull requests labeled 'queue'
  • 🚫 Left the queue2026-07-10 10:43 UTC · at 7dc6cdd35814b75a95637bf0910d7e09001f4b56

This pull request spent 10 minutes 7 seconds in the queue, with no time running CI.

Reason

Pull request #510 has been dequeued

Queue conditions are not satisfied:

  • -conflict [📌 queue requirement]

Hint

You should look at the reason for the failure and decide if the pull request needs to be fixed or if you want to requeue it.
If you do update this pull request, it will automatically be requeued once the queue conditions match again.
If you think this was a flaky issue instead, you can requeue the pull request, without updating it, by posting a @mergifyio queue comment.

Requeued — the merge queue status continues in this comment ↓.

@mergify mergify Bot added dequeued and removed queued labels Jul 10, 2026
@charliecreates

Copy link
Copy Markdown
Contributor Author

gitStream.cm is failing because this PR is currently conflicting with main; Mergify removed it from the queue because the -conflict requirement is not met.

I did not push a change because branch refresh and conflict resolution are outside this repair scope. Please resolve the conflict on feat/499-structured-client-logging, then requeue the PR if needed.

— charlied/pr-check-repair

@charliecreates

Copy link
Copy Markdown
Contributor Author

Conflict repair is blocked. I re-fetched this branch and the current main head; they no longer have a common Git ancestor, so Git cannot perform a merge-based branch update.

The branch is feat/499-structured-client-logging. Repair would require an unrelated-history merge, rebase, or recreating the branch, none of which is a safe mechanical conflict resolution for this daemon.

Please recreate or manually update the PR branch from current main while preserving the intended change, then push it for review.

— charlied/pr-merge-conflict-repair

@charliecreates

Copy link
Copy Markdown
Contributor Author

Charlie could not resolve this conflict safely.

  • Conflict: src/index.ts, src/index.test.ts, and src/tools/templates.ts overlap recent server registration and template-cache changes on main.
  • Attempted: refreshed the PR head and base, requested GitHub’s branch update, and evaluated a merge with main.
  • Blocker: retaining structured client logging requires choosing how it composes with the newer shared template cache and registration behavior.
  • Next: a maintainer should resolve these files against main and push the result.

— charlied/pr-merge-conflict-repair

@chrisdoc

Copy link
Copy Markdown
Owner

@Mergifyio rebase

@mergify

mergify Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

rebase

☑️ Nothing to do, the required conditions are not met

Details
  • -conflict [📌 rebase requirement]
  • -closed [📌 rebase requirement]
  • queue-position = -1 [📌 rebase requirement]
  • any of:
    • #commits-behind > 0 [📌 rebase requirement]
    • -linear-history [📌 rebase requirement]

@greptile-apps

greptile-apps Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR sends structured logging events to MCP clients. The main changes are:

  • Logging capability advertised during server initialization.
  • A best-effort MCP client logger shared by API and template tooling.
  • Structured Hevy API retry, rate-limit, and error events.
  • Structured exercise template cache refresh events.
  • A minor changeset for the new logging behavior.

Confidence Score: 4/5

This is close, but the cache refresh logging should be corrected before merging.

  • Warm-cache refreshes can still be logged as initial loads.
  • The API logging and MCP send path are guarded so logging failures should not change tool behavior.
  • The remaining issue is contained to cache refresh telemetry.

src/utils/exercise-template-catalog.ts

Important Files Changed

Filename Overview
src/utils/exercise-template-catalog.ts Adds refresh reason reporting for exercise template catalog reloads, but warm-cache refreshes can still be reported as initial loads.
src/tools/templates.ts Passes cache refresh events to structured logging and keeps logger failures isolated from tool behavior.
src/utils/hevyClientKubb.ts Adds sanitized structured logging for Hevy API retries, rate limits, and terminal failures.
src/utils/mcp-client-logger.ts Adds a fire-and-forget MCP client logger with connection checks and error isolation.
src/index.ts Advertises MCP logging support and injects the client logger into API and template registration.

Reviews (2): Last reviewed commit: "fix: resolve structured logging merge co..." | Re-trigger Greptile

Comment thread src/tools/templates.ts Outdated
Comment on lines +288 to +289
: exerciseTemplateCatalogCache.size === 0
? "initial-load"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Cache Refresh Reason Mislabels

When a warm cache entry expires or is invalidated by a concurrent refresh, size can be 0 before this call reaches getOrFetch. A concurrent refresh can then log initial-load even though the catalog was already loaded, which makes cache refresh telemetry report the wrong cause.

@mergify mergify Bot removed the dequeued label Jul 10, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Unit Test Results

  1 files   26 suites   1s ⏱️
358 tests 358 ✅ 0 💤 0 ❌
359 runs  359 ✅ 0 💤 0 ❌

Results for commit 2f79244.

@mergify mergify Bot added the queued label Jul 10, 2026
@mergify

mergify Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Merge Queue Status

This pull request spent 4 minutes 30 seconds in the queue, including 2 minutes 5 seconds running CI.

Required conditions to merge
  • check-success = "Build and test (Node 24.x)"
  • check-success = "Build and test (Node 26.x)"

mergify Bot added a commit that referenced this pull request Jul 10, 2026
@mergify
mergify Bot merged commit caf294d into main Jul 10, 2026
28 checks passed
@mergify
mergify Bot deleted the feat/499-structured-client-logging branch July 10, 2026 13:03
@mergify mergify Bot removed the queued label Jul 10, 2026
Comment on lines +78 to 82
: exerciseTemplateCatalogCache.size === 0
? "initial-load"
: "ttl-expired";

return exerciseTemplateCatalogCache.getOrFetch(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Refresh reason still mislabels

This still chooses the refresh reason from exerciseTemplateCatalogCache.size before getOrFetch decides why it needs to fetch. If the catalog was already loaded but the entry expires or is cleared before this call reaches the cache, size is 0, so the log reports initial-load for a warm-cache refresh. That makes the MCP cache event report the wrong cause. The reason needs to come from the cache decision path, or otherwise distinguish a first load from an expired or removed existing entry.

@github-actions github-actions Bot mentioned this pull request Jul 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-ai-review queue Add to Mergify merge queue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: Send structured logging messages to MCP clients

2 participants