DEV Community

Cover image for MCP Apps: AI Agents Just Became Applications - Here's How to Build One
Jialu
Jialu

Posted on

MCP Apps: AI Agents Just Became Applications - Here's How to Build One

MCP Apps (SEP-1865) is the open standard that lets AI agents render interactive UIs inside the conversation. Cross-platform, backed by Anthropic + OpenAI, live in Claude and ChatGPT today. Here's how to build one that actually runs in production."

AI agents just crossed a line.

For years, every agent output was the same: text. Need to show a dashboard? Return JSON. Need a 20-field config form? Walk the user through each field, prompt by prompt. Need a product catalog? Describe it.

That era is over.

MCP Apps - the first official extension to the Model Context Protocol, co-authored by Anthropic and OpenAI - lets your MCP server return fully interactive HTML interfaces that render directly inside Claude, ChatGPT, VS Code, and Goose. Not in a new tab. Not alongside the conversation. Inside it.

This post covers how it works, how to build one with LeanMCP, and what it takes to run one in production.


Why "Text Wall" Was Always the Hard Ceiling

The problem isn't just UX. It's capability.

Commerce, data exploration, multi-step approval workflows, complex configuration - none of these map cleanly to a chat thread. You can approximate them with text. You can't actually do them well.

MCP Apps solves this with a security-first architecture:

  • Pre-declared UI resources registered in the server manifest - hosts review and cache them before any tool is called
  • Sandboxed iframes that isolate third-party code from the host application
  • Bidirectional communication via existing MCP JSON-RPC over postMessage - no new SDKs, no new transport
  • Full backward compatibility - text-only fallbacks for clients that don't yet support UI

How It Works

  1. At connection time, your server declares UI resources in the manifest. The host pre-fetches and caches them.
  2. The AI invokes a tool. If that tool has an associated UI resource URI, the host renders a UI.
  3. The host loads HTML into a sandboxed iframe. The UI hydrates with the tool's input and output data.
  4. The user interacts. The iframe communicates back to the host - and to the model - via JSON-RPC over postMessage.

You control exactly what the model sees vs. what the view sees. Sensitive data can pass to the view without polluting the model's context window.


Building an MCP App with LeanMCP

LeanMCP handles the production layer - auth, observability, multi-tenancy - so you can focus on the interface itself.

Step 1: Create your project

npm install -g @leanmcp/cli
npx @leanmcp/cli create my-mcp-app
cd my-mcp-app
npm install
Enter fullscreen mode Exit fullscreen mode

This generates a clean structure:

my-mcp-app/
├── main.ts              # Entry point
├── package.json
├── tsconfig.json
└── mcp/
    └── example/
        └── index.ts     # Your services go here
Enter fullscreen mode Exit fullscreen mode

Step 2: Define your tool

// mcp/analytics/index.ts
import { Tool, SchemaConstraint, Optional } from "@leanmcp/core";

class SalesReportInput {
  @SchemaConstraint({ description: "Region to filter by", minLength: 1 })
  region!: string;

  @Optional()
  @SchemaConstraint({
    description: "Date range",
    enum: ["7d", "30d", "90d"],
    default: "30d"
  })
  range?: string;
}

export class AnalyticsService {
  @Tool({
    description: "Get sales report data",
    inputClass: SalesReportInput
  })
  async getSalesReport(args: SalesReportInput) {
    const data = await fetchSalesData(args.region, args.range);

    // Return data - your MCP Apps UI resource
    // is registered separately in the server manifest
    return {
      region: args.region,
      totalRevenue: data.revenue,
      topProducts: data.products,
      chartData: data.chart
    };
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Configure the HTTP server with logging

// main.ts
import { createHTTPServer } from "@leanmcp/core";

await createHTTPServer({
  name: "my-mcp-app",
  version: "1.0.0",
  port: 8080,
  cors: true,
  logging: true,      // tool calls, latency, errors - all tracked
  autoDiscover: true  // picks up everything in ./mcp automatically
});
Enter fullscreen mode Exit fullscreen mode

Your server is live at http://localhost:8080/mcp.

Step 4: Add auth - one decorator

// mcp/analytics/index.ts
import { Tool, SchemaConstraint } from "@leanmcp/core";
import { AuthProvider, Authenticated } from "@leanmcp/auth";

const authProvider = new AuthProvider('cognito', {
  region: process.env.AWS_REGION,
  userPoolId: process.env.COGNITO_USER_POOL_ID,
  clientId: process.env.COGNITO_CLIENT_ID
});
await authProvider.init();

// Entire service is protected - token validated automatically
// from _meta.authorization.token in each request
@Authenticated(authProvider)
export class AnalyticsService {
  @Tool({
    description: "Get sales report - authenticated users only",
    inputClass: SalesReportInput
  })
  async getSalesReport(args: SalesReportInput) {
    // No token parsing needed here - LeanMCP handles it
    return { data: await fetchSalesData(args.region) };
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 5: Add a new service anytime

leanmcp add dashboard
# Creates mcp/dashboard/index.ts with boilerplate
# Auto-registers in main.ts
# Ready to customize immediately
Enter fullscreen mode Exit fullscreen mode

MCP Apps vs GPT Apps - Why Open Wins

OpenAI's GPT Apps launched in October 2025 with an impressive partner list. But it's ChatGPT-only, proprietary, and controlled by one company.

MCP Apps is an open standard, jointly governed by the MCP UI Community Working Group.

GPT Apps MCP Apps With LeanMCP
Platform ChatGPT only Claude, ChatGPT, VS Code, Goose Both, one server
Standard Proprietary (OpenAI) Open (SEP-1865) Supports both
Auth Not included Not included @Authenticated decorator
Observability Not included Not included logging: true
Multi-tenancy Your problem Your problem ✅ Per-user API keys built in
Type safety Manual Manual @SchemaConstraint decorators
Service discovery Manual Manual autoDiscover: true

The same LeanMCP server covers both ecosystems. One codebase, full coverage.


The Production Gap Nobody Talks About

The MCP spec gives you the protocol. It does not give you:

  • Multi-tenant auth - how do you authenticate each user separately?
  • Per-user tracking - who's consuming your API budget?
  • Secrets management - how do you inject API keys safely per-request?
  • Audit trails - for compliance, who did what and when?
  • Type-safe schemas - catch input errors before they reach your tool logic
  • Observability - when something breaks at 2am, how do you even know?

LeanMCP is purpose-built for this gap. It's the production runtime layer underneath your MCP App - not a framework on top, not another abstraction. The ground your agent runs on.

Here's what you get with one config line:

// Logging ON = dashboards for tool calls, latency, errors, per-user usage
await createHTTPServer({
  name: "my-mcp-app",
  version: "1.0.0",
  logging: true
});
Enter fullscreen mode Exit fullscreen mode

And the complete auth flow that usually takes a week to implement:

// Client calls your tool like this:
await mcpClient.callTool({
  name: "getSalesReport",
  arguments: { region: "APAC" },
  _meta: {
    authorization: {
      type: "bearer",
      token: "user-jwt-token"
    }
  }
});

// LeanMCP validates the token automatically.
// Your tool only runs if auth passes.
// No middleware code needed on your end.
Enter fullscreen mode Exit fullscreen mode

Real-World Use Cases

What MCP Apps actually unlocks:

  • 📊 Data exploration - Sales analytics tool returns a live dashboard. Filter by region, drill into accounts, export - without leaving the conversation
  • ⚙️ Configuration wizards - Selecting 'production' reveals security options dynamically. No sequential prompts
  • 📄 Document review - Contract tool displays PDF inline with highlighted clauses. Approve or flag in real time
  • 🛒 Commerce - Interactive product catalogs, size selectors, cart flows embedded in agent conversations
  • 📡 Real-time monitoring - Server health tool shows live metrics without re-running the tool

As of January 2026, officially supported in Claude (web + desktop), ChatGPT, Goose, and VS Code Insiders.


The Full Agent Stack

MCP Apps completes the platform:

Layer Technology
Tools - what agents can do MCP protocol
Auth - who can use them OAuth in MCP spec
UI - what users see MCP Apps (SEP-1865)
Runtime - how it all runs in production LeanMCP

The ext-apps repository already includes working examples: threejs-server for 3D visualization, map-server for interactive maps, pdf-server for document viewing, system-monitor-server for live dashboards.


TL;DR

  • 🚀 MCP Apps = interactive UIs inside Claude, ChatGPT, VS Code, Goose - open standard
  • 🔓 Unlike GPT Apps, cross-platform: write once, run everywhere
  • ⚡ LeanMCP = production runtime - auth, observability, type safety, multi-tenancy, all built in
  • 🛠️ One LeanMCP server covers both GPT Apps and MCP Apps
# Start in 60 seconds
npm install -g @leanmcp/cli
npx @leanmcp/cli create my-mcp-app
cd my-mcp-app && npm start
# -> http://localhost:8080/mcp
Enter fullscreen mode Exit fullscreen mode

Links:

Building an MCP App? Drop a comment - would love to see what you're working on. 👇

Top comments (0)