🥞PancakeJS

Capabilities Model

Not all features are available in every host environment. PancakeJS provides a capabilities system for graceful degradation and feature detection.

Available Capabilities

CapabilityMCP AppsChatGPT AppsDescription
followups⚠️*Send follow-up prompts
modalsOpen modal dialogs
filesAccess file system
themeGet theme (light/dark)
localeGet user locale
persistentState⚠️*Persist widget state

*May vary based on host implementation

Checking Capabilities

Using useHost()

function MyWidget() {
  const host = useHost();

  return (
    <div>
      {host.capabilities.followups && (
        <button onClick={() => sendFollowUp('...')}>
          Continue Conversation
        </button>
      )}
    </div>
  );
}

Using useCapability()

function MyWidget() {
  const hasFollowups = useCapability('followups');

  if (hasFollowups) {
    // Feature available
  }
}

Using Specific Hooks

Capability-specific hooks return support status:

function MyWidget() {
  const followUp = useFollowUp();

  if (followUp.supported) {
    await followUp.send('Tell me more about this');
  } else {
    // Show alternative UI or message
  }
}

Strict Mode

Enable strict mode to catch unsupported capability usage during development:

<UniversalAppProvider strict={true}>
  <MyWidget />
</UniversalAppProvider>

In strict mode, using an unsupported capability throws an error:

CapabilityNotSupportedError: Capability "followups" is not supported by chatgpt

Graceful Degradation Patterns

Conditional Rendering

function FollowUpButton() {
  const followUp = useFollowUp();

  if (!followUp.supported) {
    return null;  // Don't render
  }

  return (
    <button onClick={() => followUp.send('...')}>
      Ask More
    </button>
  );
}

Alternative UI

function ActionButton() {
  const followUp = useFollowUp();

  if (followUp.supported) {
    return (
      <button onClick={() => followUp.send('...')}>
        Ask AI
      </button>
    );
  }

  // Fallback: copy to clipboard
  return (
    <button onClick={() => copyToClipboard('...')}>
      Copy Response
    </button>
  );
}

Feature Flags

function MyWidget() {
  const host = useHost();

  const features = {
    canAskFollowups: host.capabilities.followups,
    canPersistState: host.capabilities.persistentState,
    hasTheme: host.capabilities.theme,
  };

  return <WidgetContent features={features} />;
}

Theme Support

Access the host's theme:

function MyWidget() {
  const theme = useTheme('light');  // default if not provided

  return (
    <div className={theme === 'dark' ? 'dark-mode' : 'light-mode'}>
      {/* Content */}
    </div>
  );
}

Or use useHost():

function MyWidget() {
  const host = useHost();

  return (
    <div style={{
      background: host.context.theme === 'dark' ? '#1a1a1a' : '#ffffff',
      color: host.context.theme === 'dark' ? '#ffffff' : '#000000',
    }}>
      {/* Content */}
    </div>
  );
}

Locale Support

Access the user's locale:

function MyWidget() {
  const locale = useLocale('en');  // default if not provided

  return (
    <div>
      <FormattedDate locale={locale} date={new Date()} />
    </div>
  );
}

Adding New Capabilities

When new host features become available, the SDK will add them to the capabilities model. Check the changelog for updates.

Your widgets can gracefully handle new capabilities by defaulting to false:

// Future-proof capability check
const hasNewFeature = host.capabilities.newFeature ?? false;

Best Practices

  1. Always check before using — Don't assume a capability exists
  2. Provide fallbacks — Give users an alternative when features aren't available
  3. Use strict mode in dev — Catch issues before production
  4. Test in both harnesses — Verify behavior in MCP and ChatGPT environments

Next Steps

On this page