Skip to content

Add interactive TUI dashboard for managing MCP servers#4680

Open
peppescg wants to merge 13 commits intomainfrom
issues/4370
Open

Add interactive TUI dashboard for managing MCP servers#4680
peppescg wants to merge 13 commits intomainfrom
issues/4370

Conversation

@peppescg
Copy link
Copy Markdown
Contributor

@peppescg peppescg commented Apr 8, 2026

Summary

Managing MCP servers through individual CLI commands requires remembering flags and switching between multiple terminal windows. An interactive dashboard lets users monitor, inspect, and operate servers from a single view.

  • Add thv tui command with a real-time terminal dashboard built on Bubble Tea
  • Workload list with status indicators, filtering, and auto-refresh
  • Tabbed panels: logs, server info, tools, proxy logs, and tool inspector
  • Tool inspector with form-based calling and JSON tree response viewer
  • Registry browser with search, detail view, and run-from-registry support
  • Log search with case-insensitive highlighting and match navigation
  • 130+ unit subtests covering pure functions across 8 test files
  • Only existing code touched: one line added to commands.go to register the subcommand

Closes #4370

Type of change

  • New feature

Test plan

  • Unit tests (task test)
  • Linting (task lint-fix)
  • Manual testing (describe below)

Manually verified: launching thv tui, browsing workloads, switching tabs, inspecting/calling tools, searching logs, browsing the registry, running a server from registry, and keyboard navigation including help overlay.

Changes

File Change
cmd/thv/app/tui.go New thv tui command entry point
cmd/thv/app/commands.go Register TUI subcommand (single line addition)
cmd/thv/app/ui/ Shared UI primitives: styles, spinner, help overlay, log handler
pkg/tui/model.go Core Bubble Tea model and state
pkg/tui/init.go Model initialization and initial commands
pkg/tui/update.go Main update loop and message dispatch
pkg/tui/update_navigation.go Keyboard navigation and tab switching
pkg/tui/update_inspector.go Tool inspector form and call logic
pkg/tui/update_registry.go Registry browsing and run-from-registry
pkg/tui/update_search.go Log search with highlighting
pkg/tui/view*.go View rendering (main, info, inspector, registry, status bar, helpers)
pkg/tui/actions.go Async commands (refresh, logs, tool calls)
pkg/tui/inspector.go Inspector form field management
pkg/tui/registry.go Registry data helpers
pkg/tui/json_tree.go Collapsible JSON tree for tool responses
pkg/tui/logformat.go Structured log line parsing
pkg/tui/*_test.go 8 test files with 130+ subtests

Does this introduce a user-facing change?

Yes. Users can now run thv tui to open an interactive terminal dashboard for monitoring and managing MCP servers. The dashboard provides real-time status, log viewing, tool inspection and calling, and registry browsing — all from a single terminal window.

Special notes for reviewers

  • This is a large PR because it introduces an entirely new feature surface area. All files except commands.go are new — no existing command logic or packages were modified.
  • The TUI is built on the Charmbracelet ecosystem:
    • Bubble Tea (bubbletea) — main TUI framework (already a dependency before this branch)
    • Bubbles (bubbles) — companion component library (text inputs, viewports, spinners, etc.)
    • Lip Gloss (lipgloss) — styling and layout (already a dependency before this branch)
    • charmbracelet/x/ansi — ANSI string width and truncation utilities
    • atotto/clipboard — clipboard support (the only non-Charmbracelet addition)
  • Unit tests cover pure functions (parsing, formatting, filtering, tree operations) but not the interactive Bubble Tea model itself.
  • Registry "run" spawns thv run as a subprocess rather than calling internal APIs directly, keeping the TUI decoupled from runtime internals.

Large PR Justification

This PR is large because it introduces an entirely self-contained new feature with no modifications to existing packages. All ~7000 lines are new files in pkg/tui/ and cmd/thv/app/ui/ — the only change to existing code is a single line in commands.go to register the subcommand. Splitting this into smaller PRs would either ship a non-functional intermediate state or require artificial scaffolding that would be immediately removed.

Generated with Claude Code

peppescg and others added 5 commits April 8, 2026 14:28
Add a terminal dashboard (thv tui) for managing MCP servers with:
- Real-time workload list with status indicators and filtering
- Tabbed panels for logs, server info, tools, proxy logs, and inspector
- Tool inspector with form-based calling and JSON tree response viewer
- Registry browser with search and detail view
- Log search with case-insensitive highlighting and match navigation
- Horizontal scrolling for long log lines
- Keyboard-driven navigation with contextual help overlay

The codebase is organized into focused files by area (inspector,
registry, search, navigation) with shared form field helpers and
sub-structs for clean state management.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Allow launching MCP servers directly from the registry browser. Press
'r' in the registry detail view to open a form with the workload name
and environment variable fields pre-populated from registry metadata.
Secret fields use password masking. On submit, the TUI spawns
'thv run' as a subprocess and refreshes the workload list on completion.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Truncate long tool descriptions in the inspector form to 2 lines with
an ellipsis and [i] hint to view the full text in the detail modal.
This prevents verbose MCP tool descriptions from pushing form fields
off screen.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sort environment variable keys alphabetically before rendering in the
server info panel. Go maps have non-deterministic iteration order,
which caused env vars to shuffle on every 5-second refresh cycle.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cover core logic functions with 130+ subtests across 8 test files:
registry (sanitize, filter), view helpers (wrap, truncate, count),
inspector (required set, field values, result formatting), logs (split,
diff), log formatting (parse, level style), JSON tree (parse, flatten,
collapse, roundtrip), search (highlight), and form helpers (navigation,
blur).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Large PR Detected

This PR exceeds 1000 lines of changes and requires justification before it can be reviewed.

How to unblock this PR:

Add a section to your PR description with the following format:

## Large PR Justification

[Explain why this PR must be large, such as:]
- Generated code that cannot be split
- Large refactoring that must be atomic
- Multiple related changes that would break if separated
- Migration or data transformation

Alternative:

Consider splitting this PR into smaller, focused changes (< 1000 lines each) for easier review and reduced risk.

See our Contributing Guidelines for more details.


This review will be automatically dismissed once you add the justification section.

@github-actions github-actions bot added the size/XL Extra large PR: 1000+ lines changed label Apr 8, 2026
@codecov
Copy link
Copy Markdown

codecov bot commented Apr 8, 2026

Codecov Report

❌ Patch coverage is 12.51364% with 2405 lines in your changes missing coverage. Please review.
✅ Project coverage is 65.99%. Comparing base (d04eb02) to head (8dc016b).
⚠️ Report is 9 commits behind head on main.

Files with missing lines Patch % Lines
pkg/tui/update_navigation.go 0.00% 321 Missing ⚠️
pkg/tui/view_inspector.go 0.00% 250 Missing ⚠️
pkg/tui/view_registry.go 9.92% 235 Missing and 1 partial ⚠️
pkg/tui/update.go 0.00% 229 Missing ⚠️
pkg/tui/update_inspector.go 0.00% 222 Missing ⚠️
pkg/tui/view.go 0.00% 191 Missing ⚠️
pkg/tui/view_statusbar.go 0.00% 183 Missing ⚠️
pkg/tui/update_registry.go 0.00% 135 Missing ⚠️
pkg/tui/update_search.go 21.12% 111 Missing and 1 partial ⚠️
pkg/tui/view_info.go 0.00% 81 Missing ⚠️
... and 12 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4680      +/-   ##
==========================================
- Coverage   68.84%   65.99%   -2.86%     
==========================================
  Files         508      528      +20     
  Lines       52604    55348    +2744     
==========================================
+ Hits        36215    36525     +310     
- Misses      13587    16012    +2425     
- Partials     2802     2811       +9     

☔ View full report in Codecov by Sentry.
📢 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.

Escape single quotes in buildCurlStr to prevent shell injection when
the generated curl command is pasted into a terminal (CodeQL finding).

Cap log buffers at 10k lines to prevent unbounded memory growth during
long-running sessions. Bound horizontal scroll to the longest line.
Extract shared newBackendClientAndTarget helper to deduplicate
fetchTools/callTool setup. Remove unused parameter from
renderRegistryOverlay. Complete the --help keybinding list (8 → 20
bindings). Document diffLines duplicate-line limitation and secrets
process-visibility tradeoff.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@peppescg peppescg self-assigned this Apr 8, 2026
@github-actions github-actions bot added size/XL Extra large PR: 1000+ lines changed and removed size/XL Extra large PR: 1000+ lines changed labels Apr 8, 2026
@peppescg peppescg requested a review from Copilot April 8, 2026 18:52
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a new thv tui subcommand that launches an interactive Bubble Tea terminal dashboard for monitoring and operating MCP servers (workloads) from a single view, including logs, server info, tools, tool inspector, and a registry browser.

Changes:

  • Add thv tui Cobra command entrypoint and register it on the root CLI.
  • Introduce a new pkg/tui Bubble Tea model with update/view logic for workload navigation, log streaming/search, tool inspection/calls, and registry browsing/run.
  • Add shared CLI/TUI UI helpers (styles, help rendering, spinner, slog handlers) and new dependencies (clipboard + Charmbracelet components).

Reviewed changes

Copilot reviewed 38 out of 39 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
cmd/thv/app/tui.go Adds the thv tui command to start the Bubble Tea dashboard and route WARN/ERROR logs to the TUI.
cmd/thv/app/commands.go Registers the new tui subcommand on the root command.
cmd/thv/app/ui/styles.go Introduces shared color palette and ANSI-aware width/padding helpers used by the TUI.
cmd/thv/app/ui/spinner.go Adds a simple TTY-only spinner utility for CLI progress output.
cmd/thv/app/ui/log_handler.go Adds slog handlers for styled CLI output and for sending WARN/ERROR records into the TUI.
cmd/thv/app/ui/help.go Updates styled CLI help output to include and describe the new tui command.
go.mod Adds clipboard + bubbles + charmbracelet/x/ansi and bumps related Charmbracelet indirect deps.
go.sum Records checksums for newly added/updated Go module dependencies.
pkg/tui/model.go Defines core Bubble Tea model state (panels, filters, viewports, streams, overlays).
pkg/tui/init.go Initializes the TUI model and default viewports with initial workload listing.
pkg/tui/update.go Implements the main Bubble Tea update loop, message handling, and streaming orchestration.
pkg/tui/update_navigation.go Adds key handling for navigation, actions (stop/restart/delete), panel switching, scrolling, and searches.
pkg/tui/update_search.go Implements shared case-insensitive log/proxy-log search with highlighting and match navigation.
pkg/tui/update_inspector.go Implements inspector-specific key handling (tool filter, form focus, JSON tree navigation/copy, tool calls).
pkg/tui/update_registry.go Implements registry overlay navigation, filter input, detail view, and run-form interactions.
pkg/tui/actions.go Adds async commands for workload operations and for running registry items via a thv run subprocess.
pkg/tui/tools.go Adds MCP client/target setup and tool fetching from running servers.
pkg/tui/logs.go Implements workload log polling + line-diffing helpers.
pkg/tui/proxylogs.go Implements proxy log polling + streaming into the TUI.
pkg/tui/logformat.go Adds structured slog JSON parsing + formatting and ANSI-aware horizontal scrolling.
pkg/tui/inspector.go Builds inspector form fields from tool schemas and formats curl + tool call results.
pkg/tui/json_tree.go Implements a collapsible JSON tree for tool call responses (parse/flatten/render/copy).
pkg/tui/registry.go Adds registry fetching plus helpers for filtering and building run-form fields.
pkg/tui/form_helpers.go Adds reusable focus/blur/forward-key helpers for form fields.
pkg/tui/view.go Renders the overall layout frame (sidebar + main panel + status bar) and overlays.
pkg/tui/view_statusbar.go Renders the status/help bar and the help modal content.
pkg/tui/view_helpers.go Adds shared rendering utilities (form fields, curl line highlighting, wrapping, truncation).
pkg/tui/view_info.go Renders the Info panel combining workload runtime info and loaded RunConfig details.
pkg/tui/view_inspector.go Renders the 3-column inspector (tool list + form + request/response + JSON tree/logs).
pkg/tui/view_registry.go Renders registry overlays (list/detail/run form) and helper formatting/builders.
pkg/tui/*_test.go Adds unit tests for pure helpers: logs diff/split, search highlighting, registry filtering, JSON tree ops, inspector helpers, form helpers, etc.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Single-quote and escape all dynamic values from registry metadata
(server name, transport, permission profile, env var names) in the
generated `thv run` command that is copied to clipboard. Without
escaping, a malicious registry entry containing shell metacharacters
could lead to arbitrary command execution when the user pastes the
command into a terminal.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions github-actions bot added size/XL Extra large PR: 1000+ lines changed and removed size/XL Extra large PR: 1000+ lines changed labels Apr 8, 2026
Replace single-line matching with a 3-line suffix sequence match to
reliably detect the boundary between old and new log lines when the
same message repeats. Falls back to single-line matching when prev
has fewer than 3 lines or no sequence match is found.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions github-actions bot added size/XL Extra large PR: 1000+ lines changed and removed size/XL Extra large PR: 1000+ lines changed labels Apr 8, 2026
- Fix --secret flag format: pass secret env vars as --env NAME=VALUE since
  the run form collects actual values, not secrets-manager references
- Add height < 2 guard in View() to prevent index-out-of-bounds panic on
  very small terminal resize events
- Remove unused base-frame parameter from renderHelpOverlay and update
  its comment to match actual behavior
- Fix three UTF-8 backspace bugs (registry filter, sidebar filter,
  inspector tool filter) that sliced by byte instead of by rune
- Rewrite highlightSubstring to operate on rune indices so Unicode case
  mappings that change byte length cannot desync slice positions
- Add defer in tuiCmdFunc to emit OSC 111 background-colour reset on
  abnormal exit paths (panics) that bypass View()'s quitting branch
- Add unit tests for buildRunCmd covering transport, permission-profile,
  required/optional env vars, and combined flag cases

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions github-actions bot removed the size/XL Extra large PR: 1000+ lines changed label Apr 8, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 8, 2026

✅ Large PR justification has been provided. The size review has been dismissed and this PR can now proceed with normal review.

@github-actions github-actions bot dismissed their stale review April 8, 2026 19:13

Large PR justification has been provided. Thank you!

@github-actions github-actions bot added the size/XL Extra large PR: 1000+ lines changed label Apr 8, 2026
peppescg and others added 2 commits April 8, 2026 21:28
- gci: fix import block ordering in actions.go and json_tree.go
- exhaustive: add all ContentType cases to formatInspResult switch;
  extract per-panel status bar hint helpers so renderStatusBar switch
  covers every activePanel value explicitly
- gocyclo: suppress with nolint on inherently complex key-handler and
  renderer switch statements (handleInspectorKey, handleNormalKey,
  handleMsg, renderMain, renderInspResponse, renderStatusBar,
  renderJSONItem)
- gosec G115: annotate uintptr→int conversions in help.go and spinner.go
- gosec G204: annotate exec.CommandContext in actions.go
- lll: break long runFromRegistry signature onto multiple lines
- revive: add godoc comment to Model.View; rename unused width param
  to _ in renderTools
- staticcheck: replace sb.WriteString(fmt.Sprintf(...)) with fmt.Fprintf
- thelper: add t.Helper() to inline check closures in inspector_test.go
- unparam: remove n parameter from makeFormFields (always 3)
- unused: delete unreachable rebuildLogSearch and rebuildProxyLogSearch

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions github-actions bot added size/XL Extra large PR: 1000+ lines changed and removed size/XL Extra large PR: 1000+ lines changed labels Apr 8, 2026
- Guard runFormSubmit against empty fields slice before indexing fields[0]
- Initialize toolsView viewport in New() to avoid zero-width renders
  before the first WindowSizeMsg arrives
- Cap inspector slog buffer at 500 lines (same pattern as logLines)
- Validate that env var and secret keys do not contain '=' before
  constructing --env NAME=VALUE arguments
- Clarify buildRunCmd comment: --secret references a secrets-manager
  entry, not an inline value; distinguish from runFromRegistry --env path

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions github-actions bot added size/XL Extra large PR: 1000+ lines changed and removed size/XL Extra large PR: 1000+ lines changed labels Apr 8, 2026
- errcheck: wrap deferred fmt.Fprint to discard return values
- gci/gofmt: fix struct field alignment in keys.go (Space, CopyNode);
  add missing final newline in model.go; add spaces around arithmetic
  operators in view.go lipgloss chain
- gosec G115: annotate three IsTerminal uintptr→int casts in spinner.go
- lll: shorten nolint comment in view_inspector.go to fit 130-char limit
- staticcheck QF1012: replace remaining sb.WriteString(fmt.Sprintf(...))
  with fmt.Fprintf in help.go (footerHint, RenderCommandUsage,
  renderParentHelp)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions github-actions bot added size/XL Extra large PR: 1000+ lines changed and removed size/XL Extra large PR: 1000+ lines changed labels Apr 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/XL Extra large PR: 1000+ lines changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(cli): add thv tui command for an interactive terminal UI experience

3 participants