MCP Apps let you build interactive micro-applications that run directly inside DarcyIQ. Create dashboards, forms, data visualizations, and fully interactive experiences—all powered by your MCP integration.
Beyond Text Responses: While standard MCP tools return text, MCP Apps return rich HTML interfaces that users can interact with directly in Chat or the Artifact panel.
What are MCP Apps?
MCP Apps extend the standard MCP tool pattern by returning interactive user interfaces instead of (or alongside) text responses. When your tool returns a UI resource, DarcyIQ renders it as a fully interactive web application within a secure sandbox.
Feature
Standard MCP Tool
MCP App
Output
Text/JSON data
Interactive HTML/JavaScript UI
User Interaction
None (read-only)
Buttons, forms, charts, navigation
Visual Richness
Plain text or markdown
Full HTML/CSS styling
Callbacks
N/A
UI can trigger other tools
State
Stateless
Can maintain state and update dynamically
Use Cases for MCP Apps
MCP Apps are ideal for scenarios where visual interaction enhances the user experience:
Use Case
Example
Why MCP App?
Dashboards
Sales metrics, system status
Real-time data visualization with charts
Data Browsers
Customer lists, product catalogs
Sortable tables, filters, pagination
Configuration Panels
Settings, preferences
Forms with validation and save buttons
Wizards
Multi-step workflows
Guided step-by-step interfaces
Reports
Financial summaries, analytics
Formatted tables, charts, export options
Interactive Forms
Data entry, surveys
Input validation, dropdowns, date pickers
Approval Workflows
Review and approve items
Action buttons, status indicators
How MCP Apps Work
Architecture Overview
The UI Resource Format
MCP Apps return a special UIResource object that tells DarcyIQ to render interactive content:
Field
Required
Description
uri
Yes
Unique identifier for the UI resource
name
Yes
Display name shown in the UI header
mimeType
Yes
Must be text/html for interactive UIs
content
Yes
Your HTML, CSS, and JavaScript code
description
No
Brief description of the UI
Building Your First MCP App
Step 1: Create the MCP Server
Start with a standard MCP server and add a tool that returns a UI resource:
Step 2: Add Callback Tools
Create additional tools that the UI can invoke:
Step 3: Deploy and Test
Deploy your MCP server using MCP Studio
Open DarcyIQ Chat
Ask the AI to "show me the sales dashboard"
Interact with the dashboard—buttons will call back to your tools!
MCP-UI Callbacks
The most powerful feature of MCP Apps is the ability to call back to your MCP server from the UI.
How Callbacks Work
User clicks a button or triggers an action in your UI
Your JavaScript sends a postMessage to the parent window
DarcyIQ intercepts the message and validates it
The tool is invoked on your MCP server
The result is sent back to your iframe
Your UI updates based on the result
Callback Message Format
Receiving Callback Results
Your UI can listen for results:
Dynamic UI Updates
When a callback tool returns a new ui resource, DarcyIQ automatically replaces the current UI:
Best Practices
Security
Practice
Why
Sanitize inputs
Prevent XSS attacks in your HTML
Validate callback params
Don't trust data from the UI blindly
Use HTTPS resources
External assets must be HTTPS
Avoid sensitive data in HTML
UI content may be cached
Sandbox Security: MCP Apps run in a sandboxed iframe with restricted permissions. Some browser APIs may not be available.
Performance
Practice
Description
Inline CSS/JS
Avoid external dependencies when possible
Minimize HTML size
Large UIs take longer to render
Lazy load data
Fetch data via callbacks, not initial load
Cache static content
Reuse UI components across calls
User Experience
Practice
Description
Loading states
Show spinners during callback operations
Error handling
Display friendly error messages
Responsive design
UIs should work at different sizes
Fullscreen support
Design for both inline and fullscreen modes
Example: Loading State
Example MCP Apps
Interactive Data Table
Form with Validation
Displaying MCP Apps
MCP Apps can be displayed in multiple contexts within DarcyIQ:
Context
Description
Size
Chat Inline
Embedded directly in the chat conversation
Compact, expandable
Artifact Panel
Full artifact view alongside chat
Large, side panel
Fullscreen
Maximized view (click expand button)
Full browser window
Fullscreen Mode
Users can click the expand button to view your MCP App in fullscreen mode. Design your UI to take advantage of the extra space:
Limitations
Limitation
Details
No localStorage
Sandbox prevents persistent storage
No cookies
Third-party cookies blocked
Limited APIs
Some browser APIs restricted in sandbox
Same-origin callbacks only
Can only call tools on the same MCP server
No external fetch
Cannot make HTTP requests from iframe
Working Around Limitations: Use tool callbacks to fetch external data. Your MCP server can make HTTP requests and return the data to your UI.
@app.tool()
async def get_latest_metrics() -> list[TextContent]:
"""
Fetch the latest metrics from the database.
Called by the dashboard UI refresh button.
"""
# Fetch real data from your database
metrics = await fetch_metrics_from_db()
# Return updated UI with new data
updated_html = generate_dashboard_html(metrics)
result = {
"ui": {
"uri": "app://sales-dashboard",
"name": "Sales Dashboard (Updated)",
"mimeType": "text/html",
"content": updated_html
}
}
return [TextContent(type="text", text=json.dumps(result))]
@app.tool()
async def export_sales_report(format: str = "pdf") -> list[TextContent]:
"""
Export the sales report in the specified format.
Args:
format: Export format (pdf, csv, xlsx)
"""
# Generate and return the report
report_url = await generate_report(format)
return [TextContent(
type="text",
text=f"Report generated: {report_url}"
)]
window.parent.postMessage({
type: 'tool', // Must be 'tool' for tool invocations
payload: {
toolName: 'my_tool_name', // Name of the tool to call
params: { // Parameters to pass
key: 'value',
another: 123
}
}
}, '*');
// Listen for tool results
window.addEventListener('message', function(event) {
// Check if this is a tool result
if (event.data && event.data.success !== undefined) {
if (event.data.success) {
console.log('Tool result:', event.data.result);
// If the tool returned new UI, it will automatically render
// But you can also update your current UI based on result data
updateDashboard(event.data.result);
} else {
console.error('Tool error:', event.data.error);
showError(event.data.error);
}
}
});
function updateDashboard(data) {
document.getElementById('revenue').textContent = '$' + data.revenue;
document.getElementById('customers').textContent = data.customers;
}
function showError(message) {
alert('Error: ' + message);
}