MCP App Support

July 8, 2026 · View on GitHub

MCP App is the module that treats third-party MCP applications as first-class citizens in OpenClaw.NET. It provides manifest-based automatic discovery, lifecycle and process management, and tool bridging — letting MCP applications be installed, discovered, and used much like VS Code extensions.

OpenClaw integrates with the MCP App ecosystem MCP-first, compatible with the Model Context Protocol 2025-03-26 spec and the text/html;profile=mcp-app interactive UI specification.

How It Differs from Existing MCP Integrations

OpenClaw already has two layers of MCP support. MCP App is the third:

LayerModuleDirectionPurpose
MCP ServerOpenClaw.Gateway/McpOutbound exposureExpose OpenClaw as an MCP Server for clients like Claude Desktop
MCP ClientOpenClaw.Agent/PluginsInbound consumptionConnect to external MCP Servers and register their tools as OpenClaw Agent native tools
MCP AppOpenClaw.McpAppHosting & managementDiscover, install, isolate, and manage third-party MCP applications with full lifecycle control

MCP App is designed for MCP applications that need to be packaged and distributed — each App carries its own manifest, version number, tool prefix, and optional UI resources.

What It Adds

  • Automatic discovery based on openclaw.mcpapp.json manifest files
  • Recursive multi-path scanning with nested directory support
  • Two transport modes: stdio and http
  • Allowlist / denylist filtering with glob pattern matching
  • Per-app enable/disable toggle and parameter overrides
  • Automatic tool name prefix application to avoid collisions
  • MCP App UI resource detection (text/html;profile=mcp-app)
  • Six-stage lifecycle tracking (Discovered → Validated → Loaded → Running → Stopped → Failed)
  • Seamless integration with NativePluginRegistry — discovered tools become immediately available to the Agent
  • Browser host endpoints that let an MCP App UI reuse the same connected upstream MCP session as the Agent

Browser Host Endpoints

OpenClaw.NET exposes a small gateway-facing host surface for browser-side MCP App UIs:

RoutePurpose
/apps/healthReturns the selected MCP App id plus the gateway MCP endpoint the browser should connect to
/apps/chatStreams chat-host SSE events (session, text, tool, result, done) into the existing GatewayAppRuntime
/apps/mcp/{appId}Proxies MCP requests to the already connected McpClient for that App

The important detail is that browser UIs should connect to /apps/mcp/{appId}, not directly to the App's raw upstream MCP URL. That keeps browser-driven MCP calls and model-driven MCP calls on the same OpenClaw-managed session.

Session Reuse Behavior

  • /apps/health returns an mcp URL that points back to the gateway's own /apps/mcp/{appId} route.
  • /apps/mcp/{appId} forwards tools/list, resources/list, resources/read, and tools/call to the App already loaded in McpAppRegistry.
  • When the browser includes ?sessionId=... on the MCP endpoint URL, OpenClaw injects that value into tools/call as _meta.sessionId before forwarding upstream.
  • /apps/chat creates or resumes a gateway session with that same id and streams host-friendly SSE frames back to the browser.

This is the bridge that lets a rich MCP App UI and the Agent collaborate against the same App session instead of creating two unrelated MCP connections.

Not the Same as Workspace MCP Server Hot Reload

/admin/workspace/mcp is a separate admin surface for ordinary Plugins:Mcp server definitions. It persists workspace MCP server JSON and hot-reloads the standard MCP plugin tool surface. It does not manage manifest-discovered MCP Apps or replace the /apps/* host/proxy routes described in this document.

Browser Compatibility Notes

  • tools/list on /apps/mcp/{appId} is intentionally a raw pass-through. App-only tools still appear there because the browser host may need them.
  • Model visibility filtering happens later, when OpenClaw registers App tools into NativePluginRegistry for Agent use.
  • UI tools with _meta.ui.resourceUri suppress structuredContent when results flow back into the model, but the browser-side MCP proxy still forwards normal MCP responses unchanged.
  • For cross-origin browser MCP clients, OpenClaw allows the MCP Streamable HTTP headers mcp-protocol-version and Mcp-Session-Id in CORS.

Architecture

appsettings.json / GatewayConfig.McpApps


McpAppDiscovery
  Scans DiscoveryPaths (default ./mcpapps/)
  Looks for ** /openclaw.mcpapp.json
  Deserializes + validates + filters (allow/deny)


McpAppInstallState (one per App)
  Lifecycle: Discovered → Validated → [Disabled|Failed] → Loaded → Running → Stopped


McpAppRegistry
  Central registry for all Apps
  Calls McpAppServer to establish connections


McpAppServer
  Connects to the MCP App via stdio subprocess / HTTP endpoint
  Enumerates tools, resources, and prompts
  Produces an IMcpAppInfoProvider


McpAppNativeTool (implements ITool)
  Registered in NativePluginRegistry
  Agent can call MCP App tools the same as built-in tools

Project Structure

src/mcpapp/
├── OpenClaw.McpApp/
│   ├── OpenClaw.McpApp.csproj      # Depends on OpenClaw.Core + ModelContextProtocol
│   ├── shared/
│   │   └── McpAppInfoProvider.cs   # IMcpAppInfoProvider + descriptor types + default impl
│   ├── Models/
│   │   ├── McpAppManifest.cs       # openclaw.mcpapp.json model + AOT JSON source gen
│   │   └── McpAppInstallState.cs   # Install state + six-stage lifecycle enum
│   ├── McpAppDiscovery.cs          # Manifest scanning, loading, validation, allow/deny filtering
│   ├── McpAppServer.cs             # Single-App lifecycle — connect, enumerate, disconnect
│   ├── McpAppToolProvider.cs       # McpAppNativeTool — bridges MCP App tools to ITool
│   └── McpAppServiceExtensions.cs  # DI registration + McpAppRegistry
└── test-apps/
    └── GroceryInventory/
        └── openclaw.mcpapp.json    # Example MCP App manifest (GroceryInventory.Api)

Integration Points

Integration PointFileWhat It Does
Config modelGatewayConfig.csAdds McpApps config section
Plugin configsPluginModels.csMcpAppsConfig + McpAppEntryConfig classes
DI registrationToolServicesExtensions.csCalls AddOpenClawMcpAppServices()
Runtime initRuntimeInitializationExtensions.csRegisterMcpAppToolsAsync on startup + cleanup on shutdown
CompositionRuntimeInitializationExtensions.CompositionStages.csAdds McpAppRegistry to RuntimeServices
Gateway bridgeMcpAppToolRegistrationExtensions.csGateway-layer tool registration into NativePluginRegistry
Project referenceOpenClaw.Gateway.csprojReferences OpenClaw.McpApp

Manifest Format (openclaw.mcpapp.json)

{
  "id": "grocery-inventory",
  "name": "Grocery Inventory Dashboard",
  "description": "Multi-store grocery inventory management MCP App with interactive dashboard. Manage products, categories, suppliers, stores, and inventory levels across a retail chain. Includes stock adjustment, low-stock alerts, restock recommendations, and purchase order drafting.",
  "version": "1.0.0",
  "protocolVersion": "2025-03-26",
  "iconUrl": "https://img.icons8.com/color/48/grocery-store.png",
  "author": "OpenClaw MCP App Demo",
  "license": "MIT",
  "homepageUrl": "https://github.com/boclifton-MSFT/MCPAppsDemo",
  "tags": ["grocery", "inventory", "retail", "supply-chain", "dashboard"],
  "transport": "http",
  "url": "https://localhost:5001/mcp",
  "hasUi": true,
  "uiResourceUri": "ui://grocery/store-dashboard.html",
  "category": "data",
  "capabilities": ["tools", "resources", "prompts", "completions"],
  "startupTimeoutSeconds": 10,
  "requestTimeoutSeconds": 60,
  "toolNamePrefix": "grocery.",
  "headers": {}
}

Manifest Fields

FieldTypeRequiredDescription
idstringYesUnique application identifier
namestringNoHuman-readable display name
descriptionstringNoShort feature description
versionstringRecommendedSemantic version
protocolVersionstringNoMCP protocol version, default 2025-03-26
transportstringNostdio or http, default stdio
commandstringRequired for stdioLaunch command
argumentsstring[]NoCommand-line arguments
workingDirectorystringNoWorking directory for the process
urlstringRequired for httpServer endpoint URL
headersdictNoHTTP request headers
environmentdictNoProcess environment variables
hasUiboolNoWhether the App includes an MCP App UI bundle
uiResourceUristringNoPrimary UI resource URI
toolNamePrefixstringNoTool name prefix (e.g. grocery.)
startupTimeoutSecondsintNoStartup timeout, default 15 sec
requestTimeoutSecondsintNoRequest timeout, default 60 sec
tagsstring[]NoCategorization tags
categorystringNoGrouping category
capabilitiesstring[]NoCapability declarations, default ["tools"]

Configuration

GatewayConfig

Add to appsettings.json:

{
  "McpApps": {
    "Enabled": true,
    "DiscoveryPaths": ["./mcpapps"],
    "AllowlistSemantics": "legacy",
    "Allow": ["*"],
    "Deny": [],
    "Entries": {
      "grocery-inventory": {
        "Enabled": true,
        "Url": "https://localhost:5001/mcp",
        "ToolNamePrefix": "grocery.",
        "StartupTimeoutSeconds": 15
      }
    }
  }
}

Configuration Fields

FieldDefaultDescription
EnabledfalseMaster toggle
DiscoveryPaths["./mcpapps"]Directories to scan for openclaw.mcpapp.json
AllowlistSemantics"legacy"legacy (empty=allow all) or strict (empty=deny all)
Allow[]Allowlist, supports * and prefix-* wildcards
Deny[]Denylist, deny takes precedence over allow
Entries{}Per-App fine-grained overrides

Entries Overrides

Entries can override these manifest fields per App ID:

  • Enabled — disable a specific App individually
  • Transport, Command, Url — override connection method
  • ToolNamePrefix — override tool name prefix
  • StartupTimeoutSeconds, RequestTimeoutSeconds — override timeouts
  • Environment — append environment variables

Lifecycle

Discovered ──→ Validated ──→ Loaded ──→ Running
     │              │            │           │
     │              │            │           │
     └──────────────┴────────────┴─→ Failed  │

                                     Stopped ←
StateTriggerDescription
DiscoveredManifest file found by scannerInitial state, not yet validated
ValidatedPassed field validationManifest is valid, ready to load
DisabledFiltered by allow/deny or entry configWill not be loaded
LoadedConnection to MCP Server startedConnection in progress
RunningSuccessfully enumerated tools/resources/promptsTools are available
StoppedExplicit disconnect or DisposeConnection closed
FailedConnection or enumeration errorLastError records the cause

Tool Bridging

Every tool discovered from an MCP App is registered into the NativePluginRegistry via McpAppNativeTool (which implements ITool):

  • Registration ID: mcpapp:{appId} (e.g. mcpapp:grocery-inventory)
  • Tool name prefix: automatically applied from the manifest's toolNamePrefix
  • Visibility handling: tools marked with MCP App visibility metadata such as "ui": { "visibility": ["app"] } stay available to the browser host but are not registered as model-visible Agent tools
  • Error handling: invalid JSON arguments and remote execution errors return result text prefixed with Error:
  • Structured output: supports both text content and structured content from MCP tools, except that UI tools suppress structuredContent when OpenClaw feeds results back into the model
// Internal flow — automatically executed during Gateway startup
await appRegistry.RegisterMcpAppToolsAsync(
    nativeRegistry,
    config.McpApps,
    cancellationToken);

After registration, MCP App tools participate in tool selection alongside built-in tools and plugin tools through the unified preference resolver (NativePluginRegistry.ResolvePreference).

Authoring an MCP App

An MCP App can be any application implementing the MCP protocol. Minimal example (ASP.NET Core):

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddMcpServer()
    .WithToolsFromAssembly()
    .WithHttpTransport();

var app = builder.Build();
app.MapMcp("/mcp");
app.Run();

Then create an openclaw.mcpapp.json in the project root, setting transport: "http" and url to point to the MCP endpoint.

For MCP Apps that include an interactive UI (such as GroceryInventory.Api's dashboard), serve the HTML resource with the "text/html;profile=mcp-app" MIME type, and reference it from tool call _meta.ui.resourceUri.

Testing Tutorial: GroceryInventory.Api

GroceryInventory.Api is a complete multi-store grocery inventory management MCP App, used as the test fixture for OpenClaw.McpApp. Its manifest lives at src/mcpapp/test-apps/GroceryInventory/openclaw.mcpapp.json.

Application Architecture

GroceryInventory.Api (ASP.NET Core + MCP)
├── Program.cs                     # Entry point: AddMcpServer() + MapMcp("/mcp")
├── Models/
│   ├── Category.cs                # Category (Id, Name)
│   ├── Product.cs                 # Product (Id, Sku, Name, UnitPrice, UnitOfMeasure)
│   ├── Supplier.cs                # Supplier (Id, Name, ContactEmail, Phone)
│   ├── Store.cs                   # Store (Id, Name, City, State)
│   └── InventoryItem.cs           # Inventory row (StoreId, ProductId, QuantityOnHand, ReorderThreshold)
├── Services/
│   ├── IInventoryService.cs       # Data layer abstraction
│   └── InMemoryInventoryService.cs # In-memory implementation with seed data
├── Endpoints/                     # REST API (/api/*)
│   ├── CategoryEndpoints.cs
│   ├── ProductEndpoints.cs
│   ├── SupplierEndpoints.cs
│   ├── StoreEndpoints.cs
│   └── InventoryEndpoints.cs
├── McpTools/
│   ├── Tools.cs                   # Core MCP tools (get_categories, get_inventory, set_inventory…)
│   ├── DashboardTools.cs          # Dashboard tool (show_store_inventory_dashboard + structured output)
│   ├── ResourceTemplates.cs       # Dynamic resource templates (inventory://stores/{storeId}/summary)
│   ├── Prompts.cs                 # MCP prompt templates (analyze store, draft order…)
│   ├── Completions.cs             # MCP completion handler
│   └── ElicitationTools.cs        # Elicitation-style interactive tools
├── McpApp/
│   ├── GroceryDashboardResource.cs # MCP App UI resource (text/html;profile=mcp-app)
│   ├── src/                       # TypeScript frontend source (Vite build)
│   └── dist/index.html            # Build output: single-file interactive dashboard
└── Properties/
    └── launchSettings.json

MCP Tools (12+ tools)

Data Query Tools (McpTools.cs)

Tool NameDescription
grocery.get_categoriesQuery categories, optional id filter
grocery.get_productsQuery products, supports id/categoryId/supplierId filters
grocery.get_suppliersQuery suppliers, optional id
grocery.get_storesQuery stores, optional id
grocery.get_inventoryQuery inventory, supports storeId/productId filters
grocery.get_low_stockList items at or below reorder threshold
grocery.set_inventoryCreate or replace an inventory row (absolute values)
grocery.adjust_stockAdjust on-hand quantity by delta (positive/negative), sends resource change notification

Dashboard Tool (DashboardTools.cs)

Tool NameDescription
grocery.show_store_inventory_dashboardRender interactive multi-store inventory dashboard, with _meta.ui.resourceUri pointing to ui://grocery/store-dashboard.html

Elicitation Tools (ElicitationTools.cs)

Supports step-by-step data entry and guided decision-making.

MCP Resources

Resource URIDescription
ui://grocery/store-dashboard.htmlMCP App UI: single-file HTML dashboard (MIME: text/html;profile=mcp-app)
inventory://stores/{storeId}/summaryPer-store inventory summary JSON
inventory://products/{productId}/detailsProduct details with per-store stock levels

MCP Prompts

Prompt NameDescription
analyze_store_performanceAnalyze a store's inventory health: coverage, low-stock hotspots, value at risk, suggested actions
draft_supplier_orderDraft a supplier purchase order from low-stock data, with routine/urgent/emergency urgency levels
explain_inventory_termExplain an inventory management term in plain language

Integrating GroceryInventory.Api with OpenClaw

Step 1: Start GroceryInventory.Api

cd E:\GitHub\MCPAppsDemo\src\GroceryInventory.Api
dotnet run
# Output: Now listening on: https://localhost:5001

Note: After first checkout or after modifying the frontend, run npm run build (in the McpApp/ directory) to ensure McpApp/dist/index.html is the latest build. Skipping this step renders a placeholder page instead of the interactive dashboard.

Step 2: Deploy the Manifest

Copy the manifest into OpenClaw's discovery path:

# Create the discovery directory
mkdir e:\GitHub\openclaw.net\.openclaw\mcpapps\grocery-inventory\

# Copy the manifest
copy src\mcpapp\test-apps\GroceryInventory\openclaw.mcpapp.json \
     e:\GitHub\openclaw.net\.openclaw\mcpapps\grocery-inventory\

Step 3: Configure OpenClaw Gateway

Enable MCP App support in appsettings.json:

{
  "McpApps": {
    "Enabled": true,
    "DiscoveryPaths": [".openclaw/mcpapps"],
    "Allow": ["*"],
    "Entries": {
      "grocery-inventory": {
        "Enabled": true
      }
    }
  }
}

Step 4: Start Gateway and Verify

cd e:\GitHub\openclaw.net
dotnet run --project src/OpenClaw.Gateway

Expected startup log output:

info: OpenClaw.McpApp.McpAppDiscovery[0]
      Scanning for MCP Apps in: .openclaw/mcpapps
info: OpenClaw.McpApp.McpAppDiscovery[0]
      Discovered McpApp 'grocery-inventory' (Grocery Inventory Dashboard) v1.0.0
info: OpenClaw.McpApp.McpAppServer[0]
      Connecting to McpApp 'grocery-inventory' via http
info: OpenClaw.McpApp.McpAppServer[0]
      McpApp 'grocery-inventory' connected: 12 tools, 3 resources, 3 prompts

Verify tools are registered — check the OpenClaw Admin panel or MCP client; tools should appear with the grocery. prefix.

Step 5: Call MCP App Tools

Via Agent conversation or MCP client:

> Show store inventory dashboard for all stores
> get_low_stock, storeId=1
> Analyze the Downtown Market store's inventory health (use analyze_store_performance prompt)

Running the Unit Tests

GroceryInventory.Api's MCP tool structure has a mirror test class GroceryMcpTools in McpAppTests.cs, verifying tool discovery, argument passing, and structured output integration paths:

cd e:\GitHub\openclaw.net
dotnet test src/OpenClaw.Tests --filter "FullyQualifiedName~McpAppTests"
# Output: Passed! - Failed: 0, Passed: 47

Testing

The MCP App module has comprehensive unit test coverage (47 test cases):

Test CategoryCountCoverage
Manifest serialization4JSON round-trip, minimal JSON defaults, stdio transport, invalid JSON
Install state3Defaults, lifecycle transition timestamps, validation errors
InfoProvider3Basic properties, name fallback, descriptor management
Discovery16Disabled return, invalid paths, valid loading, nested scan, required field validation, unsupported transport, invalid JSON skip, allow/deny, strict allowlist semantics, invalid allowlist semantics, glob matching, entry config disable
NativeTool4HTTP invocation, argument passing, invalid JSON error, array JSON error
Server8Connection enumeration, idempotent reconnect, disconnect state transition, invalid command failure, Dispose cleanup, manifest/entryConfig prefix override, transport override normalization
Registry4Graceful degradation on failure, GetApp not found, idempotent loading, double Dispose
Config models2McpAppsConfig and McpAppEntryConfig defaults
Descriptor models3Default schema, UI resource flag, prompt arguments

Tests reuse the existing McpServerToolRegistryTests pattern of spinning up in-process MCP HTTP Servers via WebApplication.CreateSlimBuilder() for end-to-end validation.

Troubleshooting

  • No Apps discovered: check McpApps.Enabled=true, DiscoveryPaths exists and is readable, subdirectories contain openclaw.mcpapp.json
  • App marked Invalid: inspect ValidationErrors — common causes are a missing id field or stdio mode without a command
  • Connection failure (Failed): inspect LastError — check url reachability, command executability, whether startupTimeoutSeconds is sufficient
  • Tools not registered: verify the App is not filtered by Deny or Entries[appId].Enabled=false, verify the MCP Server's tools/list response is non-empty
  • Tool name collisions: assign a unique toolNamePrefix per App
  • UI resource not detected: ensure the resource MIME type is text/html;profile=mcp-app and hasUi: true is set in the manifest