
Use shinymcp with shinychat
Source:vignettes/use-shinymcp-with-shinychat.Rmd
use-shinymcp-with-shinychat.Rmdshinymcp supports two runtime-first shinychat paths:
- Wrap a small
McpAppas an ellmer tool withas_shinychat_tool(). - Embed a live
McpAppdirectly in Shiny withmcp_host_ui()/mcp_host_server()ormcp_embed().
In both paths, the card is a portable MCP App iframe. When it runs inside Shiny, the active Shiny session owns the live host state and executes tool calls in R. The iframe stays lightweight HTML plus the shinymcp bridge rather than starting a nested Shiny runtime for every chat card.
shinymcp also sets shinychat’s
display$full_screen flag for tool cards when available, and
the embedded host shell includes its own full-screen control for direct
Shiny embeds and older shinychat development builds.
Tool-card path with chat_mod_server()
library(shiny)
library(bslib)
library(ellmer)
library(shinychat)
library(shinymcp)
card_app <- mcp_app(
ui = htmltools::tagList(
mcp_text_input("name", "Name"),
mcp_text("greeting")
),
tools = list(
list(
name = "greet",
description = "Generate a greeting card",
inputSchema = list(
type = "object",
properties = list(name = list(type = "string"))
),
fun = function(name = "world") {
list(greeting = paste("Hello", name))
}
)
),
name = "greeting-card"
)
greet_tool <- as_shinychat_tool(
card_app,
value_fn = function(raw_result) list(greeting = raw_result$greeting),
summary = function(raw_result) raw_result$greeting,
title = "Greeting Card"
)
ui <- page_fillable(chat_mod_ui("chat"))
server <- function(input, output, session) {
client <- ellmer::chat("openai/gpt-4.1-nano")
client$register_tool(greet_tool)
chat_mod_server("chat", client)
}
shinyApp(ui, server)The wrapped tool returns:
- A machine-facing value, which defaults to the raw tool result
transformed by
value_fn. - A human-facing shinychat tool card, which defaults to a live
embedded
McpApp. - Full-screen affordances for larger card inspection, using shinychat’s native tool-card mode when available.
Content-streaming path with chat_ui() +
chat_append()
Use mcp_content_result() when you already have a live
Shiny session and want to append a card yourself:
ui <- page_fillable(chat_ui("chat"))
server <- function(input, output, session) {
observeEvent(input$chat_user_input, {
card <- mcp_content_result(
app = card_app,
value = list(status = "ready"),
title = "Greeting Card"
)
chat_append("chat", card)
})
}Direct embedding in Shiny
For authored Shiny UIs, use the host shell helpers directly:
ui <- page_fillable(
mcp_host_ui("card")
)
server <- function(input, output, session) {
host <- mcp_host_server(
"card",
app = card_app,
trigger = "submit"
)
}mcp_embed(card_app) is the convenience helper for
dynamic server-side contexts where a live session already exists.
The returned host object also exposes read-only reactives so the outer Shiny app can observe the embedded card:
server <- function(input, output, session) {
host <- mcp_host_server("card", app = card_app)
observeEvent(host$model_context(), {
# Current values the card is sending to the model context.
str(host$model_context())
})
observeEvent(host$last_tool_call(), {
# Raw R result plus formatted MCP structuredContent.
str(host$last_raw_result())
str(host$last_result()$structuredContent)
})
}Interaction modes
mcp_embed() and mcp_host_server() support
four trigger modes:
-
change: execute immediately on input change. -
debounce: execute after a short quiet period. -
submit: require the host shell Apply button. -
manual: require an explicit host-side command such ashost$execute().
Recommended usage
- Prefer small single-purpose cards.
- Prefer sequential tool execution in shinychat until you have a strong reason to expose many parallel cards.
- Treat the chat card as the human surface and the
value_fnoutput as the model-facing value. - Use Shiny modules directly when you need a Shiny-only app with full reactive UI semantics.
- Use
McpAppcards when you want the same experience to work in shinychat and MCP hosts.