In diesem Tutorial lernst du, wie du eine eigene App für ChatGPT baust. Wir erstellen eine einfache „Hello“-App, die einen Namen von ChatGPT empfängt und diesen in der App anzeigt.
Was wir bauen: Eine App, die auf den Prompt @Hello Begrüsse David in der App reagiert und „Hallo David!“ anzeigt.
Voraussetzungen
- Node.js (Version 18 oder höher)
- ngrok (um deinen lokalen Server für ChatGPT erreichbar zu machen)
- Ein ChatGPT Plus/Pro Account mit aktiviertem Developer Mode
Projektstruktur
Unsere App besteht aus nur 3 Dateien:
hello-app/
├── server.js # Der MCP-Server
├── public/
│ └── hello.html # Das UI-Widget
└── package.json # Abhängigkeiten
Schritt 1: Projekt initialisieren
Erstelle einen neuen Ordner und initialisiere das Projekt:
mkdir hello-app
cd hello-app
npm init -y
Installiere die benötigten Pakete:
npm install @modelcontextprotocol/sdk zod
Füge in der package.json den Typ module hinzu:
{
"name": "hello-app",
"version": "1.0.0",
"type": "module",
"scripts": {
"start": "node server.js"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.12.1",
"zod": "^3.25.23"
}
}
Schritt 2: Das HTML-Widget erstellen
Erstelle den Ordner public und darin die Datei hello.html:
mkdir public
public/hello.html:
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<title>Hello App</title>
<style>
body {
font-family: system-ui, sans-serif;
display: flex;
justify-content: center;
align-items: center;
min-height: 100px;
margin: 0;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.greeting {
font-size: 2rem;
color: white;
text-align: center;
padding: 20px;
}
</style>
</head>
<body>
<div class="greeting" id="output">Warte auf Daten...</div>
<script>
const outputEl = document.getElementById("output");
function render() {
// Daten von ChatGPT über window.openai.toolOutput abrufen
const data = window.openai?.toolOutput;
if (data?.name) {
outputEl.textContent = `Hallo ${data.name}!`;
}
}
// Initiales Rendern
render();
// Neu rendern, wenn ChatGPT neue Daten setzt
window.addEventListener("openai:set_globals", render, { passive: true });
</script>
</body>
</html>
Wie funktioniert das?
Der entscheidende Teil ist das Auslesen der Daten:
const data = window.openai?.toolOutput;
ChatGPT stellt im iframe das globale Objekt window.openai bereit. Dein structuredContent vom Server landet in window.openai.toolOutput.
Das Event openai:set_globals wird ausgelöst, wenn ChatGPT neue Daten an das Widget übergibt – so kann dein UI dynamisch reagieren.
Schritt 3: Den MCP-Server erstellen
server.js:
import { createServer } from "node:http";
import { readFileSync } from "node:fs";
import { randomUUID } from "node:crypto";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { z } from "zod";
// HTML-Widget laden
const helloHtml = readFileSync("public/hello.html", "utf8");
function createHelloServer() {
const server = new McpServer({ name: "hello-app", version: "1.0.0" });
// 1. Widget als Resource registrieren
server.registerResource(
"hello-widget",
"ui://widget/hello.html",
{},
async () => ({
contents: [{
uri: "ui://widget/hello.html",
mimeType: "text/html+skybridge",
text: helloHtml,
}],
})
);
// 2. Tool registrieren, das ChatGPT aufrufen kann
server.registerTool(
"greet",
{
title: "Begrüssung",
description: "Begrüsst eine Person mit Namen",
inputSchema: {
name: z.string().describe("Der Name der Person"),
},
_meta: {
"openai/outputTemplate": "ui://widget/hello.html",
},
},
async (args) => {
return {
content: [{ type: "text", text: `Begrüsse ${args.name}` }],
structuredContent: {
name: args.name,
},
};
}
);
return server;
}
// HTTP-Server starten
const port = 8787;
const httpServer = createServer(async (req, res) => {
const url = new URL(req.url, `http://${req.headers.host}`);
// CORS für Preflight-Requests
if (req.method === "OPTIONS") {
res.writeHead(204, {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "POST, GET, DELETE, OPTIONS",
"Access-Control-Allow-Headers": "content-type, mcp-session-id",
"Access-Control-Expose-Headers": "mcp-session-id",
});
res.end();
return;
}
// MCP-Endpunkt
if (url.pathname === "/mcp") {
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Expose-Headers", "mcp-session-id");
const server = createHelloServer();
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: randomUUID,
enableJsonResponse: true,
});
res.on("close", () => {
transport.close();
server.close();
});
await server.connect(transport);
await transport.handleRequest(req, res);
return;
}
res.writeHead(404).end("Not Found");
});
httpServer.listen(port, () => {
console.log(`🚀 Hello App läuft auf http://localhost:${port}/mcp`);
});
Der Datenfluss erklärt
┌─────────────────────────────────────────────────────────────────┐
│ ChatGPT: "@Hello Begrüsse David in der App" │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ ChatGPT erkennt das Tool "greet" und ruft es auf mit: │
│ { name: "David" } │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Server empfängt den Aufruf und gibt zurück: │
│ { │
│ content: [{ type: "text", text: "Begrüsse David" }], │
│ structuredContent: { name: "David" } │
│ } │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ ChatGPT lädt das Widget (hello.html) und stellt │
│ structuredContent in window.openai.toolOutput bereit │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Widget liest window.openai.toolOutput und zeigt: │
│ "Hallo David!" │
└─────────────────────────────────────────────────────────────────┘
Schritt 4: Server starten und mit ChatGPT verbinden
Server starten:
npm start
Mit ngrok öffentlich machen:
ngrok http 8787
Du erhältst eine URL wie https://abc123.ngrok.app.
In ChatGPT verbinden:
- Gehe zu Settings → Apps & Connectors → Advanced settings
- Aktiviere den Developer Mode
- Gehe zu Settings → Connectors → Create
- Füge deine ngrok-URL mit
/mcpein:https://abc123.ngrok.app/mcp - Gib der App einen Namen (z.B. „Hello“)
Testen:
Öffne einen neuen Chat, wähle deine App aus und schreibe:
@Hello Begrüsse David in der App
Die App zeigt nun „Hallo David!“ an!
Zusammenfassung
Die wichtigsten Konzepte:
- Resource registrieren – Das HTML-Widget, das in ChatGPT angezeigt wird
- Tool registrieren – Die Funktion, die ChatGPT aufrufen kann
- structuredContent – Die Daten, die an das Widget gesendet werden
- window.openai.toolOutput – So liest das Widget die Daten im Browser
- openai:set_globals – Event, um auf neue Daten zu reagieren
Mit diesen Grundlagen kannst du beliebige interaktive Apps für ChatGPT bauen!