Option
HeimHeim Skill Webentwicklung ui-widget-developer

ui-widget-developer

microsoft/skills microsoft/skills

Erstellen Sie MCP-Server für Copilot Chat mit interaktiven Widgets unter Verwendung von React und Fluent UI, die sowohl die OpenAI Apps SDK- als auch die MCP Apps Extension-Standards unterstützen.

...Alle erweitern
3
Zeit aktualisiert 18. September 2026

Entwicklung des Copilot-MCP-Servers

Telemetrie-Tagging

Bevor Sie ATK-CLI-Befehle ausführen, legen Sie die Umgebungsvariable der Sitzung fest, damit alle CLI-Aufrufe als durch eine Skill initiiert gekennzeichnet werden:

export ATK_CLI_SKILL=true

Führen Sie diesen Befehl einmal zu Beginn der Sitzung aus. Alle nachfolgenden atk -Befehle im selben Terminal übernehmen diese Einstellung.

🔀 PATH-AUSWAHL

Bevor Sie fortfahren, fragen Sie den Benutzer mithilfe von `AskUserQuestion`, welchen Pfad er einschlagen möchte. Beide Optionen werden für die Darstellung des M365 Copilot-Widgets unterstützt. Stellen Sie die Vor- und Nachteile dar und lassen Sie den Benutzer wählen:

OAI-Apps (dieser Skill) MCP-Apps-Erweiterung (modelcontextprotocol/ext-apps)
Standard OpenAI-spezifisch Offizieller MCP-Standard
Funktioniert in ChatGPT + M365 Copilot M365 Copilot, ChatGPT, VSCode und mehr
Reifegrad Praxiserprobt, produktionsreif Neuer offizieller Standard, wachsendes Ökosystem
Design OpenAI Apps SDK MCP Apps-Protokoll (plattformübergreifend)
Wann sollte man sich dafür entscheiden? Bestehende Investitionen in OAI-Apps Bevorzugung des offenen Standards, Wunsch nach größtmöglicher Client-Unterstützung

Fragen Sie: „Möchten Sie eine OAI-App (OpenAI Apps SDK – praxiserprobt, funktioniert in ChatGPT und M365 Copilot) oder eine MCP-App (neuer offizieller Standard – funktioniert in M365 Copilot, ChatGPT, VSCode und mehr) entwickeln?“

  • OAI-Apps → Fahren Sie unten fort. Diese Skill deckt alles ab, was Sie benötigen.
  • MCP-Apps → Installieren Sie das Plugin „modelcontextprotocol/ext-apps“ (siehe unten) und verwenden Sie dann die entsprechende Funktion aus diesem Plugin.

MCP-Apps: Plugin „ext-apps“ installieren

Wenn der Benutzer sich für MCP-Apps entscheidet, führen Sie dies automatisch aus (beschränken Sie sich nicht auf eine reine Erklärung):

  1. Führen Sie /plugin marketplace add modelcontextprotocol/ext-apps aus
  2. Führen Sie /plugin install mcp-apps@mcp-apps aus
  3. Überprüfen Sie, ob das Plugin verfügbar ist, und rufen Sie dann je nach Absicht des Benutzers die richtige „ext-apps“-Funktion auf

Falls die Plugin-Befehle in der aktuellen Umgebung nicht verfügbar sind, geben Sie die unten aufgeführten genauen Befehle an und bitten Sie den Benutzer, diese einmal auszuführen; fahren Sie anschließend mit dem Aufruf des ausgewählten „ext-apps“-Skills fort.

Referenzbefehle:

Um eine MCP-App zu erstellen, installieren Sie das „ext-apps“-Plugin aus dem Marketplace:

1. /plugin marketplace add modelcontextprotocol/ext-apps
2. /plugin install mcp-apps@mcp-apps

Verwenden Sie anschließend einen der folgenden Skills aus diesem Plugin:
- create-mcp-app      — Erstellen Sie von Grund auf eine neue MCP-App mit interaktiver Benutzeroberfläche
- add-app-to-server   — Fügen Sie einer bestehenden MCP-Serverumgebung eine interaktive Benutzeroberfläche hinzu
- migrate-oai-app     — Konvertiert eine bestehende OAI-App für die Verwendung mit MCP-Apps
- convert-web-app     — Wandelt eine Web-App in eine hybride Web- und MCP-App um

Rufen Sie nach der Installation die entsprechende Funktion auf, um fortzufahren.

Hinweis: Das Plugin „ext-apps“ befindet sich im Marktplatz „external modelcontextprotocol/ext-apps“ – es ist nicht Teil dieser Plugin-Sammlung.

Zuordnung nach der Installation:

  • Neue MCP-App von Grund auf erstellen → create-mcp-app
  • App-Benutzeroberfläche zu einem bestehenden MCP-Server hinzufügen → add-app-to-server
  • Bestehende OAI-App migrieren → migrate-oai-app
  • Vorhandene Web-App konvertieren → convert-web-app

📛 PROJEKTERKENNUNG 📛

Diese Funktion wird ausgelöst, wenn MCP-Server mit OAI-App oder Widget-Rendering für Microsoft 365 Copilot Chat erstellt werden. Der MCP-Server kann in jeder Sprache geschrieben sein, die das MCP-Protokoll unterstützt (TypeScript, Python, C# usw.). Das Agent-Projekt und der MCP-Server können sich im selben Repo, in separaten Ordnern oder in völlig unterschiedlichen Projekten befinden.

Szenario-Routing

Ausgangspunkt Was Sie benötigen Pfad
Bevorzugen Sie den MCP-Apps-Standard Plattformübergreifende Widget-Unterstützung (M365 Copilot, ChatGPT, VSCode und mehr) Installieren Sie „modelcontextprotocol/ext-apps“ und verwenden Sie anschließend „create-mcp-app“ oder „add-app-to-server“ – siehe „Auswahl des Vorgehens“ oben
Von Grund auf neu (kein Agent, kein MCP-Server) Vollständige OAI-App-Einrichtung Beauftragen Sie zunächst „declarative-agent-developer“ mit der Erstellung des Agent-Gerüsts und kehren Sie anschließend hierher zurück, um den MCP-Server und die Widgets einzurichten
Bestehender M365-Agent, neuer MCP-Server MCP-Server + Widgets + „mcpPlugin.json“ Beginnen Sie bei der Implementierung
Bestehender MCP-Server, Copilot-Widgets hinzufügen Widget-Unterstützung zum bestehenden Server hinzugefügt Beginnen Sie mit dem Copilot-Widget-Protokoll
Sprachauswahl (nicht TypeScript) Protokollanforderungen Siehe „Copilot Widget Protocol“ für die zu implementierenden Funktionen, „MCP Server Pattern (TypeScript)“ als Referenz

🚨 WICHTIGE AUSFÜHRUNGSREGELN 🚨

FLUENT-UI-VORGABE (ERFORDERLICH): Widget-Implementierungen MÜSSEN React- und Fluent-UI-Komponenten verwenden. Bevor der Agent Widget-Code schreibt, MUSS er Folgendes lesen und befolgen:

  • references/widget-patterns.md
  • references/best-practices.md FLUENT-UI-PAKETANFORDERUNG (ERFORDERLICH): Das Widget-Projekt MUSS vor der Implementierung die Fluent-UI-Abhängigkeiten einbinden. Installiere mindestens die folgenden Komponenten und behalte sie in den Abhängigkeiten des Widget-Pakets bei:
  • @fluentui/react-components
  • react
  • react-dom

Sollte eines dieser Pakete fehlen, installieren Sie es automatisch, bevor Sie mit der Generierung des Widget-Codes fortfahren.

Wenn das generierte Widget keine React-Einstiegsdateien (z. B. „widgets/src//main.tsx“ und eine React-Komponentendatei) sowie Fluent-Importe aus „@fluentui/react-components“ enthält, ist die Aufgabe unvollständig und MUSS korrigiert werden, bevor Ergebnisse zurückgegeben werden.

KEINE REINEN HTML-WIDGETS (STANDARD): Implementieren Sie den App-Inhalt nicht direkt mit statischen HTML-Vorlagen und Inline-JS als endgültige Widget-Lösung. Eine minimale HTML-Shell-Datei ist nur als Loader für gebaute React-Assets zulässig. Reine, in sich geschlossene HTML-Widgets sind nur zulässig, wenn der Benutzer explizit einen Nicht-React-Prototyp anfordert.

HINTERGRUNDPROZESSE: Der MCP-Server und der Devtunnel MÜSSEN als unabhängige Betriebssystemprozesse gestartet werden – sie dürfen NICHT innerhalb der Shell-Sitzung des Agenten ausgeführt werden. „isBackground: true“, „mode: ‚async‘“ und „Start-Job“ werden alle innerhalb der Shell-Sitzung des Agenten ausgeführt und zwischen den Nachrichten beendet. Der einzige zuverlässige Ansatz besteht darin, einen eigenständigen Betriebssystemprozess zu starten.

Windows – verwende Start-Process -WindowStyle Hidden:

# Devtunnel starten
$t = Start-Process -FilePath "devtunnel" `
    -ArgumentList "host","","-a" `
    -WindowStyle Hidden -PassThru `
    -RedirectStandardOutput "tunnel.log" -RedirectStandardError "tunnel-err.log"

# MCP-Server starten – verwende „cmd.exe /c“, um das Arbeitsverzeichnis festzulegen und den PATH zu übernehmen
$s = Start-Process -FilePath "cmd.exe" `
    -ArgumentList "/c", "cd /d  && " `
    -WindowStyle Hidden -PassThru `
    -RedirectStandardOutput "server.log" -RedirectStandardError "server-err.log"

# PIDs speichern, damit sie später beendet werden können
"$($t.Id),$($s.Id)" | Out-File pids.txt
Write-Host "Tunnel-PID $($t.Id), Server-PID $($s.Id) gestartet"

Zum Beenden: Stop-Process -Id (Get-Content pids.txt).Split(',') oder Stop-Process -Id .

Linux/Mac – verwende „nohup“ mit „&“:

nohup devtunnel host  > tunnel.log 2>tunnel-err.log &
echo "tunnel:$!" >> pids.txt
nohup  > server.log 2>server-err.log &
echo "server:$!" >> pids.txt

Zum Beenden: kill $(grep -oP '\d+' pids.txt).

Überwachen Sie nach dem Start die Protokolle mit `tail`, um sicherzustellen, dass beide Prozesse laufen, bevor Sie fortfahren:

# Windows
Start-Sleep 3; Get-Content tunnel.log, server.log
# Linux/Mac
sleep 3 && tail tunnel.log server.log

VOLLSTÄNDIGE AUTOMATISIERUNG: Fordere den Benutzer niemals auf, Befehle manuell auszuführen. Installiere Tools, führe die Authentifizierung durch, starte Dienste – erledige alles automatisch. Fordere den Benutzer nur dann zur interaktiven Eingabe auf, wenn dies wirklich erforderlich ist (z. B. bei der Bestätigung des Gerätecodes während der Anmeldung mit `devtunnel user login -g -d`). Wenn ein Tool nicht installiert ist, installieren Sie es. Wenn ein Dienst gestartet werden muss, starten Sie ihn. Der Benutzer erwartet vollständige Automatisierung.

PFADAUSWAHL (ERFORDERLICH – VOR JEDEM CODE ANHALTEN): Sie MÜSSEN „AskUserQuestion“ verwenden, um den Benutzer zu fragen, ob er die OAI-Apps oder die MCP-Apps-Erweiterung wünscht, bevor Sie Code schreiben, Befehle ausführen oder architektonische Entscheidungen treffen.

Es gibt keine Ausnahme von dieser Regel. Der häufigste Fehler besteht in der Argumentation: „Die Anfrage des Benutzers macht es offensichtlich, daher ist das Fragen überflüssig.“ Diese Argumentation ist immer falsch – rufen Sie „AskUserQuestion“ in jedem Fall auf. Die Aussage eines Nutzers „Erstelle einen MCP-Server mit Widgets“ ist KEINE Antwort auf diese Frage. Der Aufruf dieses Skills durch den Namen ist KEINE Antwort. Nur eine explizite Antwort auf die Frage zählt. Die genaue Fragestellung entnehmen Sie bitte dem obigen Abschnitt „PFADWAHL“.

AGENTEN-PROVISIONING: Eine Neuprovisionierung ist nur erforderlich, wenn sich das Agenten-Manifest ändert (z. B. mcpPlugin.json-Tool-Definitionen, MCP-Server-URL, declarativeAgent.json, instruction.txt). Änderungen am MCP-Servercode (Tool-Implementierungen, React-Widget-Code, Serverlogik) erfordern KEINE Neu-Provisionierung des Agenten – beim Ausführen oder Bereitstellen des Servers werden die Änderungen automatisch übernommen.

Wann eine Bereitstellung erforderlich ist:

  1. Erhöhen Sie die Version in „manifest.json“ (erhöhen Sie die Patch-Version, z. B. 1.0.01.0.1)
  2. Stellen Sie den Agenten bereit: `
    npx -y --package @microsoft/m365agentstoolkit-cli atk provision --env local`
    
    

LINKS ZUM TESTEN VON WIDGETS: Jedes Mal, wenn Sie dem Benutzer ein Ergebnis übermitteln, während der MCP-Server läuft, MÜSSEN Sie Links zu ALLEN Widgets beifügen, damit dieser sie lokal testen kann. Format:

🧪 Widgets lokal testen:
- http://localhost:3001/widgets/widget-name.html
- http://localhost:3001/widgets/another-widget.html

Listen Sie alle .html- Dateien im Verzeichnis „mcp-server/widgets/“ (oder einem entsprechenden Widget-Ordner) auf. Dies hilft den Benutzern, die Darstellung der Widgets zu überprüfen, bevor sie diese in Copilot testen.

AUTOMATISCHE BEREITSTELLUNG NACH ABSCHLUSS (ERFORDERLICH – NICHT ÜBERSPRINGEN): Wenn die Programmierung abgeschlossen ist, fahren Sie automatisch fort, ohne auf den Benutzer zu warten:

  1. Starten Sie den MCP-Server + Devtunnel im Hintergrund (gemäß „HINTERGRUNDPROZESSE“ oben)
  2. Führen Sie eine E2E-Überprüfung mit MCP Inspector durch (gemäß der unten stehenden „MCP-TOOL-KONFIGURATIONSREGEL“) – beheben Sie etwaige Fehler, bevor Sie fortfahren
  3. Bereitstellen des Agenten, falls erforderlich (gemäß „AGENT-BEREITSTELLUNG“ oben)
  4. Drucken Sie eine Projektzusammenfassung in diesem Format aus:
## ✅  — Bereit

### Widgets
- [widget-name.html](http://localhost:/widgets/widget-name.html)
- [widget-name2.html](http://localhost:/widgets/widget-name2.html)

### Endpunkte
- MCP-Server: http://localhost:/mcp
- MCP über Tunnel: https:///mcp

### Test in Copilot
Lokal:      https://m365.cloud.microsoft/chat/?titleId={M365_TITLE_ID aus env/.env.local}
Andere Umgebungen: {SHARE_LINK aus env/.env.{environment}}

AGENT-PROJEKT-DELEGIERUNG: Diese Funktion erstellt MCP-Server und Widgets, KEINE deklarativen Agentenprojekte. Wenn die Anfrage des Benutzers die Erstellung oder Konfiguration des deklarativen Agenten selbst betrifft (Scaffolding, m365agents.yml, m365agents.local.yml, declarativeAgent.json, Manifest-Lebenszyklus), delegieren Sie die Anfrage an die Skill „declarative-agent-developer “.

MCP-RESSOURCENREGISTRIERUNG: Jedes Widget MUSS über eine entsprechende MCP-Ressource verfügen. Ohne Ressourcen kann Copilot keine Widget-Shells über das MCP-Protokoll abrufen, und die Widgets werden nicht gerendert.

Führen Sie für jedes neue Widget diese Checkliste durch:

  1. ☐ Erstellen Sie eine HTML-Datei für die Widget-Shell im Verzeichnis „widgets/“ und einen React-Widget-Eintrag unter „widgets/src//“ (siehe „widget-patterns.md“)
  2. ☐ Definieren Sie eine URI-Konstante ui://widget/.html
  3. ☐ Fügen Sie dem „resources “-Array einen „Resource “-Eintrag mit folgendem Inhalt hinzu:
    • uri: die URI „ui://widget/.html“
    • mimeType: „text/html+skybridge“
    • _meta: CSP-Konfiguration mit „openai/widgetDomain“ und „openai/widgetCSP“ (aus der Umgebung)
  4. ☐ Fügen Sie einen Handler für „resources/read“ hinzu, der den HTML-Code der Widget-Shell für diese URI zurückgibt
  5. ☐ Fügen Sie das Tool hinzu, wobei _meta.openai/outputTemplate auf dieselbe URI „ui://widget/.html“ verweist
  6. ☐ Überprüfen, ob die Serverfähigkeiten in der Initialisierungsantwort „resources: {} “ enthalten

Überlegungen zur Widget-Shell und zu Assets:

  • Bevorzugt (React + Fluent UI): Die Ressourcen-HTML-Datei sollte eine minimale Hülle sein, die auf vorgefertigte JS-/CSS-Assets verweist, die über die Route /assets/ des MCP-Servers bereitgestellt werden.
  • Nur als Ausnahme: Eigenständige HTML-Dateien über „resources/read“ sind ausschließlich für explizit vom Benutzer angeforderte Prototypen vorgesehen. Der Standard- und Produktionspfad ist React + Fluent UI.

Beispiel-Shell für die React-Build-Ausgabe:


  
  

  

Verwenden Sie die Umgebungsvariable WIDGET_BASE_URL oder MCP_SERVER_URL als Basis-URL für Assets (siehe mcp-server-pattern.md, Abschnitt „Configurable Widget Base URL“).

Die vollständigen Muster für die Bereitstellung von Ressourcen und Assets finden Sie in mcp-server-pattern.md.

⚠️ REGEL ZUR MCP-TOOL-KONFIGURATION ⚠️

Schreiben Sie Tool-Definitionen NIEMALS manuell in die Datei „mcpPlugin.json“. Verwenden Sie immer den MCP Inspector, um die vollständigen Tool-Definitionen vom laufenden MCP-Server abzurufen.

KONVENTION FÜR DIE BENENNUNG VON TOOLS: Tool-Namen MÜSSEN dem Muster ^[A-Za-z0-9_]+$ entsprechen (nur Buchstaben, Zahlen und Unterstriche). Verwenden Sie NIEMALS Bindestriche (-) in Tool-Namen. Verwenden Sie stattdessen Unterstriche (z. B. „render_profile“ statt „render-profile“).

VORGESCHRIEBENER ARBEITSABLAUF:

  1. Starten Sie den MCP-Server (im Hintergrund)
  2. Verwenden Sie den MCP Inspector, um die neuesten Tool-Definitionen abzurufen:
    npx @modelcontextprotocol/[email protected] --cli https://my-mcp-server.example.com --transport http --method tools/list
    
    
  3. Kopieren Sie die VOLLSTÄNDIGE Tool-Definition aus dem Inspector (einschließlich Name, Beschreibung, inputSchema, _meta, Anmerkungen, Titel)
  4. Fügen Sie diese in die Datei ` mcpPlugin.json` unter ` runtimes[].spec.mcp_tool_description.tools `ein (innerhalb des `spec `-Objekts der ` RemoteMCPServer `-Laufzeitumgebung)
  5. Führen Sie eine E2E-Überprüfung über den Devtunneldurch – rufen Sie jedes Tool auf und vergewissern Sie sich, dass die Antwort „structuredContent“ und „_meta.openai/widgetAccessible: true“ enthält:
    npx @modelcontextprotocol/[email protected] --cli https:///mcp --transport http --method tools/call --tool-name 
    
    
    Überprüfen Sie außerdem, ob der GET-Aufruf https:///health den Wert {"status":"ok"} zurückgibt. Beheben Sie etwaige Fehler vor der Bereitstellung.

Der MCP Inspector zeigt das genaue Tool-Schema Ihres Servers an. Kopieren Sie es vollständig – schreiben oder ändern Sie diese Definitionen nicht manuell. Dadurch wird sichergestellt, dass mcpPlugin.json mit dem MCP-Server synchron bleibt.

Erstellen Sie MCP-Server, die sich in Microsoft 365 Copilot Chat integrieren lassen und reichhaltige, interaktive Widgets darstellen.

Architektur

M365 Copilot ──▶ mcpPlugin.json ──▶ MCP-Server ──▶ structuredContent ──▶ React + Fluent UI-Widget
     │              (RemoteMCPServer)    (Streamable HTTP)                  (window.openai.toolOutput)
     │
     └── Funktionen (Personen usw.) stellen Daten bereit, die an MCP-Tools weitergeleitet werden

Projektstruktur

Beispiel für eine Projektstruktur; keine zwingende Vorgabe, sondern ein gängiges Muster für die Organisation der MCP-Server- und Widget-Entwicklung:

project/
├── appPackage/
│   ├── manifest.json           # Teams-Manifest (Version bei Bereitstellung erhöhen)
│   ├── declarativeAgent.json   # Agentenkonfiguration + Funktionen
│   ├── mcpPlugin.json          # Tool-Definitionen mit _meta
│   └── instruction.txt         # Anweisungen zum Verhalten des Agenten
├── mcp-server/
│   ├── src/index.ts            # Server mit Streamable HTTP
│   ├── widgets/                # Widget-Shells + React-Quellcode
│   │   ├── my-widget.html      # Minimale Shell, die von `resources/read` zurückgegeben wird
│   │   └── src/my-widget/      # React + Fluent UI-Quellcode
│   ├── assets/                 # Gebaute Widget-Bundles, die unter /assets bereitgestellt werden
│   └── package.json
├── scripts/
│   ├── setup-devtunnel.sh      # Einrichtung des DevTunnels unter Linux/Mac
│   └── setup-devtunnel.ps1     # Einrichtung des DevTunnels unter Windows
└── env/.env.local              # MCP_SERVER_URL, MCP_SERVER_DOMAIN

Anmerkung zur Sprache: Hier wird die Struktur eines TypeScript-Projekts gezeigt. Für Python ersetzen Sie „mcp-server/src/index.ts“ durch Ihren Python-Einstiegspunkt (z. B. „server.py“). Für C# verwenden Sie eine standardmäßige .NET-Projektstruktur. Die Verzeichnisse „appPackage/“, „widgets/“, „scripts/“ und „env/“ sind sprachunabhängig.

Copilot-Widget-Protokoll

Ihr MCP-Server muss diese Protokollanforderungen erfüllen, um Widgets im Copilot-Chat darzustellen. Dies gilt unabhängig von der Sprache:

  1. Streamfähiger HTTP-Transport/mcp -Endpunkt, der POST, GET und DELETE mit Sitzungsverwaltung verarbeitet
  2. CORS-Header – Herkunftsprüfung für /mcp, die m365.cloud.microsoft und *.m365.cloud.microsoft zulässt, mit den erforderlichen MCP-Headern
  3. Serverfunktionen – Die „initialize “-Antwort muss „resources: {} “ und „tools: {}“ deklarieren
  4. MCP-Ressourcen – Registrieren Sie Widgets mit den URIs „ui://widget/.html“, dem MIME-Typ „text/html+skybridge“ und CSP -_meta
  5. Format der Tool-Antwort – Rückgabe von „content“ (Text) + „structuredContent“ (Widget-Daten) + „_meta“ mit „openai/outputTemplate“
  6. Widget-Bereitstellung – HTTP-Route unter /widgets/*.html für Shell-Dateien und /assets/* für kompilierte Bundles, beide mit CORS-Herkunftsprüfung

Ausführliche Informationen zum Protokoll, JSON-Formate und eine Checkliste zur Anpassung bestehender MCP-Server finden Sie in references/copilot-widget-protocol.md.

Implementierung

MCP-Server-Muster (TypeScript-Referenz)

Die vollständige Implementierung finden Sie unter „references/mcp-server-pattern.md“.

Für andere Sprachen implementieren Sie die im Copilot-Widget-Protokoll beschriebenen Anforderungen mithilfe des MCP-SDK Ihrer Sprache. SDK-Pakete finden Sie in der Tabelle „Language SDK References“.

Kernanforderungen:

  • Stellen Sie den Streamable-HTTP-Transport unter /mcp bereit
  • Geben Sie „structuredContent“ + „_meta“ mit „openai/outputTemplate“ zurück
  • Widgets über einen HTTP-Endpunkt bereitstellen
  • CORS für Cross-Origin-Anfragen verarbeiten
  • Teilweise Daten korrekt verarbeiten (fehlende Felder mit „Unknown“ auffüllen)

Antwortformat des Tools:

return {
  content: [{ type: "text", text: "Summary" }],
  structuredContent: { /* Widget-Daten */ },
  _meta: { "openai/outputTemplate": "ui://widget/name.html", "openai/widgetAccessible": true }
};

Umgang mit unvollständigen Daten

Normalisieren Sie Eingabedaten stets, um fehlende Felder zu behandeln:

server.setRequestHandler(CallToolRequestSchema, async (request: CallToolRequest) => {
  const args = request.params.arguments as { title?: string; items?: Partial[] };

  // Daten normalisieren – fehlende Felder mit „Unbekannt“ auffüllen
  const title = args.title || „Standardtitel“;
  const items = (args.items || []).map(item => ({
    name: item.name || „Unbekannt“,
    value: item.value || „Unbekannt“,
  }));

  // „structuredContent“ für das Widget erstellen
  const structuredContent = { title, items };
  // ...
});

Widget-Muster

Vollständige Beispiele finden Sie in „references/widget-patterns.md“.

Grundlegende Anforderungen:

  • Verwende React + Fluent-UI-Komponenten (@fluentui/react-components)
  • Stellen Sie sicher, dass die Abhängigkeiten des Widget-Pakets @fluentui/react-components, react und react-dom enthalten
  • Theme mit FluentProvider (webLightTheme/webDarkTheme) und Fluent -Tokens
  • Greifen Sie über gemeinsam genutzte Hooks auf Daten zu (z. B. useOpenAiGlobal("toolOutput"))
  • Fehlerbehebung: Eingebettete Mock-Daten, wenn ` window.openai ` nicht verfügbar ist
  • Behandeln Sie „Unknown“-Werte elegant (z. B. Aktionsschaltflächen ausblenden)

Plugin-Schema

Siehe references/plugin-schema.md für das Format von mcpPlugin.json.

Kernanforderungen:

  • Schema v2.4 mit RemoteMCPServer -Laufzeitumgebung
  • Das Array„run_for_functions“ muss mit den Tool-Namen übereinstimmen
  • _meta in Tool-Definitionen für die Widget-Bindung
  • inputSchema – Eigenschaften aus Gründen der Flexibilität optional gestalten, Standardwerte in den Beschreibungen angeben

Einrichtung von DevTunnels

Nur für lokale Tests. DevTunnels dienen der Entwicklung und dem Testen auf Ihrem Rechner. Bevor Sie den Agenten in größerem Umfang freigeben, stellen Sie sowohl den MCP-Server als auch die Widget-Assets in einer gehosteten Umgebung bereit (z. B. Azure App Service, Azure Static Web Apps oder bei einem anderen Hosting-Anbieter) und aktualisieren Sie die URLs im Agenten-Manifest entsprechend.

DevTunnels stellen Ihren lokalen MCP-Server über benannte Tunnel mit stabilen URLs für M365 Copilot bereit. Informationen zu Einrichtungsskripten, Befehlsreferenz und Fehlerbehebung finden Sie in „references/devtunnels.md“.

Das Einrichtungsskript (npm run tunnel / npm run tunnel:win):

  1. Erstellt beim ersten Ausführen einen benannten Tunnel (oder verwendet den vorhandenen wieder)
  2. Startet das Hosting des Tunnels auf dem konfigurierten Port
  3. Aktualisiert die Datei „env/.env.local“ mit den Variablen „MCP_SERVER_URL“ und „MCP_SERVER_DOMAIN“ (nur beim ersten Start)
  4. Setzt das Hosten des Tunnels fort

Schnellstart

Terminal 1 – MCP-Server starten:

cd mcp-server
npm install
npm run dev

Terminal 2 – DevTunnel starten:

npm run tunnel
# Oder unter Windows:
npm run tunnel:win

Stellen Sie beim ersten Durchlauf den Agenten bereit, sobald der Tunnel eingerichtet ist (siehe Regel „AGENT PROVISIONING“). Bei nachfolgenden Durchläufen ist die Tunnel-URL stabil – eine erneute Bereitstellung ist nicht erforderlich, es sei denn, das Agenten-Manifest ändert sich.

Entwicklungs-Workflow

  1. Starten Sie den MCP-Server (Entwicklungsmodus mit Hot-Reload):

    • TypeScript: cd mcp-server && npm install && npm run dev
    • Python: cd mcp-server && pip install -r requirements.txt && python server.py
    • C#: cd mcp-server && dotnet run
  2. Starten Sie den Devtunnel (erstellt beim ersten Lauf einen benannten Tunnel, der bei nachfolgenden Läufen wiederverwendet wird):

    npm run tunnel
    
  3. Bereitstellung + Test – siehe Regel „AGENT PROVISIONING“, um zu erfahren, wann dies erforderlich ist; erhöhen Sie die Versionsnummer in „manifest.json“, falls Copilot die Änderungen nicht berücksichtigt

Bewährte Vorgehensweisen

Ausführliche Anleitungen finden Sie in „references/best-practices.md“.

Wichtige Punkte:

  1. Rendering-Tools: Daten als Eingabe akzeptieren, nicht intern abrufen
  2. Anweisungen: Weisen Sie den Agenten an, ZUERST die Funktionen zu nutzen, und leiten Sie die Daten anschließend an die MCP-Tools weiter
  3. Themes: Verwenden Sie FluentProvider + Fluent-Token für die Unterstützung von Dunkel- und Hellmodus
  4. Debug-Modus: Fügen Sie Fallback-Daten für lokale Widget-Tests ein
  5. Unvollständige Daten: Behandeln Sie fehlende Felder mit „Unknown“-Standardwerten
  6. Aktionsschaltflächen: E-Mail-/Chat-Schaltflächen ausblenden, wenn Daten „Unbekannt“ sind
  7. Versionserhöhung: Die Manifest-Version erhöhen, wenn Änderungen nicht in Copilot übernommen werden
Auf GitHub ansehen
---
name: ui-widget-developer
description: Build MCP servers for Copilot Chat with interactive widgets using React and Fluent UI, supporting both OpenAI Apps SDK and MCP Apps Extension standards.
---

# Copilot MCP Server Development

## Telemetry Tagging

Before running any `atk` CLI commands, set the session environment variable so all CLI invocations are tagged as skill-initiated:
```bash
export ATK_CLI_SKILL=true
```
Run this once at the start of the session. All subsequent `atk` commands in the same terminal will inherit it.

## 🔀 PATH SELECTION

**Before proceeding, ask the user which path they want to take using AskUserQuestion.** Both are supported for M365 Copilot widget rendering. Present the tradeoffs and let them choose:

| | **OAI Apps** (this skill) | **MCP Apps Extension** (`modelcontextprotocol/ext-apps`) |
|---|---|---|
| **Standard** | OpenAI-specific | Official MCP standard |
| **Works in** | ChatGPT + M365 Copilot | M365 Copilot, ChatGPT, VSCode, and more |
| **Maturity** | Battle-tested, production-ready | New official standard, growing ecosystem |
| **Design** | OpenAI Apps SDK | MCP Apps protocol (cross-platform) |
| **When to choose** | Existing OAI app investment | Prefer the open standard, want broadest client support |

**Ask:** _"Would you like to build an OAI app (OpenAI Apps SDK — battle-tested, works in ChatGPT and M365 Copilot) or an MCP app (new official standard — works in M365 Copilot, ChatGPT, VSCode, and more)?"_

- **OAI apps** → Continue below. This skill covers everything you need.
- **MCP apps** → Install the `modelcontextprotocol/ext-apps` plugin (see below), then use the appropriate skill from that plugin.

### MCP Apps: Install ext-apps Plugin

If the user chooses MCP Apps, do this automatically (do not stop at explanation-only):

1. Run `/plugin marketplace add modelcontextprotocol/ext-apps`
2. Run `/plugin install mcp-apps@mcp-apps`
3. Confirm the plugin is available, then invoke the correct ext-apps skill based on user intent

If plugin commands are unavailable in the current environment, provide the exact commands below and ask the user to run them once, then continue by invoking the selected ext-apps skill.

Reference commands:

```
To build an MCP App, install the ext-apps plugin from the marketplace:

1. /plugin marketplace add modelcontextprotocol/ext-apps
2. /plugin install mcp-apps@mcp-apps

Then use one of these skills from that plugin:
- create-mcp-app      — Scaffold a new MCP App with interactive UI from scratch
- add-app-to-server   — Add interactive UI to an existing MCP server's tools
- migrate-oai-app     — Convert an existing OAI app to use MCP Apps
- convert-web-app     — Turn a web app into a hybrid web + MCP App

After installing, invoke the relevant skill to continue.
```

> **Note:** The ext-apps plugin lives in the external `modelcontextprotocol/ext-apps` marketplace — it is not part of this plugin collection.

**Handoff mapping after install:**
- New MCP app from scratch → `create-mcp-app`
- Add app UI to existing MCP server → `add-app-to-server`
- Migrate existing OAI app → `migrate-oai-app`
- Convert an existing web app → `convert-web-app`

---

## 📛 PROJECT DETECTION 📛

This skill triggers when building MCP servers with OAI app or widget rendering for Microsoft 365 Copilot Chat. The MCP server can be written in any language that supports the MCP protocol (TypeScript, Python, C#, etc.). The agent project and MCP server may live in the same repo, separate folders, or entirely different projects.

## Scenario Routing

| Starting Point | What You Need | Path |
|---------------|---------------|------|
| **Prefer MCP Apps standard** | Cross-platform widget support (M365 Copilot, ChatGPT, VSCode, and more) | Install `modelcontextprotocol/ext-apps`, then use `create-mcp-app` or `add-app-to-server` — see [Path Selection](#-path-selection) above |
| **From scratch** (no agent, no MCP server) | Full OAI app setup | Delegate agent scaffolding to `declarative-agent-developer` first, then return here for MCP server + widgets |
| **Existing M365 agent, new MCP server** | MCP server + widgets + mcpPlugin.json | Start at [Implementation](#implementation) |
| **Existing MCP server, add Copilot widgets** | Widget support added to existing server | Start at [Copilot Widget Protocol](references/copilot-widget-protocol.md#adaptation-checklist-existing-mcp-server) |
| **Language choice** (non-TypeScript) | Protocol requirements | See [Copilot Widget Protocol](references/copilot-widget-protocol.md) for what to implement, [MCP Server Pattern (TypeScript)](references/mcp-server-pattern.md) as a reference |

---

## 🚨 CRITICAL EXECUTION RULES 🚨


**FLUENT UI ENFORCEMENT (REQUIRED):** Widget implementations MUST use React + Fluent UI components. Before writing any widget code, the agent MUST read and follow:
- `references/widget-patterns.md`
- `references/best-practices.md`
**FLUENT UI PACKAGE REQUIREMENT (REQUIRED):** The widget project MUST include Fluent UI dependencies before implementation. At minimum, install and keep these in the widget package dependencies:
- `@fluentui/react-components`
- `react`
- `react-dom`

If any of these packages are missing, install them automatically before continuing with widget code generation.

If the generated widget does not include React entry files (for example `widgets/src/<widget-name>/main.tsx` and a React component file) and Fluent imports from `@fluentui/react-components`, the task is incomplete and MUST be corrected before returning results.

**NO RAW HTML-ONLY WIDGETS (DEFAULT):** Do not implement app content directly with static HTML templates and inline JS as the final widget solution. A minimal shell HTML file is allowed only as a loader for built React assets. Raw/self-contained HTML-only widgets are allowed only when the user explicitly requests a non-React prototype.

**BACKGROUND PROCESSES:** MCP server and devtunnel MUST be spawned as independent OS processes — NOT run inside the agent's shell session. `isBackground: true`, `mode: "async"`, and `Start-Job` all run inside the agent's shell session and will be killed between messages. The only reliable approach is to spawn a detached OS process.

**Windows — use `Start-Process -WindowStyle Hidden`:**
```powershell
# Start devtunnel
$t = Start-Process -FilePath "devtunnel" `
    -ArgumentList "host","<tunnel-name>","-a" `
    -WindowStyle Hidden -PassThru `
    -RedirectStandardOutput "tunnel.log" -RedirectStandardError "tunnel-err.log"

# Start MCP server — use cmd.exe /c to set the working directory and inherit PATH
$s = Start-Process -FilePath "cmd.exe" `
    -ArgumentList "/c","cd /d <abs-path-to-mcp-server> && <start-command>" `
    -WindowStyle Hidden -PassThru `
    -RedirectStandardOutput "server.log" -RedirectStandardError "server-err.log"

# Save PIDs so they can be stopped later
"$($t.Id),$($s.Id)" | Out-File pids.txt
Write-Host "Started tunnel PID $($t.Id), server PID $($s.Id)"
```
To stop: `Stop-Process -Id (Get-Content pids.txt).Split(',')` or `Stop-Process -Id <pid>`.

**Linux/Mac — use `nohup` with `&`:**
```bash
nohup devtunnel host <tunnel-name> > tunnel.log 2>tunnel-err.log &
echo "tunnel:$!" >> pids.txt
nohup <start-command> > server.log 2>server-err.log &
echo "server:$!" >> pids.txt
```
To stop: `kill $(grep -oP '\d+' pids.txt)`.

After starting, tail the logs to confirm both processes are up before proceeding:
```powershell
# Windows
Start-Sleep 3; Get-Content tunnel.log, server.log
```
```bash
# Linux/Mac
sleep 3 && tail tunnel.log server.log
```

**FULL AUTOMATION:** Never tell the user to run commands manually. Install tools, authenticate, start services — do everything automatically. Only ask the user for interactive input that truly requires them (like device code confirmation during `devtunnel user login -g -d`). If a tool isn't installed, install it. If a service needs starting, start it. The user expects full automation.

**PATH SELECTION (REQUIRED — STOP BEFORE ANY CODE):** You MUST use `AskUserQuestion` to ask the user whether they want OAI Apps or MCP Apps Extension before writing any code, running any commands, or making any architectural decisions.

**There is no exception to this rule.** The most common failure mode is reasoning "the user's request makes it obvious, so asking is redundant." This reasoning is always wrong — invoke `AskUserQuestion` regardless. A user saying "build an MCP server with widgets" is NOT an answer to this question. A user invoking this skill by name is NOT an answer. Only an explicit answer to the question counts. See [PATH SELECTION](#-path-selection) above for the exact question to ask.

**AGENT PROVISIONING:** Re-provisioning is only required when the **agent manifest** changes (e.g., mcpPlugin.json tool definitions, MCP server URL, declarativeAgent.json, instruction.txt). MCP server code changes (tool implementations, React widget code, server logic) do **NOT** require re-provisioning the agent — running or deploying the server picks up changes automatically.

When provisioning is needed:
1. **Bump the version** in `manifest.json` (increment the patch version, e.g., `1.0.0` → `1.0.1`)
2. **Deploy the agent:**
   ```bash
   npx -y --package @microsoft/m365agentstoolkit-cli atk provision --env local
   ```

**WIDGET TESTING LINKS:** Every time you return to the user with a result while the MCP server is running, you MUST include links to ALL widgets so they can test them locally. Format:
```
🧪 Test widgets locally:
- http://localhost:3001/widgets/widget-name.html
- http://localhost:3001/widgets/another-widget.html
```
List every `.html` file in the `mcp-server/widgets/` directory (or equivalent widget folder). This helps users verify widget rendering before testing in Copilot.

**AUTO-DEPLOY ON COMPLETION (REQUIRED — DO NOT SKIP):** When coding is complete, proceed automatically without waiting for the user:
1. Start MCP server + devtunnel in the background (per BACKGROUND PROCESSES above)
2. Run E2E verification with MCP Inspector (per MCP TOOL CONFIGURATION RULE below) — fix any failures before continuing
3. Provision the agent if needed (per AGENT PROVISIONING above)
4. Print a project summary in this format:
```
## ✅ <Project Name> — Ready

### Widgets
- [widget-name.html](http://localhost:<PORT>/widgets/widget-name.html)
- [widget-name2.html](http://localhost:<PORT>/widgets/widget-name2.html)

### Endpoints
- MCP server: http://localhost:<PORT>/mcp
- MCP via tunnel: https://<tunnel-url>/mcp

### Test in Copilot
Local:      https://m365.cloud.microsoft/chat/?titleId={M365_TITLE_ID from env/.env.local}
Other envs: {SHARE_LINK from env/.env.{environment}}
```

**AGENT PROJECT DELEGATION:** This skill builds MCP servers and widgets, NOT declarative agent projects. If the user's request involves creating or configuring the declarative agent itself (scaffolding, `m365agents.yml`, `m365agents.local.yml`, `declarativeAgent.json`, manifest lifecycle), delegate to the `declarative-agent-developer` skill.

**MCP RESOURCE REGISTRATION:** Every widget MUST have a matching MCP resource. Without resources, Copilot cannot fetch widget shells through the MCP protocol and widgets will not render.

For each new widget, complete this checklist:
1. ☐ Create a widget shell HTML file in `widgets/` and a React widget entry under `widgets/src/<widget-name>/` (see widget-patterns.md)
2. ☐ Define a `ui://widget/<name>.html` URI constant
3. ☐ Add a `Resource` entry to the `resources` array with:
   - `uri`: the `ui://widget/<name>.html` URI
   - `mimeType`: `"text/html+skybridge"`
   - `_meta`: CSP config with `openai/widgetDomain` and `openai/widgetCSP` (from environment)
4. ☐ Add a handler for `resources/read` that returns the widget shell HTML for this URI
5. ☐ Add the tool with `_meta.openai/outputTemplate` pointing to the same `ui://widget/<name>.html` URI
6. ☐ Verify the server capabilities include `resources: {}` in the initialize response

**Widget shell + asset considerations:**
- **Preferred (React + Fluent UI)**: Resource HTML should be a minimal shell that links to built JS/CSS assets served from the MCP server's `/assets/` route.
- **Exception only**: Self-contained HTML via `resources/read` is for explicit user-requested prototypes only. Default and production path is React + Fluent UI.

Example shell for React build output:
  ```html
  <!doctype html><html><head>
    <script type="module" src="${serverUrl}/assets/my-widget.js"></script>
    <link rel="stylesheet" href="${serverUrl}/assets/my-widget.css">
  </head><body>
    <div id="widget-root"></div>
  </body></html>
  ```
  Use the `WIDGET_BASE_URL` or `MCP_SERVER_URL` environment variable for the asset URL base (see mcp-server-pattern.md "Configurable Widget Base URL" section).

See [mcp-server-pattern.md](references/mcp-server-pattern.md) for the complete resource and asset serving patterns.

---

## ⚠️ MCP TOOL CONFIGURATION RULE ⚠️

**NEVER manually write tool definitions in `mcpPlugin.json`.** Always use MCP Inspector to get the complete tool definitions from the running MCP server.

**TOOL NAMING CONVENTION:** Tool names MUST match the pattern `^[A-Za-z0-9_]+$` (letters, numbers, and underscores only). **NEVER use hyphens (-) in tool names.** Use underscores instead (e.g., `render_profile` not `render-profile`).

**MANDATORY WORKFLOW:**
1. **Start the MCP server** (in background)
2. **Use MCP Inspector** to get the latest tool definitions:
   ```bash
   npx @modelcontextprotocol/[email protected] --cli https://my-mcp-server.example.com --transport http --method tools/list
   ```
3. **Copy the COMPLETE tool definition** from the inspector (including `name`, `description`, `inputSchema`, `_meta`, `annotations`, `title`)
4. **Paste into `mcpPlugin.json`** under `runtimes[].spec.mcp_tool_description.tools` (inside the `RemoteMCPServer` runtime's `spec` object)
5. **Run E2E verification** through the devtunnel — call each tool and confirm the response contains `structuredContent` and `_meta.openai/widgetAccessible: true`:
   ```bash
   npx @modelcontextprotocol/[email protected] --cli https://<tunnel-url>/mcp --transport http --method tools/call --tool-name <tool_name>
   ```
   Also verify `GET https://<tunnel-url>/health` returns `{"status":"ok"}`. Fix any failures before provisioning.

The MCP Inspector shows the exact tool schema from your server. Copy it completely — do not manually write or modify these definitions. This ensures `mcpPlugin.json` stays in sync with the MCP server.

---

Build MCP servers that integrate with Microsoft 365 Copilot Chat and render rich interactive widgets.

## Architecture

```
M365 Copilot ──▶ mcpPlugin.json ──▶ MCP Server ──▶ structuredContent ──▶ React + Fluent UI Widget
     │              (RemoteMCPServer)    (Streamable HTTP)                  (window.openai.toolOutput)
     │
     └── Capabilities (People, etc.) provide data to pass to MCP tools
```

## Project Structure

Example project structure, not a hard requirement but a common pattern for organizing MCP server + widget development:

```
project/
├── appPackage/
│   ├── manifest.json           # Teams manifest (bump version on deploy)
│   ├── declarativeAgent.json   # Agent config + capabilities
│   ├── mcpPlugin.json          # Tool definitions with _meta
│   └── instruction.txt         # Agent behavior instructions
├── mcp-server/
│   ├── src/index.ts            # Server with Streamable HTTP
│   ├── widgets/                # Widget shells + React source
│   │   ├── my-widget.html      # Minimal shell returned by resources/read
│   │   └── src/my-widget/      # React + Fluent UI source
│   ├── assets/                 # Built widget bundles served at /assets
│   └── package.json
├── scripts/
│   ├── setup-devtunnel.sh      # Linux/Mac devtunnel setup
│   └── setup-devtunnel.ps1     # Windows devtunnel setup
└── env/.env.local              # MCP_SERVER_URL, MCP_SERVER_DOMAIN
```

**Language note**: This shows a TypeScript project layout. For Python, replace `mcp-server/src/index.ts` with your Python entry point (e.g., `server.py`). For C#, use a standard .NET project structure. The `appPackage/`, `widgets/`, `scripts/`, and `env/` directories are language-agnostic.

## Copilot Widget Protocol

Your MCP server must implement these protocol requirements to render widgets in Copilot Chat. This applies regardless of language:

1. **Streamable HTTP transport** — `/mcp` endpoint handling POST, GET, DELETE with session management
2. **CORS headers** — Origin-checking on `/mcp` allowing `m365.cloud.microsoft` and `*.m365.cloud.microsoft`, with required MCP headers
3. **Server capabilities** — `initialize` response must declare `resources: {}` and `tools: {}`
4. **MCP resources** — Register widgets with `ui://widget/<name>.html` URIs, `text/html+skybridge` mime type, and CSP `_meta`
5. **Tool response format** — Return `content` (text) + `structuredContent` (widget data) + `_meta` with `openai/outputTemplate`
6. **Widget serving** — HTTP route at `/widgets/*.html` for shell files and `/assets/*` for built bundles, both with origin-checking CORS

For full protocol details, JSON shapes, and an adaptation checklist for existing MCP servers, see [references/copilot-widget-protocol.md](references/copilot-widget-protocol.md).

## Implementation

### MCP Server Pattern (TypeScript Reference)

See [references/mcp-server-pattern.md](references/mcp-server-pattern.md) for complete implementation.

> For other languages, implement the requirements described in [Copilot Widget Protocol](references/copilot-widget-protocol.md) using your language's MCP SDK. See the [Language SDK References](references/copilot-widget-protocol.md#language-sdk-references) table for SDK packages.

Core requirements:
- Expose Streamable HTTP transport on `/mcp`
- Return `structuredContent` + `_meta` with `openai/outputTemplate`
- Serve widgets via HTTP endpoint
- Handle CORS for cross-origin requests
- Handle partial data gracefully (fill in "Unknown" for missing fields)

Tool response format:
```typescript
return {
  content: [{ type: "text", text: "Summary" }],
  structuredContent: { /* widget data */ },
  _meta: { "openai/outputTemplate": "ui://widget/name.html", "openai/widgetAccessible": true }
};
```

### Handling Partial Data

Always normalize input data to handle missing fields:

```typescript
server.setRequestHandler(CallToolRequestSchema, async (request: CallToolRequest) => {
  const args = request.params.arguments as { title?: string; items?: Partial<Item>[] };

  // Normalize data - fill in "Unknown" for missing fields
  const title = args.title || "Default Title";
  const items = (args.items || []).map(item => ({
    name: item.name || "Unknown",
    value: item.value || "Unknown",
  }));

  // Build structuredContent for widget
  const structuredContent = { title, items };
  // ...
});
```

### Widget Pattern

See [references/widget-patterns.md](references/widget-patterns.md) for complete examples.

Core requirements:
- Use React + Fluent UI components (`@fluentui/react-components`)
- Ensure widget package dependencies include `@fluentui/react-components`, `react`, and `react-dom`
- Theme with `FluentProvider` (`webLightTheme`/`webDarkTheme`) and Fluent `tokens`
- Access data through shared hooks (e.g., `useOpenAiGlobal("toolOutput")`)
- Debug fallback: embedded mock data when `window.openai` unavailable
- Handle "Unknown" values gracefully (e.g., hide action buttons)

### Plugin Schema

See [references/plugin-schema.md](references/plugin-schema.md) for mcpPlugin.json format.

Core requirements:
- Schema `v2.4` with `RemoteMCPServer` runtime
- `run_for_functions` array matching tool names
- `_meta` in tool definitions for widget binding
- `inputSchema` - make properties optional for flexibility, describe defaults in descriptions

## DevTunnels Setup

> **Local testing only.** DevTunnels are for development and testing on your machine. Before sharing the agent more broadly, deploy both the MCP server and widget assets to a hosted environment (e.g., Azure App Service, Azure Static Web Apps, or another hosting provider) and update the agent manifest URLs accordingly.

DevTunnels expose your localhost MCP server to M365 Copilot using **named tunnels** for stable URLs. See [references/devtunnels.md](references/devtunnels.md) for setup scripts, command reference, and troubleshooting.

The setup script (`npm run tunnel` / `npm run tunnel:win`):
1. Creates a named tunnel on first run (or reuses the existing one)
2. Starts hosting the tunnel on the configured port
3. Updates `env/.env.local` with `MCP_SERVER_URL` and `MCP_SERVER_DOMAIN` (first run only)
4. Continues hosting the tunnel

### Quick Start

**Terminal 1 - Start MCP Server:**
```bash
cd mcp-server
npm install
npm run dev
```

**Terminal 2 - Start DevTunnel:**
```bash
npm run tunnel
# Or on Windows:
npm run tunnel:win
```

On first run, provision the agent once the tunnel is up (see AGENT PROVISIONING rule). On subsequent runs the tunnel URL is stable — no re-provisioning needed unless the agent manifest changes.

## Development Workflow

1. **Start the MCP server** (dev mode with hot reload):
   - TypeScript: `cd mcp-server && npm install && npm run dev`
   - Python: `cd mcp-server && pip install -r requirements.txt && python server.py`
   - C#: `cd mcp-server && dotnet run`

2. **Start the devtunnel** (creates named tunnel on first run, reuses on subsequent runs):
   ```bash
   npm run tunnel
   ```

3. **Provision + test** — see AGENT PROVISIONING rule for when this is needed; bump `version` in manifest.json if Copilot doesn't reflect changes

## Best Practices

See [references/best-practices.md](references/best-practices.md) for detailed guidance.

Key points:
1. **Rendering tools**: Accept data as input, don't fetch internally
2. **Instructions**: Tell agent to use capabilities FIRST, then pass data to MCP tools
3. **Themes**: Use `FluentProvider` + Fluent `tokens` for dark/light support
4. **Debug mode**: Include fallback data for local widget testing
5. **Partial data**: Handle missing fields with "Unknown" defaults
6. **Action buttons**: Hide email/chat buttons when data is "Unknown"
7. **Version bumping**: Bump manifest version when changes aren't reflected in Copilot

Alle Dateien

0 Dateien

ui-widget-developer installieren

Laden Sie die Skill-Dateien herunter und entpacken Sie sie in Ihr Verzeichnis „.claude/skills/“.

ZIP herunterladen

Klonen Sie das Repository und kopieren Sie die Skill-Dateien in Ihr Projekt.

git clone https://github.com/microsoft/skills/tree/main/.github/plugins/microsoft-365-agents-toolkit/skills/ui-widget-developer # Copy SKILL.md to your .claude/skills/ directory

Kopieren Kopieren
Schnelle Einrichtung: Kopiere den Skill-Ordner nach .claude/skills/ Claude erkennt den Skill automatisch und nutzt ihn.
Repository microsoft/skills

Ähnliche Skills

github-code-search
Zeit aktualisiert 29. Juni 2026
drizzle-orm
Zeit aktualisiert 29. Juni 2026
clickhouse-io
Zeit aktualisiert 29. Juni 2026
prisma-client-api
Zeit aktualisiert 29. Juni 2026
OR