قبل بضعة أسابيع، قدمت OpenAI تطبيقات لـ ChatGPT. كما يمكنك أن ترى أدناه، فإنه يسمح للشركات بإدخال منتجها حقًا في الشبكة لمساعدة المستخدم على تلبية طلب المستخدم. ويمكن تشغيل التطبيق من خلال إشعار واضح أو عندما يقرر النموذج أن التطبيق سيكون مفيدًا. So, what is a ChatGPT App? بالنسبة للعميل ، فهذا هو وسيلة للحصول على تجربة المستخدم الأكثر غنى وخدمة ، إلى جانب التحديات من رابط النص. بالنسبة للشركات، فهذا هو طريقة لتصل إلى أكثر من 800 مليون مستخدم ChatGPT في الوقت المناسب فقط. بالنسبة للمطور، إنها خادم MCP وبرنامج ويب يعمل في iframe <= هذا ما نحن هنا للحديث عنه! Demo في هذه المقالة، سأذهب من خلال بناء تطبيق اختبار بسيط، والذي يظهر أدناه، باستخدامها كدليل لتثبيت الميزات المتاحة. ملاحظة مهمة: إذا كنت ترغب في اتباعها، فستحتاج إلى مكالمة ChatGPT مكلفة لتشغيل وضع المطور. If you want to follow along, سوف يكفي ترخيص العملاء القياسي $ 20 / شهر. Important Note: you will need a paid ChatGPT subscription to enable Developer Mode معدل تدفق عالية هذه هي الطريقة التي تعمل بها على مستوى عال (يختلف الترتيب الفعلي للخطوات قليلاً): أولاً ، يتم تسجيل التطبيق من قبل المطور في ChatGPT بواسطة هذا يتيح تطبيق . . الوقوف على ويسمح للمنتجات مثل ChatGPT بإستكشاف وتفاعلها مع خدمات أخرى. ” and “ يجب إنشاء تطبيقات ChatGPT في هذه الخطوة، 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. إذا كان التطبيق موجودًا بالفعل واستخدم المستخدم مثل "إجراء اختبار حول سام ألتمان"، سيقوم ChatGPT بتحقق ما إذا كان هناك تطبيق يمكن استخدامه بدلاً من إجابة نصية لتوفير تجربة أفضل للمستخدم. . (2) (3) إذا تم العثور على تطبيق، يبحث ChatGPT على التخطيط من البيانات التي يحتاجها التطبيق. يجب أن يحصل تطبيقنا على البيانات في شكل JSON التالي: (4) { questions: [ { question: "Where was Sam Altman born", options: ["San Francisco", ...], correctIndex: 2, ... }, ... ] } هذا هو ما يسمى وسيتم إرسالها إلى تطبيقنا . ChatGPT will generate quiz data exactly in this format toolInput (5) ويقوم التطبيق بإجراء وسوف تنتج سيتم عرض ChatGPT HTML "الموارد" التي توفرها التطبيق في نافذة الدردشة، وسيتم البدء بها مع البيانات . And finally, the user will see the app and will be able to interact with it . toolInput toolOutput toolOutput (6) (7) بناء مكتب MCP Code repo لبرنامج ChatGPT لدينا: . https://github.com/renal128/quizaurus-tutorial هناك 2 مشاريع: و أولاً، سنتركز على التي تستخدم JavaScript البسيط في frontend للحفاظ على الأشياء بسيطة. quizaurus-plain quizaurus-react quizaurus-plain كل رمز المورد في هذه الملفة - ما يقرب من 140 صفحة من الكود! https://github.com/renal128/quizaurus-tutorial/blob/main/quizaurus-plain/src/server.ts إعداد خادم هناك العديد من خيارات لإنشاء خادم MCP باستخدام أي من SDK المذكورة هنا: https://modelcontextprotocol.io/docs/sdk Here, we will use the . كاميرا MCP SDK الكود أدناه يظهر كيفية إعدادها: // 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); }); نقاط مهمة: التطبيق Express هو خادم عام يستقبل الاتصالات (مثل طلبات HTTP) من الخارج (من ChatGPT) باستخدام Express ، نضيف نقطة نهاية /mcp التي سنقدمها إلى ChatGPT ك عنوان خادم MCP لدينا (مثل 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(...) و mcpServer.registerResource(...) هي ما سوف نستخدمه لتنفيذ تطبيق Quiz لدينا MCP Tool دعونا نضيف الفجوة في الموقع على المرتبة الأولى لتسجيل “أداة”. mcpServer.registerTool(…) سوف يقرأ ChatGPT تعريف الأداة عند تسجيل التطبيق ، ثم ، عندما يحتاج المستخدم إلى ذلك ، سوف يقرأ ChatGPT الأداة لبدء اختبار: // 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" }, }; } ); يقدم الجزء العلوي من الكود وصف الأداة - سيعتمد ChatGPT على ذلك لتفهم متى وكيفية استخدامها: يشرح المقال في التفاصيل ما يفعله الأداة. ChatGPT سوف تستخدمه لتحديد ما إذا كان الأداة قابلة للتطبيق على الطلبة. إن inputSchema هو وسيلة لإعلام ChatGPT بالطبع ما هي البيانات التي يحتاجها لتزويد الأداة وكيفية إعدادها. كما يمكنك أن ترى أعلاه ، يحتوي على النصائح والتحديات التي يمكن استخدامها من خلال ChatGPT لإعداد بطاقة فعالة صحيحة (toolInput). يتم إزالة outputSchema هنا ، ولكن يمكنك تقديمها لكي تقول إلى ChatGPT ما سيكون له نظام StructuredContent. لذلك ، في معناه ، فإن الأداة هي ما يحدد تطبيق ChatGPT هنا. دعونا نلقي نظرة على الأسواق الأخرى 2 هنا: _meta[“openai/outputTemplate”] هو مؤشر الموارد MCP الذي سوف تستخدمه التطبيق ChatGPT لترجمتها. async (toolInput) => { ... هي الميزات التي تلقى toolInput من ChatGPT وتقوم بتنفيذ toolOutput التي ستكون متاحة لموقع الويندوز. هذا هو المكان الذي يمكننا إجراء أي منطق من جانب المورد لإجراء البيانات.في هذه الحالة، نحن لا نحتاج إلى أي معالجة لأن toolInput يحتوي على جميع المعلومات التي يحتاجها الويندوز، لذلك يعود الميزات إلى نفس البيانات في structuredContent التي ستكون متاحة ك toolOutput لموقع الويندوز. الموارد MCP أدناه هو كيفية تحديد مصدر MCP: // 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>` } ] } } ); في الأساس، "الموارد" هنا يوفر الجزء الأولي (الفيديو) من التطبيق. ui://widget/interactive-quiz.html هو ID الموارد، ويجب أن يوافق _meta[“openai/outputTemplate”] من تعريف الأداة من الفقرة السابقة أعلاه. 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 إنشاء Widget تطبيقات Widget الآن، دعونا نفكر بسرعة في التي تطبق الفيديو (الجزء المرئي من التطبيق): إصدار QuizaurusWidget.js // 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(); تذكير: سوف يتم تشغيل هذا الكود من قبل HTML التي نحددها في الموارد MCP أعلاه ( الجانب). HTML والتحرير سيكون داخل iframe على صفحة ChatGPT الترجمة. <script type="module">… ChatGPT يضيف بعض البيانات والملابس من خلال global object. Here’s what we’re using here: window.openai تحتوي window.openai.toolOutput على بيانات السؤال التي يتم إرسالها من قبل أداة MCP. في البداية سيتم إرسال HTML قبل إرسال أداة toolOutput، لذلك سيكون window.openai.toolOutput فارغًا. يمكن أن يكون ذلك أي بيانات التي نريدها، على الرغم من أن المشكلة هي الحفاظ عليها تحت 4000 طن. هنا، نحن نستخدمها لتذكر أي أسئلة قد تم إجراؤها بالفعل من قبل المستخدم، حتى إذا تم تحميل الصفحة مرة أخرى، سوف يتذكر التطبيق حالة. window.openai.sendFollowUpMessage({prompt: "...”}) هو طريقة لتقديم إشعار إلى ChatGPT كما لو أن المستخدم كتبها، وسوف يكتب ChatGPT الإجابة في الشبكة. You can find more capabilities in the OpenAI documentation here: https://developers.openai.com/apps-sdk/build/custom-ux وضع كل شيء معا حان وقت اختبارها! على سبيل المثال ، ستحتاج إلى مكالمة ChatGPT المدفوعة لتمكين وضع المطور. 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 استخدام React أعلاه، رأينا الكود الذي يستخدم JavaScript بسيط. , demonstrates how to implement a ChatGPT app using React. Quizaurus - ردود الفعل يمكنك العثور على بعض الوثائق المفيدة من OpenAI هنا: . https://developers.openai.com/apps-sdk/build/custom-ux/ إستخدامOpenAiGlobal helper hooks يمكنك مشاهدةها هنا ، يتم نسخ الكود من الوثائق: https://github.com/renal128/quizaurus-tutorial/blob/main/quizaurus-react/web/src/openAiHooks.ts وهو الأكثر فائدة هو ، الذي يسمح لك بتسجيل تطبيق React إلى التحديثات في . Remember that in the plain (non-React) app above, we had the issue that the “Start Quiz” button wasn’t doing anything until the data is ready? Now we can improve the UX by showing a loading 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 ... } When toolOutput gets populated, React will automatically re-render the app and will show the quiz instead of the loading state. ردود الفعل Router يتم ربط تاريخ المرور في iframe التي يتم عرض التطبيق بها إلى تاريخ المرور في الصفحة ، لذلك يمكنك استخدام APIات المرور مثل React Router لتنفيذ المرور داخل التطبيق. أشياء أخرى وخصائص ملاحظة: تطوير التطبيق ChatGPT ليس ثابتًا جدًا في الوقت الحالي، حيث لم يتم تطوير الوظيفة بالكامل، لذلك من الممكن توقع تغييرات غير مسبوقة في API أو أخطاء صغيرة. https://developers.openai.com/apps-sdk كيف ومتى يقرر ChatGPT عرض تطبيقك للمستخدم 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: إطلاق تطبيقات متصلة.بعد أن يتم اتصال التطبيق، يمكن لـ ChatGPT استخدامها في محادثة عند الضرورة.هل يمكن للمستخدمين إدخالها بسهولة عن طريق إشارة اسم التطبيق في النص، أو إدخال @AppName أو انقر فوق + و اختيار التطبيق في النسخة الرسمية هناك. منصات الدعم شبكة الإنترنت - نظرًا لأنها تطبق من خلال iframe ، فإن شبكة الإنترنت هي الأكثر سهولة الدعم وأنا لم أكن لدي أي مشاكل هناك. تطبيقات الهواتف المحمولة - إذا اتصلت بالبرامج على شبكة الإنترنت ، يجب أن تكون قادرة على رؤيةها على الهاتف المحمول. لم أتمكن من تشغيل التطبيق على الهاتف المحمول - لم يتمكن من إدخال الأداة ، ولكن عندما أطلقت التطبيق على شبكة الإنترنت ، تمكنت من التفاعل معها على الهاتف المحمول. الاعتماد تطبيقات ChatGPT تدعم OAuth 2.1: https://developers.openai.com/apps-sdk/build/auth هذا موضوع كبير ، دعني أعرف ما إذا كان من المفيد كتابة مقال منفصل عن ذلك! إعداد طلبات الشبكة هذا ما قاله التقرير ( ( ) : « العمل مع شريك OpenAI الخاص بك إذا كنت بحاجة إلى مراكز معينة تسمح قائمة. " المصدر Standard fetch requests are allowed only when they comply with the CSP في مكان آخر ( ) ، يُنصح بتصميم موضوع في تعريف الموارد لتمكين المجالات الخاصة بك: here _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'], } } شيء آخر يمكنك استخدامه هو callback. Your app widget (frontend) can use it to call a tool on your MCP server - you provide the MCP tool name and toolInput data and receive back toolOutput: window.openai.callTool await window.openai?.callTool("my_tool_name", { "param_name": "param_value" }); الخصائص الرئيسية الأخرى See this documentation for what’s available to your frontend code via : : window.openai https://developers.openai.com/apps-sdk/build/custom-ux يمكنك الوصول إلى الأماكن التالية (مثل: سوف يخبرك إذا كان ChatGPT في الوقت الحالي في وضع الضوء أو الظلام): 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; كما يمكنك استخدام الدعوات التالية (على سبيل المثال، محاولة لإنشاء تطبيقك على الشاشة الكاملة): 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>; شكرا لك ! هذا هو كل شيء، شكرا على القراءة وأفضل النجاح مع أي شيء أنت بناء!