> ## Documentation Index
> Fetch the complete documentation index at: https://docs.leanmcp.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Decorators & GPT Apps

> Server-side decorators and ChatGPT-specific integration

# Decorators & GPT Apps

This page covers server-side decorators for linking tools to UI components, and ChatGPT-specific providers and hooks.

## @UIApp Decorator

Links an MCP tool to a React UI component for ext-apps hosts (Claude Desktop, MCP-compatible hosts).

When a tool decorated with `@UIApp` is called, the host will:

1. Execute the tool and get the result
2. Fetch the linked UI component as an HTML resource
3. Render the UI in an iframe with the tool result

### Basic Usage

```typescript theme={null}
import { Tool } from '@leanmcp/core';
import { UIApp } from '@leanmcp/ui';

class WeatherService {
  @Tool({ description: 'Get weather for a city' })
  @UIApp({ component: './WeatherCard' })
  async getWeather(args: { city: string }) {
    const weather = await fetchWeatherAPI(args.city);
    return {
      city: args.city,
      temperature: weather.temp,
      condition: weather.condition
    };
  }
}
```

<Note>
  Use a **path string** (e.g., `'./WeatherCard'`) for the component to avoid importing browser code in your server bundle. The CLI will resolve and build the component separately.
</Note>

### With Custom URI

```typescript theme={null}
@Tool({ description: 'Show dashboard' })
@UIApp({ 
  component: './DashboardView',
  uri: 'ui://analytics/dashboard',  // Custom resource URI
  title: 'Analytics Dashboard'       // HTML document title
})
async getDashboard() {
  return { /* dashboard data */ };
}
```

### Options

| Option      | Type                            | Description                                          |
| ----------- | ------------------------------- | ---------------------------------------------------- |
| `component` | `React.ComponentType \| string` | Component or path to component file                  |
| `uri`       | `string`                        | Custom resource URI (auto-generated if not provided) |
| `title`     | `string`                        | HTML document title                                  |
| `styles`    | `string`                        | Additional CSS styles                                |

### The UI Component

Your UI component receives the tool result via `useToolResult`:

```tsx theme={null}
// WeatherCard.tsx
import { AppProvider, useToolResult, Card, RequireConnection } from '@leanmcp/ui';
import '@leanmcp/ui/styles.css';

interface WeatherData {
  city: string;
  temperature: number;
  condition: string;
}

function WeatherCardContent() {
  const { result, loading } = useToolResult<WeatherData>();

  if (loading || !result) return <div>Loading...</div>;

  return (
    <Card>
      <h2>{result.city}</h2>
      <p className="text-4xl font-bold">{result.temperature}°C</p>
      <p>{result.condition}</p>
    </Card>
  );
}

export default function WeatherCard() {
  return (
    <AppProvider appInfo={{ name: 'WeatherCard', version: '1.0.0' }}>
      <RequireConnection>
        <WeatherCardContent />
      </RequireConnection>
    </AppProvider>
  );
}
```

***

## @GPTApp Decorator

Links an MCP tool to a React UI component specifically for ChatGPT GPT Actions.

```typescript theme={null}
import { Tool } from '@leanmcp/core';
import { GPTApp } from '@leanmcp/ui';

class AnalyticsService {
  @Tool({ description: 'Show analytics dashboard' })
  @GPTApp({ component: './AnalyticsDashboard' })
  async getAnalytics(args: { period: string }) {
    return { /* analytics data */ };
  }
}
```

### Options

| Option      | Type                            | Description                         |
| ----------- | ------------------------------- | ----------------------------------- |
| `component` | `React.ComponentType \| string` | Component or path to component file |
| `uri`       | `string`                        | Custom resource URI                 |
| `title`     | `string`                        | HTML document title                 |

***

## GPTAppProvider

Context provider for ChatGPT Apps using the native `window.openai` SDK.

### Basic Setup

```tsx theme={null}
import { GPTAppProvider } from '@leanmcp/ui';
import '@leanmcp/ui/styles.css';

function MyGPTApp() {
  return (
    <GPTAppProvider appName="MyApp">
      <MyContent />
    </GPTAppProvider>
  );
}
```

### Props

| Prop       | Type        | Description                 |
| ---------- | ----------- | --------------------------- |
| `appName`  | `string`    | App name for identification |
| `children` | `ReactNode` | App content                 |

***

## useGptApp

Access the GPT App context including connection state and theme.

```tsx theme={null}
import { useGptApp } from '@leanmcp/ui';

function MyComponent() {
  const { 
    isConnected,   // Whether connected to ChatGPT
    theme,         // 'light' | 'dark'
    displayMode,   // Display mode
    locale,        // User locale
    maxHeight,     // Max height from host
    callTool,      // Call a server tool
    error          // Connection error
  } = useGptApp();

  if (!isConnected) {
    return <div>Connecting to ChatGPT...</div>;
  }

  return <div>Connected! Theme: {theme}</div>;
}
```

***

## useGptTool

Call MCP tools via ChatGPT's SDK with loading and error states.

### Basic Usage

```tsx theme={null}
import { useGptTool } from '@leanmcp/ui';

function DataViewer() {
  const { call, result, loading, error } = useGptTool('get-data');

  useEffect(() => {
    call({ id: '123' });
  }, []);

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;

  return <pre>{JSON.stringify(result, null, 2)}</pre>;
}
```

### Return Value

| Property      | Type                      | Description      |
| ------------- | ------------------------- | ---------------- |
| `call`        | `(args?) => Promise<any>` | Execute the tool |
| `result`      | `any`                     | Tool result      |
| `loading`     | `boolean`                 | Loading state    |
| `error`       | `Error \| null`           | Error if any     |
| `isConnected` | `boolean`                 | Connection state |

***

## useAuth

Handle OAuth authentication flows in GPT Apps. This hook integrates with ChatGPT's native OAuth linking UI.

### Basic Usage

```tsx theme={null}
import { useAuth } from '@leanmcp/ui';

function ProtectedContent() {
  const { 
    isAuthenticated,  // Whether user is authenticated
    isLoading,        // Auth check in progress
    user,             // User info (if authenticated)
    error,            // Auth error (if any)
    login,            // Trigger OAuth flow
    logout,           // Clear auth state
  } = useAuth();

  if (isLoading) {
    return <div>Checking authentication...</div>;
  }

  if (!isAuthenticated) {
    return (
      <div>
        <p>Please sign in to continue</p>
        <button onClick={login}>Sign In</button>
      </div>
    );
  }

  return (
    <div>
      <p>Welcome, {user?.name}!</p>
      <button onClick={logout}>Sign Out</button>
    </div>
  );
}
```

### How It Works

1. When a tool returns `_meta["mcp/www_authenticate"]`, ChatGPT displays an OAuth linking prompt
2. The `useAuth` hook detects this and updates `isAuthenticated` state
3. Calling `login()` triggers the OAuth flow by calling an auth-required tool
4. After successful OAuth, ChatGPT automatically retries the tool call

### Return Value

| Property          | Type               | Description                   |
| ----------------- | ------------------ | ----------------------------- |
| `isAuthenticated` | `boolean`          | Whether user is authenticated |
| `isLoading`       | `boolean`          | Auth check in progress        |
| `user`            | `AuthUser \| null` | User info if authenticated    |
| `error`           | `Error \| null`    | Auth error if any             |
| `login`           | `() => void`       | Trigger OAuth authentication  |
| `logout`          | `() => void`       | Clear auth state              |

### AuthUser Type

```typescript theme={null}
interface AuthUser {
  sub?: string;      // User ID
  email?: string;    // Email address
  name?: string;     // Display name
  picture?: string;  // Avatar URL
}
```

### With Protected Tools

Combine `useAuth` with tools that require authentication:

```tsx theme={null}
import { useAuth, useGptTool, Button, Card } from '@leanmcp/ui';

function PrivateData() {
  const { isAuthenticated, user, login } = useAuth();
  const { call, result, loading } = useGptTool('get-private-data');

  if (!isAuthenticated) {
    return (
      <Card>
        <p>This feature requires authentication</p>
        <Button onClick={login}>Connect Account</Button>
      </Card>
    );
  }

  return (
    <Card>
      <p>Logged in as {user?.email}</p>
      <Button onClick={() => call()} disabled={loading}>
        {loading ? 'Loading...' : 'Fetch Private Data'}
      </Button>
      {result && <pre>{JSON.stringify(result, null, 2)}</pre>}
    </Card>
  );
}
```

### Server-Side Auth Errors

For the auth flow to work, your server tool should return MCP-compliant auth errors:

```typescript theme={null}
import { Tool, createAuthError } from '@leanmcp/core';

export class DataService {
  @Tool({ description: 'Get private user data' })
  async getPrivateData(args: any, meta?: any) {
    const token = meta?.authorization?.token;
    
    if (!token) {
      return createAuthError('Authentication required', {
        resourceMetadataUrl: `${process.env.PUBLIC_URL}/.well-known/oauth-protected-resource`,
        error: 'invalid_token',
      });
    }
    
    // Proceed with authenticated request...
  }
}
```

***

## Environment Differences

### ext-apps vs ChatGPT

| Feature       | ext-apps (AppProvider)    | ChatGPT (GPTAppProvider) |
| ------------- | ------------------------- | ------------------------ |
| Transport     | PostMessage iframe        | window\.openai SDK       |
| Theme sync    | Full CSS variables        | Basic light/dark         |
| Tool calls    | `useTool`, `callTool`     | `useGptTool`             |
| Resources     | `useResource`             | Not supported            |
| Messages      | `useMessage`              | Not supported            |
| Display modes | Inline, modal, fullscreen | Inline only              |

### Shared Features

Both environments support:

* Tool execution
* Theme awareness (light/dark)
* Auto-applied styles via `@leanmcp/ui/styles.css`
* Testing with `MockAppProvider`

***

## Complete GPT App Example

```tsx theme={null}
import { GPTAppProvider, useGptTool, Card, Button } from '@leanmcp/ui';
import '@leanmcp/ui/styles.css';

function StockDashboard() {
  const { call, result, loading, error } = useGptTool('get-stock-price');

  return (
    <div className="p-4">
      <Button onClick={() => call({ symbol: 'AAPL' })} disabled={loading}>
        {loading ? 'Fetching...' : 'Get Apple Stock'}
      </Button>

      {error && (
        <div className="text-red-500 mt-2">Error: {error.message}</div>
      )}

      {result && (
        <Card className="mt-4">
          <h3>{result.symbol}</h3>
          <p className="text-2xl font-bold">${result.price}</p>
          <p className={result.change > 0 ? 'text-green-500' : 'text-red-500'}>
            {result.change > 0 ? '+' : ''}{result.change}%
          </p>
        </Card>
      )}
    </div>
  );
}

export default function App() {
  return (
    <GPTAppProvider appName="StockDashboard">
      <StockDashboard />
    </GPTAppProvider>
  );
}
```

***

## Related

* [@leanmcp/ui Overview](/sdk/ui) - Introduction to MCP-native React components
* [UI Components](/sdk/ui-components) - Pre-built MCP components
* [UI Hooks](/sdk/ui-hooks) - All available React hooks
* [Authentication Overview](/sdk/auth) - Server-side authentication with `@Authenticated`
* [OAuth Server & Proxy](/sdk/auth-oauth-server) - Build OAuth authorization servers
* [Auth & Payment Guide](/guides/auth-and-payment) - Complete authentication integration guide
