https://a.storyblok.com/f/270183/1368x665/c380cf46d1/26may_dev-blog_build-reports-dashboard-claude-desktop_r2.png

Build a Reports Dashboard in Claude Desktop with MCP Apps and Vonage

Published on May 21, 2026

Time to read: 11 minutes

Build a Reports Dashboard in Claude Desktop with MCP Apps and Vonage

In this tutorial, you’ll build a real-time reporting dashboard inside Claude Desktop.

Introduction

“SMS is down.”

That’s all the information you get from your team. No details. No logs.

Is it a regional issue? A carrier problem? A bug in your app? Answering those questions usually means jumping between dashboards, exporting reports, and manually evaluating the data. Or maybe you’re clever and quickly write some scripts to check against the API. Either way, it’s not fast. And it’s definitely not what you want to be doing under pressure.

What if you could investigate all of that directly inside the chat you use every day?

That's what Model Context Protocol (MCP) Apps make possible. Think of them as the GUI moment for AI tooling. In the same way graphical interfaces made computing accessible beyond specialists, MCP Apps bring real interactivity to the MCP tools you connect to your AI Agents.

In this post, you’ll learn how to build a reporting dashboard that runs inside Claude Desktop using the MCP Apps framework. It connects to Vonage’s Reporting API and turns raw records into an interactive UI that lets you understand what’s happening in real time.

>> TL;DR: Skip ahead and find the quickstart and app code on GitHub.

Claude Desktop interface displaying an embedded MCP App dashboard with SMS Report selected, showing input fields for date range, status, sender and recipient numbers, and buttons to run the report or copy results as CSV.Claude Desktop showing the MCP App SMS report dashboard with filters for date range, status, and phone numbers.

Prerequisites

Before you get started, make sure you have:

How Our MCP App Works

MCP Apps vs. MCP Servers

If you're familiar with the Model Context Protocol, you know about MCP Servers. They extend an AI Agent’s capabilities by providing tools and resources. The MCP Tooling Server, for example, exposes endpoints like send_sms or get_records_report that Claude can call on your behalf.

MCP Apps are different. They're MCP Servers that also bundle an interactive UI directly inside an AI Agent. That means you have access to buttons, filters, forms, and complex UI elements.

MCP Apps are really useful when a plain text response isn’t enough. If you’re exploring large datasets, configuring systems with multiple inputs, monitoring live activity, or stepping through multi-stage workflows, an interactive UI makes those tasks faster and easier to reason about. Instead of a back-and-forth conversation, users can see everything at once and interact with it directly.

Chat interface displaying an embedded MCP App with a product launch dashboard, including tables, status indicators, and interactive elements rendered inline within the conversation.Example of an MCP App rendering an interactive dashboard UI directly inside a chat interface.

How the System Flows

Here's what happens when you run a report:

  1. You interact with the dashboard inside Claude

  2. The dashboard calls a tool on the MCP App server

  3. The server calls the upstream Vonage MCP server

  4. The Vonage MCP server hits the Vonage Reports API

  5. Data flows back through the chain

  6. The server normalizes and masks it, then returns it to the UI

  7. The dashboard renders the results inline

Diagram illustrating the flow between a user, Claude Desktop, MCP App server, Vonage MCP server, and the Vonage Reports API, with request and response paths and data returned to an embedded dashboard UI.System flow diagram showing how an MCP App in Claude Desktop connects to the Vonage Reports API and returns data to an embedded UI.

Your server acts as the bridge between raw API data and an interactive interface. It normalizes the Vonage API response, masks sensitive phone numbers before displaying them, and structures the data for the UI to consume.

How It’s Put Together

  1. The Server (src/server.ts)

    The server registers two tools (sms_report and voice_report) and acts as a proxy between the UI and Vonage. Rather than calling the Vonage Reports API directly, it wraps the existing Tooling MCP server as a subprocess, calls get-records-report on your behalf, and normalizes the response for display. This keeps the implementation lean: authentication, request signing, and error handling all stay with the Vonage MCP server. Our server just shapes the data and passes it to the UI.

  2. The UI (src/ui/mcp-app.ts)

    This generates our interactive HTML dashboard. It creates date pickers, status filters, paginated tables, and expandable row details by bundling into a single self-contained file via Vite's vite-plugin-singlefile. It renders inside Claude because each tool registration includes a pointer to it:

    "_meta": 
      { "ui":
        { "resourceUri": "ui://vonage-reports/mcp-app.html" 
        }
      }

    When Claude Desktop sees that metadata, it renders the interface inline instead of returning plain text. From there, the UI manages everything client-side: calling app.callServerTool() to fetch data, tracking pagination and active filters, and handling click-to-filter on KPI cards and row expansion for message and call details.

  3. Data Safety

    Phone numbers are masked on the server side before data ever reaches the UI. The last four digits are preserved, everything else is replaced. This means exports, screenshots, and clipboard copies won't leak full numbers, even accidentally.

Install in 5 Minutes

Step 1: Download the Release

Head to the GitHub releases page and download vonage-reports-mcp-app-v0.1.0.tar.gz. Extract it to a folder on your machine (e.g., ~/vonage-reports-app/).

tar -xzf vonage-reports-mcp-app-v0.1.0.tar.gz -C ~/vonage-reports-app/

Step 2: Add to Claude Desktop Config

Open your claude_desktop_config.json

  • It lives at  ~/Library/Application Support/Claude/ on macOS, or %APPDATA%\Claude\ on Windows

  • Add this entry to the mcpServers object:

{
  "vonage-reports": {
    "command": "node",
    "args": ["/path/to/vonage-reports-app/dist/server.js"],
    "env": {
      "VONAGE_API_KEY": "your_api_key",
"VONAGE_API_SECRET": "your_api_secret",
       "VONAGE_APPLICATION_ID": "your_app_id",
       "VONAGE_PRIVATE_KEY64": "your_private_key_base64",
       "VONAGE_VIRTUAL_NUMBER": "your_virtual_number"
    }
  }
}

  • Replace the paths and credentials with your actual values.

Step 3: Restart Claude Desktop

Close Claude completely and reopen it. The MCP App will initialize on startup.

Step 4: Open the Dashboard

In Claude, look for the Vonage Reports Dashboard in the MCP Apps section. You should see two tabs: SMS Report and Voice Report. You're ready to go!

Run Your First Report

To trigger the UI, you’ll need to call the tool through a prompt like, “run the sms report”. Then you have access to the form and dashboard.

Claude Desktop interface showing an embedded MCP App SMS reporting dashboard with date range filters, phone number inputs, and summary cards displaying total records and message status counts such as delivered, rejected, expired, and failed.Claude Desktop displaying the MCP App SMS report dashboard with filters applied and results summarized in KPI cards.

SMS Report

  1. Set your date range

    • Use the date/time pickers to select a range (e.g., last 7 days).

  2. Filter by status (optional)

    • Click the "Status" dropdown and pick a value like delivered, failed, or rejected.

  3. Filter by sender or recipient (optional)

    • Enter phone numbers in E.164 format (e.g., +12025550123).

  4. Include message content (optional)

    • Check this box if you want to see the actual SMS bodies in the results. ( Warning: message bodies may contain private information)

  5. Click "Run report"

    • The dashboard fetches your records and displays them.

What You'll See

  • KPI Cards

    • At the top, colored status cards show the breakdown of your messages:

      • Green = Delivered

      • Red = Failed or Rejected

      • Yellow = Submitted, Accepted, or Buffered

      • Grey = Expired, Deleted, or Unknown

    • Click any card to filter the table to just those rows. Click again to clear the filter.

  • Paginated Table

    • The results table shows 10 records per page with columns like date, from, to, status, network, country, and price. Long values (like internal reference IDs) are truncated with a tooltip on hover.

  • Expandable Messages

    • If you enabled "Include message content," rows with actual message bodies will show a ▶ msg button. Click it to expand and see the full message text.

  • Copy CSV

    • Click the "Copy CSV" button to export the current results to your clipboard. Paste into a spreadsheet or text editor.

Embedded MCP App dashboard in Claude Desktop showing SMS report results with summary cards for delivery status counts and a table listing message records, including timestamps, sender and recipient, status, network, and country.Detailed SMS report results displayed in the MCP App dashboard with KPI summaries and a paginated data table.

Voice Report

The Voice Report tab works similarly. Pick a date range, optionally filter by status or call direction (inbound/outbound), and run the report. Results show when calls started, duration, from/to numbers, status, and more. Click the ▶ button on any row to see all available fields for that call.

Embedded MCP App dashboard in Claude Desktop showing the Voice Report view with date range and status filters, along with a table of call records including timestamps, phone numbers, direction, and call status.Claude Desktop displaying the MCP App Voice report dashboard with call filters and detailed results.

How It Was Built

This MCP app was built using AI Agents (starting with GPT 5.2 in Windsurf and then moving to Claude Code in Claude Desktop), so a step-by-step tutorial is a little difficult with probabilistic coding. Like any app, this wasn't a clean, linear build. It went through four main phases I outline below.

Project Setup

In addition to Claude Code, I needed to give the agent a few resources:

The MCP Apps skill lets the agent understand how to build an MCP App. The Tooling server gives an agent access to the get-records-report tool, which calls the Reports API. And lastly, the Documentation Server helps the agent understand exactly how the Vonage APIs work and use the most up-to-date documentation instead of potentially hallucinating answers or using old documentation from internet resources.

1. Planning with AI

The project started with a single prompt:

"I want to create a cool, meaningful MCP app for the Vonage Developer Blog using the get-records-report tool from the Vonage MCP server. Something like:‘Can you get a report of all outbound SMS sent over the last week?’‘Can you get the report for Voice Call ID 1234-abcd-5678-efgh?’

Could you help me outline how to build such an application"

From there, GPT 5.2 asked a series of scoping questions:

  • How polished should the features be? Just a demo or something almost production-ready?

  • How to wire the UI to the data layer?

  • How to handle Personally Identifiable Information (PII) in message content? E.g., phone numbers and message content

After answering all these questions, the Agent was ready with a complete implementation plan that defined the architecture, the two-tab UI structure, the proxy pattern, and the data safety defaults upfront. Read the full initial plan.

2. Getting It Running

The first version was built in Windsurf. But I couldn’t get the App to actually work inside the chat. I tried to move to Cursor and VS Code. Theoretically, MCP Apps work in all of these environments. But I spent way longer trying to figure this out than it took to actually create the app itself.

The default environment for MCP Apps to render their UI is Claude Desktop. So I downloaded it. After configuring my Vonage credentials properly, it worked immediately! The dashboard appeared on the first try.

If you're building an MCP App: test it in Claude Desktop from day one. Don't fight the environment.

3. Debugging

Once the app was running in Claude Desktop, there were four code-level issues to work through. This is where having the Vonage Docs MCP  server really helped because the agent could quickly find where it made incorrect assumptions about how Vonage or MCP Apps behaved.

  1. The first issue was that structured content was never populated. The ext-apps framework (MCP Apps) requires tools to return a structuredContent field alongside the text response. But the Vonage MCP server doesn't return structuredContent as a field. It returns everything as a JSON string embedded in content[0].text. The server was reading res.structuredContent, getting undefined, and the framework validation failed. The fix was an extractStructured() helper that reads the text field and parses it instead.

  2. The Vonage response has a human-readable prefix. Even after fixing the structuredContent issue, JSON.parse() was still failing. The raw text from the Vonage server looks like this:

    Records report for SMS:
    Total records: 47
    {"records": [...]}

    A plain JSON.parse() on that string fails because of the prefix. The fix was a regex to extract just the JSON block before parsing.

  3. A schema type mismatch between the voice report tool and MCP Apps


    The voice tool's outputSchema used z.record() at the root level. The ext-apps framework requires z.object() — z.record(), which caused a runtime crash (Cannot read properties of undefined (reading '_zod')). A small change, but not something the error message made obvious.

  4. Missing required parameter on voice callsThe original voice tool only sent call_id to the Vonage API. The API requires a date_start even for ID-based lookups and returns a 422 without it. Adding a default of isoDaysAgo(7) resolved it.

4. Iterating on the UX 

Once the data was flowing correctly, I got to the fun part: making the dashboard useful and fun to use! This happened in small steps:

  • Pagination

    • The initial table dumped all results at once. With hundreds of records, this was unusable. Adding 10-records-per-page pagination with Prev/Next controls made it navigable.

  • KPI cards with color

    • A plain count of records by status is fine. Color-coded cards let you read the status breakdown at a glance without scanning numbers.

  • Click-to-filter

    • Making the KPI cards clickable, so tapping "Status: rejected" filters the table to just those rows, turned the summary from decorative to functional.

    • This was a bonus feature I would’ve probably skipped before AI Agents made coding so much easier! This little extra feature made me very happy, turning a small demo into something really nice/fun.

  • Reworking the Voice tab

    • The original Voice tab was a single input field: paste a call ID, get the details back. This only works if you already know which call you're looking for. During an incident, you don't.

    • The tab was rebuilt from scratch to match the SMS pattern: a date range + filters first, a browsable list of calls, and then a click-to-expand detail panel for any individual call. This turned it from a lookup tool into an investigation tool.

Each of these changes came from actually using the dashboard and noticing what felt wrong. With coding agents assisting the UI development process, it’s so enjoyable to go in smaller steps, test each thing out, and then iterate until you’ve built something beautiful!

Conclusion

The Vonage Reports MCP App demonstrates how MCP Apps bring dashboard-style UIs into Claude. It's production-ready for basic reporting, and you can extend it:

  • Add charts to see how your call status’ change over time

  • Export to CSV

    • Originally, the CSV was supposed to be downloadable. But the API required to do this is blocked by Claude’s embedded browser. I didn’t want to add unnecessary complexity to the app, so the text solution worked well enough for this demo.

  • Real-time updates

    • Poll the API on an interval and stream live stats

  • Custom alerts

    • Highlight unusual patterns (spike in failures, unusual costs) and then use the Vonage Tooling Server to send a text, RCS, or make a phone call alert.

The full source code is available on GitHub, and the pre-built release is ready to download and run in 5 minutes. Fork it, modify it, and let us know what you build!

Have a question or want to share what you're building?

Stay connected and keep up with the latest developer news, tips, and events.

Share:

https://a.storyblok.com/f/270183/384x384/e4e7d1452e/benjamin-aronov.png
Benjamin AronovDeveloper Advocate

Benjamin Aronov is a developer advocate at Vonage. He is a proven community builder with a background in Ruby on Rails. Benjamin enjoys the beaches of Tel Aviv which he calls home. His Tel Aviv base allows him to meet and learn from some of the world's best startup founders. Outside of tech, Benjamin loves traveling the world in search of the perfect pain au chocolat.