Skip to content

feat: add MCP state-change callback and degraded cluster-aggregate state with per-instance drill-down - #5920

Merged
Pratham-Mishra04 merged 1 commit into
devfrom
08-06-feat_add_mcp_client_state-change_callback_hook
Aug 8, 2026
Merged

feat: add MCP state-change callback and degraded cluster-aggregate state with per-instance drill-down#5920
Pratham-Mishra04 merged 1 commit into
devfrom
08-06-feat_add_mcp_client_state-change_callback_hook

Conversation

@Pratham-Mishra04

Copy link
Copy Markdown
Collaborator

Summary

Adds a stateChangeCallback hook to MCPManager that fires on every reactive (non-admin-driven) MCP client connection-state transition, and introduces a degraded aggregate state for distributed deployments where multiple instances report conflicting states for the same client.

Changes

  • State-change callback: A new stateChangeCallback field on MCPManager is invoked whenever a client's state changes through ClientConnectionChecker.setState (periodic checker transitions) or failConnectAttempt (reactive connect-failure classification into NeedsReauth). Deliberately excluded from admin-driven transitions (CloseAndMarkNeedsReauth, DisableClient/EnableClient, UpdateClient) since those already have their own call sites. The callback is always fired outside the manager lock to avoid holding m.mu across arbitrary caller-supplied work.
  • SetMCPStateChangeCallback on Bifrost: Exposes the callback registration through the top-level Bifrost type, no-op when MCP is not configured.
  • MCPConnectionStateDegraded: A new read-time aggregate MCPConnectionState value ("degraded") that is never a node's own local state — it signals that instances in a distributed deployment currently disagree about a client's state. Single-instance deployments never produce it.
  • MCPClusterStateAggregator interface: An optional interface the configured MCPManager may implement. When present, getMCPClientsPaginated calls AggregateMCPClientState and, if the result is Degraded, replaces the response state and attaches a node_states per-instance breakdown map.
  • MCPClientResponse.NodeStates: New optional field (node_states) on the HTTP response carrying the per-instance state breakdown when state is "degraded".
  • UI — StateBadge component: Replaces the inline <Badge> in the MCP clients table with a StateBadge that renders a plain badge for all states except "degraded", which additionally gets a hover Popover drill-down. summarizeNodeStates groups instance IDs by their reported state so the drill-down reads as counts ("2 instances: Healthy, 1 instance: Unstable") rather than a raw ID list.
  • UI — degraded color token: Added degraded: "bg-blue-100 text-blue-800" to MCP_STATUS_COLORS — a distinct blue/purple rather than amber/red to signal "instances disagree" rather than "one instance is unhealthy."
  • Tests: Added TestFailConnectAttempt_NeedsReauthTransition_FiresStateChangeCallback, TestFailConnectAttempt_NoStateChange_DoesNotFireCallback, and TestSetState_FiresStateChangeCallbackOnGenuineTransition covering genuine transitions firing the callback exactly once and no-op transitions not firing it.

Type of change

  • Bug fix
  • Feature
  • Refactor
  • Documentation
  • Chore/CI

Affected areas

  • Core (Go)
  • Transports (HTTP)
  • Providers/Integrations
  • Plugins
  • UI (React)
  • Docs

How to test

# Core/Transports
go test ./core/mcp/... ./core/...

# UI
cd ui
pnpm i
pnpm build

To exercise the degraded state manually, implement MCPClusterStateAggregator on a custom MCPManager that returns MCPConnectionStateDegraded with a populated nodeStates map for a given client, register it, and call GET /api/mcp/clients. The response for that client should include "state": "degraded" and a populated node_states object. In the UI, hovering the "Degraded" badge should show the per-instance breakdown grouped by state.

Screenshots/Recordings

The "Degraded" badge renders in blue/purple and, on hover, shows a popover listing instance counts per state (e.g. "2 instances: Healthy, 1 instance: Unstable").

Breaking changes

  • Yes
  • No

Related issues

Security considerations

The stateChangeCallback is caller-supplied and may perform arbitrary work including I/O. It is always invoked outside m.mu to prevent lock contention or deadlocks. No secrets or PII are passed through the callback — only client ID, name, and state enum values.

Checklist

  • I read docs/contributing/README.md and followed the guidelines
  • I added/updated tests where appropriate
  • I updated documentation where needed
  • I verified builds succeed (Go and UI)
  • I verified the CI pipeline passes locally if applicable

This was referenced Aug 6, 2026

Pratham-Mishra04 commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added reactive notifications for MCP connection-state changes.
    • Added Needs Reauthorization and Degraded connection states.
    • MCP client responses now include per-instance node states when connections are degraded.
    • Updated MCP client status displays with badges and hover details summarizing distributed connection states.
  • Bug Fixes

    • Prevented duplicate notifications when a connection state remains unchanged.
  • Tests

    • Added coverage for genuine state transitions and callback behavior.

Walkthrough

The PR adds MCP connection-state callbacks for actual transitions. It also adds the distributed degraded state, per-node state aggregation in HTTP responses, and UI popovers showing node-state counts.

Changes

MCP state observability

Layer / File(s) Summary
Callback registration
core/mcp/interface.go, core/mcp/mcp.go, core/bifrost.go
Bifrost and MCP manager APIs now register or clear state-change callbacks.
Transition notifications and tests
core/mcp/clientmanager.go, core/mcp/connectionchecker.go, core/mcp/*_test.go
Callbacks run once for actual state changes after mutex release. Tests cover NeedsReauth, Healthy, Unstable, and unchanged states.

Distributed MCP state reporting

Layer / File(s) Summary
Aggregation and UI presentation
core/schemas/mcp.go, transports/bifrost-http/handlers/mcp.go, ui/lib/types/mcp.ts, ui/lib/constants/config.ts, ui/app/workspace/mcp-registry/views/mcpClientsTable.tsx
HTTP responses can expose aggregated degraded state and per-node states. The UI displays node-state counts in a popover.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ClientConnectionChecker
  participant MCPManager
  participant StateChangeCallback
  ClientConnectionChecker->>MCPManager: Report a connection-state transition
  MCPManager->>StateChangeCallback: Send client identifiers and old/new states
Loading
sequenceDiagram
  participant HTTPHandler
  participant MCPClusterStateAggregator
  participant MCPRegistryUI
  HTTPHandler->>MCPClusterStateAggregator: AggregateMCPClientState(clientID, localState)
  MCPClusterStateAggregator-->>HTTPHandler: Return aggregate state and node states
  HTTPHandler-->>MCPRegistryUI: Return MCPClientResponse
  MCPRegistryUI->>MCPRegistryUI: Render degraded state popover
Loading

Possibly related PRs

Suggested reviewers: akshaydeo, danpiths

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the callback, degraded aggregate state, and per-instance drill-down introduced by the pull request.
Description check ✅ Passed The description follows the template, explains the design, testing, affected areas, security, and breaking-change status; empty related issues is non-critical.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 08-06-feat_add_mcp_client_state-change_callback_hook

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

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@core/mcp/clientmanager.go`:
- Around line 2185-2201: Preserve callback delivery order by routing committed
state-transition events through one bounded, lifecycle-safe ordered dispatcher
instead of invoking callbacks directly after unlock. Update the
failure-transition path around clientmanager.go:2185-2201 to enqueue its event
in commit order, and update the checker-transition path around
connectionchecker.go:377-386 to use the same dispatcher; both files require
changes. Keep shared state race-safe and add a concurrent test covering Healthy
→ Unstable → NeedsReauth that verifies observer order.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: be253bc7-2f8c-4a37-a7f2-7b143dcc0696

📥 Commits

Reviewing files that changed from the base of the PR and between ad8cffb and 569aa6e.

📒 Files selected for processing (12)
  • core/bifrost.go
  • core/mcp/clientmanager.go
  • core/mcp/clientmanager_test.go
  • core/mcp/connectionchecker.go
  • core/mcp/connectionchecker_test.go
  • core/mcp/interface.go
  • core/mcp/mcp.go
  • core/schemas/mcp.go
  • transports/bifrost-http/handlers/mcp.go
  • ui/app/workspace/mcp-registry/views/mcpClientsTable.tsx
  • ui/lib/constants/config.ts
  • ui/lib/types/mcp.ts

Comment thread core/mcp/clientmanager.go
@Pratham-Mishra04
Pratham-Mishra04 force-pushed the 08-06-feat_add_mcp_client_state-change_callback_hook branch from 569aa6e to c80bf09 Compare August 6, 2026 21:53
@Pratham-Mishra04
Pratham-Mishra04 force-pushed the 08-06-fix_prevent_concurrent_oauth_token_refreshes_from_clobbering_each_other_via_a_refresh_token_cas_guard branch from ad8cffb to b4ccbdd Compare August 6, 2026 21:53
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

coderabbitai[bot]
coderabbitai Bot previously approved these changes Aug 6, 2026
@Pratham-Mishra04
Pratham-Mishra04 force-pushed the 08-06-feat_add_mcp_client_state-change_callback_hook branch from c80bf09 to a096a8a Compare August 8, 2026 10:29
@Pratham-Mishra04
Pratham-Mishra04 force-pushed the 08-06-fix_prevent_concurrent_oauth_token_refreshes_from_clobbering_each_other_via_a_refresh_token_cas_guard branch from b4ccbdd to 2c5dab2 Compare August 8, 2026 10:29

Pratham-Mishra04 commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator Author

Merge activity

  • Aug 8, 10:33 AM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Aug 8, 11:28 AM UTC: Graphite rebased this pull request as part of a merge.
  • Aug 8, 11:29 AM UTC: @Pratham-Mishra04 merged this pull request with Graphite.

@Pratham-Mishra04
Pratham-Mishra04 changed the base branch from 08-06-fix_prevent_concurrent_oauth_token_refreshes_from_clobbering_each_other_via_a_refresh_token_cas_guard to graphite-base/5920 August 8, 2026 11:24
@Pratham-Mishra04
Pratham-Mishra04 changed the base branch from graphite-base/5920 to dev August 8, 2026 11:27
@Pratham-Mishra04
Pratham-Mishra04 dismissed coderabbitai[bot]’s stale review August 8, 2026 11:27

The base branch was changed.

@Pratham-Mishra04
Pratham-Mishra04 requested a review from a team as a code owner August 8, 2026 11:27
@Pratham-Mishra04
Pratham-Mishra04 force-pushed the 08-06-feat_add_mcp_client_state-change_callback_hook branch from a096a8a to f951789 Compare August 8, 2026 11:27
@Pratham-Mishra04
Pratham-Mishra04 merged commit 35163d1 into dev Aug 8, 2026
14 of 15 checks passed
@Pratham-Mishra04
Pratham-Mishra04 deleted the 08-06-feat_add_mcp_client_state-change_callback_hook branch August 8, 2026 11:29
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