Sa loob ng ilang linggo, inilathala ng OpenAI ang Apps para sa ChatGPT. Tulad ng makikita mo sa ibaba, ito ay nagbibigay-daan sa mga negosyo upang i-inject ang kanilang produkto sa chat upang makatulong sa pag-attention ng user's prompt. Ang isang app ay maaaring i-activate o sa pamamagitan ng isang eksplicit na pag-iisip o kapag ang modelo ay nagsisimula na ang app ay magagamit. So, what is a ChatGPT App? Para sa isang customer, ito ay isang paraan upang makakuha ng mas mataas na user experience at functionality, higit sa mga limitasyon ng isang text interface. Para sa isang negosyo, ito ay isang paraan upang makakuha ng higit sa 800 milyong gumagamit ng ChatGPT sa parehong oras. Para sa isang developer, ito ay isang MCP server at isang web app na gumagana sa isang iframe <= ito ay kung ano ang kami ay dito upang sabihin! ang demo Sa post na ito, makukuha ko sa pamamagitan ng pagbuo ng isang simpleng app quiz, na ipinapakita sa ibaba, gamit ito bilang isang halimbawa upang ipakita ang mga katangian na magagamit. Important Note: Kung nais mong i-follow, kailangan mo ng isang paid ChatGPT subscription upang i-activate ang Developer Mode. Ang isang standard $20/month customer subscription ay matatagpuan. Kung gusto mong i-follow ang mga ito, Ang isang standard $ 20 / buwan customer subscription ay magagamit. Important Note: you will need a paid ChatGPT subscription to enable Developer Mode High level ng flow Narito ang kung paano ito gumagana sa isang mataas na antas (ang katotohanan ng mga pasahero ay maaaring mabigat): Sa unang, ang developer ng app ay nag-register ito sa ChatGPT sa pamamagitan ng Ito ay gumagamit ng app ang Pumunta para sa , at ito ay nagbibigay ng mga modelo tulad ng ChatGPT upang i-explore at mag-interact sa iba pang mga serbisyo. Ang aming MCP server ay magkaroon ng " “ at” ” kailangan upang lumikha ng isang ChatGPT quiz app. Sa linya na ito, providing a link to the MCP server (1) MCP Model Context Protocol tools resources ChatGPT learns and remembers what our app does and when it can be useful. Kapag ang App na ito ay na-existent at ang user ay gumagawa ng isang prompt tulad ng "Make a quiz about Sam Altman", ChatGPT ay i-check kung mayroong isang App na maaaring gamitin ito sa halip ng isang text reply upang magbigay ng mas mahusay na karanasan sa user . (2) (3) Kung ang isang App ay natagpuan, ChatGPT makikita ang schema ng data na kailangan ng App Ang aming App ay kinakailangan upang makuha ang data sa sumusunod na format ng JSON: (4) { questions: [ { question: "Where was Sam Altman born", options: ["San Francisco", ...], correctIndex: 2, ... }, ... ] } Ito ay tinatawag na , at ipadala ito sa aming app . ChatGPT will generate quiz data exactly in this format toolInput (5) Ang app ay nagtatrabaho sa At siya ay gumawa ng ChatGPT ay i-render HTML "resource" na ibinigay ng app sa chat window, at i-initialize ito sa ang data At sa katapusan, ang user ay makikita ang app at maaaring mag-interact sa kanya . toolInput toolOutput toolOutput (6) (7) Paggawa ng isang MCP Server Code repo para sa aming ChatGPT app: . https://github.com/renal128/quizaurus-tutorial Mayroong dalawang proyekto: at ang First, mag-focus kami sa na gumagamit ng simple JavaScript sa frontend upang maging simple ang mga bagay. quizaurus-plain quizaurus-react quizaurus-plain Ang lahat ng server code ay sa file na ito - halos 140 linya ng code! https://github.com/renal128/quizaurus-tutorial/blob/main/quizaurus-plain/src/server.ts Serbisyo ng Serbisyo May maraming mga pagpipilian upang lumikha ng isang MCP server gamit ang anumang mga SDK na itinatag dito: https://modelcontextprotocol.io/docs/sdk Sa ngayon, ginagamit natin ang . Mga pahinang tumuturo sa MCP SDK Ang code sa ibaba ay nagpapakita kung paano i-set it up: // Create an MCP server const mcpServer = new McpServer({ name: 'quizaurus-server', version: '0.0.1' }); // Add the tool that receives and validates questions, and starts a quiz mcpServer.registerTool( ... ); // Add a resource that contains the frontend code for rendering the widget mcpServer.registerResource( ... ); // Create an Express app const expressApp = express(); expressApp.use(express.json()); // Set up /mcp endpoint that will be handled by the MCP server expressApp.post('/mcp', async (req, res) => { const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined, enableJsonResponse: true }); res.on('close', () => { transport.close(); }); await mcpServer.connect(transport); await transport.handleRequest(req, res, req.body); }); const port = parseInt(process.env.PORT || '8000'); // Start the Express app expressApp.listen(port, () => { console.log(`MCP Server running on http://localhost:${port}/mcp`); }).on('error', error => { console.error('Server error:', error); process.exit(1); }); Mga Key Points: Express app ay isang generic server na makakuha ng mga komunikasyon (tulad ng HTTP requests) mula sa labas ( mula sa ChatGPT) Upang gamitin ang Express, inilagay namin ang isang /mcp endpoint na ibinigay namin sa ChatGPT bilang ang address ng aming MCP server (tulad ng https://mysite.com/mcp) The handling of the endpoint is delegated to the MCP server, that runs within the Express app /mcp all of the MCP protocol that we need is handled within that endpoint by the MCP server mcpServer.registerTool(...) at mcpServer.registerResource(...) ay kung ano ang ginagamit namin upang i-implementate ang aming app Quiz Mga Tool ng MCP Tapos, merong mga commercial na over na. lugarholder sa itaas upang i-register ang “tool”. mcpServer.registerTool(…) ChatGPT makikita ang definisyon ng tool kapag nag-register ang app, at pagkatapos, kapag kailangan ng gumagamit ito, ChatGPT ay sumulat ang tool upang bumuo ng isang quiz: // Add the tool that receives and validates questions, and starts a quiz mcpServer.registerTool( 'render-quiz', { title: 'Render Quiz', description: ` Use this when the user requests an interactive quiz. The tool expects to receive high-quality single-answer questions that match the schema in input/structuredContent: each item needs { question, options[], correctIndex, explanation }. Use 5–10 questions unless the user requests a specific number of questions. The questions will be shown to the user by the tool as an interactive quiz. Do not print the questions or answers in chat when you use this tool. Do not provide any sensitive or personal user information to this tool.`, _meta: { "openai/outputTemplate": "ui://widget/interactive-quiz.html", // <- hook to the resource }, inputSchema: { topic: z.string().describe("Quiz topic (e.g., 'US history')."), difficulty: z.enum(["easy", "medium", "hard"]).default("medium"), questions: z.array( z.object({ question: z.string(), options: z.array(z.string()).min(4).max(4), correctIndex: z.number().int(), explanation: z.string().optional(), }) ).min(1).max(40), }, }, async (toolInput) => { const { topic, difficulty, questions } = toolInput; // Here you can run any server-side logic to process the input from ChatGPT and // prepare toolOutput that would be fed into the frontend widget code. // E.g. you can receive search filters and return matching items. return { // Optional narration beneath the component content: [{ type: "text", text: `Starting a ${difficulty} quiz on ${topic}.` }], // `structuredContent` will be available as `toolOutput` in the frontend widget code structuredContent: { topic, difficulty, questions, }, // Private to the component; not visible to the model _meta: { "openai/locale": "en" }, }; } ); Ang top half ng code ay nagbibigay ng isang paglalarawan ng tool - ChatGPT ay tumingin sa kanya upang malaman kung paano at kung paano gamitin ito: Ang chatGPT ay gumagamit nito upang mag-alok kung ang tool ay magagamit sa user prompt. Ang inputSchema ay isang paraan upang sabihin sa ChatGPT kung ano ang mga data na ito ay kinakailangan upang magbigay sa tool at kung paano ito ay dapat na itakda. tulad ng maaari mong makita sa itaas, ito ay naglalaman ng mga tip at limitasyon na maaaring gamitin ng ChatGPT upang magbigay ng isang korektong payload (toolInput). is omitted here, but you can provide it to tell ChatGPT what schema will have. outputSchema structuredContent Kaya, sa isang halimbawa, ang tool ay kung ano ang defines ng ChatGPT App dito. Tingnan ang iba pang 2 fields dito: _meta[“openai/outputTemplate”] ay ang identifier ng MCP resource na ginagamit ng ChatGPT App upang i-render ang widget. async (toolInput) => { ... ay ang function na makakuha ng toolInput mula sa ChatGPT at lumikha ng toolOutput na magagamit sa widget. Ito ay kung saan maaari naming i-execute ang anumang server-side logic upang pagproseso ang data. Sa aming kaso, hindi namin kinakailangan ng anumang pagproseso dahil toolInput na naglalaman ng lahat ng impormasyon na kailangan ng widget, kaya ang function ay bumalik ang parehong data sa structuredContent na magagamit bilang toolOutput sa widget. Mga Mapagkukunan ng MCP Sa ibaba ay kung paano natuklasan ang isang MCP resource: // Add an MCP resource that contains frontend code for rendering the widget mcpServer.registerResource( 'interactive-quiz', "ui://widget/interactive-quiz.html", // must match `openai/outputTemplate` in the tool definition above {}, async (uri) => { // copy frontend script and css const quizaurusJs = await fs.readFile("./src/dist/QuizaurusWidget.js", "utf8"); const quizaurusCss = await fs.readFile("./src/dist/QuizaurusWidget.css", "utf8"); return { contents: [ { uri: uri.href, mimeType: "text/html+skybridge", // Below is the HTML code for the widget. // It defines a root div and injects our custom script from src/dist/QuizaurusWidget.js, // which finds the root div by its ID and renders the widget components in it. text: ` <div id="quizaurus-root" class="quizaurus-root"></div> <script type="module"> ${quizaurusJs} </script> <style> ${quizaurusCss} </style>` } ] } } ); Basically, “resource” dito ay nagbibigay ang frontend (widget) bahagi ng App. ui://widget/interactive-quiz.html ay ang resource ID, at ito ay dapat matugunan _meta[“openai/outputTemplate”] ng tool definition mula sa ikalawang seksyon sa itaas. provides HTML code of the widget contents the HTML here is very simple - we just define the root div and add that will find that root div by ID, create necessary elements (buttons, etc) and define the quiz app logic. We will look at the script in the next section below. quiz-app-root the custom script Paggawa ng widget Paggamit ng widget Now, let's take a quick look at ang mga ito na nag-implementate ang widget (ang makita na bahagi ng app): ang QuizaurusWidget.js script // Find the root div defined by the MCP resource const root = document.querySelector('#quiz-app-root'); // create HTML elements inside the root div ... // try to initialize for widgetState to restore the quiz state in case the chat page gets reloaded const selectedAnswers = window.openai.widgetState?.selectedAnswers ?? {}; let currentQuestionIndex = window.openai.widgetState?.currentQuestionIndex ?? 0; function refreshUI() { // Read questions from window.openai.toolOutput - this is the output of the tool defined in server.ts const questions = window.openai.toolOutput?.questions; // Initially the widget will be rendered with empty toolOutput. // It will be populated when ChatGPT receives toolOutput from our tool. if (!questions) { console.log("Questions have not yet been provided. Try again in a few sec.") return; } // Update UI according to the current state ... }; // when an answer button is clicked, we update the state and call refreshUI() optionButtons.forEach((b) => { b.onclick = (event) => { const selectedOption = event.target.textContent selectedAnswers[currentQuestionIndex] = selectedOption; // save and expose selected answers to ChatGPT window.openai.setWidgetState({ selectedAnswers, currentQuestionIndex }); refreshUI(); }; }); ... // at the end of the quiz, the user can click this button to review the answers with ChatGPT reviewResultsButton.onclick = () => { // send a prompt to ChatGPT, it will respond in the chat window.openai.sendFollowUpMessage({ prompt: "Review my answers and explain mistakes" }); reviewResultsButton.disabled = true; }; startQuizButton.onclick = refreshUI; refreshUI(); Reminder: ang code na ito ay inihanda sa pamamagitan ng HTML na kami defined sa MCP resource sa itaas (ang Ang HTML at ang script ay matatagpuan sa loob ng isang iframe sa ChatGPT chat page. <script type="module">… ChatGPT i-expose ang ilang data at hooks sa pamamagitan ng global object. Narito ang kung ano ang ginagamit namin dito: window.openai window.openai.toolOutput ay naglalaman ng data ng tanong na inilathala ng tool ng MCP. Sa unang pagkakataon, ang html ay inilathala bago ang tool ay inilathala ng toolOutput, kaya ang window.openai.toolOutput ay huling. Ito ay isang maliit na masakit, ngunit kami ay i-fix ito pagkatapos na may React. Ang window.openai.widgetState at window.openai.setWidgetState() ay nagbibigay-daan sa amin upang i-update at i-access ang estado ng widget. Ito ay maaaring maging ang anumang data na gusto natin, kahit na ang rekomendasyon ay upang matatagpuan ito sa ilalim ng 4000 tokens. Sa ngayon, ginagamit namin ito upang i-memorize kung ano ang mga tanong ay na-respond sa pamamagitan ng user, kaya kung ang pahina ay re-loaded, ang widget ay i-memorize ang estado. window.openai.sendFollowUpMessage({prompt: “...”}) ay isang paraan upang magbigay ng isang prompt sa ChatGPT bilang kung ang gumagamit ay nilikha ito, at ang ChatGPT ay mag-writing ang reaksyon sa chat. Maaari mong mahanap ang higit pang mga kakayahan sa OpenAI dokumentasyon dito: https://developers.openai.com/apps-sdk/build/custom-ux Pumunta ito sa lahat Time to test it! Isang quick reminder, kailangan mo ng isang paid ChatGPT subscription upang i-activate ang developer mode. Clone this repo [Download the code] https://github.com/renal128/quizaurus-tutorial There are 2 projects in this repo, a minimalistic one, described above, and a slicker-looking React one. We’ll focus on the first one for now. Open a terminal, navigate to the repo directory and run the following commands: [Starting the server] cd quizaurus-plain install NodeJS if you don’t have it https://nodejs.org/en/download/ to install dependencies defined in package.json npm install to start the Express app with MCP server - npm start keep it running [ ] Expose your local server to the web Create a free ngrok account: https://ngrok.com/ Open a (the other one with the Express app should keep running separately) new terminal Install ngrok: https://ngrok.com/docs/getting-started#1-install-the-ngrok-agent-cli on MacOS brew install ngrok Connect ngrok on your laptop to your ngrok account by configuring it with your auth token: https://ngrok.com/docs/getting-started#2-connect-your-account Start ngrok: ngrok http 8000 You should see something like this in the bottom of the output: Forwarding: https://xxxxx-xxxxxxx-xxxxxxxxx.ngrok-free ngrok created a tunnel from your laptop to a public server, so that your local server is available to everyone on the internet, including ChatGPT. Again, , don’t close the terminal keep it running - this is the part that r , $20/month, otherwise you may not see developer mode available. [Enable Developer Mode on ChatGPT] equires a paid customer subscription Go to ChatGPT website => Settings => Apps & Connectors => Advanced settings Enable the “Developer mode” toggle [Add the app] Go back to “Apps & Connectors” and click “Create” in the top-right corner Fill in the details as on the screenshot. For “MCP Server URL” use the URL that ngrok gave you in the terminal output and . add /mcp to it at the end Click on your newly added app You should see the MCP tool under Actions - now ChatGPT knows when and how to use the app. When you make changes to the code, sometimes , otherwise it can remain cached (sometimes I even delete and re-add the app due to avoid caching). you need to click Refresh to make ChatGPT pick up the changes [ ] Finally, we’re ready to test it! Test the app In the chat window you can nudge ChatGPT to use your app by selecting it under the “ ” button. In my experience, it’s not always necessary, but let’s do it anyway. Then try a prompt like “Make an interactive 3-question quiz about Sam Altman”. + You should see ChatGPT asking your approval to call the MCP tool with the displayed . I assume that it’s a feature for unapproved apps, and it won’t happen once the app is properly reviewed by OpenAI (although, as of Nov 2025 there’s no defined process to publish an app yet). So, just click “Confirm” and wait a few seconds. toolInput As I mentioned above, the widget gets rendered before is returned by our MCP server. This means that if you click “Start Quiz” too soon, it won’t do anything - try again a couple seconds later. (we will fix that with React in the next section below). When the data is ready, clicking “Start Quiz” should show the quiz! toolOutput Tungkol sa React Sa itaas, nakita namin ang code na gumagamit ng simpleng JavaScript. Ang iba pang proyekto sa parehong repo, , demonstrates kung paano i-implementate ang isang ChatGPT app gamit ang React. Mga Reaksiyon Maaari mong makakuha ng ilang mga useful documentation mula sa OpenAI dito: . https://developers.openai.com/apps-sdk/build/custom-ux/ Mga pahinang tumuturo sa help hooks Maaari mong makita ang mga ito dito, ang code ay kopya mula sa dokumento: https://github.com/renal128/quizaurus-tutorial/blob/main/quizaurus-react/web/src/openAiHooks.ts Ang pinaka-mahal na ito ay , na nagbibigay-daan sa iyo upang i-subscribe ang React app sa mga update sa Tandaan na sa simpleng (non-React) app sa itaas, natagpuan namin ang problema na ang "Start Quiz" button ay hindi gumagawa ng anumang bagay hanggang sa mga data ay nagsisimula? Ngayon maaari naming mapabuti ang UX sa pamamagitan ng nagpapakita ng isang pag-load animation: useToolOutput window.openai.toolOutput function App() { const toolOutput = useToolOutput() as QuizData | null; if (!toolOutput) { return ( <div className="quiz-container"> <p className="quiz-loading__text">Generating your quiz...</p> </div> ); } // otherwise render the quiz ... } Kapag ang toolOutput ay naka-populated, React ay automatically re-render ang app at ipakita ang quiz sa halip ng pag-load. Tungkol sa Router Ang kasaysayan ng navigasyon ng iframe sa kung saan ang app ay ibinigay ay naka-link sa kasaysayan ng navigasyon ng pahina, kaya maaari mong gamitin ang routing APIs tulad ng React Router upang i-implementate navigation sa loob ng app. Ang iba pang mga quirks at features Note: Ang pag-unlad ng app ng ChatGPT ay hindi napaka-stable sa kasalukuyang, dahil ang tampok ay hindi na ganap na inilapat, kaya ito ay totoo upang matanggap ang mga unannounced na mga pagbabago sa API o minor na bugs. Please rely on the official documentation for latest updates: https://developers.openai.com/apps-sdk How & when ChatGPT decides to show your app to the user The most important part is that your app’s metadata, such as the tool description, must feel relevant to the conversation. ChatGPT’s goal here is to provide the best UX to the user, so obviously if the app’s description is irrelevant to the prompt, the app won’t be shown. I’ve also seen ChatGPT asking the user to rate if the app was helpful or not, I suppose this feedback is also taken into account. App Metadata. Official recommendations: https://developers.openai.com/apps-sdk/guides/optimize-metadata The in order to be used. How would the user know to link an app? There are 2 ways: App Discovery. app needs to be linked/connected to the user’s account Manual - go to and find the app there. Settings => Apps & Connectors Contextual Suggestion - if the app is not connected, but is highly relevant in the conversation, ChatGPT may offer to connect it. I wasn’t able to make it work with my app, but I saw it working with pre-integrated apps like Zillow or Spotify: Pag-activate ng isang Connected App. Kapag ang app ay konektado, ang ChatGPT ay maaaring gamitin ito sa isang konversasyon kapag kinakailangan. Ang gumagamit ay maaaring i-push ito sa pamamagitan ng lamang na ibinigay ang pangalan ng app sa teksto, i-type @AppName o i-click ang + button at piliin ang app sa menu dito. Pagbibigay ng mga platform Web - dahil ito ay itinatag sa pamamagitan ng iframe, web ay ang pinaka-mahal na platform upang suporta at hindi ko may karamihan ng mga problema dito. Mobile app - kung i-connect ang app sa web, dapat mong, makikita ito sa mobile. Hindi ko maaaring i-activate ang app sa mobile - hindi ito maaaring i-call ang tool, ngunit kapag i-activate ang app sa web, ako ay maaaring i-interact sa kanya sa mobile. Mukhang isang temporary bug. Authentication ang ChatGPT Apps ay sumusuporta sa OAuth 2.1: https://developers.openai.com/apps-sdk/build/auth Ito ay isang malaking tema, ibinigay ko kung gusto kong mag-sign ng isang separating post tungkol sa ito! Paggawa ng Network Requests Ito ang ibig sabihin ng dokumentasyon ( ang :” Magtrabaho sa iyong OpenAI partner kung kailangan mo ng anumang mga domain permitted-listed. " ang source Standard fetch requests are allowed only when they comply with the CSP sa isang lugar ( Ito ay nagbibigay ng configuration Object sa resource definition upang i-activate ang iyong mga domain: dito _meta _meta: { ... /* Assigns a subdomain for the HTML. When set, the HTML is rendered within `chatgpt-com.web-sandbox.oaiusercontent.com` It's also used to configure the base url for external links. */ "openai/widgetDomain": 'https://chatgpt.com', /* Required to make external network requests from the HTML code. Also used to validate `openai.openExternal()` requests. */ 'openai/widgetCSP': { // Maps to `connect-src` rule in the iframe CSP connect_domains: ['https://chatgpt.com'], // Maps to style-src, style-src-elem, img-src, font-src, media-src etc. in the iframe CSP resource_domains: ['https://*.oaistatic.com'], } } Ang isang bagay na maaari mong gamitin ay ang Ang iyong app widget (frontend) ay maaaring gamitin ito upang i-call ang isang tool sa iyong MCP server - ibinigay mo ang MCP tool name at toolInput data at makakuha ng back toolOutput: window.openai.callTool await window.openai?.callTool("my_tool_name", { "param_name": "param_value" }); Mga pahinang tumuturo sa front Tingnan ang dokumentasyon na ito para sa kung ano ang magagamit sa iyong frontend code sa pamamagitan ng ang : window.openai https://developers.openai.com/apps-sdk/build/custom-ux Pwede kang mag-hire ng isang mathematician if you like. Nag-aalok mo kung ang ChatGPT ay kasalukuyang sa light o dark mode): window.openai.theme theme: Theme; userAgent: UserAgent; locale: string; // layout maxHeight: number; displayMode: DisplayMode; safeArea: SafeArea; // state toolInput: ToolInput; toolOutput: ToolOutput | null; toolResponseMetadata: ToolResponseMetadata | null; widgetState: WidgetState | null; Similarly, you can use the following callbacks (e.g. try to make your app full-screen): await window.openai?.requestDisplayMode({ mode: "fullscreen" }); /** Calls a tool on your MCP. Returns the full response. */ callTool: ( name: string, args: Record<string, unknown> ) => Promise<CallToolResponse>; /** Triggers a followup turn in the ChatGPT conversation */ sendFollowUpMessage: (args: { prompt: string }) => Promise<void>; /** Opens an external link, redirects web page or mobile app */ openExternal(payload: { href: string }): void; /** For transitioning an app from inline to fullscreen or pip */ requestDisplayMode: (args: { mode: DisplayMode }) => Promise<{ /** * The granted display mode. The host may reject the request. * For mobile, PiP is always coerced to fullscreen. */ mode: DisplayMode; }>; /** Update widget state */ setWidgetState: (state: WidgetState) => Promise<void>; Salamat sa iyo! Well, basta ang alam namin, lagi lang curious ang mga tao!