shinymcp is strongest when an AI assistant needs to do
more than print a number. The assistant can call a tool, receive
structured data for reasoning, and show the user an interactive card
with controls, tables, plots, and formatted output. The card also
reports the user’s interactions back into the model’s context, so the
assistant can act on what the user did in its next turn.
The examples in order
The bundled examples are ordered so each one adds a single new idea.
Work through them in order and you’ll have touched every major feature
of the package. Each lives at
system.file("examples", "<name>", "app.R", package = "shinymcp"),
and each runs in preview_app() (a local reference host with
a protocol log) or as a stdio MCP server via
Rscript app.R.
| Level | Example | What it adds |
|---|---|---|
| 1 | hello-mcp-minimal |
The whole contract: one input, one tool, one output, ~30 lines |
| 2 | bslib-inputs |
Native shiny/bslib inputs auto-detected by id, no wrappers |
| 3 | penguins |
A real dashboard: ggplot2, multiple outputs, a declared
outputSchema
|
| 4 | feature-tour |
The protocol features: app-only tools, lazy resources, theme
syncing, window.shinymcp
|
| 5 | multi-tool |
One app, several tools, where connected reactive groups become separate contracts |
| 6 |
use-cases gallery |
Realistic chat cards with typed model_value handoffs
(this article) |
| 7 |
shinychat-card + rpharma-hangout
|
Embedding: the same McpApp inside Shiny dashboards and
shinychat |
Every level builds on the same three ideas. Names are the contract:
tool argument names match input ids, and result keys match output ids.
That is the only binding mechanism. One result serves two consumers, so
the human gets the rendered card and the model gets structured values it
can reason over instead of scraping the display. And one definition runs
in several places: the same McpApp serves to Claude over
MCP, embeds in a Shiny app, and wraps as a shinychat tool card.
Level 4 in detail: the feature tour
feature-tour packs the MCP Apps protocol features into
one annotated app. Open it in preview_app() with the
protocol log visible and you can watch each one on the wire:
source(system.file("examples", "feature-tour", "app.R", package = "shinymcp"))What to look for:
- App-only tools (
tool_visibility): thefetch_region_detailtool is callable from the card’s “Load region detail” button but carries_meta.ui.visibility = ["app"], so the model never sees it. Use this to keep UI plumbing, or human-only detail views, out of the model’s tool list. - Lazy resources (
resourcespluswindow.shinymcp.readResource()): the region catalog isn’t inlined into the HTML. The card fetches it through the host at startup. This is how you ship large datasets without bloating theui://resource. - Declared result shapes (
tool_outputs): the analysis tool publishes anoutputSchema, so hosts and models know it returns a base64 plot namedtrendand a text summary namedsummarybefore ever calling it. - Theme syncing: flip your OS or host theme and the card follows. The
bridge maps the host’s theme onto Bootstrap’s
data-bs-theme. - Host interactions (
window.shinymcp): buttons that send a message into the conversation, open a link, and request fullscreen. These are spec methods, so a host that doesn’t support one rejects the call.
The use-case gallery
The gallery contains three small but realistic MCP Apps:
| Use case | What it demonstrates |
|---|---|
| Revenue scenario board | Scenario controls, model-facing structured values, a table, and an ARR plot |
| Experiment planner | Statistical planning, a power curve, and a concise recommendation |
| Incident triage console | Operational decision support, status HTML, and a runbook table |
The apps are intentionally card-sized: controls fit in a compact
grid, the primary answer is immediately visible, and secondary tables
stay available without taking over the chat transcript. Each declares
tool_outputs (so its result shape is part of the published
contract) and prefers_border (a rendering hint for the
host).
Run a use case as an MCP App
The gallery entrypoint serves one use case at a time. Set
SHINYMCP_USE_CASE to revenue,
experiment, or incident.
SHINYMCP_USE_CASE=revenue Rscript \
"$(Rscript -e 'cat(system.file("examples", "use-cases", "app.R", package = "shinymcp"))')"For Claude Desktop or another MCP host, point the server command at
that same app.R file and set the environment variable for
the use case you want to show.
Revenue scenario board
This card turns go-to-market assumptions into a twelve-month forecast. The assistant can reason over the structured result while the user sees the ARR trajectory and monthly forecast table.
source(system.file("examples", "use-cases", "apps.R", package = "shinymcp"))
app <- shinymcp_use_case("revenue")
app$call_tool("forecast_revenue", list(
segment = "Mid-market",
visitors = 25000,
trial_rate = 7,
win_rate = 22,
contract_value = 9000,
monthly_churn = 2.5
))Experiment planner
This card helps an assistant move from “can we detect a 15% lift?” to a specific sample size and runtime. The plot gives the human reviewer a quick sense of how much margin the design has.
source(system.file("examples", "use-cases", "apps.R", package = "shinymcp"))
app <- shinymcp_use_case("experiment")
app$call_tool("plan_experiment", list(
baseline_rate = 12,
minimum_effect = 15,
target_power = 0.9,
traffic_per_day = 6000
))Incident triage console
This card is useful for support, SRE, and operations chats. The assistant can collect facts conversationally, call the tool, and return a priority, briefing, and runbook without losing the structured values it needs for follow-up.
source(system.file("examples", "use-cases", "apps.R", package = "shinymcp"))
app <- shinymcp_use_case("incident")
app$call_tool("triage_incident", list(
service = "Payments",
severity = "Degraded",
affected_users = 1200,
minutes_open = 24,
regulated_data = TRUE
))shinychat integration
The gallery also includes a Shiny app that registers all three cards
as shinychat tool results through
as_shinychat_tool().
Rscript "$(Rscript -e 'cat(system.file("examples", "use-cases", "shinychat-app.R", package = "shinymcp"))')"When OPENAI_API_KEY is available, that app uses
chat_mod_ui() and chat_mod_server() with one
ellmer client per Shiny session. Without a configured model provider, it
falls back to local demo mode: type revenue,
experiment, or incident and the app appends a
live shinymcp card directly.
The Shiny session is still the runtime for those cards: it registers
the McpApp, receives bridge events from the iframe, and
executes tool calls in R. The iframe itself remains a portable MCP App
surface instead of a nested Shiny app, which keeps each chat card small
and reusable outside Shiny.
For the full embedding-and-governance story (contract inspectors,
typed meeting-note handoffs, approved skill registries), see the
R/Pharma demo at
system.file("examples", "rpharma-hangout", package = "shinymcp").
What the examples cover
These examples cover the surfaces most teams need before adopting MCP Apps:
- Rich UI for people: inputs, tables, plots, and formatted HTML.
- Structured values for the model: the assistant can reason over
numbers without scraping the display, and
ui/update-model-contextkeeps it aware of what the user changed in the card. - Declared contracts: input schemas, output schemas, visibility scoping, and resource metadata that a reviewer can inspect before anything runs.
- Portable deployment: the same
McpAppcan be served to an MCP host, embedded in Shiny, or wrapped as a shinychat tool card. In hosts without MCP Apps support, the same tools run as plain text tools. - Small cards over giant dashboards: each card has one job and maps cleanly to one tool call.
