Power Apps MCP App Templates: widget kit for custom tools in Microsoft 365 Copilot
April 24, 2026
Model-driven Power Apps can now generate an MCP server and declarative agent from your app. Custom tools extend the built-in grid and form tools with natural language prompts and optional HTML widgets that render tool output as interactive visuals inside chat.
The hard part isn’t the protocol. It’s designing widgets that look right, handle edge cases, and pair cleanly with the JSON your prompts produce.
This template kit solves that. It ships 18 ready-to-use widget templates and matching prompt recipes so you can go from “I want a KPI dashboard in Copilot” to a working custom tool in minutes.
Full source: GitHub repository
Widgets vs. MCP Apps
Microsoft uses two terms interchangeably, and it helps to know both:
- Widgets are the self-contained HTML files that render a tool’s JSON output visually inside a chat conversation. This is the most common term in Power Apps documentation.
- MCP Apps refers to the protocol extension (
@modelcontextprotocol/ext-apps) that enables MCP servers to deliver interactive UIs to hosts. This term comes from the MCP specification.
In this kit, widget means an individual HTML file you attach to a custom tool. MCP App means the protocol those widgets use to communicate with the host.
What’s in the kit
18 widget templates
Every widget follows the MCP Apps protocol and is ready to paste into the tool editor’s UX field.
| Widget | File | Data shape | Example use |
|---|---|---|---|
| Base Template | base-widget.html |
Any JSON | Starting point for custom widgets |
| KPI Dashboard | kpi-dashboard.html |
Aggregate metrics | Revenue, case counts, pipeline totals |
| Record Timeline | record-timeline.html |
Date-sequenced events | Case history, activity feed, audit trail |
| Status Pipeline | status-pipeline.html |
Stage-grouped records | Opportunity pipeline, case funnel |
| Comparison Grid | comparison-grid.html |
Multi-option scored matrix | Vendor comparison, option analysis |
| Data Table | data-table.html |
Rows + columns (sortable) | Account list, query results, tabular data |
| Approval Card | approval-card.html |
Single record + actions | Expense approval, change request |
| Progress Tracker | progress-tracker.html |
Sequential steps | BPF stages, onboarding checklist |
| Alert List | alert-list.html |
Severity-prioritized items | SLA breaches, overdue tasks |
| Detail Card | detail-card.html |
Sectioned record profile | Account summary, case detail |
| Donut Chart | donut-chart.html |
Labeled segments | Cases by priority, leads by source |
| Bar Chart | bar-chart.html |
Category bars | Revenue by region, count by team |
| Metric Sparkline | metric-sparkline.html |
Value + trend line | Monthly revenue trend, case volume |
| Kanban Board | kanban-board.html |
Cards grouped by column | Cases by status, task boards |
| Hierarchy Tree | hierarchy-tree.html |
Parent-child nesting | Account hierarchy, BU structure |
| Calendar Heat Map | calendar-heatmap.html |
Date x intensity grid | Case creation density, activity patterns |
| Before/After | before-after.html |
Field-level diffs | Audit history, change tracking |
| Calendar | calendar.html |
Month grid with events | Appointments, due dates, SLA deadlines |
Prompt recipes for every widget
Each widget has a matching prompt pattern you paste into the tool’s instructions field. The prompt tells the AI model how to query Dataverse and shape JSON output that the widget expects.
How to use a widget
- Create a custom tool in your model-driven app’s App MCP tab
- Write prompt instructions that produce JSON matching the widget’s contract (documented in each HTML file’s header comment)
- Test the tool and verify the JSON output
- Open the widget HTML from this kit that matches your data pattern
- Paste the HTML into the tool’s UX field (step 2 of the tool editor)
- Download and re-upload the app package to Teams or Microsoft 365 Agents
Widget architecture
Every widget follows the same structure:
┌─ <head> ──────────────────────────────────────────┐
│ Fluent Web Components (UMD) │
│ CSS using Fluent design tokens │
└────────────────────────────────────────────────────┘
┌─ <body> ──────────────────────────────────────────┐
│ #loading — spinner + contextual message │
│ #content — rendered visualization │
│ #error — error message + retry button │
└────────────────────────────────────────────────────┘
┌─ <script type="module"> ─────────────────────────┐
│ Import App class + Fluent tokens (ESM) │
│ show() / showError() / applyTheme() / esc() │
│ render(data) — widget-specific │
│ Protocol handlers → connect() │
│ Optional: callServerTool for interactivity │
│ Optional: fallback data for local preview │
└────────────────────────────────────────────────────┘
All widgets pull three CDN dependencies:
| Package | Format | Source | Purpose |
|---|---|---|---|
@modelcontextprotocol/ext-apps |
ESM | cdn.jsdelivr.net | App class (protocol) |
@fluentui/tokens |
ESM | cdn.jsdelivr.net | Theme tokens (light/dark) |
@fluentui/web-components@beta |
UMD | unpkg.com | Fluent UI elements |
Fluent design tokens
Widgets use Fluent design tokens for all colors, so they adapt to light and dark mode automatically. No hardcoded hex or RGB values.
| Purpose | Token |
|---|---|
| Primary text | var(--colorNeutralForeground1) |
| Secondary text | var(--colorNeutralForeground2) |
| Card background | var(--colorNeutralBackground2) |
| Brand/accent | var(--colorBrandBackground) |
| Borders | var(--colorNeutralStroke1) |
| Error | var(--colorStatusDangerForeground1) |
| Success | var(--colorStatusSuccessForeground1) |
Prompt recipe examples
KPI Dashboard
Query the {table} table. Count all records where statecode eq 0.
Sum the {column} column. Calculate the average {column}.
Count records created in the last 30 days.
Return JSON with "title" (string) and "metrics" (array).
Each metric has: "label" (string), "value" (number),
"format" ("currency", "percent", or "number"),
"trend" (number, percent change from previous period),
"target" (number, optional goal for progress bar).
Record Timeline
Query the {table} table. Select {date_column}, {title_column},
{description_column}, {status_column}.
Filter to records related to the input parameter {record_id}.
Order by {date_column} descending. Top 20.
Return JSON with "title" (string) and "events" (array).
Each event has: "date" (ISO 8601), "title" (string),
"description" (string), "type" ("success", "warning", "error", or "info").
Map status: Resolved→success, Escalated→warning, Failed→error, other→info.
Kanban Board
Query {table} assigned to the current user. Group by {status_column}.
For each record return ticket number (id), subject (title),
customer or parent name (subtitle), and priority (badge).
Return JSON with "title" (string) and "columns" (array).
Each column has: "name" (status label) and "cards" (array).
Each card has: "id", "title", "subtitle", "badge" (optional).
Order columns by workflow progression.
The kit includes prompt recipes for all 18 widgets plus a tool chaining pattern for passing structured data between tools.
Interactive callbacks with callServerTool
Widgets aren’t limited to displaying data. The Approval Card widget demonstrates how to call back into the MCP server to trigger actions.
When the user clicks Approve or Reject, the widget calls:
const result = await app.callServerTool({
name: TOOL_NAME,
arguments: { action: "approve", id: record.id }
});
The MCP server executes the target tool, which updates Dataverse, and returns a result. The widget shows success or failure feedback inline.
To set this up:
- Create a second custom tool in the same app (for example, “Process Approval”)
- In its prompt instructions, handle the
actionandidparameters to update the record’s status - In the Approval Card widget HTML, set
TOOL_NAMEto that tool’s name
When TOOL_NAME is null (the default), button clicks show visual feedback only without calling the server.
Local preview
Every widget includes a commented-out fallback section at the bottom of the <script> block. Uncomment it to render sample data in a plain browser without an MCP host:
// --- Local preview (uncomment to test in a browser) ---
const SAMPLE = {
"title": "My Dashboard",
"metrics": [{ "label": "Open Cases", "value": 42, "format": "number" }]
};
render(SAMPLE); show('content');
Open the .html file directly in a browser, uncomment the SAMPLE block, replace the JSON with your test data, and verify the layout. Re-comment the block before pasting into Power Apps.
Theme tokens won’t resolve in a plain browser (no host context), so colors fall back to CSS defaults.
Generating custom widgets
For data shapes not covered by this kit, use the /generate-mcp-app-ui skill:
/generate-mcp-app-ui Show a bar chart of revenue by region.
Tool output: {"regions":[{"name":"West","revenue":340000},{"name":"East","revenue":520000}]}
Install the skill via: /plugin marketplace add microsoft/power-platform-skills
Beyond the kit
These niche visualizations can be generated on demand with /generate-mcp-app-ui:
| Visualization | Data shape | Example use |
|---|---|---|
| Geo Map | Points with lat/lng | Account locations, service territory |
| Gantt Timeline | Tasks with start/end dates | Project schedule, SLA tracking |
| Sankey Diagram | Nodes and links | Lead flow, process transitions |
| Scatter Plot | X/Y points | Revenue vs. deal count, risk matrix |
| Stacked Bar | Categories with segments | Revenue by region broken down by product |
Hybrid pattern: App MCP + external MCP
A model-driven app’s declarative agent can combine built-in App MCP tools with external MCP servers:
- App MCP custom tools handle Dataverse queries and visualizations with no hosting required
- External MCP server (Power Platform custom connector) handles external API calls such as Salesforce, HubSpot, or Graph
The declarative agent manifest supports multiple plugin references pointing to different MCP endpoints. Your existing custom connectors can work alongside App MCP tools in the same agent.
Prerequisites
- A model-driven Power App
- Microsoft 365 Copilot license
- Permission to upload custom apps in Microsoft Teams
- (Optional) GitHub Copilot CLI or Claude Code with the
generate-mcp-app-uiskill