[This article was first published on The Jumping Rivers Blog, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.The agent we built in part 1 lives in an R console, which suits one person. A trial team needs an app in the browser, where several people can ask questions at once without seeing each other’s conversations.This is part two of a five-part series on putting AI agents into production with R.Each part builds on the same app, a clinical trial safety assistant. The parts are:From Chatbot to AgentPutting the Agent in a Shiny App (this post)Can You Trust It?Audit Trails, Costs and ModelsSharing Your Tools with MCPAs a reminder, the agent answers questions about a clinical trial’s safety data with a model, four tools and a loop. The model chooses which tool to call, and the tools do the counting in R. The model is also told never to do the maths itself, and part 3 checks that. (Part 1 also explains the trial terms.)In this post we:build a chat app around the agent with {shiny} and {shinychat}give every user their own conversation, and test itshow the agent’s tool calls in the chatstop files being uploaded to the modelhandle the model being slow or unavailable, and deploy to Posit ConnectWant to take your agent into production? Join Pedro for our free Agents in Production webinar on Thursday 19 November 2026 at 1:15 PM UK time, covering testing, logging and safeguards.What is {shinychat}?{shinychat} is Posit’s package of chat components for Shiny, and we need two of them. The chat window comes from chat_ui(). Behind it, chat_server() connects the window to an {ellmer} chat client. It sends each question to the model, streams the answer back and shows each tool call as it happens.The client is the one part 1’s make_agent() builds, so the tools, the system prompt and the loop all carry over unchanged. If you’ve used chat_mod_ui() and chat_mod_server() before, {shinychat} 0.5.0 soft-deprecates them in favour of these two.The appWe keep the page in its own function, app_ui(), so that tests can check it without starting the app:GREETING