Skip to contents

Inspect inputs and tool groups

Start with the parser and analyzer:

ir <- parse_shiny_app("path/to/app")
analysis <- analyze_reactive_graph(ir)

analysis$tool_groups
ir$inputs
ir$outputs

That tells you whether shinymcp found the same reactive islands you expected.

Inspect bridge traffic in preview

preview_app() now uses the shared host controller and exposes a protocol log in the preview page. Use it to inspect:

  • ui/initialize
  • tools/call
  • ui/update-model-context
  • ui/notifications/size-changed

Inspect display payloads

Use format_tool_result() during development when you want to see exactly what the runtime will send back to the host:

format_tool_result(list(
  summary = mcp_result_text("ready"),
  table = mcp_result_table(head(mtcars))
))

For shinychat wrappers, inspect the ContentToolResult directly:

tool <- as_shinychat_tool(card_app)
result <- tool(dataset = "mtcars")

result@value
result@extra$display

Common failure modes

  • Input ids do not match tool argument names.
  • Large converted groups should really be split into smaller cards.
  • Generated conversion tools are still placeholders.
  • Rich display works in shinychat, but plain fallback text still matters for other hosts.

Nothing renders in the chat client

MCP Apps is an optional, negotiated MCP extension. If the host does not advertise capabilities.extensions["io.modelcontextprotocol/ui"] during initialize, shinymcp serves your tools with no UI metadata and they run as plain text tools. That is graceful degradation, not a bug. Check that your client version actually supports MCP Apps, and confirm the app works in preview_app() first (it is a known-good reference host).

Tool calls appear to hang or the UI silently stops updating

A JSON-RPC error response from the host (e.g. a rejected or misnamed tool call) is surfaced by the bridge as a rejected Promise and logged to the iframe’s browser console ([shinymcp-bridge] Tool call failed ...). Open the host’s devtools and check the console for the iframe context. The preview_app() protocol log shows every message in both directions.

External assets or API calls don’t load

Hosts apply a restrictive Content Security Policy to the app iframe. External scripts, styles, fonts, and fetch() calls are blocked by default, and they fail with no error you can see. Either inline everything (shinymcp’s default behavior covers htmltools dependencies automatically) or declare the domains via mcp_app(csp = list(connect_domains = ..., resource_domains = ...)). See vignette("mcp-apps-protocol").

The app looks wrong in dark mode

The bridge applies the host’s theme by setting data-bs-theme on the document element. bslib (Bootstrap 5.3+) UIs adapt automatically; custom CSS needs explicit :root[data-bs-theme="dark"] rules.