I made an app in MCP Apps that allows you to verify JavaScript execution in chat!

I made an app in MCP Apps that allows you to verify JavaScript execution in chat!

2026.03.21

This page has been translated by machine translation. View original

Hello, I'm Shunta Toda from the Retail App Co-creation Department.

I've created a VS Code-like playground with MCP Apps × Next.js × Monaco Editor that allows you to edit and run JavaScript code within AI chats.
Using this tool, you can complete the entire process from asking AI questions about JS syntax to executing code, making modifications, and asking follow-up questions all within the chat interface.

What I Created

When you ask the AI something like "Teach me how to use spread syntax," it responds with a text explanation plus an editable and executable code widget displayed within the chat.

CleanShot 2026-03-21 at 20.01.41@2x.png

Traditionally, you would need to copy code blocks and paste them into DevTools to run them, but with this app, you can modify the code right there and click the Run button to check the results.

Traditional Experience

  1. Ask a question to AI
  2. Get a response with code blocks
  3. Copy the code
  4. Paste into DevTools or another environment to run it
  5. If you make modifications, copy and paste again... (return to step 3)

Experience with this App

  1. Ask a question to AI
  2. VS Code-like editor + terminal widget appears in the chat
  3. Edit the code there and click ▶ Run to execute it
  4. If you don't understand something, click 💬 Ask AI to ask the AI directly (return to step 2)

What is MCP Apps?

MCP Apps is a mechanism that allows you to display custom HTML/React UIs inline with MCP tool call results. Claude Desktop, ChatGPT, and others support it.

While normal MCP tools only return text, MCP Apps can display React UIs in a sandboxed iframe.

Claude Desktop chat screen

  ├── User question (normal chat)
  ├── AI response text (normal chat)
  └── MCP tool call
        └── returns structuredContent
              └── MCP Apps widget (iframe) rendered inside chat

If you want to learn more about MCP Apps, please read this article.

https://dev.classmethod.jp/articles/mcp-apps-introduction-overview/

Architecture

Technology Stack

Technology Purpose
Next.js 16 (App Router) Framework
@modelcontextprotocol/ext-apps MCP Apps SDK
mcp-handler MCP handler for Next.js (by Vercel)
@monaco-editor/react VS Code's editor engine
Tailwind CSS 4 Styling
Zod Schema validation
Vercel Hosting

Directory Structure

study-programming-mcp-apps/
├── app/
│   ├── mcp/
│   │   └── route.ts           # MCP server + playground tool definition
│   ├── components/
│   │   ├── PlaygroundWidget.tsx # Overall widget
│   │   ├── MonacoEditor.tsx    # Monaco Editor (dynamic import)
│   │   ├── TabBar.tsx          # script.js tab
│   │   ├── Terminal.tsx        # Terminal output panel
│   │   └── Toolbar.tsx         # Run / Clear / Copy / Ask AI buttons
│   ├── hooks/
│   │   ├── use-mcp-app.ts     # MCP connection bridge
│   │   └── useCodeExecution.ts # Code execution management
│   ├── lib/
│   │   └── vscode-theme.ts    # VS Code-like color palette
│   ├── page.tsx               # Entry point
│   └── layout.tsx             # iframe compatible layout
├── baseUrl.ts
├── middleware.ts
└── next.config.ts

Implementation Details

MCP Tool Definition — Data Flow from AI to Widget

With MCP Apps, you register two things on the server side:

  • Tool — A function that AI calls. In this case, it's named playground and takes code and autoRun as arguments
  • Resource — The widget's HTML. This is the UI body displayed in the chat when the tool is called

These two are linked by a resourceUri, creating a flow where "AI calls a tool → corresponding UI is displayed".

https://github.com/ShuntaToda/study-programming-mcp-apps/blob/main/app/mcp/route.ts

Tool return values include two types: content and structuredContent. content is text that remains in the AI's conversation context, while structuredContent is data only delivered to the widget, invisible to the AI. In this case, we pass the code we want to display in the widget via structuredContent.

Widget-Side Reception — useApp Hook and State Management

To receive data on the widget side, we register ontoolinput and ontoolresult handlers with the official SDK's useApp hook.

https://github.com/ShuntaToda/study-programming-mcp-apps/blob/main/app/hooks/use-mcp-app.ts

One important note is that handlers must be registered within the onAppCreated callback. This is because useApp sets up handlers before connect() internally, ensuring no events are missed right after connection.

One issue I encountered during development was mounting the Monaco Editor with default code before MCP data arrived. Once mounted, the Monaco Editor doesn't update its display even when props change.

https://github.com/ShuntaToda/study-programming-mcp-apps/blob/main/app/page.tsx#L14-L53

To address this, I decided not to mount the widget during connected && !data (connected but no data yet), and only render the PlaygroundWidget after the data arrives.

Code Execution — Hijacking the Console

Let's look at what happens when the ▶ Run button is pressed.

https://github.com/ShuntaToda/study-programming-mcp-apps/blob/main/app/hooks/useCodeExecution.ts#L21-L63

The basic flow is to temporarily override console.log and other methods, direct the output to React state for display in the terminal panel, and then restore the original methods after execution. Code execution uses the AsyncFunction constructor, allowing it to handle code containing await.

Since MCP Apps widgets already run in a sandboxed iframe on the host side, the security risks of this eval-based approach are limited.

Two-Way Communication from Widget to AI — sendMessage

So far, the flow has been one-way: AI → widget. However, for a complete learning experience, it would be better if users could ask the AI about their code execution results after modifying the code.

Using app.sendMessage(), we can send messages from the widget to the Claude conversation.

https://github.com/ShuntaToda/study-programming-mcp-apps/blob/main/app/components/PlaygroundWidget.tsx#L46-L68

When the "💬 Ask AI" button is pressed, it sends the current code from the editor along with execution results to Claude. This is useful when you've modified and run the code but don't understand the results. The learning loop of AI provides code → user tries it → user asks AI if confused is completed entirely within the chat.

Development Considerations

Monaco Editor CDN Loading and CSP

The MCP Apps iframe sandbox has strict CSP settings. By default, Monaco Editor loads worker files from cdn.jsdelivr.net, so I needed to explicitly allow this in the CSP configuration.

https://github.com/ShuntaToda/study-programming-mcp-apps/blob/main/app/mcp/route.ts#L49-L60

If you forget this, the editor will remain in a perpetual "Loading..." state.

Try It Out

It's deployed on Vercel, so you can try it immediately from Claude Desktop. Add the following to your claude_desktop_config.json and restart Claude Desktop:

{
  "mcpServers": {
    "js-playground": {
      "command": "npx",
      "args": ["mcp-remote", "https://study-programming-mcp-apps.vercel.app/mcp"]
    }
  }
}

Then, ask Claude something like "Teach me how to use spread syntax" or any JavaScript question, and the playground tool will be called, displaying the widget.

Summary

Using MCP Apps, we can embed interactive UIs within AI conversations. While I created a JavaScript playground this time, the same mechanism can be used to implement various widgets like graph drawing tools, form builders, data visualizers, and more.

The app introduced here eliminates the need to "ask AI for code, copy-paste it, and run it elsewhere," creating a learning experience that takes place entirely within the chat.

I hope this is helpful for everyone.

References

https://github.com/ShuntaToda/study-programming-mcp-apps

https://modelcontextprotocol.io/docs/extensions/apps

https://github.com/modelcontextprotocol/ext-apps

https://github.com/vercel-labs/mcp-apps-nextjs-starter

https://dev.classmethod.jp/articles/mcpapps-20260309/

Share this article