Skip to content

Skip widget JSON for Claude Code clients#235

Merged
RafaelPo merged 2 commits intomainfrom
feat/skip-widget-claude-code
Feb 26, 2026
Merged

Skip widget JSON for Claude Code clients#235
RafaelPo merged 2 commits intomainfrom
feat/skip-widget-claude-code

Conversation

@RafaelPo
Copy link
Contributor

Summary

  • Skip emitting widget JSON in tool responses when the connected client is Claude Code, which doesn't render MCP App widgets. Saves ~500-2000 context tokens per results call.
  • Add client_supports_widgets() detection using two approaches:
    1. MCP Apps UI capability — checks experimental["io.modelcontextprotocol/ui"] (spec-recommended, not yet advertised by any client)
    2. Name-based fallback — denylists "claude-code" based on clientInfo.name (to be removed once clients adopt the capability)
  • Add log_client_info() helper that logs client name, version, and capability flags on tool calls for observability.

Test plan

  • All 369 existing tests pass
  • Manual E2E: connect Claude Code to HTTP server, verify no widget JSON in results response
  • Manual E2E: connect Claude.ai to HTTP server, verify widget still renders

🤖 Generated with Claude Code

Claude Code doesn't render MCP App widgets, so the widget JSON blob
in the tool response just wastes context. Detect the client using two
approaches:

1. MCP Apps UI capability — check experimental["io.modelcontextprotocol/ui"]
   (spec-recommended, but not yet advertised by any client)
2. Name-based fallback — denylist "claude-code" based on clientInfo.name
   sent during initialization (to be removed once clients adopt the cap)

Also adds log_client_info() helper that logs client name, version, and
capability flags on every tool call for observability.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@RafaelPo
Copy link
Contributor Author

@claude code review

@RafaelPo RafaelPo requested a review from rgambee February 26, 2026 12:37
@github-actions
Copy link
Contributor

github-actions bot commented Feb 26, 2026

Claude finished @RafaelPo's task in 1m 17s —— View job


Code Review: Skip widget JSON for Claude Code clients

  • Read changed files and gather context
  • Review tool_helpers.py changes
  • Review result_store.py changes
  • Review tools.py changes
  • Post review summary

Overall this is a clean, well-scoped change. The skip_widget flag threads cleanly through the result pipeline and the inline comments are thorough. A few items worth addressing:


1. Docstring says "allowlist" but the implementation is a denylist

tool_helpers.py:93-98 — the docstring says "Falls back to a name-based allowlist for clients that don't advertise the capability yet" but the code on line 114 uses a denylist (name not in ("claude-code",)). These are opposite concepts — an allowlist would enumerate the clients that do support widgets. The current logic is correct for the goal (block known non-widget clients, default to allowing), but the docstring is misleading.

Fix this →


2. Inconsistent defaults for unknown clients

tool_helpers.py:101-117 — when client_params is None (line 102), the function returns False (skip widgets). But when an exception occurs (line 117), it returns True (include widgets). Both represent "we can't determine the client" but give opposite results.

If the intent is fail-safe (include widgets when unsure), line 102 should also return True. If the intent is conservative (only include when we're confident), line 117 should return False. I'd suggest aligning on True (include widgets) for both, since wasting tokens is less harmful than breaking the widget for a legitimate client:

if not cp:
    return True  # Unknown client — default to including widgets

Fix this →


3. No tests for the new behavior

The existing test suite covers _build_result_response thoroughly (9+ test cases in test_result_store.py), but none exercise skip_widget=True. Similarly, client_supports_widgets and log_client_info have no unit tests. Since these functions involve branching logic and name matching, a few test cases would add confidence:

  • _build_result_response(..., skip_widget=True) at offset=0 should return 1 TextContent (no widget JSON)
  • client_supports_widgets with mocked client_params.clientInfo.name = "claude-code" should return False
  • client_supports_widgets with mocked UI capability should return True
  • client_supports_widgets with unknown client name should return True

4. Minor: log_client_info only called from 2 tools

tools.py:724,880log_client_info is called from everyrow_upload_data and everyrow_results_http but not from submission tools (everyrow_agent, everyrow_rank, etc.). If the goal is observability into which clients are connecting, it might be worth calling it from at least one submission tool too — but this is a nice-to-have, not a blocker.


Summary: The core logic is sound and the skip_widget threading is clean. The main actionable items are (1) fixing the allowlist/denylist docstring mismatch and (2) aligning the None vs exception default. Tests would be good to add but the existing 369 passing tests and the guarded nature of the change reduce risk.

@RafaelPo RafaelPo removed the request for review from rgambee February 26, 2026 12:41
- Switch client_supports_widgets from denylist to whitelist approach:
  unknown clients default to no widget (saves context tokens)
- Whitelist verified client names: Claude.ai and Claude Desktop both
  report "Anthropic/ClaudeAI" (same identity, single entry covers both)
- Add log_client_info to all tools except everyrow_progress
- Update result_store comment to reflect whitelist approach

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@RafaelPo RafaelPo requested a review from rgambee February 26, 2026 13:14
@RafaelPo RafaelPo merged commit a0eb188 into main Feb 26, 2026
5 checks passed
@RafaelPo RafaelPo deleted the feat/skip-widget-claude-code branch February 26, 2026 13:14
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.

1 participant