Token导航 LogoToken导航

Agent Skill · 研究检索

chatgpt-app-builder(chatgpt 应用程序构建器)

chatgpt-app-builder 用于查找、检索和筛选相关信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要根据关键词、任务场景或来源线索快速定位候选结果时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

chatgpt-app-builder 是研究检索类 Agent Skill,可用宿主为 Codex、Claude、Cursor、Gemini CLI,通过GitHub 仓库获取,权限为调用外部服务;许可证为 MIT,第三方扫描已通过,按安装量在研究检索类的 73,675 个 Skill 里排第 41,504。

总安装 1,164周安装 50GitHub Stars 1下载 408核验 2026-05-01

安装说明

本站只整理中文说明,不托管安装包,也不代用户安装。
GitHub
许可证
MIT

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:chatgpt-app-builder(chatgpt 应用程序构建器)
来源仓库:https://github.com/mcp-use/skills
仓库路径:skills/chatgpt-app-builder
安装命令:
npx skills add https://github.com/mcp-use/skills --skill chatgpt-app-builder
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。不同来源提供的安装方式可能略有差异;本站展示可直接复制的安装命令,安装前请核对来源页面。

skills.shnpx skills
npx skills add https://github.com/mcp-use/skills --skill chatgpt-app-builder

这个 Skill 是做什么的

研究检索类 Skill 主要解决「找资料、核对事实、把来源整理清楚」的问题,通常会让助手先检索再总结。

用于查找、检索和筛选相关信息。chatgpt-app-builder 属于研究检索类 Skill,可作为该场景下的辅助能力补充。适合在关键词搜索或任务场景中快速定位候选结果。通过 GitHub 安装,建议核验权限范围和联网行为。使用前应确认维护状态及是否会触发文件读写操作。可结合原始 README 进一步了解具体用法。

来源仓库内路径:skills/chatgpt-app-builder

上手步骤

4 步
  1. 1

    确认自己的宿主工具

    公开资料标注支持 Codex、Claude、Cursor、Gemini CLI;先确认自己用的是其中之一,再按对应工具的 Skills 目录放置。

  2. 2

    拿到安装命令

    可以直接复制的命令是 npx skills add https://github.com/mcp-use/skills --skill chatgpt-app-builder;命令里的占位符换成自己的路径或密钥再执行。

  3. 3

    安装后重启宿主

    命令执行完要完全退出宿主工具再打开,让新装的 Skill 被重新扫描到,否则列表里可能看不到。

  4. 4

    按最小权限试用

    它需要调用外部服务,先在测试目录或测试账号里跑,确认没问题再放到正式项目。

常见问题

6 条

chatgpt-app-builder 怎么安装?

在终端执行 npx skills add https://github.com/mcp-use/skills --skill chatgpt-app-builder。装完完全退出宿主工具再打开,让它重新扫描本地 Skill。

chatgpt-app-builder 支持哪些 AI 工具?

公开资料标注支持 Codex、Claude、Cursor、Gemini CLI。其他工具只要能读取本地 Skill 目录,一般也能手动放进去,但要自己确认触发方式是否一致。

chatgpt-app-builder 需要什么权限?

权限级别是调用外部服务。该 Skill 可能调用第三方服务、云服务或外部模型 API,使用前需要确认账号、额度、数据发送范围和服务条款。

chatgpt-app-builder 是免费的吗?

来源标注的许可证是 MIT。本站不代收任何费用,也不托管安装包;能否商用、要不要署名,以许可证原文为准。

chatgpt-app-builder 用的人多吗?

公开数据是累计安装 1,164 次,近一周安装 50 次,下载 408 次,GitHub 1 星。这些数字来自第三方目录统计,只能当作热度参考,不代表质量或安全性。

chatgpt-app-builder 安全吗?

第三方扫描结果为通过,但这只覆盖已知的静态检查项,不能替代自己读一遍源码和安装命令。

值得选的地方和要先想清楚的地方

值得选的地方

  • 同时适配 4 种工具,换客户端不用重新找替代品。
  • 3 个目录同时收录,说明这个 Skill 流传比较广。
  • 许可证明确为 MIT,商用和分发边界好判断。
  • 第三方扫描已通过,常见的高风险行为没有命中。

先想清楚的地方

  • 第三方目录整理,版本更新和行为细节以来源仓库为准。

适不适合你

更适合

  • 已经在用 Codex,想直接扩展它能力的人。
  • 需要处理研究检索类任务、又不想每次都手写提示词的人。
  • 需要许可证清楚、并且有第三方扫描结果的团队项目。

不太适合

  • 需要厂商级技术支持和 SLA 的生产系统。

研究检索类 Skill 对比

6 个同类
Skill总安装GitHub 星权限许可证扫描
find-skills

寻找技能 · Codex、Claude

31,199,81316,486操作浏览器MIT有提醒
self-improving-agent

自我改进 Agent · OpenClaw

16,464,0023,396接触敏感数据MIT-0通过
soultrace

灵魂追踪 · Codex、Claude

7,588,94412需要联网未明确有提醒
self-improving

自我完善 · OpenClaw

6,829,2841,046需要联网MIT-0通过
microsoft-foundry

微软代工厂 · Codex、Claude

6,614,144764调用外部服务未明确有提醒
chatgpt-app-builder

chatgpt 应用程序构建器 · 本页

1,1641调用外部服务MIT通过

按安装量取同分类前几名;数字来自第三方目录统计,只作热度参考,不代表质量结论。

SKILL.md 原文

ChatGPT App Builder

Build production-ready ChatGPT apps with interactive widgets using the mcp-use framework and OpenAI Apps SDK. This skill provides zero-config widget development with automatic registration and built-in React hooks.

Quick Start

Always bootstrap with the MCP Apps template:

npx create-mcp-use-app my-chatgpt-app --template mcp-apps
cd my-chatgpt-app
yarn install
yarn dev

This creates a project structure:

my-chatgpt-app/
├── resources/              # React widgets (auto-registered!)
│   ├── display-weather.tsx # Example widget
│   └── product-card.tsx    # Another widget
├── public/                 # Static assets
│   └── images/
├── index.ts               # MCP server entry
├── package.json
├── tsconfig.json
└── README.md

Why mcp-use for ChatGPT Apps?

Traditional OpenAI Apps SDK requires significant manual setup:

  • Separate project structure (server/ and web/ folders)
  • Manual esbuild/webpack configuration
  • Custom useWidgetState hook implementation
  • Manual React mounting code
  • Manual CSP configuration
  • Manual widget registration

mcp-use simplifies everything:

  • ✅ Single command setup
  • ✅ Drop widgets in resources/ folder - auto-registered
  • ✅ Built-in useWidget() hook with state, props, tool calls
  • ✅ Automatic bundling with hot reload
  • ✅ Automatic CSP configuration
  • ✅ Built-in Inspector for testing
  • ✅ Dual-protocol support (works with ChatGPT AND MCP Apps clients)

MCP Apps vs ChatGPT Apps SDK

mcp-use supports multiple widget protocols, giving you maximum compatibility:

ProtocolUse CaseCompatibilityStatus
MCP Apps (type: "mcpApps")Maximum compatibility✅ ChatGPT + MCP Apps clientsRecommended
ChatGPT Apps SDK (type: "appsSdk")ChatGPT-only features✅ ChatGPT onlySupported
MCP-UISimple, static content✅ MCP clients onlySpecialized

Why MCP Apps?

MCP Apps is the official standard (SEP-1865) for interactive widgets in the Model Context Protocol:

  • Universal: Works with ChatGPT, Claude Desktop, Goose, and all MCP Apps clients
  • Future-proof: Based on open specification, ensuring long-term compatibility
  • Secure: Double-iframe sandbox with granular CSP control
  • Zero config: With type: "mcpApps", mcp-use automatically generates metadata for BOTH protocols

Key Point: When you use type: "mcpApps" in your server configuration, your widgets automatically work with both ChatGPT (Apps SDK protocol) and MCP Apps clients. You write the widget once, and mcp-use handles the protocol translation.

Creating Widgets

Simple Widget (Single File)

Create resources/weather-display.tsx:

import { McpUseProvider, useWidget, type WidgetMetadata } from "mcp-use/react";
import { z } from "zod";

// Define widget metadata
export const widgetMetadata: WidgetMetadata = {
  description: "Display current weather for a city",
  props: z.object({
    city: z.string().describe("City name"),
    temperature: z.number().describe("Temperature in Celsius"),
    conditions: z.string().describe("Weather conditions"),
    humidity: z.number().describe("Humidity percentage"),
  }),
};

const WeatherDisplay: React.FC = () => {
  const { props, isPending } = useWidget();

  // Always handle loading state first
  if (isPending) {
    return (
      <McpUseProvider autoSize>
        <div className="animate-pulse p-4">Loading weather...</div>
      </McpUseProvider>
    );
  }

  return (
    <McpUseProvider autoSize>
      <div className="weather-card p-4 rounded-lg shadow">
        <h2 className="text-2xl font-bold">{props.city}</h2>
        <div className="temp text-4xl">{props.temperature}°C</div>
        <p className="conditions">{props.conditions}</p>
        <p className="humidity">Humidity: {props.humidity}%</p>
      </div>
    </McpUseProvider>
  );
};

export default WeatherDisplay;

That's it! The widget is automatically:

  • Registered as MCP tool weather-display
  • Registered as MCP resource ui://widget/weather-display.html
  • Bundled for Apps SDK compatibility
  • Ready to use in ChatGPT

Complex Widget (Folder Structure)

For widgets with multiple components:

resources/
└── product-search/
    ├── widget.tsx          # Entry point (required name)
    ├── components/
    │   ├── ProductCard.tsx
    │   └── FilterBar.tsx
    ├── hooks/
    │   └── useFilter.ts
    ├── types.ts
    └── constants.ts

Entry point (widget.tsx):

import { McpUseProvider, useWidget, type WidgetMetadata } from "mcp-use/react";
import { z } from "zod";
import { ProductCard } from "./components/ProductCard";
import { FilterBar } from "./components/FilterBar";

export const widgetMetadata: WidgetMetadata = {
  description: "Display product search results with filtering",
  props: z.object({
    products: z.array(
      z.object({
        id: z.string(),
        name: z.string(),
        price: z.number(),
        image: z.string(),
      })
    ),
    query: z.string(),
  }),
};

const ProductSearch: React.FC = () => {
  const { props, isPending, state, setState } = useWidget();

  if (isPending) {
    return (
      <McpUseProvider autoSize>
        <div>Loading...</div>
      </McpUseProvider>
    );
  }

  return (
    <McpUseProvider autoSize>
      <div>
        <h1>Search: {props.query}</h1>
        <FilterBar onFilter={(filters) => setState({ filters })} />
        <div className="grid grid-cols-3 gap-4">
          {props.products.map((p) => (
            <ProductCard key={p.id} product={p} />
          ))}
        </div>
      </div>
    </McpUseProvider>
  );
};

export default ProductSearch;

Widget Metadata

Required metadata for automatic registration:

export const widgetMetadata: WidgetMetadata = {
  // Required: Human-readable description
  description: "Display weather information",

  // Required: Zod schema for widget props
  props: z.object({
    city: z.string().describe("City name"),
    temperature: z.number(),
  }),

  // Optional: Disable automatic tool registration
  exposeAsTool: true, // default

  // Optional: Unified metadata (works for BOTH ChatGPT and MCP Apps)
  metadata: {
    csp: {
      connectDomains: ["https://api.weather.com"],
      resourceDomains: ["https://cdn.weather.com"],
    },
    prefersBorder: true,
    autoResize: true,
    widgetDescription: "Interactive weather display",
  },
};

Important:

  • description: Used for tool and resource descriptions
  • props: Zod schema defines widget input parameters
  • exposeAsTool: Set to false if only using widget via custom tools
  • metadata: Unified configuration that works for both protocols (recommended)

Content Security Policy (CSP)

Control what external resources your widget can access using CSP configuration:

export const widgetMetadata: WidgetMetadata = {
  description: "Weather widget",
  props: z.object({ city: z.string() }),
  metadata: {
    csp: {
      // APIs your widget needs to call
      connectDomains: ["https://api.weather.com", "https://weather-backup.com"],

      // Static assets (images, fonts, stylesheets)
      resourceDomains: ["https://cdn.weather.com"],

      // External content to embed in iframes
      frameDomains: ["https://embed.weather.com"],

      // Script CSP directives (use carefully!)
      scriptDirectives: ["'unsafe-inline'"],
    },
  },
};

CSP Field Reference:

  • connectDomains: APIs to call via fetch, WebSocket, XMLHttpRequest
  • resourceDomains: Load images, fonts, stylesheets, videos
  • frameDomains: Embed external content in iframes
  • scriptDirectives: Script-src CSP directives (avoid 'unsafe-eval' in production)

Security Best Practices:

  • ✅ Specify exact domains: https://api.weather.com
  • ❌ Avoid wildcards: https://*.weather.com (less secure)
  • ❌ Never use 'unsafe-eval' unless absolutely necessary
  • ✅ Test CSP in development before deploying

Metadata Configuration Options

Modern Unified Approach (Recommended)

Use the metadata field for dual-protocol support:

export const widgetMetadata: WidgetMetadata = {
  description: "Weather widget",
  props: propSchema,
  metadata: {
    // Works for BOTH MCP Apps AND ChatGPT
    csp: {
      connectDomains: ["https://api.weather.com"],
      resourceDomains: ["https://cdn.weather.com"],
    },
    prefersBorder: true,
    autoResize: true,
    widgetDescription: "Displays current weather",
  },
};

Legacy Apps SDK Approach (Deprecated)

The old ChatGPT-only format (still supported but not recommended):

export const widgetMetadata: WidgetMetadata = {
  description: "Weather widget",
  props: propSchema,
  appsSdkMetadata: {
    // ChatGPT only - snake_case with openai/ prefix
    "openai/widgetCSP": {
      connect_domains: ["https://api.weather.com"],
      resource_domains: ["https://cdn.weather.com"],
    },
    "openai/widgetPrefersBorder": true,
    "openai/toolInvocation/invoking": "Loading...",
    "openai/toolInvocation/invoked": "Loaded",
  },
};

Migration Note: The old format uses appsSdkMetadata with openai/ prefixes and snake_case (e.g., connect_domains). The new format uses metadata with camelCase (e.g., connectDomains) and works for both protocols.

Using Both for Custom ChatGPT Features

You can combine both fields to use standard metadata plus ChatGPT-specific overrides:

export const widgetMetadata: WidgetMetadata = {
  description: "Weather widget",
  props: propSchema,
  // Unified metadata (dual-protocol)
  metadata: {
    csp: { connectDomains: ["https://api.weather.com"] },
    prefersBorder: true,
  },
  // ChatGPT-specific overrides/additions
  appsSdkMetadata: {
    "openai/widgetDescription": "ChatGPT-specific description",
    "openai/customFeature": "some-value", // Any custom OpenAI metadata
    "openai/locale": "en-US",
  },
};

Use Case: When you need to pass custom OpenAI-specific metadata that doesn't exist in the unified format, add it to appsSdkMetadata. The fields will be passed directly to ChatGPT with the openai/ prefix

useWidget Hook

The useWidget hook provides everything you need:

const {
  // Widget props from tool input
  props,

  // Loading state (true = tool still executing)
  isPending,

  // Persistent widget state
  state,
  setState,

  // Theme from host (light/dark)
  theme,

  // Call other MCP tools
  callTool,

  // Display mode control
  displayMode,
  requestDisplayMode,

  // Additional tool output
  output,
} = useWidget<MyPropsType, MyOutputType>();

Props and Loading States

Critical: Widgets render BEFORE tool execution completes. Always handle isPending:

const { props, isPending } = useWidget<WeatherProps>();

// Pattern 1: Early return
if (isPending) {
  return <div>Loading...</div>;
}
// Now props are safe to use

// Pattern 2: Conditional rendering
return <div>{isPending ? <LoadingSpinner /> : <div>{props.city}</div>}</div>;

// Pattern 3: Optional chaining (partial UI)
return (
  <div>
    <h1>{props.city ?? "Loading..."}</h1>
  </div>
);

Widget State

Persist data across widget interactions:

const { state, setState } = useWidget();

// Save state (persists in ChatGPT localStorage)
const addFavorite = async (city: string) => {
  await setState({
    favorites: [...(state?.favorites || []), city],
  });
};

// Update with function
await setState((prev) => ({
  ...prev,
  count: (prev?.count || 0) + 1,
}));

Calling MCP Tools

Widgets can call other tools:

const { callTool } = useWidget();

const refreshData = async () => {
  try {
    const result = await callTool("get-weather", {
      city: "Tokyo",
    });
    console.log("Result:", result.content);
  } catch (error) {
    console.error("Tool call failed:", error);
  }
};

Display Mode Control

Request different display modes:

const { displayMode, requestDisplayMode } = useWidget();

const goFullscreen = async () => {
  await requestDisplayMode("fullscreen");
};

// Current mode: 'inline' | 'pip' | 'fullscreen'
console.log(displayMode);

Custom Tools with Widgets

Create tools that return widgets with dual-protocol support:

import { MCPServer, widget, text } from "mcp-use/server";
import { z } from "zod";

const server = new MCPServer({
  name: "weather-app",
  version: "1.0.0",
});

server.tool(
  {
    name: "get-weather",
    description: "Get current weather for a city",
    schema: z.object({
      city: z.string().describe("City name"),
    }),
    // Widget config (registration-time metadata)
    widget: {
      name: "weather-display", // Must match widget in resources/
      invoking: "Fetching weather...",
      invoked: "Weather data loaded",
    },
  },
  async ({ city }) => {
    // Fetch data from API
    const data = await fetchWeatherAPI(city);

    // Return widget with runtime data
    return widget({
      props: {
        city,
        temperature: data.temp,
        conditions: data.conditions,
        humidity: data.humidity,
      },
      output: text(`Weather in ${city}: ${data.temp}°C`),
      message: `Current weather for ${city}`,
    });
  }
);

server.listen();

Key Points:

  • baseUrl in server config enables proper asset loading
  • Widget works with BOTH ChatGPT and MCP Apps clients automatically
  • widget: {name, invoking, invoked} on tool definition
  • widget({props, output}) helper returns runtime data
  • props passed to widget, output shown to model
  • Widget must exist in resources/ folder

Static Assets

Use the public/ folder for images, fonts, etc:

my-app/
├── resources/
├── public/              # Static assets
│   ├── images/
│   │   ├── logo.svg
│   │   └── banner.png
│   └── fonts/
└── index.ts

Using assets in widgets:

import { Image } from "mcp-use/react";

function MyWidget() {
  return (
    <div>
      {/* Paths relative to public/ folder */}
      <Image src="/images/logo.svg" alt="Logo" />
      <img src={window.__getFile?.("images/banner.png")} alt="Banner" />
    </div>
  );
}

Components

McpUseProvider

Unified provider combining all common setup:

import { McpUseProvider } from "mcp-use/react";

function MyWidget() {
  return (
    <McpUseProvider
      autoSize // Auto-resize widget
      viewControls // Add debug/fullscreen buttons
      debug // Show debug info
    >
      <div>Widget content</div>
    </McpUseProvider>
  );
}

Image Component

Handles both data URLs and public paths:

import { Image } from "mcp-use/react";

function MyWidget() {
  return (
    <div>
      <Image src="/images/photo.jpg" alt="Photo" />
      <Image src="data:image/png;base64,..." alt="Data URL" />
    </div>
  );
}

ErrorBoundary

Graceful error handling:

import { ErrorBoundary } from "mcp-use/react";

function MyWidget() {
  return (
    <ErrorBoundary
      fallback={<div>Something went wrong</div>}
      onError={(error) => console.error(error)}
    >
      <MyComponent />
    </ErrorBoundary>
  );
}

Testing

Using the Inspector

  1. Start development server: yarn dev
  2. Open Inspector:

- Navigate to http://localhost:3000/inspector

  1. Test widgets:

- Click Tools tab - Find your widget tool - Enter test parameters - Execute to see widget render

  1. Debug interactions:

- Use browser console - Check RPC logs - Test state persistence - Verify tool calls

Testing in ChatGPT

  1. Enable Developer Mode:

- Settings → Connectors → Advanced → Developer mode

  1. Add your server:

- Go to Connectors tab - Add remote MCP server URL

  1. Test in conversation:

- Select Developer Mode from Plus menu - Choose your connector - Ask ChatGPT to use your tools

Prompting tips:

  • Be explicit: "Use the weather-app connector's get-weather tool..."
  • Disallow alternatives: "Do not use built-in tools, only use my connector"
  • Specify input: "Call get-weather with {city: 'Tokyo'}"

Dual-Protocol Note: When using type: "mcpApps" in your server configuration, your widgets automatically work in both ChatGPT (via Apps SDK) and MCP Apps clients (like Claude Desktop, Goose). You can test the same widget in multiple clients without any code changes!

Best Practices

Schema Design

Use descriptive schemas:

// ✅ Good
const schema = z.object({
  city: z.string().describe("City name (e.g., Tokyo, Paris)"),
  temperature: z.number().min(-50).max(60).describe("Temp in Celsius"),
});

// ❌ Bad
const schema = z.object({
  city: z.string(),
  temp: z.number(),
});

Theme Support

Always support both themes:

const { theme } = useWidget();

const bgColor = theme === "dark" ? "bg-gray-900" : "bg-white";
const textColor = theme === "dark" ? "text-white" : "text-gray-900";

Loading States

Always check isPending first:

const { props, isPending } = useWidget<MyProps>();

if (isPending) {
  return <LoadingSpinner />;
}

// Now safe to access props.field
return <div>{props.field}</div>;

Widget Focus

Keep widgets focused:

// ✅ Good: Single purpose
export const widgetMetadata: WidgetMetadata = {
  description: "Display weather for a city",
  props: z.object({ city: z.string() }),
};

// ❌ Bad: Too many responsibilities
export const widgetMetadata: WidgetMetadata = {
  description: "Weather, forecast, map, news, and more",
  props: z.object({
    /* many fields */
  }),
};

Error Handling

Handle errors gracefully:

const { callTool } = useWidget();

const fetchData = async () => {
  try {
    const result = await callTool("fetch-data", { id: "123" });
    if (result.isError) {
      console.error("Tool returned error");
    }
  } catch (error) {
    console.error("Tool call failed:", error);
  }
};

Configuration

Production Setup

Set base URL for production:

const server = new MCPServer({
  name: "my-app",
  version: "1.0.0",
  baseUrl: process.env.MCP_URL || "https://myserver.com",
});

Environment Variables

# Server URL
MCP_URL=https://myserver.com

# For static deployments
MCP_SERVER_URL=https://myserver.com/api
CSP_URLS=https://cdn.example.com,https://api.example.com

Variable usage:

  • MCP_URL: Base URL for widget assets and CSP
  • MCP_SERVER_URL: MCP server URL for tool calls (static deployments)
  • CSP_URLS: Additional domains for Content Security Policy

Deployment

Deploy to mcp-use Cloud

# Login
npx mcp-use login

# Deploy
yarn deploy

Build for Production

# Build
yarn build

# Start
yarn start

Build process:

  • Compiles TypeScript
  • Bundles React widgets
  • Optimizes assets
  • Generates production HTML

Common Patterns

Data Fetching Widget

const DataWidget: React.FC = () => {
  const { props, isPending, callTool } = useWidget();

  if (isPending) {
    return <div>Loading...</div>;
  }

  const refresh = async () => {
    await callTool("fetch-data", { id: props.id });
  };

  return (
    <div>
      <h1>{props.title}</h1>
      <button onClick={refresh}>Refresh</button>
    </div>
  );
};

Stateful Widget

const CounterWidget: React.FC = () => {
  const { state, setState } = useWidget();

  const increment = async () => {
    await setState({
      count: (state?.count || 0) + 1,
    });
  };

  return (
    <div>
      <p>Count: {state?.count || 0}</p>
      <button onClick={increment}>+1</button>
    </div>
  );
};

Themed Widget

const ThemedWidget: React.FC = () => {
  const { theme } = useWidget();

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

Troubleshooting

Widget Not Appearing

Problem: Widget file exists but tool doesn't appear

Solutions:

  • Ensure .tsx extension
  • Export widgetMetadata object
  • Export default React component
  • Check server logs for errors
  • Verify widget name matches file/folder name

Props Not Received

Problem: Component receives empty props

Solutions:

  • Check isPending first (props empty while pending)
  • Use useWidget() hook (not React props)
  • Verify widgetMetadata.props is valid Zod schema
  • Check tool parameters match schema

CSP Errors

Problem: Widget loads but assets fail

Solutions:

  • Set baseUrl in server config
  • Add domains to CSP via metadata.csp (modern) or appsSdkMetadata['openai/widgetCSP'] (legacy)
  • Use HTTPS for all resources
  • Check browser console for CSP violations

CSP Errors in Production

Problem: Resources blocked by Content Security Policy in production

Solutions:

  1. Check browser console for CSP violation messages
  2. Add missing domains to your CSP configuration: metadata: {csp: {connectDomains: ['https://api.example.com'], // Add missing API domain resourceDomains: ['https://cdn.example.com'], // Add missing CDN domain}}
  3. Use exact domains - avoid wildcards in production
  4. Test in Inspector before deploying to catch CSP issues early
  5. Environment variable alternative: Set CSP_URLS environment variable with comma-separated domains

Protocol Compatibility Issues

Problem: Widget works in ChatGPT but not MCP Apps clients (or vice versa)

Solutions:

  • Use type: "mcpApps" for dual-protocol support (recommended)
  • Check baseUrl is set correctly in server config
  • Verify metadata format: Use metadata (camelCase) not appsSdkMetadata (snake_case) for dual-protocol
  • Test in Inspector which supports both protocols

When to use each type:

  • type: "mcpApps" - Maximum compatibility (recommended)
  • type: "appsSdk" - ChatGPT only (use if you need ChatGPT-specific features not in spec)

Learn More

Quick Reference

Commands:

  • npx create-mcp-use-app my-app --template mcp-apps - Bootstrap
  • yarn dev - Development with hot reload
  • yarn build - Build for production
  • yarn start - Run production server
  • yarn deploy - Deploy to mcp-use Cloud

Widget structure:

  • resources/widget-name.tsx - Single file widget
  • resources/widget-name/widget.tsx - Folder-based widget entry
  • public/ - Static assets

Widget metadata:

  • description - Widget description
  • props - Zod schema for input
  • exposeAsTool - Auto-register as tool (default: true)
  • metadata - Unified config (dual-protocol, recommended)
  • metadata.csp - Content Security Policy configuration
  • appsSdkMetadata - ChatGPT-specific overrides (optional)

CSP fields:

  • connectDomains - APIs to call
  • resourceDomains - Static assets to load
  • frameDomains - Iframes to embed
  • scriptDirectives - Script policies

useWidget hook:

  • props - Widget input parameters
  • isPending - Loading state flag
  • state, setState - Persistent state
  • callTool - Call other tools
  • theme - Current theme (light/dark)
  • displayMode, requestDisplayMode - Display control

平台分布

Claude Code

27.51%
按下载量换算112

OpenCode

22.6%
按下载量换算92

Cursor

15.96%
按下载量换算65

Codex

12.98%
按下载量换算53

Antigravity

7.63%
按下载量换算31

windsurf

3.61%
按下载量换算15

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

调用外部服务

该 Skill 可能调用第三方服务、云服务或外部模型 API,使用前需要确认账号、额度、数据发送范围和服务条款。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。

继续浏览同类 Skills