diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 7a08605..35bda7e 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -19,8 +19,8 @@ }, { "name": "microsoft-365-agents-toolkit", - "description": "Toolkit for building and evaluating Microsoft 365 Copilot declarative agents \u2014 scaffolding, JSON manifest development, capability configuration, and eval workflows.", - "version": "1.3.1", + "description": "Toolkit for building and evaluating Microsoft 365 Copilot declarative agents \u2014 scaffolding, JSON manifest development, capability configuration, serverless MCP Apps hosting on Azure Functions, and eval workflows.", + "version": "1.4.0", "source": "./plugins/microsoft-365-agents-toolkit" }, { diff --git a/AGENTS.md b/AGENTS.md index a190099..0caaa1e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -80,6 +80,7 @@ plugins// - `declarative-agent-developer` skill — Scaffolding, JSON manifest authoring, capability configuration, deployment - `teams-app-developer` skill — Build, test, and deploy code-based Teams apps: bots, CEA, tabs, message extensions, Agents Playground, Azure provision/deploy, and Slack-to-Teams migration - `ui-widget-developer` skill — Build MCP servers with OpenAI Apps SDK widget rendering for Copilot Chat + - `mcp-apps-azure-functions` skill — Scaffold, run, and deploy a serverless MCP Apps server on Azure Functions (C# / .NET) with azd + Flex Consumption - `m365-agent-evaluator` skill — Generate, run, and analyze evaluation suites for M365 Copilot declarative agents - **workiq-productivity** — Read-only WorkIQ productivity insights. Bundles: diff --git a/PLUGINS.md b/PLUGINS.md index 22fe4ba..35d1be2 100644 --- a/PLUGINS.md +++ b/PLUGINS.md @@ -79,7 +79,7 @@ copilot plugin uninstall workiq-productivity |---|--------|--------|-------------| | 1 | [**workiq**](#workiq) | 1 | Full WorkIQ tool surface — agentic queries plus direct M365 reads and writes | | 2 | [**workiq-preview**](#workiq-preview) | 1 | Preview build with the full entity tool surface (read + write) | -| 3 | [**microsoft-365-agents-toolkit**](#microsoft-365-agents-toolkit) | 4 | Toolkit for building M365 Copilot declarative agents | +| 3 | [**microsoft-365-agents-toolkit**](#microsoft-365-agents-toolkit) | 5 | Toolkit for building M365 Copilot declarative agents | | 4 | [**workiq-productivity**](#workiq-productivity) | 9 | Read-only productivity insights across M365 | --- @@ -162,6 +162,7 @@ copilot plugin uninstall workiq-productivity | [**install-atk**](./plugins/microsoft-365-agents-toolkit/skills/install-atk/SKILL.md) | Install or update the M365 Agents Toolkit CLI and VS Code extension | | [**declarative-agent-developer**](./plugins/microsoft-365-agents-toolkit/skills/declarative-agent-developer/SKILL.md) | Scaffolding, JSON manifest authoring, capability configuration, deployment | | [**ui-widget-developer**](./plugins/microsoft-365-agents-toolkit/skills/ui-widget-developer/SKILL.md) | Build MCP servers with OpenAI Apps SDK widget rendering for Copilot Chat | +| [**mcp-apps-azure-functions**](./plugins/microsoft-365-agents-toolkit/skills/mcp-apps-azure-functions/SKILL.md) | Scaffold, run, and deploy a serverless MCP Apps server on Azure Functions (C# / .NET) with azd + Flex Consumption | | [**m365-agent-evaluator**](./plugins/microsoft-365-agents-toolkit/skills/m365-agent-evaluator/SKILL.md) | Generate, run, and analyze evaluation suites for M365 Copilot declarative agents | ### Example prompts @@ -170,6 +171,8 @@ copilot plugin uninstall workiq-productivity "Scaffold a new declarative agent for HR FAQ" "Add web search to my agent" "Deploy my agent with ATK" +"Scaffold a serverless MCP Apps server on Azure Functions in C#" +"Deploy my MCP Apps server to Azure Flex Consumption with azd" "Create eval prompts for my agent" "Run my evals and explain the failures" "Improve my agent instructions based on the latest eval results" diff --git a/marketplace.json b/marketplace.json index 91286fa..00192ce 100644 --- a/marketplace.json +++ b/marketplace.json @@ -20,8 +20,8 @@ { "name": "microsoft-365-agents-toolkit", "source": "./plugins/microsoft-365-agents-toolkit", - "description": "Toolkit for building and evaluating Microsoft 365 Copilot declarative agents \u2014 scaffolding, JSON manifest development, capability configuration, and eval workflows.", - "version": "1.3.1" + "description": "Toolkit for building and evaluating Microsoft 365 Copilot declarative agents \u2014 scaffolding, JSON manifest development, capability configuration, serverless MCP Apps hosting on Azure Functions, and eval workflows.", + "version": "1.4.0" }, { "name": "workiq-productivity", diff --git a/plugins/microsoft-365-agents-toolkit/.claude-plugin/plugin.json b/plugins/microsoft-365-agents-toolkit/.claude-plugin/plugin.json index e102d1a..e9eb6b5 100644 --- a/plugins/microsoft-365-agents-toolkit/.claude-plugin/plugin.json +++ b/plugins/microsoft-365-agents-toolkit/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "microsoft-365-agents-toolkit", - "version": "1.3.1", - "description": "Toolkit for building and evaluating Microsoft 365 Copilot declarative agents \u2014 scaffolding, JSON manifest development, capability configuration, and eval workflows.", + "version": "1.4.0", + "description": "Toolkit for building and evaluating Microsoft 365 Copilot declarative agents \u2014 scaffolding, JSON manifest development, capability configuration, serverless MCP Apps hosting on Azure Functions, and eval workflows.", "skills": "./skills/" } \ No newline at end of file diff --git a/plugins/microsoft-365-agents-toolkit/.codex-plugin/plugin.json b/plugins/microsoft-365-agents-toolkit/.codex-plugin/plugin.json index e102d1a..e9eb6b5 100644 --- a/plugins/microsoft-365-agents-toolkit/.codex-plugin/plugin.json +++ b/plugins/microsoft-365-agents-toolkit/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "microsoft-365-agents-toolkit", - "version": "1.3.1", - "description": "Toolkit for building and evaluating Microsoft 365 Copilot declarative agents \u2014 scaffolding, JSON manifest development, capability configuration, and eval workflows.", + "version": "1.4.0", + "description": "Toolkit for building and evaluating Microsoft 365 Copilot declarative agents \u2014 scaffolding, JSON manifest development, capability configuration, serverless MCP Apps hosting on Azure Functions, and eval workflows.", "skills": "./skills/" } \ No newline at end of file diff --git a/plugins/microsoft-365-agents-toolkit/.github/plugin/plugin.json b/plugins/microsoft-365-agents-toolkit/.github/plugin/plugin.json index e102d1a..e9eb6b5 100644 --- a/plugins/microsoft-365-agents-toolkit/.github/plugin/plugin.json +++ b/plugins/microsoft-365-agents-toolkit/.github/plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "microsoft-365-agents-toolkit", - "version": "1.3.1", - "description": "Toolkit for building and evaluating Microsoft 365 Copilot declarative agents \u2014 scaffolding, JSON manifest development, capability configuration, and eval workflows.", + "version": "1.4.0", + "description": "Toolkit for building and evaluating Microsoft 365 Copilot declarative agents \u2014 scaffolding, JSON manifest development, capability configuration, serverless MCP Apps hosting on Azure Functions, and eval workflows.", "skills": "./skills/" } \ No newline at end of file diff --git a/plugins/microsoft-365-agents-toolkit/README.md b/plugins/microsoft-365-agents-toolkit/README.md index 3292999..238da69 100644 --- a/plugins/microsoft-365-agents-toolkit/README.md +++ b/plugins/microsoft-365-agents-toolkit/README.md @@ -48,6 +48,7 @@ npx -y --package @microsoft/m365-copilot-eval@latest runevals --prompts-file eva | [**install-atk**](./skills/install-atk/SKILL.md) | Install or update the ATK CLI and VS Code extension | | [**declarative-agent-developer**](./skills/declarative-agent-developer/SKILL.md) | Scaffolding, JSON manifest authoring, capability configuration, security patterns, deployment via ATK CLI | | [**ui-widget-developer**](./skills/ui-widget-developer/SKILL.md) | Build MCP servers with OpenAI Apps SDK widget rendering for Copilot Chat | +| [**mcp-apps-azure-functions**](./skills/mcp-apps-azure-functions/SKILL.md) | Scaffold, run, and deploy a serverless MCP Apps server on Azure Functions (C# / .NET) with azd + Flex Consumption | | [**m365-agent-evaluator**](./skills/m365-agent-evaluator/SKILL.md) | Generate, run, and analyze evaluation suites for M365 Copilot declarative agents | ## License diff --git a/plugins/microsoft-365-agents-toolkit/skills/mcp-apps-azure-functions/SKILL.md b/plugins/microsoft-365-agents-toolkit/skills/mcp-apps-azure-functions/SKILL.md new file mode 100644 index 0000000..5e51a9d --- /dev/null +++ b/plugins/microsoft-365-agents-toolkit/skills/mcp-apps-azure-functions/SKILL.md @@ -0,0 +1,184 @@ +--- +name: mcp-apps-azure-functions +description: > + Scaffold, run, and deploy a serverless MCP Apps server on Azure Functions (C# / .NET isolated) + with the Azure Functions MCP extension and the Azure Developer CLI (azd). MCP Apps are MCP servers + whose tools return rich interactive UI widgets. Use when the user wants a serverless, pay-per-use + host for an MCP Apps server on the Flex Consumption plan. Covers: azd template init + (remote-mcp-functions-dotnet), building the UI app, McpToolTrigger + McpResourceTrigger + + McpMetadata patterns, local run with Azurite + Functions Core Tools, verifying with GitHub Copilot, + and deploy via azd or func with built-in Microsoft Entra auth. DO NOT USE FOR: building the MCP + server + widget without serverless hosting (use ui-widget-developer) or scaffolding the declarative + agent (use declarative-agent-developer). Triggers: "MCP Apps on Azure Functions", "serverless MCP + server", "MCP server Azure Functions C#", "azd MCP Apps", "Flex Consumption MCP", "McpToolTrigger", + "remote-mcp-functions-dotnet" +--- + +# MCP Apps on Azure Functions (Serverless, C# / .NET) + +Build and deploy a **serverless MCP Apps server** on **Azure Functions** using the **Azure Functions +MCP extension** (C# / .NET isolated) and the **Azure Developer CLI (`azd`)**. The result is an MCP +server whose tools return **rich interactive UI widgets** — hosted on the **Flex Consumption** plan +(pay-for-what-you-use) and secured by default with **Microsoft Entra** built-in authorization. + +Based on the official quickstart: [Build an MCP Apps server using Azure Functions](https://learn.microsoft.com/azure/azure-functions/scenario-mcp-apps?pivots=programming-language-csharp) +and the template repo [Azure-Samples/remote-mcp-functions-dotnet](https://github.com/Azure-Samples/remote-mcp-functions-dotnet). + +## When to use + +- The user wants a **serverless** hosting option for an MCP Apps server (no always-on server/VM). +- The user wants **C# / .NET** on Azure Functions with the MCP extension. +- The user wants an `azd` + Bicep deployment to the **Flex Consumption** plan following best practices. +- The user wants an MCP server whose tools render **interactive UI** (MCP Apps standard) in Copilot. + +## When NOT to use + +| Situation | Use instead | +|-----------|-------------| +| Build the MCP server + widget code itself (any host), design widgets | `ui-widget-developer` | +| Scaffold or configure the **declarative agent** (manifest, capabilities, deploy the agent) | `declarative-agent-developer` | +| Generate/run **evals** for an agent | `m365-agent-evaluator` | + +> This skill focuses on the **serverless hosting path** on Azure Functions. It scaffolds a working +> MCP Apps server from the official template and takes it to production on Azure. + +## Architecture + +``` +MCP host (Copilot / VS Code) ──▶ Remote MCP server on Azure Functions ──▶ MCP tool (McpToolTrigger) + (Flex Consumption, Entra auth) │ returns JSON data + ▼ + ui.resourceUri ──▶ MCP resource (McpResourceTrigger) + serves bundled HTML widget + at ui://weather/index.html +``` + +An MCP Apps tool requires **two components**: +1. A **tool with UI metadata** that declares a `ui.resourceUri` (via `[McpMetadata]`). +2. A **resource** that serves the bundled HTML/JS at the matching `ui://` URI (via `[McpResourceTrigger]`). + +## Prerequisites + +Install/verify these before scaffolding (install automatically when missing — see Automation rules): + +| Tool | Notes | +|------|-------| +| [.NET 10 SDK](https://dotnet.microsoft.com/download/dotnet/10.0) | Required by the .NET isolated template | +| [Azure Functions Core Tools](https://learn.microsoft.com/azure/azure-functions/functions-run-local) `>= 4.5.0` | Local run + `func` publish | +| [Azure Developer CLI (`azd`)](https://aka.ms/azd) `1.23.x`+ | Provision + deploy | +| [Node.js 22](https://nodejs.org/) | Build the MCP Apps UI (`npm run build`) | +| [Azurite](https://learn.microsoft.com/azure/storage/common/storage-use-azurite) (or Docker) | Local storage emulator | +| [Azure CLI](https://learn.microsoft.com/cli/azure/install-azure-cli) + Azure subscription | Auth + deployment target | + +Optional but recommended: VS Code with the **Azure Functions** and **Azure Developer CLI** extensions. + +## Workflow + +### 1. Scaffold from the official azd template + +```bash +azd init --template remote-mcp-functions-dotnet -e mcpweather-dotnet +cd remote-mcp-functions-dotnet +``` + +`-e` sets the `azd` environment name (used for the deployment context and resource names). This pulls the +full project (functions, `infra/` Bicep, `.vscode/mcp.json`, `azure.yaml`). The MCP Apps demo lives under +`src/McpWeatherApp` (with the UI app in `src/McpWeatherApp/app`). + +### 2. Build the MCP Apps UI + +The widget UI must be built before running — the resource serves `app/dist/index.html`. + +```bash +cd src/McpWeatherApp/app +npm install +npm run build +cd ../ +``` + +### 3. Start the storage emulator (Azurite) + +Start Azurite before running the Functions host. VS Code: press F1 → `Azurite: Start`. CLI: run `azurite` +in a background process, or start the Azurite Docker container. See [references/deployment.md](references/deployment.md#local-storage-azurite). + +### 4. Run the MCP server locally + +- **VS Code:** press F5 (Run and Debug). When prompted, select **`src/McpWeatherApp`**. +- **CLI:** from `src/McpWeatherApp`, run `func start`. + +The Functions host prints the running functions (`GetWeather`, `GetWeatherWidget`) and the local MCP endpoint. + +> Spawn the Functions host and Azurite as **independent background OS processes** (see Automation rules) so +> they survive between agent turns. + +### 5. Verify with GitHub Copilot + +The template ships a `.vscode/mcp.json` with a `local-mcp-function` server pointing at your local endpoint. + +1. With the host running, open `.vscode/mcp.json` and click **Start** above `local-mcp-function`. +2. In Copilot Chat, select **Agent** mode → **Configure tools** → enable `MCP Server: local-mcp-function`. +3. Prompt: `What's the weather in Seattle?` → approve the tool run. Because the tool declares UI metadata, + the host fetches the UI resource and renders the **interactive weather widget** in a sandboxed iframe. + +### 6. Review / adapt the code + +The MCP App is defined in `src/McpWeatherApp` using `[McpToolTrigger]`, `[McpMetadata]`, and +`[McpResourceTrigger]`. To rename the tool or add your own tool + widget, follow the C# patterns in +**[references/csharp-code-patterns.md](references/csharp-code-patterns.md)**. + +### 7. Deploy to Azure (serverless) + +`azd` provisions a Flex Consumption function app via the included Bicep and deploys the app: + +```bash +azd env set DEPLOY_SERVICE weather +azd provision # pick subscription + region; vnetEnabled = false to simplify +azd deploy --service weather +``` + +For the full deployment flow, the Core Tools alternative, remote connection, cost notes, and +troubleshooting, see **[references/deployment.md](references/deployment.md)**. + +### 8. Connect to the remote server & clean up + +- Get the app name: `azd env get-value AZURE_FUNCTION_NAME`. +- In `.vscode/mcp.json`, click **Start** above `remote-mcp-function`; enter the app name; complete the + Microsoft Entra sign-in (built-in MCP authorization is on by default). Re-run the same Copilot prompt. +- When finished, remove all resources: `azd down`. + +## Automation rules + +- **Full automation:** install missing tools, authenticate, build the UI, start Azurite + the Functions + host, and deploy — do not ask the user to run commands manually. Only stop for interactive input that + genuinely requires the user (Azure sign-in / device code, subscription & region selection). +- **Background processes:** the Functions host (`func start` / F5) and Azurite must run as **detached OS + processes**, not inside the agent shell session (which is killed between turns). On Windows use + `Start-Process -WindowStyle Hidden`; on Linux/Mac use `nohup ... &`. Save PIDs to stop them later. +- **Build before run:** always run `npm run build` in the UI `app/` folder before starting the host, or + the widget resource returns the fallback HTML. +- **Tool naming:** MCP tool names must match `^[A-Za-z0-9_]+$` (no hyphens). +- **Region:** `azd provision` only lists regions that currently support the **Flex Consumption** plan. + +## Authentication & SSO + +The `remote-mcp-functions-dotnet` template secures the deployed server with **built-in Microsoft Entra +authorization** (App Service Authentication / "Easy Auth"), provisioned automatically by its Bicep — no +custom token guard is required. Because SSO is built in, you typically do **not** need `setup-sso-ui-widget` +here (that skill is for Express/raw-http servers). Use these for the model and for hardening: + +- **Token-flow mental model** (how the Copilot SSO token is issued/validated and how claims reach your + tool): [`setup-sso-ui-widget` → sso-explained.md](../setup-sso-ui-widget/references/sso-explained.md). +- **Harden the Function app's Authentication blade** — audience = **client id** (not the `api://…` URI) + and allow-list the **Copilot host client id**; the same App Service Authentication feature applies to + Function apps: [`setup-sso-ui-widget` → easy-auth.md](../setup-sso-ui-widget/references/easy-auth.md). +- **Adding SSO to a server-full Express / raw-http MCP server** (from `ui-widget-developer` / + `create-mcp-app`): [`setup-sso-ui-widget`](../setup-sso-ui-widget/SKILL.md). +- **Entra SSO vs third-party OAuth** patterns for the plugin manifest: + [`declarative-agent-developer` → authentication.md](../declarative-agent-developer/references/authentication.md). + +## References + +- [references/csharp-code-patterns.md](references/csharp-code-patterns.md) — `McpToolTrigger`, + `McpResourceTrigger`, `McpMetadata`, the `ui.resourceUri` contract, and how to add a new tool + widget. +- [references/deployment.md](references/deployment.md) — `azd` provision/deploy, `func` Core Tools + alternative, Flex Consumption, Entra built-in auth, Azurite, cost, and troubleshooting. diff --git a/plugins/microsoft-365-agents-toolkit/skills/mcp-apps-azure-functions/references/csharp-code-patterns.md b/plugins/microsoft-365-agents-toolkit/skills/mcp-apps-azure-functions/references/csharp-code-patterns.md new file mode 100644 index 0000000..2d9d401 --- /dev/null +++ b/plugins/microsoft-365-agents-toolkit/skills/mcp-apps-azure-functions/references/csharp-code-patterns.md @@ -0,0 +1,117 @@ +# C# / .NET Code Patterns — MCP Apps on Azure Functions + +Reference for the Azure Functions MCP extension (.NET isolated worker). An **MCP Apps tool** is made of +**two functions**: + +1. A **tool** decorated with `[McpToolTrigger]` + `[McpMetadata]` that declares a `ui.resourceUri`. +2. A **resource** decorated with `[McpResourceTrigger]` that serves the bundled HTML at the matching + `ui://` URI. + +The template implements this in `src/McpWeatherApp`. Source of truth: +[remote-mcp-functions-dotnet / McpWeatherApp](https://github.com/Azure-Samples/remote-mcp-functions-dotnet/tree/main/src/McpWeatherApp). + +## Project layout (template) + +``` +remote-mcp-functions-dotnet/ +├── azure.yaml # azd service definitions +├── infra/ # Bicep for Flex Consumption + storage + auth +├── .vscode/ +│ └── mcp.json # local-mcp-function + remote-mcp-function servers +└── src/ + └── McpWeatherApp/ + ├── McpWeatherApp.csproj + ├── host.json + ├── Program.cs + ├── .cs # GetWeather tool + GetWeatherWidget resource + └── app/ # UI (Node) — build to app/dist/index.html + ├── package.json + └── dist/index.html # produced by `npm run build` +``` + +## Tool with UI metadata + +`[McpToolTrigger]` registers the tool; `[McpMetadata]` attaches the UI metadata; `[McpToolProperty]` +declares each input argument. + +```csharp +[Function(nameof(GetWeather))] +public async Task GetWeather( + [McpToolTrigger(nameof(GetWeather), "Returns current weather for a location via Open-Meteo.")] + [McpMetadata(ToolMetadata)] + ToolInvocationContext context, + [McpToolProperty("location", "City name to check weather for (e.g., Seattle, New York, Miami)")] + string location) +{ + try + { + var result = await _weatherService.GetCurrentWeatherAsync(location); + + if (result is WeatherResult weather) + _logger.LogInformation("Weather fetched for {Location}: {TempC}°C", weather.Location, weather.TemperatureC); + else if (result is WeatherError error) + _logger.LogWarning("Weather error for {Location}: {Error}", error.Location, error.Error); + + return result; + } + catch (Exception ex) + { + _logger.LogError(ex, "Failed to get weather for {Location}", location); + return new WeatherError(location ?? "Unknown", $"Unable to fetch weather: {ex.Message}", "api.open-meteo.com"); + } +} +``` + +The metadata constant tells the MCP host which UI resource to fetch after the tool runs: + +```csharp +private const string ToolMetadata = """ + { + "ui": { + "resourceUri": "ui://weather/index.html" + } + } + """; +``` + +## Resource that serves the widget HTML + +`[McpResourceTrigger]` serves the bundled UI at the same `ui://` URI. The MIME type +`text/html;profile=mcp-app` marks it as an MCP App UI resource. + +```csharp +[Function(nameof(GetWeatherWidget))] +public string GetWeatherWidget( + [McpResourceTrigger( + "ui://weather/index.html", + "Weather Widget", + MimeType = "text/html;profile=mcp-app", + Description = "Interactive weather display for MCP Apps")] + [McpMetadata(ResourceMetadata)] + ResourceInvocationContext context) +{ + var file = Path.Combine(AppContext.BaseDirectory, "app", "dist", "index.html"); + return File.ReadAllText(file); +} +``` + +> The widget HTML (`app/dist/index.html`) is produced by `npm run build` in the UI `app/` folder and +> copied to the function output. Always build the UI before running or the resource fails to read the file. + +## Adding your own tool + widget + +1. **Build the UI** for the new widget so a bundled `index.html` exists at a known path. +2. **Add a resource function** with `[McpResourceTrigger("ui:///index.html", "", MimeType = "text/html;profile=mcp-app", Description = "...")]` that returns the bundled HTML. +3. **Add a tool function** with `[McpToolTrigger("<ToolName>", "<description>")]` + `[McpMetadata(...)]` + whose JSON sets `ui.resourceUri` to the **same** `ui://<name>/index.html` URI. +4. Declare inputs with `[McpToolProperty("<arg>", "<description>")]` (one per argument). +5. Keep the tool name matching `^[A-Za-z0-9_]+$` (no hyphens). +6. Return structured data (an object / JSON) from the tool — the host passes it to the UI resource. +7. Rebuild, restart the host, and re-verify in Copilot (see SKILL.md step 5). + +## Key contract points + +- The tool's `ui.resourceUri` **must exactly match** the resource's `[McpResourceTrigger]` URI. +- Resource MIME type must be `text/html;profile=mcp-app`. +- Tool handlers should return data (not HTML); the resource returns the HTML shell/bundle. +- Handle missing/invalid input gracefully and return a well-formed error object rather than throwing. diff --git a/plugins/microsoft-365-agents-toolkit/skills/mcp-apps-azure-functions/references/deployment.md b/plugins/microsoft-365-agents-toolkit/skills/mcp-apps-azure-functions/references/deployment.md new file mode 100644 index 0000000..8f42a35 --- /dev/null +++ b/plugins/microsoft-365-agents-toolkit/skills/mcp-apps-azure-functions/references/deployment.md @@ -0,0 +1,111 @@ +# Deployment & Local Run — MCP Apps on Azure Functions + +Covers running locally and deploying the serverless MCP Apps server to Azure with `azd` (recommended) or +the Azure Functions Core Tools. Source: [Build an MCP Apps server using Azure Functions](https://learn.microsoft.com/azure/azure-functions/scenario-mcp-apps?pivots=programming-language-csharp). + +## Local storage (Azurite) + +The Functions host needs a storage connection. Use Azurite locally: + +- **VS Code:** press F1 → run `Azurite: Start`. +- **CLI:** run `azurite` (install with `npm install -g azurite`) as a background process. +- **Docker:** `docker run -p 10000:10000 -p 10001:10001 -p 10002:10002 mcr.microsoft.com/azure-storage/azurite`. + +Start Azurite **before** `func start` / F5. Spawn it as a detached background process (see SKILL.md +Automation rules). + +## Deploy with azd (recommended — Flex Consumption) + +The template includes Bicep in `infra/` that `azd` uses to create a secure Flex Consumption deployment +following best practices. `DEPLOY_SERVICE` selects which sample app's resources to provision. + +```bash +# 1. Select the weather app service +azd env set DEPLOY_SERVICE weather + +# 2. Provision Azure resources (pick subscription + region; vnetEnabled = false to simplify) +azd provision + +# 3. Deploy the app code +azd deploy --service weather +``` + +Provisioning prompts: + +| Parameter | Description | +|-----------|-------------| +| Azure subscription | Subscription in which resources are created | +| Azure location | Region — only regions that support the **Flex Consumption** plan are listed | +| `vnetEnabled` | `false` skips virtual network resources (simpler deployment) | + +> `azd up` runs package + provision + deploy in one step. Use the split `provision` → `deploy` flow when +> you want to provision once and redeploy code changes repeatedly (`azd deploy --service weather`). + +After deploy, `azd` prints links to the created resources. + +## Connect to the remote MCP server + +Built-in MCP authorization (Microsoft Entra) is enabled by default; VS Code handles the OAuth sign-in. See **Authentication (built-in Entra) & hardening** below for the token-flow model and how to harden the endpoint. + +```bash +azd env get-value AZURE_FUNCTION_NAME +``` + +1. In `.vscode/mcp.json`, click **Start** above the `remote-mcp-function` configuration. +2. When prompted, enter the function app name from the command above. +3. Complete the Microsoft Entra sign-in to authorize access. +4. In Copilot Agent mode, re-run the same prompt (e.g., `What's the weather in Seattle?`) — the code now + runs securely in Azure and renders the same widget. + +## Authentication (built-in Entra) & hardening + +The `remote-mcp-functions-dotnet` template turns on **built-in Microsoft Entra authorization** for the +Function app (App Service Authentication, a.k.a. "Easy Auth") via its Bicep — so the deployed MCP server +validates the Copilot SSO token at the platform edge and you don't add a custom token guard. + +- **How the SSO token flow works** (issuance, validation, claims reaching your tool): + [`setup-sso-ui-widget` → sso-explained.md](../../setup-sso-ui-widget/references/sso-explained.md). +- **Harden / verify the Authentication blade** — the same App Service Authentication feature applies to + Function apps: set **Allowed token audiences** to the app's **client id** (not the `api://…` URI) and + add the **Copilot host client id** to *Allowed client applications*, or valid tokens still get `403`. + Field-by-field guide: [`setup-sso-ui-widget` → easy-auth.md](../../setup-sso-ui-widget/references/easy-auth.md). +- **Express / raw-http MCP servers** (from `ui-widget-developer` / `create-mcp-app`) don't get built-in + auth — add Entra SSO with the [`setup-sso-ui-widget`](../../setup-sso-ui-widget/SKILL.md) skill instead. +- **Entra SSO vs third-party OAuth** for the plugin manifest: + [`declarative-agent-developer` → authentication.md](../../declarative-agent-developer/references/authentication.md). + +## Alternative: Azure Functions Core Tools + +If you provisioned the function app separately (e.g., with Azure CLI/Bicep) and only need to publish code: + +```bash +# From the function app project folder (src/McpWeatherApp), after building the UI +func azure functionapp publish <function-app-name> +``` + +`azd` remains the recommended path because it provisions the Flex Consumption plan, storage, and Entra +auth via the template's Bicep. Use `func publish` only for code-only updates to an already-provisioned app. + +## Cost + +The app runs on the **Flex Consumption** plan — a *pay-for-what-you-use* model. Completing the quickstart +typically costs a few USD cents or less. Delete resources when done to avoid ongoing charges. + +## Clean up + +```bash +azd down +``` + +Removes the function app and all related resources created by `azd provision`. + +## Troubleshooting + +| Symptom | Fix | +|---------|-----| +| Widget shows fallback "content not found" HTML | Run `npm run build` in the UI `app/` folder before starting the host; confirm `app/dist/index.html` exists | +| Host fails to start / storage error | Ensure Azurite is running before `func start` / F5 | +| `azd provision` region list is short | Only Flex Consumption regions are shown — pick one of the listed regions | +| Build/runtime errors about SDK version | Install the **.NET 10 SDK**; ensure Core Tools `>= 4.5.0` | +| Remote MCP connection prompts repeatedly | Complete the Entra sign-in; built-in MCP authorization is on by default | +| Tool not discovered by the host | Tool name must match `^[A-Za-z0-9_]+$` (no hyphens); restart the MCP server entry in `.vscode/mcp.json` | diff --git a/plugins/microsoft-365-agents-toolkit/skills/setup-sso-ui-widget/references/easy-auth.md b/plugins/microsoft-365-agents-toolkit/skills/setup-sso-ui-widget/references/easy-auth.md index 79ca701..f2b4aa8 100644 --- a/plugins/microsoft-365-agents-toolkit/skills/setup-sso-ui-widget/references/easy-auth.md +++ b/plugins/microsoft-365-agents-toolkit/skills/setup-sso-ui-widget/references/easy-auth.md @@ -2,6 +2,8 @@ > **Local vs cloud.** The `auth.ts` JWKS guard this skill injects is for **local dev/testing**. When you **deploy** the MCP server to **Azure App Service**, prefer the platform's built-in authentication (**"Easy Auth"**) over the custom guard — it validates the Copilot SSO token at the platform edge, before your code runs, and offloads key/issuer handling. Configure it in **App Service → Authentication → Microsoft Entra identity provider**. This reference is the field-by-field companion to that blade; use it *instead of* relying on `auth.ts` once hosted. +> **Serverless (Azure Functions).** The same App Service Authentication feature is available on **Function apps**. If you host a serverless MCP Apps server, see the [`mcp-apps-azure-functions`](../../mcp-apps-azure-functions/SKILL.md) skill — its template enables built-in Entra authorization by default, and the field mappings below apply to the Function app's Authentication blade. + ## How this skill's values map to the blade The tables below use generic placeholders. Fill them from your project: diff --git a/plugins/microsoft-365-agents-toolkit/skills/ui-widget-developer/SKILL.md b/plugins/microsoft-365-agents-toolkit/skills/ui-widget-developer/SKILL.md index 60fa18d..fe23fba 100644 --- a/plugins/microsoft-365-agents-toolkit/skills/ui-widget-developer/SKILL.md +++ b/plugins/microsoft-365-agents-toolkit/skills/ui-widget-developer/SKILL.md @@ -14,30 +14,31 @@ description: | # Copilot MCP Server Development -## Telemetry Tagging +Build MCP servers that integrate with Microsoft 365 Copilot Chat and render rich interactive widgets. -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. +Make two decisions **before writing any code**: + +1. **Architecture** — MCP Apps vs OAI Apps (this section). +2. **Hosting model** — server-full vs serverless (next section). -## 🔀 PATH SELECTION +> **Setup:** Before running any `atk` CLI commands, tag this session as skill-initiated by running `export ATK_CLI_SKILL=true` once at the start. All later `atk` commands in the same terminal inherit it. -**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: +## 1. Choose your architecture — START HERE 🔀 -| | **OAI Apps** (this skill) | **MCP Apps Extension** (`modelcontextprotocol/ext-apps`) | +**Before proceeding, ask the user which path they want to take using AskUserQuestion.** Both are supported for M365 Copilot widget rendering. **MCP Apps is the recommended default** — the official, cross-platform standard; prefer it unless the user has an existing OAI Apps investment. Present the tradeoffs and let them choose: + +| | **MCP Apps** ⭐ recommended (`modelcontextprotocol/ext-apps`) | **OAI Apps** (this skill) | |---|---|---| -| **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 | +| **Standard** | Official MCP standard | OpenAI-specific | +| **Works in** | M365 Copilot, ChatGPT, VSCode, and more | ChatGPT + M365 Copilot | +| **Maturity** | Official standard, growing ecosystem | Battle-tested, production-ready | +| **Design** | MCP Apps protocol (cross-platform) | OpenAI Apps SDK | +| **When to choose** | Default — open standard, broadest client support | Existing OAI app investment | -**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)?"_ +**Ask:** _"Would you like to build an MCP app (recommended — the official standard; works in M365 Copilot, ChatGPT, VSCode, and more) or an OAI app (OpenAI Apps SDK — battle-tested, works in ChatGPT and M365 Copilot)?"_ +- **MCP apps (recommended)** → Install the `modelcontextprotocol/ext-apps` plugin (see below), then use the appropriate skill from that plugin. - **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 @@ -76,6 +77,25 @@ After installing, invoke the relevant skill to continue. --- +## 2. Choose your hosting model + +Decide where the MCP server runs in production. DevTunnels (later in this skill) are for **local development only** — they are not a hosting model. + +| | **Server-full** (always-on) | **Serverless** (pay-per-use) | +|---|---|---| +| **Where** | Azure App Service, Azure Container Apps, or any always-on Node/host | Azure Functions (Flex Consumption plan) | +| **Best for** | Steady traffic, persistent sessions, predictable latency, existing infra | Spiky/low traffic, minimal ops, scale-to-zero, cost efficiency | +| **Cost** | Pay for the running instance | Pay only for executions | +| **Cold starts** | None (always warm) | Possible after idle | +| **Path** | Continue in **this skill**, then deploy the finished server to your host | Use the **`mcp-apps-azure-functions`** skill | + +- **Server-full** → keep going here; deploy the finished MCP server to your always-on host and update the agent manifest URLs. +- **Serverless** → hand off to the **`mcp-apps-azure-functions`** skill, which scaffolds, runs, and deploys a serverless MCP Apps server on Azure Functions with `azd` + Flex Consumption. + +> **Note:** the serverless skill uses the **MCP Apps** standard in **C# / .NET**. If you chose OAI Apps in TypeScript above and still want serverless, either adopt the MCP Apps path or deploy your Node server to an always-on (server-full) host. + +--- + ## 📛 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. @@ -84,7 +104,7 @@ This skill triggers when building MCP servers with OAI app or widget rendering f | 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 | +| **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 **Choose your architecture** 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) | @@ -154,9 +174,9 @@ 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. +**PATH SELECTION (REQUIRED — STOP BEFORE ANY CODE):** You MUST use `AskUserQuestion` to ask the user whether they want MCP Apps (recommended) or OAI Apps 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. +**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 **Choose your architecture** 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. @@ -254,9 +274,7 @@ The MCP Inspector shows the exact tool schema from your server. Copy it complete --- -Build MCP servers that integrate with Microsoft 365 Copilot Chat and render rich interactive widgets. - -## Architecture +## How it works ``` M365 Copilot ──▶ mcpPlugin.json ──▶ MCP Server ──▶ structuredContent ──▶ React + Fluent UI Widget @@ -373,7 +391,7 @@ Core requirements: ## 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. +> **Local testing only.** DevTunnels are for development and testing on your machine — not a hosting model. For production, pick a hosting model (see **Choose your hosting model** above): deploy to an always-on host (**server-full**), or hand off to the `mcp-apps-azure-functions` skill (**serverless**) — then 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.