Skip to content

MCP Apps: generated typed client + shared bridge runtime from route contracts #594

Description

@ScriptedAlchemy

Problem

ScriptedAlchemy/cargo-hauler demonstrates that MCP App authors currently rebuild too much framework plumbing inside the browser bundle.

Its src/mcp/hauler/apps/dashboard.tsx manually owns:

  • JSON-RPC request ids;
  • window.parent.postMessage(..., '*');
  • pending request bookkeeping and 15 s timeouts;
  • tools/call request construction;
  • result envelope unwrapping (structuredContent nested or direct);
  • dozens of hand-written unknown interfaces for data already described by the route resultSchema;
  • runtime shape readers to turn those hand-written interfaces back into typed dashboard models.

The corresponding tool already exports canonical inputSchema / resultSchema. The browser App cannot consume a generated, browser-safe typed client for that contract, so protocol and schema knowledge is duplicated across server and client.

This is exactly the seam #592 intends to remove: Apps should be views over the canonical application graph, not mini MCP clients.

Direction

Generate a browser-safe App client surface from Application IR / route contracts.

Illustrative author experience:

import { createAppClient } from 'agent-bundle/app';
import type { Routes } from 'agent-bundle/routes';

const client = createAppClient<Routes>();

const status = await client.tools.hauler.hauler_status({ limit: 40 });
const record = await client.tools.hauler.hauler_result({ ticket });

or generated helpers:

import { tools } from 'agent-bundle/routes/client';

await tools.hauler.hauler_status.call({ limit: 40 });

Exact API is open. Required properties are not.

Bridge runtime

The framework should own:

Do not make each App use raw postMessage('*').

Types

The compiler already knows route input/output contracts. Emit browser-safe generated declarations from the same Application IR so importing types does not pull Zod or Node/runtime code into the App bundle.

For a route with resultSchema, client.call(...) should return the inferred structured result type directly or a typed result wrapper with an explicit .structured field. App code should not need to rediscover whether data is under structuredContent.

React integration

A thin optional React layer may provide query/subscription hooks, but the core client should not require React or Effect.

Examples:

const status = useTool('hauler/hauler_status', { limit: 40 }, {
  refetchInterval: 5_000,
});

This should reuse the same underlying client/runtime rather than introduce another transport implementation.

Relationship to #564

#564 should use this same browser bridge for standalone production hosting. Workbench preview, MCP-host embedded Apps, serve-app, and <plugin> web must not each grow different App client semantics.

Acceptance

  • Generated browser-safe types expose tool input/output contracts without bundling server route modules or Zod.
  • An App can call a known tool through a typed framework client without hand-writing JSON-RPC messages.
  • Structured results are decoded consistently with the MCP projection.
  • Error, timeout, cancellation, consent, and close/rebind behavior are framework-owned.
  • Transport validates the expected parent/source/origin semantics rather than requiring app code to use unrestricted postMessage('*') directly.
  • Workbench, serve-app, host embedding, and First-class web surface (production): ship a browser host for the plugin's MCP Apps inside the artifact, openable from the installed CLI (<plugin> web); /web in dev for parity #564's production browser host exercise the same core bridge contract.
  • cargo-hauler can delete its local JSON-RPC client, PendingRequest, manual StructuredContent envelope plumbing, and the protocol-shape interfaces duplicated from its result schemas.

Consumer evidence

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestmeta-frameworkAgent Bundle compiler-coupled meta-framework

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions