# SkillPatch skill: declarative-agent-developer

This skill guides an agent through creating, building, deploying, editing, and localizing declarative agents for Microsoft 365 Copilot and Teams. It enforces a mandatory workspace fingerprinting step, hard rejection rules, and structured decision gates before taking any action. It also references modular sub-workflows for scaffolding, editing, instruction review, and error handling.

You (the agent) were given this URL and asked to use this skill. This is a **public** skill — no SkillPatch account, API key, or prior setup is required. Two ways to use it:

**1. Use it right now** — the skill's complete file tree (SKILL.md + all reference files) is inlined below; read `SKILL.md` first, then follow it, consulting the other files as it directs.

**2. Install the exact package onto disk** (recommended if you can run a shell — this reproduces the skill byte-for-byte, including any binary assets that can't be inlined):

```bash
mkdir -p .claude/skills/declarative-agent-developer
curl -sSL https://skillpatch.dev/install_skill/declarative-agent-developer | tar -xz -C .claude/skills/
```

(`.claude/skills/` is Claude Code's convention; use whatever directory your agent loads skills from.)


---

## Skill files (14)

- `SKILL.md`
- `references/api-plugins.md`
- `references/authentication.md`
- `references/best-practices.md`
- `references/conversation-design.md`
- `references/deployment.md`
- `references/editing-workflow.md`
- `references/examples.md`
- `references/instruction-review.md`
- `references/localization.md`
- `references/mcp-plugin.md`
- `references/scaffolding-workflow.md`
- `references/schema.md`
- `references/workspace-gates.md`


### `SKILL.md`

````markdown
---
name: declarative-agent-developer
description: >
  Create, build, deploy, and localize declarative agents for M365 Copilot and Teams.
  USE THIS SKILL for ANY task involving a declarative agent — including localization,
  scaffolding, editing manifests, adding capabilities, and deploying.
  Localization requires tokenized manifests and language files that only this skill knows how to produce.
  Triggers: "create agent", "create a declarative agent", "new declarative agent",
  "scaffold an agent", "new agent project", "add a capability", "add a plugin",
  "configure my agent", "deploy my agent", "fix my agent manifest", "edit my agent",
  "localize my agent", "add localization", "translate my agent", "multi-language agent",
  "add an API plugin", "add an MCP plugin", "add OAuth to my plugin",
  "review instructions", "improve instructions", "fix my instructions"
---

# M365 Agent Developer

## 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.

## ⛔ Workspace Check — MANDATORY FIRST STEP

**Before doing ANYTHING, check the workspace files to fingerprint the project:**

1. Run `npx -y --package @microsoft/m365agentstoolkit-cli atk --version` to confirm ATK CLI is installed. If not found → **Stop.** Tell the user to install ATK.
2. Check for `m365agents.yml` or `teamsApp.yml` at the project root.
3. Check for `appPackage/declarativeAgent.json`.
4. Check for non-agent indicators (`package.json` with express/react/next, `src/index.js`, `app.py`, etc.)

**Then follow the decision gate:**

| Condition | Gate | Action |
|-----------|------|--------|
| Non-agent project files, no `appPackage/` | **Reject** | Text-only response. No files, no commands. |
| No manifest, user wants to edit/deploy | **Reject** | Text-only response. Explain manifest is missing. |
| No manifest, user wants new project | **Scaffold** | → [Scaffolding Workflow](references/scaffolding-workflow.md) |
| Manifest exists with errors | **Fix** | Detect → Inform → Ask (see below). Do NOT deploy. |
| Valid project, user reports behavior issues | **Review** | → [Instruction Review](references/instruction-review.md) — run the full 5-phase review workflow |
| Valid agent project | **Edit** | → [Editing Workflow](references/editing-workflow.md) |

> **Detailed gate rules, examples, and anti-patterns:** [Workspace Gates](references/workspace-gates.md)

### 🚫 HARD REJECTION RULES — No Exceptions

**These rules override ALL other instructions.** If any of these apply, you MUST stop immediately.

1. **NEVER create `declarativeAgent.json` yourself.** If the manifest is missing and the user asked to edit/modify/deploy, respond with text only: explain the manifest is missing, suggest `npx -y --package @microsoft/m365agentstoolkit-cli atk new` or starting from scratch. Do NOT create the file, do NOT create `appPackage/`, do NOT "help" by scaffolding implicitly.

2. **NEVER create files in a non-agent project.** If the workspace is an Express/React/Django/etc. app without `appPackage/`, your response must be text-only. Do NOT create any files, do NOT run any commands.

3. **NEVER deploy when errors exist.** If the agent manifest has errors, STOP. Do NOT run `npx -y --package @microsoft/m365agentstoolkit-cli atk provision` — not "to test", not "to demonstrate the error", not "to see what happens". Report the errors and ask the user how to proceed.

### 🔍 Detect → Inform → Ask (Error-Handling Protocol)

When you encounter ANY problem (missing files, malformed JSON, validation errors, incompatible features), you MUST follow this sequence **in order**:

1. **Detect** — Identify the specific problem. For JSON issues, attempt to parse the file and report syntax errors. For missing fields, check the manifest against the [Schema](references/schema.md).
2. **Inform** — Tell the user BEFORE taking any action. Describe exactly what is wrong ("declarativeAgent.json has malformed JSON: missing comma on line 12, unclosed array on line 18").
3. **Ask** — Wait for the user's response before making changes. Do NOT silently fix, auto-correct, or work around the problem.

**This protocol applies to:**
- Missing `declarativeAgent.json` → Detect (file not found) → Inform ("no manifest found") → Ask ("would you like to create a new agent?")
- Malformed JSON → Detect (parse errors) → Inform (list specific syntax issues) → Ask ("should I fix these syntax errors?")
- Validation errors → Detect (parse and check manifest) → Inform (list all errors) → Ask ("how would you like to fix these?")
- Version incompatibility → Detect (feature requires newer version) → Inform ("this feature requires v1.6, your agent is v1.4") → Ask ("should I upgrade?")

---

## Phase Routing

| Scenario | Workflow Reference |
|----------|-------------------|
| Creating a NEW project from scratch | [Scaffolding Workflow](references/scaffolding-workflow.md) |
| Working with existing `.json` manifests | [Editing Workflow](references/editing-workflow.md) |
| Adding an API plugin | [API Plugins](references/api-plugins.md) |
| Adding an MCP server | [MCP Plugin](references/mcp-plugin.md) |
| Adding OAuth to an MCP or API plugin | [Authentication](references/authentication.md) |
| Reviewing or improving existing agent instructions | [Instruction Review](references/instruction-review.md) |
| User reports agent gives generic/wrong answers | [Instruction Review](references/instruction-review.md) |
| Localizing an agent into multiple languages | [Localization](references/localization.md) |
| Adding a new language to an already-localized agent | [Localization](references/localization.md) |
| Writing agent instructions | [Conversation Design](references/conversation-design.md) |

---

## ATK CLI Setup

Before running any ATK commands, check if the ATK CLI is available by running `npx -y --package @microsoft/m365agentstoolkit-cli atk --version`. If not found, **STOP and tell the user** — do NOT attempt to install it yourself.

All commands use the `npx -y --package @microsoft/m365agentstoolkit-cli atk` prefix (e.g., `npx -y --package @microsoft/m365agentstoolkit-cli atk provision --env local`).

---

## Critical Rules

### 1. Deploy After EVERY Edit

After ANY change to files in `appPackage/`, you MUST deploy and show the test link before responding:

```bash
npx -y --package @microsoft/m365agentstoolkit-cli atk provision --env local --interactive false
```

Then read `M365_TITLE_ID` from `env/.env.local` and **ALWAYS** present the review UX:

```
✅ Agent deployed successfully!

🚀 Test Your Agent in M365 Copilot:
🔗 https://m365.cloud.microsoft/chat/?titleId={M365_TITLE_ID}
```

**⛔ Never respond without this link.** If you deployed, the test link MUST appear in your response. This is not optional — it is how the user tests their agent.

- If the manifest has errors → **STOP. Fix errors. Do NOT deploy.**
- Exception: user explicitly asks you not to deploy

### 2. Never Invent Content or Create Missing Files

- Do NOT invent placeholder names, descriptions, or instructions
- Do NOT create `declarativeAgent.json` or `appPackage/` if they don't exist — this is a REJECT scenario, not a "help by creating" scenario
- If required fields are missing, report the gaps, and ASK the user
- If JSON is malformed, follow Detect → Inform → Ask: parse the file first, tell the user what's broken, then ask before fixing. Use surgical edits (not rewrites)
- **⛔ NEVER set placeholder values for environment variables** that are populated by automation (e.g., `<PREFIX>_MCP_AUTH_ID`, `TEAMS_APP_ID`). Leave them empty (`VAR_NAME=`). Placeholders will be treated as real values and will NOT be overwritten by provisioning.

### 3. Schema Version Compatibility

Before adding ANY feature, read the `version` field in `declarativeAgent.json` and check the [Schema](references/schema.md) feature matrix. If the feature isn't supported in that version, **refuse** and offer to upgrade.

Key version gates:
- `sensitivity_label`, `worker_agents`, `EmbeddedKnowledge` → **v1.6 only**
- `Meetings` → **v1.5+**
- `ScenarioModels`, `behavior_overrides`, `disclaimer` → **v1.4+**
- `Dataverse`, `TeamsMessages`, `Email`, `People` → **v1.3+**

### 4. Use `npx -y --package @microsoft/m365agentstoolkit-cli atk add action` for API Plugins — NEVER Create Plugin Files Manually

You are **forbidden** from manually creating `ai-plugin.json`, OpenAPI specs, adaptive cards, or editing the `actions` array. Use the CLI:

```bash
# ⛔ Always list ALL operations in a single call — NEVER run separate calls per operation
npx -y --package @microsoft/m365agentstoolkit-cli atk add action --api-plugin-type api-spec --openapi-spec-location URL --api-operation "GET /path,POST /path,PATCH /path/{id},DELETE /path/{id}" -i false
```

Run a **single** `npx -y --package @microsoft/m365agentstoolkit-cli atk add action` call per OpenAPI spec, listing **all** operations as a comma-separated list in `--api-operation`. Never run separate `npx -y --package @microsoft/m365agentstoolkit-cli atk add action` calls for different operations from the same spec — this creates multiple plugins instead of one. If `npx -y --package @microsoft/m365agentstoolkit-cli atk add action` fails, report the error; do NOT fall back to manual creation.

> **Exception:** MCP servers are not supported by `npx -y --package @microsoft/m365agentstoolkit-cli atk add action`. Use the [MCP Plugin workflow](references/mcp-plugin.md) instead.

### 5. MCP Server Integration

When the user mentions an MCP server URL, follow the [MCP Plugin workflow](references/mcp-plugin.md). You MUST discover tools via the MCP protocol handshake (initialize → notifications/initialized → tools/list) — **NEVER fabricate tool names/descriptions**. For authenticated MCP servers, follow the [authentication guide](references/authentication.md) to configure OAuth.

### 6. Always Update Instructions & Starters After Changes

Adding a capability or plugin without updating instructions is incomplete. After ANY change:
1. Update instructions to describe the new/changed functionality — every data source should have clear intent coverage (WHEN and WHY to use it) per the [Instruction Review](references/instruction-review.md) quality bar. Built-in capabilities don't need exact names; actions/plugins should be named.
2. **Do NOT list tool names, descriptions, or parameters in instructions** — these are already in the plugin metadata (`ai-plugin.json`, MCP manifests, capability config). Instructions should contain decision logic only: WHEN to use each tool, chaining rules, and failure handling.
3. **Stay within the 8,000-character instruction limit** — if close to the limit, cut tool descriptions first
4. Add at least 1 conversation starter per added capability/plugin
5. Remove starters that reference removed capabilities
6. Run the [Diagnostic Checklist](references/instruction-review.md) against the updated instructions to verify quality

### 7. App Name Requirement

Always update the app name and description to something meaningful. Never leave defaults like "My Agent".

---

## References

### Shared
- **[Authentication](references/authentication.md)** — OAuth discovery, credentials, oauth/register lifecycle, OAuthPluginVault
- **[Best Practices](references/best-practices.md)** — Security, performance, testing, compliance
- **[Conversation Design](references/conversation-design.md)** — Authoring instructions and conversation starters from scratch
- **[Instruction Review](references/instruction-review.md)** — Auditing, diagnosing, and improving existing instructions; anti-pattern detection; before/after rewrites
- **[Deployment](references/deployment.md)** — ATK CLI workflows, environments, CI/CD
- **[Localization](references/localization.md)** — Multi-language support, tokenized manifests, language files
- **[Workspace Gates](references/workspace-gates.md)** — Detailed gate rules, examples, anti-patterns

### Scaffolding
- **[Scaffolding Workflow](references/scaffolding-workflow.md)** — Step-by-step scaffolding instructions, naming rules, error handling

### JSON Development
- **[Editing Workflow](references/editing-workflow.md)** — Step-by-step JSON development instructions
- **[Schema](references/schema.md)** — Official JSON schema for agent manifests
- **[API Plugins](references/api-plugins.md)** — OpenAPI integration for JSON agents
- **[MCP Plugin](references/mcp-plugin.md)** — MCP server integration with RemoteMCPServer, OAuth, response semantics, logo handling
- **[Examples](references/examples.md)** — JSON manifest examples

````


### `references/api-plugins.md`

````markdown
# API Plugin Architecture for M365 JSON Agents

> **Note:** This guide is for JSON-based agents. API plugins are defined using JSON manifest files that reference OpenAPI specifications.

## Overview

API plugins (also called "actions") allow your M365 Copilot agent to interact with external REST APIs. They consist of:

1. **OpenAPI Specification** - Describes the REST API endpoints, parameters, and responses
2. **API Plugin Manifest** (JSON) - Configures how M365 Copilot interacts with the API
3. **Declarative Agent Reference** - Links the plugin to your agent via the `actions` array

---

## ⛔ Post-`npx -y --package @microsoft/m365agentstoolkit-cli atk add action` Checklist — MANDATORY

After running `npx -y --package @microsoft/m365agentstoolkit-cli atk add action`, you **MUST** complete ALL of these before validating and deploying:

1. **Update `name_for_human`** in ai-plugin.json — descriptive, user-facing name (max 20 chars)
2. **Update `description_for_model`** in ai-plugin.json — detailed guidance for the AI on when and how to use each function
3. **Customize adaptive cards** in `appPackage/adaptiveCards/` for each operation — different layouts per HTTP verb (list view for GET collections, detail view for GET by ID, confirmation for DELETE, etc.)
4. **Add `confirmation` dialogs** for all destructive operations (POST, PUT, PATCH, DELETE)
5. **Deploy** with `npx -y --package @microsoft/m365agentstoolkit-cli atk provision --env local --interactive false`

Skipping ANY of these steps = incomplete work. The `npx -y --package @microsoft/m365agentstoolkit-cli atk add action` command generates scaffolding — **you must finish the job** by customizing every generated file.

---

## Adding API Plugins with ATK CLI

> **⚠️ IMPORTANT:** When adding an API, OpenAPI spec, or REST API to your agent, you **MUST** use the `npx -y --package @microsoft/m365agentstoolkit-cli atk add action` command. This is the required method for adding API plugins to M365 Copilot agents. **Do NOT manually create plugin files** - the path resolution between local packaging and M365 service validation is complex and error-prone.

> **⛔ ONE PLUGIN PER API — HARD RULE:** Always add ALL operations from the same OpenAPI spec in a **single** `npx -y --package @microsoft/m365agentstoolkit-cli atk add action` call. List every operation in the `--api-operation` parameter as a comma-separated list. **NEVER** run separate `npx -y --package @microsoft/m365agentstoolkit-cli atk add action` calls for different operations from the same spec — this creates multiple plugins instead of one unified plugin. One OpenAPI spec = one `npx -y --package @microsoft/m365agentstoolkit-cli atk add action` call = one plugin.

### The `npx -y --package @microsoft/m365agentstoolkit-cli atk add action` Command

**ALWAYS use this command** when asked to add an API, OpenAPI specification, or REST API to an M365 Copilot agent:

```bash
npx -y --package @microsoft/m365agentstoolkit-cli atk add action \
  --api-plugin-type api-spec \
  --openapi-spec-type enter-url-or-open-local-file \
  --openapi-spec-location URL_OR_FILE_PATH \
  --api-operation "OPERATIONS_TO_MAP" \
  -i false
```

### Command Parameters

| Parameter | Description |
|-----------|-------------|
| `--api-plugin-type` | Type of plugin. Use `api-spec` for OpenAPI-based plugins. |
| `--openapi-spec-type` | How to provide the spec. Use `enter-url-or-open-local-file`. |
| `--openapi-spec-location` | URL or local file path to the OpenAPI specification. |
| `--api-operation` | Comma-separated list of operations to include (format: `METHOD /path`). |
| `-i false` | Non-interactive mode. |

### ⚠️ CRITICAL: Use Absolute Paths for Local Files

When using a local OpenAPI specification file, you **MUST use an absolute path**:

```bash
# ✅ CORRECT - Absolute path
--openapi-spec-location /home/user/project/openapi.json

# ❌ WRONG - Relative path (will fail!)
--openapi-spec-location ./openapi.json
--openapi-spec-location openapi.json
```

**Why?** The ATK CLI executes from a temporary directory, so relative paths cannot be resolved. Always use the full absolute path to your OpenAPI specification file.

### Operation Format

The `--api-operation` parameter uses the format: `METHOD /path,METHOD /path,...`

**Example with Repairs API (URL):**
```bash
npx -y --package @microsoft/m365agentstoolkit-cli atk add action \
  --api-plugin-type api-spec \
  --openapi-spec-type enter-url-or-open-local-file \
  --openapi-spec-location "https://repairshub.azurewebsites.net/openapi.json" \
  --api-operation "GET /repairs,GET /repairs/{id},POST /repairs,PATCH /repairs/{id},DELETE /repairs/{id}" \
  -i false
```

**Example with local file (absolute path):**
```bash
npx -y --package @microsoft/m365agentstoolkit-cli atk add action \
  --api-plugin-type api-spec \
  --openapi-spec-type enter-url-or-open-local-file \
  --openapi-spec-location /home/user/myproject/nhl-openapi.json \
  --api-operation "GET /v1/standings/now,GET /v1/score/now,GET /v1/schedule/now" \
  -i false
```

### What the Command Creates

After running `npx -y --package @microsoft/m365agentstoolkit-cli atk add action`, the following files are created/updated:

```
appPackage/
├── declarativeAgent.json     # Updated with action reference
├── ai-plugin.json            # API plugin manifest (generated)
├── adaptiveCards/            # Response templates (auto-generated)
│   ├── getRepairs.json
│   ├── getRepairById.json
│   └── ...
└── apiSpecificationFile/
    ├── openapi.yaml          # OpenAPI spec (converted to 3.0 if needed)
    └── openapi.yaml.original # Original spec (if converted)
```

> **Note:** OpenAPI 3.1 specifications are automatically converted to OpenAPI 3.0 format for compatibility.

### Post-Generation: Enhance the Plugin

After running `npx -y --package @microsoft/m365agentstoolkit-cli atk add action`, you **MUST** enhance the generated files:

1. **Update `name_for_human`** - Make it descriptive and user-friendly
2. **Update `description_for_model`** - Add detailed guidance on when and how to use each function
3. **🎨 Update adaptive cards for each action** - Customize the auto-generated cards in `appPackage/adaptiveCards/` to present meaningful, well-structured data for each operation

**Example plugin enhancement:**
```json
{
  "name_for_human": "NHL Data API",
  "description_for_human": "Access real-time NHL standings, scores, and player statistics",
  "description_for_model": "Use this plugin to access real-time NHL data. Call getCurrentStandings for league standings. Call getTodaysScores for today's game scores or getScoresByDate for historical scores (YYYY-MM-DD format). Call getCurrentTeamStats or getCurrentRoster with a 3-letter team code (TOR, BOS, NYR, MTL, EDM, VGK, etc.). Always provide context about the data format and available parameters."
}
```

The `description_for_model` is critical - it tells the AI when to use each function and what parameters to provide.

### 🎨 Post-Generation: Enhance Adaptive Cards for Each Action

The `npx -y --package @microsoft/m365agentstoolkit-cli atk add action` command auto-generates basic adaptive cards in `appPackage/adaptiveCards/` — one per operation. These default cards are generic and only display raw data. **You MUST customize each card** to provide a valuable UX tailored to the data each action returns.

#### ⚠️ CRITICAL: Check ALL Operations Have Adaptive Cards

After running `npx -y --package @microsoft/m365agentstoolkit-cli atk add action`, **verify that an adaptive card exists for EVERY operation**. The command may not generate cards for POST, PATCH, or DELETE operations. **If any operation is missing an adaptive card, CREATE one manually** in `appPackage/adaptiveCards/`.

- **GET operations** → List or detail layout showing returned data
- **POST operations** → Confirmation/summary layout showing what was created
- **PATCH operations** → Summary layout showing what was updated
- **DELETE operations** → Confirmation layout confirming what was deleted

#### Why Customize Adaptive Cards?

- **Default cards are generic** — they list all response fields as plain text with no hierarchy or formatting
- **Users expect rich, scannable output** — titles, subtitles, key metrics, and visual hierarchy help users quickly understand results
- **Different actions need different layouts** — a list of items needs a different card than a single detail view or a confirmation response

#### How to Enhance Each Card

For **each** adaptive card file generated in `appPackage/adaptiveCards/`:

1. **Examine the API response schema** — understand what fields each operation returns
2. **Identify the most important fields** — determine what users care about most (title, status, date, assignee, etc.)
3. **Design a clear visual hierarchy** — use `weight: "Bolder"`, `size: "Medium"/"Large"`, `spacing`, and `separator` to create structure
4. **Use appropriate layouts** — `ColumnSet` for side-by-side data, `FactSet` for key-value pairs, `Container` for grouping
5. **Add conditional styling** — use `"color"` properties and `"style"` to highlight important states (e.g., urgent, overdue)
6. **Include action buttons** — add `Action.OpenUrl` for links to external resources when the data includes URLs
7. **Handle arrays with `$foreach`** — use `${$root}` for the items array and `${$data}` for individual items within the template

#### Example: Before vs After Enhancement

**❌ Auto-generated card (generic — poor UX):**
```json
{
  "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
  "type": "AdaptiveCard",
  "version": "1.5",
  "body": [
    {
      "type": "TextBlock",
      "text": "id: ${if(id, id, 'N/A')}",
      "wrap": true
    },
    {
      "type": "TextBlock",
      "text": "title: ${if(title, title, 'N/A')}",
      "wrap": true
    },
    {
      "type": "TextBlock",
      "text": "description: ${if(description, description, 'N/A')}",
      "wrap": true
    },
    {
      "type": "TextBlock",
      "text": "assignedTo: ${if(assignedTo, assignedTo, 'N/A')}",
      "wrap": true
    },
    {
      "type": "TextBlock",
      "text": "date: ${if(date, date, 'N/A')}",
      "wrap": true
    },
    {
      "type": "TextBlock",
      "text": "image: ${if(image, image, 'N/A')}",
      "wrap": true
    }
  ]
}
```

**✅ Enhanced card (rich UX — tailored to data):**
```json
{
  "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
  "type": "AdaptiveCard",
  "version": "1.5",
  "body": [
    {
      "type": "ColumnSet",
      "columns": [
        {
          "type": "Column",
          "width": "auto",
          "items": [
            {
              "type": "Image",
              "url": "${image}",
              "size": "Medium",
              "altText": "${title}"
            }
          ]
        },
        {
          "type": "Column",
          "width": "stretch",
          "items": [
            {
              "type": "TextBlock",
              "text": "${title}",
              "weight": "Bolder",
              "size": "Medium",
              "wrap": true
            },
            {
              "type": "TextBlock",
              "text": "Assigned to: ${assignedTo}",
              "spacing": "None",
              "isSubtle": true,
              "wrap": true
            }
          ]
        }
      ]
    },
    {
      "type": "TextBlock",
      "text": "${description}",
      "wrap": true,
      "spacing": "Medium"
    },
    {
      "type": "FactSet",
      "separator": true,
      "facts": [
        {
          "title": "Repair ID",
          "value": "#${id}"
        },
        {
          "title": "Date",
          "value": "${date}"
        }
      ]
    }
  ]
}
```

#### Example: List Action Card (Array Response with `$foreach`)

When an action returns an array of items, use the `$foreach` data binding pattern to render each item:

```json
{
  "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
  "type": "AdaptiveCard",
  "version": "1.5",
  "body": [
    {
      "type": "Container",
      "$data": "${$root}",
      "separator": true,
      "items": [
        {
          "type": "TextBlock",
          "text": "${title}",
          "weight": "Bolder",
          "wrap": true
        },
        {
          "type": "ColumnSet",
          "columns": [
            {
              "type": "Column",
              "width": "stretch",
              "items": [
                {
                  "type": "TextBlock",
                  "text": "Assigned to: ${assignedTo}",
                  "isSubtle": true,
                  "spacing": "None",
                  "wrap": true
                }
              ]
            },
            {
              "type": "Column",
              "width": "auto",
              "items": [
                {
                  "type": "TextBlock",
                  "text": "${date}",
                  "isSubtle": true,
                  "spacing": "None",
                  "horizontalAlignment": "Right"
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}
```

#### Card Design Guidelines Per Action Type

| Action Type | Recommended Layout | Key Elements |
|-------------|-------------------|--------------|
| **List / Search** (GET returning array) | Repeating `Container` with `$data: "${$root}"` | Title, subtitle, key status field per item, separator between items |
| **Get by ID** (GET returning single object) | `ColumnSet` + `FactSet` | Image/icon, title, description, key-value pairs for metadata |
| **Create** (POST) | Confirmation summary | Show created item's key fields, success indicator |
| **Update** (PATCH/PUT) | Before/after or summary | Show updated fields, confirmation status |
| **Delete** (DELETE) | Simple confirmation | Item identifier, success message |

---

## When to Use API Plugins

Use API plugins (actions) when the agent needs to:
- **Call external APIs** not available through M365 capabilities
- **Perform CRUD operations** (Create, Read, Update, Delete)
- **Access real-time transactional data** from external systems
- **Modify state** in external systems (create tickets, update records)
- **Integrate with line-of-business systems** (CRM, ticketing, databases)

**Don't use API plugins when:**
- M365 capabilities already provide the data (use capabilities like `OneDriveAndSharePoint`, `Email`, etc.)
- Read-only access to documents is sufficient
- No external API interaction needed

## API Plugin vs Capabilities Decision Tree

```
Need external API?
  Yes → Create API Plugin Action
  No → Use built-in Capability
    ↓
Need real-time transactional data?
  Yes → API Plugin
  No → Capability (SharePoint, Connectors)
    ↓
Need CRUD operations?
  Yes → API Plugin
  No → Capability
    ↓
Need to modify state?
  Yes → API Plugin
  No → Capability
```

---

## API Plugin Manifest Structure (v2.4)

The API plugin manifest is a JSON file that configures how M365 Copilot interacts with your API.

### Basic Structure

```json
{
  "schema_version": "v2.4",
  "name_for_human": "Plugin Name",
  "namespace": "pluginNamespace",
  "description_for_human": "What this plugin does for users",
  "description_for_model": "Detailed instructions for the AI model on when and how to use this plugin",
  "functions": [],
  "runtimes": []
}
```

### Complete Example: Repairs API Plugin

```json
{
  "schema_version": "v2.4",
  "name_for_human": "Repairs API",
  "namespace": "repairs",
  "description_for_human": "Manage repair tickets and track issues",
  "description_for_model": "Use this plugin to search, create, update, and delete repair tickets. Call getRepairs to list all repairs or filter by assignee. Call getRepairById to get details about a specific repair. Call createRepair to create new tickets. Call updateRepair to modify existing repairs. Call deleteRepair to remove completed repairs.",
  "logo_url": "https://repairshub.azurewebsites.net/logo.png",
  "contact_email": "support@contoso.com",
  "legal_info_url": "https://contoso.com/terms",
  "privacy_policy_url": "https://contoso.com/privacy",
  "functions": [
    {
      "name": "getRepairs",
      "description": "Get all repairs, optionally filtered by assignee",
      "capabilities": {
        "response_semantics": {
          "data_path": "$",
          "properties": {
            "title": "$.title",
            "subtitle": "$.assignedTo"
          }
        }
      }
    },
    {
      "name": "getRepairById",
      "description": "Get a specific repair by its ID"
    },
    {
      "name": "createRepair",
      "description": "Create a new repair ticket",
      "capabilities": {
        "confirmation": {
          "type": "AdaptiveCard",
          "title": "Create Repair?",
          "body": "Are you sure you want to create this repair ticket?"
        }
      }
    },
    {
      "name": "updateRepair",
      "description": "Update an existing repair ticket",
      "capabilities": {
        "confirmation": {
          "type": "AdaptiveCard",
          "title": "Update Repair?",
          "body": "Are you sure you want to update this repair?"
        }
      }
    },
    {
      "name": "deleteRepair",
      "description": "Delete a repair ticket",
      "capabilities": {
        "confirmation": {
          "type": "AdaptiveCard",
          "title": "Delete Repair?",
          "body": "Are you sure you want to permanently delete this repair?"
        }
      }
    }
  ],
  "runtimes": [
    {
      "type": "OpenApi",
      "auth": {
        "type": "None"
      },
      "spec": {
        "url": "apiSpecificationFile/openapi.json"
      },
      "run_for_functions": ["getRepairs", "getRepairById", "createRepair", "updateRepair", "deleteRepair"]
    }
  ]
}
```

---

## OpenAPI Specification

The OpenAPI specification describes your REST API endpoints. Here's an example based on the Repairs API:

### Example: Repairs API OpenAPI Spec

```json
{
  "openapi": "3.1.0",
  "info": {
    "title": "Repairs API",
    "description": "A simple service to manage repairs for various items",
    "version": "1.0.0"
  },
  "servers": [
    {
      "url": "https://repairshub.azurewebsites.net/"
    }
  ],
  "paths": {
    "/repairs": {
      "get": {
        "operationId": "getRepairs",
        "summary": "Get all repairs",
        "parameters": [
          {
            "name": "assignedTo",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Filter repairs by assignee name"
          }
        ],
        "responses": {
          "200": {
            "description": "List of repairs",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Repair"
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createRepair",
        "summary": "Create a new repair",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateRepairRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Repair created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Repair"
                }
              }
            }
          }
        }
      }
    },
    "/repairs/{id}": {
      "get": {
        "operationId": "getRepairById",
        "summary": "Get a repair by ID",
        "parameters": [
          {
            "name": "id",
            "in": "pa
...<truncated>
````


### `references/authentication.md`

````markdown
# OAuth Authentication for M365 Agent Plugins

This guide explains how to configure OAuth authentication for MCP server plugins and API plugins in your M365 Copilot agent. It covers endpoint discovery, credential acquisition, PKCE, and the `oauth/register` lifecycle step in `m365agents.yml`.

> **When to use this guide:**
> - Your MCP server requires OAuth authentication (most third-party MCP servers do)
> - Your API plugin requires OAuth (not just API key auth)
> - You need to register OAuth credentials in the Teams Developer Portal via ATK

> **When NOT to use this guide:**
> - The MCP server or API is unauthenticated → use `"auth": {"type": "None"}` directly
> - You're using API key authentication → handle via environment variables in the OpenAPI spec

---

## Overview

Authenticated plugins use a three-part setup:

1. **Discover** OAuth endpoints from the server's well-known metadata
2. **Obtain** client credentials (via Dynamic Client Registration or manual entry)
3. **Register** the OAuth configuration in `m365agents.yml` so ATK provisions it in the Teams Developer Portal

The result is a `<PREFIX>_MCP_AUTH_ID` environment variable that the plugin manifest references via `OAuthPluginVault`.

---

## Step 1: OAuth Endpoint Discovery

Attempt to auto-discover OAuth endpoints from the server's well-known metadata. Try **both** URLs in parallel:

```
GET <SERVER_ROOT>/.well-known/oauth-authorization-server
GET <SERVER_ROOT>/.well-known/openid-configuration
```

Where `<SERVER_ROOT>` is the scheme + host of the server URL (e.g., `https://mcp.example.com`).

### Field Mapping

| Plugin field | Well-known field |
|---|---|
| `authorizationUrl` | `authorization_endpoint` |
| `tokenUrl` | `token_endpoint` |
| `refreshUrl` | `token_endpoint` (same endpoint handles refresh grants) |
| `scope` | `scopes_supported` → join with comma (e.g., `"openid,email,profile"`). If no scopes are discovered or provided, default to `"openid"`. **If `scope` has no value, it MUST be quoted as `""`** — a bare `scope:` with no value is YAML null, not an empty string, and will fail schema validation. |

### If discovered

Show the values to the user and confirm:

> "I found the following OAuth endpoints for [name]. Shall I use these?
> - Authorization URL: ...
> - Token URL: ...
> - Refresh URL: ...
> - Scopes: ..."

### If not discovered

Ask the user to provide the four values. If the user doesn't have them, offer:

> "I can search for these values online — shall I proceed?"

Only search if the user confirms. Show results and confirm before using.

---

## Step 2: Client Credentials

### Dynamic Client Registration (DCR)

First, check if `registration_endpoint` is present in the well-known metadata from Step 1.

**If `registration_endpoint` is present → attempt DCR automatically:**

```bash
curl -s -X POST <registration_endpoint> \
  -H "Content-Type: application/json" \
  -d '{
    "client_name": "<display name> M365 Connector",
    "redirect_uris": ["https://teams.microsoft.com/api/platform/v1.0/oAuthRedirect"],
    "grant_types": ["authorization_code", "refresh_token"],
    "response_types": ["code"],
    "token_endpoint_auth_method": "client_secret_basic",
    "scope": "<discovered scopes>"
  }'
```

- If the response contains `client_id` and `client_secret` → use them directly. Tell the user credentials were obtained via dynamic registration. **Do NOT ask the user for credentials.**
- If DCR returns an error or no `client_secret` → fall through to manual entry below.

### Manual Credential Entry

**If `registration_endpoint` is absent OR DCR fails → ask the user:**

> "Please provide your OAuth client credentials for [name]:
> - Client ID:
> - Client Secret:"

### PKCE

After obtaining credentials (whether via DCR or manual entry), ask the user:

> "Would you like to enable PKCE (Proof Key for Code Exchange) for this connector? (yes/no)"

- If the user says yes → `isPKCEEnabled: true`
- If the user says no, or asks you to decide → `isPKCEEnabled: false`

### ⛔ Security Rules

- **NEVER** print, display, or reveal access tokens, bearer tokens, or client secrets in your output
- **NEVER** write secrets to any file — they are passed as OS environment variables at provision time only
- Treat `client_secret` as sensitive — store it only in `.env.*.user` files (which are gitignored)

---

## Step 3: Register in `m365agents.yml` and `m365agents.local.yml`

**⛔ CRITICAL:** You MUST add the `oauth/register` step to BOTH `m365agents.yml` AND `m365agents.local.yml`. Both files need identical `oauth/register` blocks — if you only update one, authentication will fail in that environment.

Add the `oauth/register` step to the `provision` lifecycle in both files, after `teamsApp/create` and before `teamsApp/zipAppPackage`:

```yaml
provision:
  - uses: teamsApp/create
    with:
      name: <app-name>${{APP_NAME_SUFFIX}}
    writeToEnvironmentFile:
      teamsAppId: TEAMS_APP_ID

  - uses: oauth/register
    with:
      name: <slug>-oauth
      appId: ${{TEAMS_APP_ID}}
      clientId: ${{<PREFIX>_MCP_CLIENT_ID}}
      clientSecret: ${{<PREFIX>_MCP_CLIENT_SECRET}}
      authorizationUrl: <authorizationUrl>
      tokenUrl: <tokenUrl>
      refreshUrl: <refreshUrl>
      scope: <comma-separated-scopes or "openid" if none provided>
      # ⚠️ If scope has no value, use `scope: ""` (quoted empty string).
      # A bare `scope:` is YAML null and will fail schema validation.
      flow: authorizationCode
      identityProvider: Custom
      isPKCEEnabled: <true or false>
      tokenExchangeMethodType: PostRequestBody
      baseUrl: <SERVER_URL>
    writeToEnvironmentFile:
      configurationId: <PREFIX>_MCP_AUTH_ID

  - uses: teamsApp/zipAppPackage
    with:
      manifestPath: ./appPackage/manifest.json
      outputZipPath: ./appPackage/build/appPackage.zip
      outputFolder: ./appPackage/build

  - uses: teamsApp/update
    with:
      appPackagePath: ./appPackage/build/appPackage.zip
```

### Naming Conventions

| Value | Derivation | Example |
|---|---|---|
| `<PREFIX>` | Uppercase slug, hyphens/spaces → underscores | `CANVA_V1`, `HUBSPOT` |
| `<slug>` | Display name lowercased, spaces → hyphens | `canva-v1`, `hubspot` |
| `name` in oauth/register | `<slug>-oauth` | `canva-v1-oauth` |
| `<PREFIX>_MCP_CLIENT_ID` | Client ID env var | `CANVA_V1_MCP_CLIENT_ID` |
| `<PREFIX>_MCP_CLIENT_SECRET` | Client secret env var | `CANVA_V1_MCP_CLIENT_SECRET` |
| `<PREFIX>_MCP_AUTH_ID` | Auth config ID (written by provision) | `CANVA_V1_MCP_AUTH_ID` |

### Environment Files

**`env/.env.dev`** (committed, no secrets):
```
TEAMS_APP_ID=
<PREFIX>_MCP_AUTH_ID=
APP_NAME_SUFFIX=-dev
TEAMSFX_ENV=dev
```

**`env/.env.dev.user`** (gitignored, contains secrets):
```
<PREFIX>_MCP_CLIENT_ID=<client_id>
<PREFIX>_MCP_CLIENT_SECRET=<client_secret>
```

> **Important:** Add `<PREFIX>_MCP_AUTH_ID=` to `.env.dev` as soon as you detect the server requires OAuth — before running provision. The `oauth/register` step will populate its value during provisioning.
>
> **⛔ NEVER set a placeholder value** for `<PREFIX>_MCP_AUTH_ID` (e.g., `PLACEHOLDER`, `TODO`, `temp`). Leave it empty (`<PREFIX>_MCP_AUTH_ID=`). The `oauth/register` automation will write the real value during provisioning. If a placeholder is present, it will be treated as the actual value and will NOT be overwritten.

---

## Step 4: Plugin Manifest Auth Block

In the plugin manifest's `runtimes[]` entry, reference the registered OAuth configuration:

### Authenticated (OAuthPluginVault)

```json
{
  "type": "RemoteMCPServer",
  "auth": {
    "type": "OAuthPluginVault",
    "reference_id": "${{<PREFIX>_MCP_AUTH_ID}}"
  },
  "spec": {
    "url": "<SERVER_URL>",
    "mcp_tool_description": {
      "tools": [ ... ]
    }
  },
  "run_for_functions": [ ... ]
}
```

### Unauthenticated (None)

```json
{
  "type": "RemoteMCPServer",
  "auth": {
    "type": "None"
  },
  "spec": {
    "url": "<SERVER_URL>",
    "mcp_tool_description": {
      "tools": [ ... ]
    }
  },
  "run_for_functions": [ ... ]
}
```

---

## Decision Tree

Use this decision tree to determine the authentication flow:

```
MCP server URL provided
│
├── Probe /.well-known/oauth-authorization-server
│   AND /.well-known/openid-configuration
│
├── OAuth metadata found?
│   ├── YES → Step 1 (map endpoints) → Step 2 (DCR or manual creds) → Step 3 (oauth/register) → Step 4 (OAuthPluginVault)
│   └── NO → Use "auth": {"type": "None"} — skip Steps 1-3
│
└── For API plugins: same flow applies — add oauth/register to m365agents.yml if OAuth is needed
```

---

## Common Issues

| Issue | Solution |
|---|---|
| `registration_endpoint` returns 404 | DCR not supported — ask user for credentials manually |
| Token refresh fails | Verify `refreshUrl` matches `token_endpoint` from well-known metadata |
| `<PREFIX>_MCP_AUTH_ID` empty after provision | Check that `oauth/register` step is in `m365agents.yml` and credentials are correct |
| "Invalid redirect URI" during OAuth | Ensure redirect URI is exactly `https://teams.microsoft.com/api/platform/v1.0/oAuthRedirect` |
| PKCE errors | Some providers don't support PKCE — set `isPKCEEnabled: false` |

````


### `references/best-practices.md`

```markdown
# M365 Agent Developer Best Practices

Follow these best practices for successful M365 Copilot agent development.

## Security

- **Principle of Least Privilege:** Always scope capabilities to the minimum necessary resources
- **Credential Management:** Use secure credential storage for production environments
- **Input Validation:** Validate all user inputs and API responses
- **PII Handling:** Follow data protection regulations when handling personal information
- **Audit Logging:** Implement comprehensive audit trails for all agent actions
- **Secret Storage:** Never hardcode credentials; use Azure Key Vault or environment variables

## Performance

- **Scoped Queries:** Use scoped capabilities to reduce query time and improve response quality
- **Efficient API Design:** Design API plugins with pagination and filtering
- **Caching Strategy:** Implement appropriate caching for frequently accessed data
- **Response Time:** Keep operations under 30 seconds to avoid timeouts
- **Batch Operations:** Use batch APIs when processing multiple items

## Error Handling

- **Graceful Degradation:** Handle errors without breaking the conversation flow
- **Clear Error Messages:** Provide actionable error messages to users
- **Retry Logic:** Implement retry mechanisms for transient failures
- **Fallback Behavior:** Define fallback behavior when capabilities are unavailable
- **Error Logging:** Log errors with sufficient context for troubleshooting

## Testing

- **Test All Conversation Starters:** Verify each starter works as intended
- **Test Edge Cases:** Test with missing data, invalid inputs, and error conditions
- **Security Testing:** Verify scoping and permission controls
- **Cross-Environment Testing:** Test in dev, staging, and production environments
- **User Acceptance Testing:** Conduct UAT with actual users before production release

## Compliance

- **Data Residency:** Consider data residency requirements for multi-region deployments
- **Retention Policies:** Follow organizational data retention policies
- **Access Controls:** Implement role-based access controls (RBAC)
- **Compliance Frameworks:** Follow relevant frameworks (GDPR, HIPAA, SOC 2, etc.)
- **Documentation:** Maintain compliance documentation and audit trails

## Maintainability

- **Documentation:** Add `@doc` decorators to all operations, models, and properties
- **Naming Conventions:** Use PascalCase for models/enums, camelCase for properties/actions
- **Code Organization:** Separate concerns (capabilities, API plugins, models)
- **Version Control:** Use semantic versioning for shared agents
- **Change Management:** Document changes and maintain changelog

## Conversation Design

- **Specific Instructions:** Write directive instructions with clear role definition
- **Actionable Starters:** Create 3-5 specific, actionable conversation starters
- **Clear Boundaries:** Define what the agent can and cannot do
- **Appropriate Tone:** Match tone to audience and context
- **Confirmation Patterns:** Require confirmation for destructive or sensitive actions

**Reference:** [conversation-design.md](conversation-design.md)

## Deployment

- **Environment Strategy:** Use separate environments for dev, staging, and production
- **CI/CD Integration:** Automate testing and deployment using ATK CLI
- **Version Management:** Bump versions before re-provisioning shared agents
- **Rollback Plan:** Have a rollback strategy for failed deployments
- **Monitoring:** Implement monitoring and alerting for production agents

**Reference:** [deployment.md](deployment.md)

```


### `references/conversation-design.md`

````markdown
# Conversation and Instruction Design for M365 Agents

> Based on the official Microsoft guidance: [Write effective instructions for declarative agents](https://learn.microsoft.com/en-us/microsoft-365-copilot/extensibility/declarative-agent-instructions)

## Agent Instructions

The `instructions` field in `declarativeAgent.json` references an external `instructions.txt` file. This keeps the JSON manifest clean and makes instructions easy to edit. This is the most critical aspect of agent design.

### Instruction Limits and Token Budget

**Instructions are limited to 8,000 characters.** Every character counts — be concise and deliberate about what goes into `instructions.txt`.

**⛔ Do NOT duplicate tool/capability metadata in instructions.** Tool names, descriptions, parameters, and schemas are already available to the orchestrator through `ai-plugin.json` (`description_for_model`), MCP plugin manifests (`mcp_tool_description.tools[]`), and capability configuration in the agent manifest. Listing them in instructions wastes the 8,000-character budget.

Instructions should contain **decision logic only**: WHEN to use each capability, HOW to chain them, and WHAT to do on failure. See [Instruction Review](instruction-review.md) for the full anti-pattern catalog.

### Instruction Structure

In `declarativeAgent.json`, reference the instructions file using `${{file:instructions.txt}}`:

```json
{
  "instructions": "${{file:instructions.txt}}"
}
```

Then create `appPackage/instructions.txt` with the actual instructions in Markdown format.

---

## Instruction Components

A well-structured set of instructions ensures the agent understands its role, the tasks it should perform, and how to interact with users. The main components are:

**Required:**
- **Purpose** — What goal must the agent accomplish?
- **General guidelines** — Tone, restrictions, and general directions
- **Skills** — What capabilities and actions does the agent have?

**When relevant:**
- Step-by-step instructions
- Error handling and limitations
- Interaction examples
- Nonstandard terms / domain vocabulary
- Follow-up and closing behavior

---

## Best Practices for Instructions

### 1. Use Clear, Actionable Language

- **Focus on what Copilot should do**, not what to avoid.
- **Use precise, specific verbs**: "ask", "search", "send", "check", "use".
- **Supplement with examples** to minimize ambiguity.
- **Define any terms** that are nonstandard or unique to the organization.

❌ **Vague**: "Help users with their questions"
✅ **Specific**: "Answer questions about company policies using documents from the HR SharePoint site. If the answer isn't in the documents, direct users to hr@company.com"

### 2. Build Step-by-Step Workflows with Transitions

Break workflows into modular, unambiguous, and nonconflicting steps. Each step should include:

- **Goal**: The purpose of the step.
- **Action**: What the agent should do and which tools to use.
- **Transition**: Clear criteria for moving to the next step or ending the workflow.

### 3. Use Strict Structure

Structure is one of the strongest signals used to interpret intent:

- Use **sections** to group related tasks into logical categories, without implying sequence.
- Use **bullets** for parallel tasks that can be completed independently. Avoid numbering that might introduce unintended order.
- Use **steps** for actions that must occur in a required sequence, and reserve them only for true workflows.

### 4. Make Tasks Atomic

Break multiaction instructions into clearly separated units.

- Instead of: "Extract metrics and summarize findings."
- Use separate steps:
  1. Extract metrics.
  2. Summarize findings.

### 5. Specify Tone, Verbosity, and Output Format

If you don't specify these, the model might infer them inconsistently. Always specify:

- **Tone**: professional and concise
- **Output**: Three bullet points per section
- **Constraint**: Return only the requested format; no explanations

### 6. Structure Instructions in Markdown

Use [Markdown](https://www.markdownguide.org/basic-syntax) for emphasis and clarity:

- Use `#`, `##`, `###` for section headers
- Use `-` for unordered lists and `1.` for numbered lists
- Highlight tool or system names with backticks (e.g., `Jira`, `ServiceNow`)
- Make critical instructions bold with `**`

### 7. Provide Clear Intent for Each Data Source

Describe the purpose and context for each data source the agent can use. You don't need to use the exact capability names from the manifest — M365 Copilot maps them internally. What matters is clear intent: **when** and **why** each data source should be used.

- **Actions/plugins**: Reference by name — "Use `Jira` to fetch tickets."
- **Copilot connector knowledge**: Reference by connector name — "Use `ServiceNow KB` for help articles."
- **SharePoint/OneDrive**: Describe the data source — "Search internal HR policy documents" or "Reference company documents in SharePoint."
- **Email**: Describe the scenario — "Check user emails for relevant information."
- **Teams messages**: "Search Teams channels and chat messages." (messages only — NOT transcripts)
- **Meetings**: "Check calendar events, attendees, and meeting transcripts." (transcripts come from `Meetings`, not `TeamsMessages`)
- **Code interpreter**: "Use code interpreter to generate charts."
- **People knowledge**: "Look up people in the organization for contact info."

> **Common mistake:** Assuming meeting transcripts are part of `TeamsMessages`. Transcripts are retrieved through the `Meetings` capability. `TeamsMessages` covers channel posts, DMs, and meeting chat messages only. See the [Capability Reference](instruction-review.md#capability-reference-v16) for the full mapping.

### 8. Provide Examples

- For simple scenarios, examples aren't needed.
- For complex scenarios, use **few-shot prompting** — give more than one example to illustrate different aspects or edge cases.

### 9. Control Reasoning Through Phrasing

Your wording signals how much reasoning the model should apply:

**Deep reasoning:**
```md
Use deep reasoning. Break the problem into steps, analyze each step, evaluate alternatives, and justify the final decision. Reflect before answering.
Task: Determine the optimal 3-year migration strategy given constraints A, B, and C.
```

**Moderate reasoning (balanced):**
```md
Provide a concise but structured explanation. Include a short summary, 3 key drivers, and a final recommendation. No step-by-step reasoning required.
Task: Explain the tradeoffs between solution X and Y.
```

**Fast and minimal reasoning:**
```md
Short answer only. No reasoning or explanation. Provide the final result only.
Task: Extract the product name and renewal date from this paragraph.
```

### 10. Add a Self-Evaluation Step

A self-check step reinforces completeness. For example:

> Before finalizing, confirm that all items from Section A appear in the summary.

### 11. Iterate on Your Instructions

Developing instructions is an iterative process:

1. **Create** instructions and conversation starters following this guidance.
2. **Publish** your agent.
3. **Test** your agent:
   - Compare results against Microsoft 365 Copilot.
   - Verify conversation starters work as expected.
   - Verify the agent acts according to instructions.
   - Confirm prompts outside conversation starters are handled appropriately.
4. **Iterate** on instructions to further improve output.

---

## Example Instructions

The following example is for an IT help desk agent:

**`appPackage/instructions.txt`:**
```md
# OBJECTIVE
Guide users through issue resolution by gathering information, checking outages, narrowing down solutions, and creating tickets if needed. Ensure the interaction is focused, friendly, and efficient.

# RESPONSE RULES
- Ask one clarifying question at a time, only when needed.
- Present information as concise bullet points or tables.
- Avoid overwhelming users with details or options.
- Always confirm before moving to the next step or ending.
- Use tools only if data is sufficient; otherwise, ask for missing info.

# WORKFLOW

## Step 1: Gather Basic Details
- **Goal:** Identify the user's issue.
- **Action:**
  - Proceed if the description is clear.
  - If unclear, ask a single, focused clarifying question.
    - Example:
      User: "Issue accessing a portal."
      Assistant: "Which portal?"
- **Transition:** Once clear, proceed to Step 2.

## Step 2: Check for Ongoing Outages
- **Goal:** Rule out known outages.
- **Action:**
  - Query `ServiceNow` for current outages.
  - If an outage is found:
    - Share details and ETA.
    - Ask: "Is your issue unrelated? If yes, I can help further."
    - If yes, go to Step 3. If no/no response, end politely.
  - If none, inform the user and go to Step 3.

## Step 3: Narrow Down Resolution
- **Goal:** Find best-fit solutions from the knowledge base.
- **Action:**
  - Search `ServiceNow KB` for related articles.
  - **Iterative narrowing:** Don't list all results. Instead:
    - Ask clarifying questions based on article differences.
    - Eliminate irrelevant options with user responses.
    - Repeat until the best solution is found.
  - Provide step-by-step fix instructions.
  - Confirm: "Did this help? If not, I can go deeper or create a ticket."

## Step 4: Create Support Ticket
- **Goal:** Log unresolved issues.
- **Action:**
  1. Map category and subcategory from the `sys_choice` SharePoint file.
  2. Fetch user's UPN (email) with the people capability.
  3. Fill the ticket with: Caller ID, Category, Subcategory, Description, attempted steps, error codes.
- **Transition:** Confirm ticket creation and next steps.

# OUTPUT FORMATTING RULES
- Use bullets for actions, lists, next steps.
- Use tables for structured data where UI allows.
- Avoid long paragraphs; keep responses skimmable.
- Always confirm before ending or submitting tickets.
```

---

## Instruction Design Patterns

### Pattern 1: Deterministic Workflows

Remove ambiguity by defining atomic steps, explicit formulas, and required validation:

```md
## Task: Metrics and ROI (Deterministic)

### Definitions (Do not invent)
- Metrics to compute: [Metric1], [Metric2], [Metric3]
- ROI definition: ROI = (Benefit - Cost) / Cost
- Source of truth: Use ONLY the provided document(s) for inputs

### Steps (Sequential — do not reorder)
Step 1: Locate inputs for [Metric1-3] in the document. Quote the source.
Step 2: Compute [Metric1-3] exactly as defined. If any input is missing, stop and ask.
Step 3: Compute ROI using the definition above. Do not substitute other formulas.
Step 4: Output ONLY the table in the format below.

### Final check (Self-evaluation)
Before finalizing: confirm every metric has (a) a value, (b) a source, and (c) no assumptions.
```

### Pattern 2: Parallel vs Sequential Structure

Make sure the model separates parallel and sequential logic:

```md
## Section A — Extract Data
- Extract pricing changes.
- Extract margin changes.
- Extract sentiment themes.

## Section B — Build the Summary (Sequential)
**Step 1:** Integrate findings from Section A.
**Step 2:** Produce the 2-page call prep summary.
```

### Pattern 3: Explicit Decision Rules

Add if/then rules to prevent unintended model interpretation:

```md
Read the product report.
Check category performance.
If performance is stable or improving, write the summary section.
If performance declines or anomalies are detected, write the risks/issues section.
```

### Pattern 4: Output Contract

Define shape, structure, tone, and allowed content:

```md
## Output Contract (Mandatory)
Goal: [one sentence]
Format: [bullet list | table | 2 pages | JSON]
Detail level: [short | medium | detailed] — do not exceed [X] bullets per section
Tone: [Professional | Friendly | Efficient]
Include: [A, B, C]
Exclude: No extra recommendations, no extra context, no "helpful tips"
```

### Pattern 5: Self-Evaluation Gate

Add an explicit self-check step before responding:

```md
## Final Check: Self-Evaluation
Before finalizing the output, review your response for completeness, ensure that all Section A elements are accurately represented, check for inconsistencies, and revise if needed.
```

### Pattern 6: Literal-Execution Header

Use when an agent shows inference drift or step reordering, especially after a model update:

```md
Always interpret instructions literally.
Never infer intent or fill in missing steps.
Never add context, recommendations, or assumptions.
Follow step order exactly with no optimization.
Respond concisely and only in the requested format.
Do not call tools unless a step explicitly instructs you to do so.
```

---

## Conversation Starters

Conversation starters are pre-written prompts that users can click to begin conversations. They showcase the agent's capabilities and guide users toward productive interactions. Defined in the `conversation_starters` array in `declarativeAgent.json`.

### Best Practices

#### 1. Showcase Different Capabilities

```json
{
  "conversation_starters": [
    {
      "title": "Employee Handbook",
      "text": "What are the latest updates to the employee handbook?"
    },
    {
      "title": "Leadership Emails",
      "text": "Summarize emails from the leadership team this week"
    },
    {
      "title": "My Tickets",
      "text": "Show me open support tickets assigned to me"
    },
    {
      "title": "Satisfaction Trends",
      "text": "Analyze customer satisfaction trends from last month"
    }
  ]
}
```

#### 2. Make Them Specific and Actionable
❌ **Too vague**: "Help me with something"
❌ **Too broad**: "Tell me about the project"
✅ **Specific**: "What are the Q4 deliverables for the Phoenix project?"
✅ **Actionable**: "Create a support ticket for a printer issue"

#### 3. Cover Common Use Cases

Identify the top 3-5 tasks users will perform:

```json
{
  "conversation_starters": [
    { "title": "Vacation Days", "text": "How many vacation days do I have left?" },
    { "title": "Remote Work", "text": "What is the remote work policy?" },
    { "title": "Expense Reports", "text": "How do I submit an expense report?" },
    { "title": "Benefits Contact", "text": "Who do I contact about benefits questions?" }
  ]
}
```

#### 4. Use Natural Language

Write starters as users would naturally speak:

✅ Good: "What's the latest on Project Phoenix?"
❌ Bad: "Query project status for Phoenix"

#### 5. Provide 3-6 Starters (Not Too Many)
- **Too few** (<3): Users don't see the full capability range
- **Just right** (3-6): Good variety without overwhelming
- **Too many** (>6): Clutters UI, users won't read them all

---

## Avoiding Common Prompt Failures

> **Reviewing existing instructions?** If you are auditing or improving instructions that already exist (rather than writing from scratch), use the [Instruction Review](instruction-review.md) guide instead. It provides a diagnostic checklist, named anti-patterns, and before/after rewrite examples.

| Problem | Solution |
|---------|----------|
| **Overeager tool use** — model calls tools without needed inputs | Add: "Only call the tool if necessary inputs are available; otherwise, ask the user." |
| **Repetitive phrasing** — model reuses example phrasing verbatim | Use few-shot prompting with varied examples. |
| **Verbose explanations** — model overexplains or provides excessive formatting | Add verbosity constraints and concise examples. |
| **Too vague** — instructions lack specific guidance | Add concrete examples for each capability. |
| **Too restrictive** — over-constrained agents refuse reasonable requests | Relax constraints; focus on what to do, not what to avoid. |
| **Missing error handling** — no guidance for when things go wrong | Add explicit error handling sections. |
| **Scope creep** — instructions cover too many unrelated domains | Focus on one domain; redirect out-of-scope requests. |

````


### `references/deployment.md`

````markdown
# ATK CLI and Deployment for M365 Agents

## ATK CLI Overview

The Agents Toolkit (ATK) CLI is the official toolchain for M365 agent project management. It handles the complete agent lifecycle from creation to deployment.

**Golden Rule:**
Check if ATK CLI is available (`npx -y --package @microsoft/m365agentstoolkit-cli atk --version`). If not found, **STOP and tell the user** that the ATK CLI is required but not installed. Do NOT attempt to install it yourself.
Then use `npx -y --package @microsoft/m365agentstoolkit-cli atk` for all commands.

🚨 **Never** use shortcuts, .vscode tasks, or abbreviated commands.

## Agent Lifecycle

### 1. Project Creation
```bash
# Create new agent project
npx -y --package @microsoft/m365agentstoolkit-cli atk new \
  -n my-agent \
  -c declarative-agent \
  -with-plugin type-spec \
  -i false

# Navigate into project
cd my-agent
```

**Project structure created:**
```
my-agent/
├── appPackage/
│   ├── manifest.json                   # Teams app manifest
│   ├── declarativeAgent.json           # Declarative agent definition
│   ├── instructions.txt                # Agent instructions
│   └── adaptiveCards/
│       └── card.json                   # Adaptive card template (from template)
├── assets/                             # Asset files directory
├── env/
│   ├── .env.local                      # Local environment (template)
│   └── .env.local.user                 # Local environment (secrets, generated)
├── package.json                        # Node.js dependencies
├── m365agents.yml                      # M365 agents config
├── m365agents.local.yml                # M365 agents local config
└── README.md   
```

### 2. Provisioning
Provisioning generates M365 Title ID on first time and makes the updated agent available to the developer on Microsoft 365 Copilot.

```bash
# Provision for development
npx -y --package @microsoft/m365agentstoolkit-cli atk provision --env dev --interactive false

# Provision for staging
npx -y --package @microsoft/m365agentstoolkit-cli atk provision --env staging --interactive false

# Provision for production
npx -y --package @microsoft/m365agentstoolkit-cli atk provision --env prod --interactive false

# Provision for a custom environment
npx -y --package @microsoft/m365agentstoolkit-cli atk provision --env custom --interactive false
```

**What provisioning does:**
- Registers agent in Microsoft 365 Copilot
- Generates `M365_TITLE_ID` and adds to env file
- Sets up authentication and permissions
- Registers OAuth credentials (if `oauth/register` is in the lifecycle — see [authentication.md](authentication.md))

> **⚠️ `M365_TITLE_ID` requires `teamsApp/extendToM365`:** The `M365_TITLE_ID` environment variable is generated by the `teamsApp/extendToM365` lifecycle step during provisioning. Without this step, the Teams app registers but the agent will **not** appear in Copilot Chat. If you scaffolded the project with `npx -y --package @microsoft/m365agentstoolkit-cli atk new`, this step is included automatically. If you set up the project manually, verify that your `m365agents.yml` includes the `teamsApp/extendToM365` lifecycle action — see the [mcp-plugin.md scaffold section](mcp-plugin.md#scaffold-the-agent-project-first) for the required lifecycle steps.

**⚠️ CRITICAL: ALWAYS RENDER THIS AFTER ANY PROVISION OPERATION ⚠️**

After EVERY provisioning command (regardless of environment or whether it's first-time or re-provisioning), you MUST output a test link:

**Local environment** — read `M365_TITLE_ID` from `env/.env.local` and construct the URL:
```
✅ Provision completed successfully!

🚀 Test Your Agent:
🔗 https://m365.cloud.microsoft/chat/?titleId={M365_TITLE_ID}
```

**Non-local environments (dev, staging, prod, etc.)** — use the `SHARE_LINK` value from `env/.env.{environment}`:
```
✅ Provision completed successfully!

🚀 Test Your Agent:
🔗 {SHARE_LINK}
```

**This is REQUIRED for:**
- ✅ First-time provisioning
- ✅ Re-provisioning after changes
- ✅ Any environment (local, dev, staging, prod, custom)
- ✅ Every single `npx -y --package @microsoft/m365agentstoolkit-cli atk provision` command — **ALWAYS** use `--interactive false`

**Do NOT skip this output. The user needs this link to test their agent.**

### 4. Packaging (Optional)
Package agent for distribution or publishing.

```bash
# Package for development
npx -y --package @microsoft/m365agentstoolkit-cli atk package --env dev

# Package for production
npx -y --package @microsoft/m365agentstoolkit-cli atk package --env prod
```

**What packaging does:**
- Creates `.zip` file in `appPackage/build/`
- Validates manifest and package structure
- Prepares for sharing or publishing

### 5. Sharing (Shared Agents Only)
Share agents with users or entire tenant.

**IMPORTANT:** Only for agents with `AGENT_SCOPE=shared`

**Check before sharing:**
```bash
grep "AGENT_SCOPE=shared" env/.env.dev
```

If the developers isn't clear on the sharing scope, ask follow-up questions to clarify.
- Do you want to share the agent with the entire tenant or specific users / groups?
- What is the environment you want to share in (dev, staging, prod)?
- If they schoose specific users or groups, what are the email addresses of those users or groups?

**Share with entire tenant:**
```bash
npx -y --package @microsoft/m365agentstoolkit-cli atk share \
  --scope tenant \
  --env dev \
  -i false
```

**Share with specific users:**
```bash
npx -y --package @microsoft/m365agentstoolkit-cli atk share \
  --scope users \
  --email 'user1@contoso.com,user2@contoso.com' \
  --env dev \
  -i false
```

### 6. Publishing (Optional)
Publish to Microsoft 365 App Store or organizational catalog.

```bash
# Publish to catalog
npx -y --package @microsoft/m365agentstoolkit-cli atk publish --env prod
```

**What publishing does:**
- Submits agent to Microsoft 365 catalog
- Requires admin approval in tenant
- Makes agent discoverable to users

## Environment Management

### Agent Scope
Two deployment models:

**Personal Agents (`AGENT_SCOPE=personal`):**
- Each user gets their own instance
- Agent accesses user's personal data
- No sharing required
- Use for: Personal productivity agents

**Shared Agents (`AGENT_SCOPE=shared`):**
- Single instance shared by multiple users
- Requires explicit sharing via `npx -y --package @microsoft/m365agentstoolkit-cli atk share`
- Use for: Team agents, organizational assistants

### Environment Files Structure
```
env/
├── .env.local      # Local development (not committed)
├── .env.dev        # Development environment
├── .env.staging    # Staging environment
└── .env.prod       # Production environment
```

If there are any secrets or sensitive values, the content will be in a separate file named `.env.{environment}.user` that is not committed to source control.

```
env/
├── .env.local.user      # Local development (not committed)
├── .env.dev.user        # Development environment
├── .env.staging.user    # Staging environment
└── .env.prod.user       # Production environment
```

**Common variables in .env files:**
```bash
# Agent identification
APP_NAME_SHORT=MyAgent
M365_TITLE_ID=U_abc123xyz           # Generated during provision

# Agent configuration
AGENT_SCOPE=shared                  # or 'personal'

# API configuration (if using API plugins)
API_ENDPOINT=https://api.example.com

# Azure resources (generated during provision)
AZURE_RESOURCE_GROUP=rg-myagent-dev
AZURE_APP_SERVICE=app-myagent-dev
```

**Common variables in .env.{environment}.user files:**
```bash
# API configuration (if using API plugins)
API_KEY=your-api-key
```

**Security best practices:**
- Add `env/.env.local` to `.gitignore`
- Never commit secrets to source control
- Use different credentials per environment

## Version Management

### When to Bump Version
Version must be bumped before re-provisioning a shared agent that already has M365_TITLE_ID.

**Check if version bump is required:**
```bash
grep -q "AGENT_SCOPE=shared" env/.env.dev && \
grep -q "M365_TITLE_ID=" env/.env.dev && \
echo "⚠️ VERSION BUMP REQUIRED"
```

If there is no environment variable to handle the APP_VERSION, create one and assign the current value of the version in the manifest.

### How to Bump Version
Edit `appPackage/manifest.json`:
```json
{
  "version": "${{APP_VERSION}}",  // Update this field
  // ... rest of manifest
}
```

Edit `env/.env.{environment}`:
```bash
APP_VERSION=1.0.1  # Bump patch, minor, or major as needed
# ... rest of env variables
```

### Semantic Versioning
Follow semver (major.minor.patch):
- **Patch (1.0.0 → 1.0.1)**: Bug fixes, content updates, minor changes
- **Minor (1.0.0 → 1.1.0)**: New features, capabilities, backward compatible
- **Major (1.0.0 → 2.0.0)**: Breaking changes, incompatible updates

## Complete Workflows

### Initial Deployment Workflow
```bash
# 1. Validate
# Run the validation to ensure everything is correct

# 2. Provision (first time only)
# Run the provsion to register agent in M365 and make it available

# 3. Share (only if AGENT_SCOPE=shared)
# Share with users or tenant as needed

# 4. Test agent
# Open link provided in deploy output
```

### Update Workflows

**For code changes or manifest changes:**
```bash
# 1. Validate
# Run validation to ensure changes are correct

# 2. Provision
# Run provision to update agent in M365
```

**For shared agent re-provisioning:**
```bash
# 1. Bump APP_VERSION in env/.env.{environment}
# Edit version: "1.0.0" → "1.0.1"

# 2. Validate
# Run validation to ensure everything is correct

# 3. Re-provision
# Run provision to update agent in M365

# 4. Share with users (if not already shared)
# Run share command if needed
```

### Multi-Environment Deployment
```bash
# 1. Deploy to dev
# Run provision for dev environment
# Test in dev environment

# 2. Deploy to staging
# Run provision for staging environment
# Validate in staging

# 3. Deploy to production
# Run provision for production environment
# Share with tenant if needed
```

## Authentication

### Microsoft 365 Authentication
Required for sharing and publishing agents.

```bash
# Login to M365
npx -y --package @microsoft/m365agentstoolkit-cli atk auth login m365

# List current authentication
npx -y --package @microsoft/m365agentstoolkit-cli atk auth list

# Logout
npx -y --package @microsoft/m365agentstoolkit-cli atk auth logout m365
```

**Required permissions:**
- Need to have a M365 Copilot license

## Testing Agents

### Testing Deployed Agents
After deployment, ATK provides a test link:
```
🚀 Test Your Agent:
🔗 https://m365.cloud.microsoft/chat/?titleId=abc123xyz
```

**Testing checklist:**
- ✅ Agent appears in Copilot
- ✅ Conversation starters display correctly
- ✅ Capabilities work (search, API calls, etc.)
- ✅ Instructions are followed
- ✅ Error scenarios handled gracefully
- ✅ Permissions are appropriate

## Troubleshooting

### Check System Prerequisites
```bash
npx -y --package @microsoft/m365agentstoolkit-cli atk doctor
```

**Checks:**
- Node.js version
- npm version
- Azure CLI installation
- Authentication status
- Network connectivity

### Common Issues

**"Command not found" or slow first run:**
- ATK CLI downloads on first use (10-30 seconds)
- Wait for download to complete
- Ensure internet connectivity

**"Authentication required":**
```bash
# Check auth status
npx -y --package @microsoft/m365agentstoolkit-cli atk auth list

# Login to M365
npx -y --package @microsoft/m365agentstoolkit-cli atk auth login m365
```

**"Environment not provisioned":**
- Check `env/.env.{environment}` exists
- Check `M365_TITLE_ID` is present in env file
- Run provision for the environment

**"Permission denied":**
- Verify Azure Contributor/Owner role
- Verify M365 admin permissions
- Check Azure subscription is active

**"Version conflict" (shared agents):**
- Bump APP_VERSION in env/.env.{environment}
- Re-run provision after version bump

**"Validation failed":**
- Verify all required manifest fields
- Ensure icons exist in `appPackage/`

## Best Practices

### Environment Strategy
- **Local (.env.local)**: Developer personal testing
- **Dev (.env.dev)**: Shared development environment
- **Staging (.env.staging)**: Pre-production validation
- **Prod (.env.prod)**: Production deployment

### Version Control
- Commit environment templates (without secrets)
- Don't commit `.env.user.local` or files with secrets
- Use `.gitignore` for sensitive files
- Document required environment variables

### Deployment Strategy
1. Develop and test locally
2. Deploy to dev environment
3. Validate in staging
4. Deploy to production
5. Monitor and iterate

### Sharing Strategy
- Start with user-scoped sharing for testing
- Expand to tenant-wide after validation
- Document who has access
- Review sharing permissions regularly
````


### `references/editing-workflow.md`

````markdown
# JSON Development Workflow

This document provides step-by-step instructions for developing M365 Copilot agents using JSON manifest files.

## Prerequisites

- Project must be scaffolded first (use scaffolding workflow if needed)
- Project contains `m365agents.yml` at the root
- Project uses JSON manifest files (`.json`)

---

## ✅ APP NAME & DESCRIPTION REQUIREMENT ✅

When developing an agent, you MUST ALWAYS update the app name and description in `manifest.json` to something **meaningful and descriptive** that reflects the agent's purpose. Never leave default/placeholder names like "My Agent" or generic descriptions.

**NEVER use "(local)" suffix in app names.** Always remove any "(local)" suffix from the app name.

---

## 🚨 CRITICAL DEPLOYMENT RULE 🚨

When making ANY edits to an agent — including instructions, conversation starters, capabilities, plugins, or any file in `appPackage/` — you MUST ALWAYS deploy using `npx -y --package @microsoft/m365agentstoolkit-cli atk provision --env local --interactive false` before returning to the user. This applies to EVERY turn, not just the final turn. Never return to the user with undeployed changes.

**You must NEVER:**
- Skip deploy because "it's just instructions" — deploy after every change
- Tell the user to "run `npx -y --package @microsoft/m365agentstoolkit-cli atk provision` yourself" — YOU must run it
- Deploy when validation found errors — not even "to test" or "to demonstrate"
- Deploy "to show the user what happens" when there are errors — just report the errors
- Run `npx -y --package @microsoft/m365agentstoolkit-cli atk provision` "for educational purposes" to demonstrate failure — errors = STOP, not a teaching moment

**Only exception:** The user explicitly asks you NOT to deploy. Only the user can opt out, never you.

---

---

## 💬 CONVERSATION STARTERS REQUIREMENT 💬

Every agent needs meaningful conversation starters that help users understand what the agent can do. Review the agent's capabilities and add/update conversation starters that showcase the agent's primary functions. Never leave an agent without conversation starters.

---

## 📝 ALWAYS UPDATE INSTRUCTIONS & STARTERS AFTER CHANGES — MANDATORY 📝

**This is NOT optional.** Adding a capability without updating instructions is incomplete work.

When you add, remove, or modify ANY capability or plugin, you MUST complete ALL of these steps before deploying:

1. **Update `instructions`** — Add a section describing what the new capability/plugin enables. For removals, delete all references to the removed capability.
2. **Add conversation starters** — Add at least 1 new conversation starter per added capability or plugin. Each starter should demonstrate the new functionality.
3. **Remove stale starters** — Delete conversation starters that reference removed capabilities.
4. **Update description** in `manifest.json` if the agent's purpose has expanded.
5. **Review existing instructions** for stale references to removed capabilities.

**This applies to EVERY edit operation:** adding capabilities, removing capabilities, adding API plugins, adding MCP servers, modifying scoping, or any other manifest change.

---

## Instructions

### Step 1: Understand the Requirements

**Action:** Gather and analyze the agent requirements:
- Identify the agent's primary purpose and target users
- Determine required data sources (M365 services, external APIs)
- List necessary actions the agent must perform
- Identify security and compliance requirements

### Step 2: Design the Agent Architecture

**Action:** Create a comprehensive architectural design:
- Select deployment model (personal or shared)
- Choose appropriate M365 capabilities with scoping
- Design API plugin integrations if needed
- Plan authentication and authorization strategy
- Design conversation flow and instructions

### Step 3: Edit JSON Manifest Files

**⚠️ PRE-EDIT CHECK — Before making ANY edits, do ALL of these:**

1. **Check for malformed JSON**: Read `declarativeAgent.json` and verify it parses correctly. If it has syntax errors (missing commas, unclosed brackets, trailing commas, etc.):
   - **STOP** — do NOT proceed with your edit
   - **INFORM** the user: list every syntax issue with line numbers
   - **ASK** the user if you should fix the syntax errors first
   - Only after the user confirms, fix with surgical edits, then re-read the file
   - Then continue with the user's original request as a separate step

2. **Check the schema version**: Read the `"version"` field in `declarativeAgent.json` (e.g., `"v1.4"`, `"v1.6"`). For EVERY feature you plan to add, verify it exists in that version using the [feature matrix](schema.md). If a requested feature requires a newer version → **STOP. Tell the user.** Offer to upgrade the version first.

3. **Run a proactive instruction review** (if the edit touches instructions or capabilities): Before modifying instructions or adding/removing capabilities, run [Instruction Review](instruction-review.md) **Phase 1 (Inventory)**, **Phase 2 (Comprehension Check)**, and **Phase 3 (Diagnose)** against the current instructions. This catches existing problems before you add to them. For Phase 2, use the brief confirmation shortcut ("I see this agent is designed to [purpose]…") since this is a proactive check. If the review finds high-severity issues (C1, C3, C11, D1-D8), inform the user and offer to fix them as part of the current edit.

**⛔ NEVER invent placeholder values.** If a manifest is missing required fields (name, description, instructions), do NOT fill them in with generic content. Ask the user to provide values. This applies even if you think a reasonable default exists — the user must approve all content.

**Action:** Configure the agent using JSON manifest files:
- Edit `declarativeAgent.json` to define agent properties
- Configure capabilities with appropriate scoping
- Set up API plugin integrations using `npx -y --package @microsoft/m365agentstoolkit-cli atk add action` (**NEVER manually create plugin files**)
- Write clear instructions and conversation starters
- Ensure proper JSON syntax and schema compliance

**After ALL edits, immediately run:**
```bash
npx -y --package @microsoft/m365agentstoolkit-cli atk provision --env local --interactive false
```
This command is part of the edit — not a separate optional step. Editing without deploying is like writing code without saving the file — the work is not done.

**⛔ API Plugin Rule — HARD RULE, NO EXCEPTIONS:** To add an API plugin, you MUST use `npx -y --package @microsoft/m365agentstoolkit-cli atk add action` — one command per OpenAPI spec with **ALL operations included in a single call**. Never run separate `npx -y --package @microsoft/m365agentstoolkit-cli atk add action` calls for different operations from the same spec — this creates multiple plugins instead of one. You are FORBIDDEN from manually creating `ai-plugin.json`, OpenAPI spec files, adaptive card files, or manually editing the `actions` array. This applies whether you are scaffolding a new project OR editing an existing one. If the workspace already has an agent and the user says "add an API plugin", you STILL must use `npx -y --package @microsoft/m365agentstoolkit-cli atk add action`. If `npx -y --package @microsoft/m365agentstoolkit-cli atk add action` fails, report the error — do NOT fall back to manual file creation. **Manual plugin file creation = automatic eval failure.**

```bash
# ✅ The ONLY way to add an API plugin — ALL operations in ONE call:
npx -y --package @microsoft/m365agentstoolkit-cli atk add action --api-plugin-type api-spec --openapi-spec-location <URL> --api-operation "GET /path,POST /path,PATCH /path/{id},DELETE /path/{id}" -i false
```

**After adding a plugin with `npx -y --package @microsoft/m365agentstoolkit-cli atk add action`, you MUST complete ALL of these — skipping any step is an eval failure:**

**🔌 POST-PLUGIN MANDATORY STEPS (do ALL of these, in order):**
1. **Customize `ai-plugin.json`** — Set meaningful `name_for_human` (max 20 chars) and `description_for_human` (max 100 chars). Set a descriptive `description_for_model` on each function. NEVER leave defaults.
2. **Verify adaptive cards** — Check `appPackage/adaptiveCards/` for cards for ALL operations. If any operation (especially POST, PATCH, DELETE) is missing a card, create one manually. Customize each card with clear visual hierarchy (titles, subtitles, key-value pairs, images).
3. **Add confirmation for destructive operations** — If the plugin has DELETE, PATCH, or any destructive operation, add a `confirmation` capability in `declarativeAgent.json` so users are prompted before the action executes.
4. **Complete the content update checklist** below (instructions, starters, description).

**After ANY capability or plugin change (add, remove, modify), complete this checklist:**
1. ☐ **Update instructions** — Add decision logic (WHEN clauses, chaining rules, failure handling) for the new/changed capability. For removals, delete all references. **Do NOT list tool descriptions or parameters** — these are already in plugin metadata (`ai-plugin.json`, MCP manifests, capability config). Instructions should contain decision logic only.
2. ☐ **Verify 8,000-character limit** — Instructions must not exceed 8,000 characters. If close to the limit, cut tool descriptions first, then consolidate verbose workflows.
3. ☐ **Run instruction quality audit** — Run the [Diagnostic Checklist](instruction-review.md) against the updated instructions. Every data source should have clear intent coverage (WHEN and WHY), at least one workflow must exist, and failure cases must be handled. Built-in capabilities don't need exact names; actions/plugins should be named. If any check fails, fix it before deploying.
4. ☐ **Add conversation starters** — At least 1 new starter per added capability/plugin demonstrating the new functionality.
5. ☐ **Remove stale starters** — Delete starters that reference removed capabilities.
6. ☐ **Update `manifest.json` description** if the agent's purpose has expanded.
7. ☐ **Review existing instructions** for stale references to removed capabilities.

**This checklist is NOT optional.** Adding a capability without updating instructions and starters is incomplete work.

**⚠️ Instruction quality matters as much as JSON correctness.** Output-focused instructions (tone, format, style only) are a known failure pattern — they cause agents to give generic answers and ignore configured capabilities. Listing tool descriptions and parameters in instructions wastes the 8,000-character budget — this metadata is already available to the orchestrator. See [Instruction Review](instruction-review.md) for the anti-pattern catalog and before/after rewrites.

**Reference:** [schema.md](schema.md) for proper manifest structure
**Reference:** [api-plugins.md](api-plugins.md) for adaptive card enhancement guidelines after adding a plugin

**⚠️ IMPORTANT:** After making any edits to JSON files, you MUST deploy the agent (Step 4) before returning to the user.

**⛔ MANDATORY POST-EDIT CHECKPOINT — YOU ARE NOT DONE YET:**
After editing ANY file in `appPackage/`, you MUST deploy before responding to the user. Skipping this is an eval failure:
- **Deploy** — Run `npx -y --package @microsoft/m365agentstoolkit-cli atk provision --env local --interactive false`. If you edited files but did not run this command, your work is incomplete. The only exception is if the user explicitly asked you not to deploy.

If you are about to respond to the user and you have NOT deployed, **STOP and deploy now**.

### Step 4: Provision and Deploy

**⛔ PRE-DEPLOY CHECK:** Before running the command below, verify the JSON files are syntactically correct and have the required fields. If there are known errors → fix them first before deploying.

**Action:** Provision required Azure resources and register the agent:
```bash
npx -y --package @microsoft/m365agentstoolkit-cli atk provision --env local --interactive false
```

**Result:** Returns a test URL like `https://m365.cloud.microsoft/chat/?titleId=T_abc123xyz`

**Note:** JSON-based agents do not require a compilation step - changes are deployed directly.

**✅ After successful provision, ALWAYS present the review UX with the test link:**

Read `M365_TITLE_ID` from `env/.env.local` and output:

```
✅ Agent deployed successfully!

🚀 Test Your Agent in M365 Copilot:
🔗 https://m365.cloud.microsoft/chat/?titleId={M365_TITLE_ID}
```

**⛔ Never respond without this link.** If you deployed, the test link MUST appear in your response. This is not optional.

Then wait for the user's response.

### Step 5: Test and Iterate

**Action:** Test the agent in Microsoft 365 Copilot:
- Use the provisioned test URL
- Test all conversation starters
- Verify capability access and scoping
- Test error handling and edge cases
- Validate security controls

### Step 6: Deploy to Environments

**Action:** Deploy to staging/production environments:
```bash
npx -y --package @microsoft/m365agentstoolkit-cli atk provision --env prod --interactive false
```

**Reference:** [deployment.md](deployment.md) for environment management and CI/CD patterns

### Step 7: Package and Share

**Action:** Package and share the agent:
```bash
# Package the agent
npx -y --package @microsoft/m365agentstoolkit-cli atk provision --env dev --interactive false

# Share to tenant (for shared agents)
npx -y --package @microsoft/m365agentstoolkit-cli atk share --scope tenant --env dev
```

---

## Critical Workflow Rules

### Always Deploy After Edits

**RULE:** When making any changes to an agent (JSON manifest files, instructions, capabilities, API plugins), you MUST complete the following workflow before returning to the user:

1. Provision/deploy the agent: `npx -y --package @microsoft/m365agentstoolkit-cli atk provision --env local --interactive false`
2. Read `M365_TITLE_ID` from `env/.env.local`
3. Present the review UX with the test link:
   ```
   ✅ Agent deployed successfully!

   🚀 Test Your Agent in M365 Copilot:
   🔗 https://m365.cloud.microsoft/chat/?titleId={M365_TITLE_ID}
   ```

**⛔ Never respond without this link after deploying.**

### Always Clean Up Unused Files

**RULE:** Every time you work on an agent project, check for and remove unused or obsolete files:

- `TODO.md` or planning files no longer needed
- Old backup files (`.bak`, `.old`, `.orig`)
- Unused JSON files not referenced anywhere
- Stale environment files (`.env.old`, `.env.backup`)
- Empty or placeholder files
- Outdated manifest versions
- Unused API plugin definitions

---

## ⛔ FINAL GATE — Before Responding to the User

**STOP.** Before writing your response to the user, verify ALL of the following:

- [ ] I ran `npx -y --package @microsoft/m365agentstoolkit-cli atk provision --env local --interactive false` and it succeeded
- [ ] I read `M365_TITLE_ID` from `env/.env.local`
- [ ] I presented the review UX with the `🚀 Test Your Agent in M365 Copilot:` link

**If you cannot check ALL boxes, you are NOT done.** Go back and complete the missing steps.

This checklist applies to **EVERY turn** — not just the last turn in a multi-turn conversation. Even if you "only edited instructions," you must deploy before responding.

````


### `references/examples.md`

````markdown
# M365 JSON Agent Developer Examples

This document provides workflow examples for common M365 Copilot JSON-based agent development scenarios.

> **Note:** This guide is for JSON-based agents that use `.json` manifest files directly.

---

## Example 1: Development and Provisioning

Complete workflow for provisioning a JSON-based agent to a development environment:

```bash
# Install dependencies (if any)
npm install

# Provision agent to development environment (no compile step needed for JSON agents)
npx -y --package @microsoft/m365agentstoolkit-cli atk provision --env local --interactive false
```

**Result:** Returns a test URL like `https://m365.cloud.microsoft/chat/?titleId=T_abc123xyz` to test the agent in Microsoft 365 Copilot.

**Use case:** Testing agent functionality in a live environment during development.

---

## Example 2: Provision and Share Agent

Workflow for provisioning and sharing an agent with your organization:

```bash
# Provision agent to target environment
npx -y --package @microsoft/m365agentstoolkit-cli atk provision --env dev --interactive false

# Share agent with tenant users
npx -y --package @microsoft/m365agentstoolkit-cli atk share --scope tenant --env dev
```

**Result:** Agent becomes available to all users in the Microsoft 365 tenant.

**Use case:** Deploying a shared agent for organizational use after testing and validation.

---

## Example 3: Package Agent for Distribution

Workflow for creating an agent package for distribution:

```bash
# Package agent for distribution
npx -y --package @microsoft/m365agentstoolkit-cli atk package --env prod
```

**Result:** Creates a distributable package file that can be uploaded to the Microsoft 365 admin center or shared externally.

**Use case:** Creating a final package for production deployment or external distribution.

---

## Example 4: Basic Declarative Agent JSON

A minimal declarative agent manifest file (`declarativeAgent.json`):

```json
{
  "version": "v1.6",
  "name": "My Support Agent",
  "description": "An agent to help with customer support inquiries",
  "instructions": "You are a helpful customer support agent. Help users find information about their issues and guide them to solutions. Be polite and professional at all times."
}
```

---

## Example 5: Agent with Capabilities

A declarative agent with SharePoint and Email capabilities:

```json
{
  "version": "v1.6",
  "name": "Knowledge Base Agent",
  "description": "An agent that searches company knowledge bases and emails",
  "instructions": "You help employees find information from our SharePoint knowledge base and relevant emails. Always cite your sources when providing information.",
  "capabilities": [
    {
      "name": "OneDriveAndSharePoint",
      "items_by_url": [
        {
          "url": "https://contoso.sharepoint.com/sites/KnowledgeBase/Documents"
        }
      ]
    },
    {
      "name": "Email"
    }
  ]
}
```

---

## Example 6: Agent with Conversation Starters

A declarative agent with helpful conversation starters:

```json
{
  "version": "v1.6",
  "name": "HR Assistant",
  "description": "An agent that helps employees with HR-related questions",
  "instructions": "You are an HR assistant helping employees with common HR questions about policies, benefits, and procedures.",
  "conversation_starters": [
    {
      "title": "Time Off Policy",
      "text": "What is our company's time off policy?"
    },
    {
      "title": "Benefits Overview",
      "text": "Can you explain our health insurance benefits?"
    },
    {
      "title": "Expense Reports",
      "text": "How do I submit an expense report?"
    }
  ]
}
```

---

## Example 7: Agent with API Plugin Action

A declarative agent connected to an external API:

```json
{
  "version": "v1.6",
  "name": "Repairs Agent",
  "description": "An agent that helps manage repair tickets",
  "instructions": "You help users create, find, and track repair tickets. Use the repairs API to search for existing tickets and create new ones when requested.",
  "actions": [
    {
      "id": "repairsPlugin",
      "file": "plugins/repairs-plugin.json"
    }
  ],
  "conversation_starters": [
    {
      "title": "My Repairs",
      "text": "What repairs are assigned to me?"
    },
    {
      "title": "Create Repair",
      "text": "I need to create a new repair ticket"
    }
  ]
}
```

---

## Example 8: API Plugin Manifest

A complete API plugin manifest file (`plugins/repairs-plugin.json`):

```json
{
  "schema_version": "v2.4",
  "name_for_human": "Repairs API",
  "namespace": "repairs",
  "description_for_human": "Search and manage repair tickets",
  "description_for_model": "Use this plugin to search for repair tickets, get details about specific repairs, and create new repair requests.",
  "functions": [
    {
      "name": "searchRepairs",
      "description": "Search for repair tickets by keyword or status"
    },
    {
      "name": "getRepair",
      "description": "Get details about a specific repair ticket",
      "parameters": {
        "type": "object",
        "properties": {
          "repairId": {
            "type": "string",
            "description": "The unique ID of the repair ticket"
          }
        },
        "required": ["repairId"]
      }
    },
    {
      "name": "createRepair",
      "description": "Create a new repair ticket",
      "parameters": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string",
            "description": "Title of the repair"
          },
          "description": {
            "type": "string",
            "description": "Detailed description of the issue"
          }
        },
        "required": ["title", "description"]
      },
      "capabilities": {
        "confirmation": {
          "type": "AdaptiveCard",
          "title": "Create Repair?",
          "body": "Create a new repair ticket?"
        }
      }
    }
  ],
  "runtimes": [
    {
      "type": "OpenApi",
      "auth": {
        "type": "None"
      },
      "spec": {
        "url": "https://api.contoso.com/openapi.yaml"
      }
    }
  ]
}
```

---

## Example 9: Full Agent with All Features

A complete agent combining all features:

```json
{
  "version": "v1.6",
  "id": "customer-support-agent",
  "name": "Customer Support Agent",
  "description": "A comprehensive support agent for customer inquiries",
  "instructions": "You are a professional customer support agent for Contoso.\n\nResponsibilities:\n1. Search the knowledge base for relevant documentation\n2. Look up repair tickets and their status\n3. Create new repair tickets when requested\n4. Search support emails for context\n\nGuidelines:\n- Always be polite and professional\n- Cite sources when providing information\n- Ask clarifying questions when needed\n- Never share confidential information",
  "capabilities": [
    {
      "name": "OneDriveAndSharePoint",
      "items_by_url": [
        {
          "url": "https://contoso.sharepoint.com/sites/Support/Documents"
        }
      ]
    },
    {
      "name": "Email",
      "shared_mailbox": "support@contoso.com"
    },
    {
      "name": "WebSearch",
      "sites": [
        {
          "url": "https://docs.contoso.com"
        }
      ]
    }
  ],
  "actions": [
    {
      "id": "repairsApi",
      "file": "plugins/repairs-plugin.json"
    }
  ],
  "conversation_starters": [
    {
      "title": "Check Repair Status",
      "text": "What is the status of my repairs?"
    },
    {
      "title": "Search Knowledge Base",
      "text": "How do I troubleshoot connection issues?"
    },
    {
      "title": "Create New Ticket",
      "text": "I need to create a new support ticket"
    }
  ],
  "disclaimer": {
    "text": "This agent provides general support assistance. For urgent issues, please contact our support hotline."
  }
}
```

---

## Example 10: Localized Agent

A declarative agent with tokenized strings for multi-language support.

### Tokenized `declarativeAgent.json`

```json
{
  "$schema": "https://developer.microsoft.com/json-schemas/copilot/declarative-agent/v1.6/schema.json",
  "version": "v1.6",
  "name": "[[agent_name]]",
  "description": "[[agent_description]]",
  "instructions": "$[file]('instructions.txt')",
  "conversation_starters": [
    {
      "title": "[[starter_status_title]]",
      "text": "[[starter_status_text]]"
    },
    {
      "title": "[[starter_kb_title]]",
      "text": "[[starter_kb_text]]"
    }
  ],
  "disclaimer": {
    "text": "[[disclaimer_text]]"
  }
}
```

### `manifest.json` with `localizationInfo`

```json
{
  "$schema": "https://developer.microsoft.com/json-schemas/teams/vDevPreview/MicrosoftTeams.schema.json",
  "manifestVersion": "devPreview",
  "localizationInfo": {
    "defaultLanguageTag": "en",
    "defaultLanguageFile": "en.json",
    "additionalLanguages": [
      {
        "languageTag": "fr",
        "file": "fr.json"
      }
    ]
  },
  "name": {
    "short": "Support Agent",
    "full": "Customer Support Agent"
  },
  "description": {
    "short": "Get help with support issues",
    "full": "An agent that helps resolve customer support issues using internal knowledge bases."
  },
  "copilotAgents": {
    "declarativeAgents": [
      {
        "id": "declarativeAgent",
        "file": "declarativeAgent.json"
      }
    ]
  }
}
```

### Default language file (`en.json`)

```json
{
  "$schema": "https://developer.microsoft.com/json-schemas/teams/vDevPreview/MicrosoftTeams.Localization.schema.json",
  "name.short": "Support Agent",
  "name.full": "Customer Support Agent",
  "description.short": "Get help with support issues",
  "description.full": "An agent that helps resolve customer support issues using internal knowledge bases.",
  "localizationKeys": {
    "agent_name": "Customer Support Agent",
    "agent_description": "An agent that helps resolve customer support issues using internal knowledge bases.",
    "starter_status_title": "Check ticket status",
    "starter_status_text": "What is the status of my open tickets?",
    "starter_kb_title": "Search knowledge base",
    "starter_kb_text": "How do I reset my password?",
    "disclaimer_text": "This agent provides general support guidance. For urgent issues, contact the helpdesk."
  }
}
```

### French language file (`fr.json`)

```json
{
  "$schema": "https://developer.microsoft.com/json-schemas/teams/vDevPreview/MicrosoftTeams.Localization.schema.json",
  "name.short": "Agent de support",
  "name.full": "Agent de support client",
  "description.short": "Obtenez de l'aide pour vos problèmes",
  "description.full": "Un agent qui aide à résoudre les problèmes de support client à l'aide des bases de connaissances internes.",
  "localizationKeys": {
    "agent_name": "Agent de support client",
    "agent_description": "Un agent qui aide à résoudre les problèmes de support client à l'aide des bases de connaissances internes.",
    "starter_status_title": "Vérifier le statut du ticket",
    "starter_status_text": "Quel est le statut de mes tickets ouverts ?",
    "starter_kb_title": "Rechercher la base de connaissances",
    "starter_kb_text": "Comment réinitialiser mon mot de passe ?",
    "disclaimer_text": "Cet agent fournit des conseils de support généraux. Pour les problèmes urgents, contactez le service d'assistance."
  }
}
```

**Reference:** [localization.md](localization.md) for the full localization workflow

````


### `references/instruction-review.md`

````markdown
# Instruction Review & Quality Audit

This reference defines how to evaluate, diagnose, and improve existing agent instructions. Use it whenever you touch instructions — whether auditing an existing agent, adding a capability, or responding to a user who says their agent "doesn't work well."

> **When to use this guide:**
> - User asks to "review", "improve", "audit", or "fix" their agent's instructions
> - User reports the agent "doesn't use the right tool", "gives generic answers", or "doesn't follow the process"
> - You are adding a capability or plugin and need to update instructions (mandatory per the editing workflow)
> - You are reviewing an agent before deployment
> - Agent behavior changed after a model update (GPT 5.0 → 5.1 → 5.2)
> - User wants to migrate instructions for a newer model version

> **Official references:** This guide synthesizes and operationalizes the official Microsoft guidance:
> - [Write effective instructions for declarative agents](https://learn.microsoft.com/en-us/microsoft-365/copilot/extensibility/declarative-agent-instructions)
> - [Instructions for agents with API plugins](https://learn.microsoft.com/en-us/microsoft-365/copilot/extensibility/instructions-api-plugins)
> - [Model changes in GPT 5.1+ for declarative agents](https://learn.microsoft.com/en-us/microsoft-365/copilot/extensibility/declarative-model-migration-overview)

---

## GPT 5.2 Model Awareness

As of April 2026, M365 Copilot uses **GPT 5.2**. Understanding the model's behavior is essential for writing and reviewing instructions, because the same instructions can produce very different results across model versions.

### Instruction Token Budget

**Instructions are limited to 8,000 characters.** Every character counts. This hard limit means you must be surgical about what goes into `instructions.txt`:

- **DO include:** Decision logic (WHEN to use which capability), workflows with transitions, failure handling, chaining rules, domain vocabulary, output contracts, self-evaluation gates
- **DO NOT include:** Tool descriptions, function parameter lists, API schemas, or anything already documented in the plugin metadata

> **⛔ CRITICAL: Do NOT duplicate tool/capability metadata in instructions.**
>
> The orchestrator already has access to tool names, descriptions, and parameters through:
> - `ai-plugin.json` → `description_for_model` and function definitions
> - MCP plugin manifest → `mcp_tool_description.tools[]` with full `inputSchema`
> - Capability configuration in `declarativeAgent.json`
> - Meta-prompts injected by the orchestration layer
>
> Listing tools and their parameters in instructions is a **waste of the 8,000-character budget**. Instead, instructions should provide **decision logic** that the metadata cannot express: WHEN to choose one tool over another, HOW to chain tools together, and WHAT to do when a tool returns no results.

### What belongs in instructions vs metadata

| Belongs in `instructions.txt` | Belongs in plugin metadata (NOT instructions) |
|-------------------------------|------------------------------------------------|
| WHEN to use a capability: "Search SharePoint first, fall back to web search" | Tool name and description |
| Chaining logic: "After getting weather, create a task with the result" | Function parameters and types |
| Failure handling: "If no results, ask the user to rephrase" | Input schemas and validation rules |
| Confirmation gates: "Always confirm before deleting" | Response format / adaptive cards |
| Multi-turn rules: "Collect all 3 values before calling the API" | API endpoint details |
| Domain vocabulary and business rules | Tool-level `description_for_model` |
| Output format and reasoning depth | MCP `inputSchema`, `annotations`, `execution` |

### The Model Shift: Literal-First → Intent-First

| Behavior | GPT 5.0 (old) | GPT 5.1+ / 5.2 (current) |
|----------|---------------|---------------------------|
| Interpretation | Literal — follows instructions step-by-step as written | Intent-first — interprets what instructions *intended*, not just what they said |
| Missing steps | Fails or responds narrowly | Fills gaps, infers missing steps, replans its approach |
| Ambiguity | Follows the first plausible path | Dynamically selects reasoning depth; may reorder or merge steps |
| Tone | Direct and factual by default | Adapts tone based on inferred context; supports 8 output profiles |
| Reasoning | Fixed: chat model OR reasoning model | Adaptive: chooses model + reasoning depth per sub-task within a single request |

### What This Means for Instruction Review

GPT 5.2's intent-first behavior **amplifies the impact of instruction quality**:

- **Well-structured instructions** → GPT 5.2 follows them more precisely than GPT 5.0 and can adaptively fill in routine details
- **Ambiguous instructions** → GPT 5.2 will *replan and improvise*, which may produce unexpected behavior: reordered steps, merged tasks, tone drift, added/removed steps based on inferred context
- **Output-only instructions** → GPT 5.2 will infer the entire process, often incorrectly, because the instructions give it maximum freedom to interpret intent

**Bottom line:** The weaker the instructions, the more GPT 5.2 will improvise — and improvisation in structured workflows is a bug, not a feature.

### Fixed vs Adaptive Reasoning — When to Use Each

GPT 5.2 supports two modes, and your instructions should signal which one to use:

**Use strict step-by-step instructions when:**
- The agent must follow a defined business process
- Specific formatting rules or compliance templates are required
- A fixed retrieval/reasoning sequence must be honored
- Destructive operations need confirmation gates

**Use goal-focused instructions with guardrails when:**
- Tools and knowledge sources are well-defined
- The output format is flexible
- The goal matters more than the exact path
- You want the model to adaptively plan and handle edge cases

> **Key insight:** You can mix both in the same instruction set. Use strict process for critical workflows (ticket creation, data modification) and goal-focused for open-ended tasks (information retrieval, summarization).

### Output Style Profiles

GPT 5.2 has 8 built-in output profiles. Instead of writing verbose tone instructions, reference the profile directly:

| Profile | Behavior |
|---------|----------|
| **Default** | Verbose, explanatory, teacher-like |
| **Professional** | Neutral, structured, business-oriented |
| **Friendly** | Conversational, supportive |
| **Candid** | Direct, concise |
| **Quirky** | Expressive, informal |
| **Efficient** | Minimal verbosity, outcome-focused |
| **Nerdy** | Technical, detail-oriented, precise |
| **Cynical** | Skeptical, dry, matter-of-fact |

**Anti-pattern:** Writing 5+ lines about tone ("Be professional but approachable, don't be too formal, use simple language...").
**Fix:** `Tone: Professional` — one line is enough. The model maps this to the built-in profile.

---

## The Core Problem: Output-Focused vs Process-Focused Instructions

Most instruction failures share a single root cause: **the instructions describe what the response should look like, not how the agent should produce it.**

### Output-focused (❌ anti-pattern)

Tells the model the *shape* of the answer — tone, format, length, style — but gives it no strategy for *finding* the answer.

```md
You are a helpful HR assistant. Provide accurate answers about company policies.
Include policy numbers when available. Be concise and professional. Use bullet points.
Format responses with headers when appropriate.
```

**Why this fails:**
- The model has no idea WHERE to look (SharePoint? Email? Web search?)
- It doesn't know WHEN to use which capability
- It will hallucinate policy numbers because you told it to "include" them but didn't tell it where to find them
- Every response will have the same shape regardless of the question

### Process-focused (✅ correct pattern)

Tells the model the *decision logic* — WHEN to use which tool, in what order, and what to do when things fail. Does NOT duplicate tool descriptions or parameters already in the plugin metadata.

```md
# OBJECTIVE
Help employees find answers to HR policy questions using the company's official policy documents.

# DECISION LOGIC
- Policy/process questions → search **HR Policies** (SharePoint) first. This is the primary source.
- Org/people questions → use People knowledge directly.
- Email → search user's email ONLY when the user mentions a specific HR email or announcement.
- If the answer isn't in any source → direct users to hr@company.com. Do not guess.

# WORKFLOW

## Step 1: Classify the question
- **Goal:** Determine if this is a policy lookup, a process question, or an org question.
- **Action:** Read the user's message. Identify the topic (benefits, time off, expenses, etc.).
- **Transition:** If policy → Step 2. If org/people → use People knowledge directly. If unclear → ask one clarifying question.

## Step 2: Search policy documents
- **Goal:** Find the authoritative answer in SharePoint.
- **Action:** Search the HR Policies library for documents matching the topic. Read the relevant sections.
- **Transition:** If found → Step 3. If not found → tell the user: "I couldn't find a policy on [topic]. Contact hr@company.com for help."

## Step 3: Respond with citation
- **Goal:** Give a clear, traceable answer.
- **Action:** Summarize the policy in 2-4 bullets. Include the document name and section. If the policy references a form or process, link to it.
- **Constraint:** Never paraphrase in a way that changes the policy's meaning. If the policy is ambiguous, quote it directly and note the ambiguity.

# RESPONSE RULES
- Cite the source document for every factual claim.
- If you cannot find the answer, say so. Do not guess.
- One clarifying question at a time, only when needed.
```

**Why this works:**
- Every data source has a clear role and intent (WHEN and WHY to use it)
- The model has a decision tree, not just a personality description
- Failure cases are handled ("if not found → tell the user")
- The response rules are minimal and complementary to the process, not a substitute for it

---

## Diagnostic Checklist

Run this checklist against any set of instructions. Each failed check is a specific, fixable problem.

### A. Capability Coverage

| # | Check | How to verify | Failure signal |
|---|-------|---------------|----------------|
| A1 | Every configured capability has clear intent coverage in instructions | For each capability in `capabilities[]`, check whether the instructions describe **when and why** the agent should use the underlying data source — e.g., "search company documents" covers `OneDriveAndSharePoint` even without naming it. The exact capability name is NOT required; what matters is that the instructions give the model a clear reason and context to invoke it. | Capability is configured but instructions provide no context for when to use it → model may underuse it or use it at the wrong time |
| A2 | Every action/plugin has a matching section in instructions | Compare `actions[]` array against instruction text. Unlike built-in capabilities, plugins are custom and SHOULD be referenced by name so the model knows they exist. | Plugin exists but instructions don't reference it → model may never invoke it |
| A3 | Each data source or action has a WHEN clause | Look for conditional intent: "when the user asks about...", "for X questions, search Y", "use [data source] for [scenario]". For built-in capabilities, the WHEN clause can reference the data source by purpose ("search internal docs") rather than by capability name. For plugins, reference functions by name. | Instructions mention a data source but provide no trigger or decision logic for when to use it |
| A4 | Instructions provide decision logic, not tool descriptions | Check that instructions don't duplicate `description_for_model`, parameter lists, or schemas from plugin metadata | Token waste — this information is already available to the orchestrator |
| A5 | Instructions don't assume data sources that aren't configured | Read instruction text for intent that implies a specific capability (see Capability Reference below). Cross-check against `capabilities[]` in the manifest. Focus on **intent mismatch** — e.g., instructions say "check the user's calendar" but no `Meetings` capability is configured. Minor phrasing overlaps (e.g., "look up" could mean many things) should NOT be flagged. | Instructions clearly direct the agent to use a data source that isn't configured → the agent will try and fail, or hallucinate |

### B. Process Structure

| # | Check | How to verify | Failure signal |
|---|-------|---------------|----------------|
| B1 | Instructions contain at least one workflow or decision tree | Look for steps, numbered sequences, or if/then rules | Instructions are a flat list of personality traits — model has no strategy |
| B2 | Workflows have Goal → Action → Transition per step | Each step names what it achieves, what to do, and when to move on | Steps are vague or lack transitions → model gets stuck or skips ahead |
| B3 | Decision points have explicit if/then rules | Ambiguous situations have defined behavior | Model guesses instead of following a prescribed path |
| B4 | Failure cases are handled | "If not found", "if unclear", "if error" have defined responses | Model hallucinates or goes silent when things don't go as expected |

### C. Anti-Pattern Detection

| # | Anti-pattern | What it looks like | Fix |
|---|---|---|---|
| C1 | **Output-only instructions** | 80%+ of text is about tone, format, length, style | Add a CAPABILITIES section and at least one WORKFLOW |
| C2 | **Personality-first instructions** | Opens with "You are a friendly, helpful..." and stays there | Move personality to a short RESPONSE RULES section at the end; lead with OBJECTIVE and CAPABILITIES |
| C3 | **Capability gap** | `declarativeAgent.json` has 3 capabilities and 2 plugins; instructions provide intent coverage for only 1 | Add decision logic for each uncovered data source — describe WHEN and WHY the agent should use it (exact capability names are not required for built-in capabilities) |
| C4 | **Orphaned starters** | Conversation starter references a capability not mentioned in instructions | Either add the capability to instructions or remove the starter |
| C5 | **Tool ambiguity** | Instructions say "search for documents" but the agent has multiple document sources and no guidance on which to prefer | Clarify the intent: "Search the **HR Policies** library first; if not found, try the **Company Wiki**" |
| C6 | **Hallucination invitation** | "Include [specific data] in your response" without specifying where to find it | Add: "Retrieve [data] from [capability]. If not found, do not include it." |
| C7 | **Compound tasks** | "Extract metrics and summarize findings and create a report" | Break into separate atomic steps with transitions |
| C8 | **Over-restriction** | Long list of "do NOT" rules with few "DO" rules | Rewrite as positive directives; keep restrictions to genuine guardrails only |
| C9 | **Missing reasoning calibration** | No indication of how deep the model should think | Add a reasoning header: "Short answer only" or "Break the problem into steps" depending on task complexity |
| C10 | **No self-evaluation** | Instructions end without a verification step | Add: "Before responding, confirm: [checklist]" |
| C11 | **Tool/parameter dumping** | Instructions list tool names with descriptions and parameters already in plugin metadata | Remove tool descriptions and parameters from instructions. Keep only WHEN/chaining/failure logic. Reclaim token budget for decision logic. |

### D. GPT 5.2 Model-Sensitivity Anti-Patterns

These anti-patterns are specifically caused or amplified by the GPT 5.1+/5.2 intent-first behavior. They may not have caused issues on GPT 5.0 but will cause problems now.

| # | Anti-pattern | What it looks like | Fix |
|---|---|---|---|
| D1 | **Fused/ambiguous tasks** | Single instruction with multiple actions: "extract metrics and summarize" | GPT 5.2 may merge steps or infer unintended processes. Split into atomic steps with explicit transitions. |
| D2 | **Incorrect numbering** | Numbered lists used for parallel tasks that have no required order | GPT 5.2 treats numbering as a strict sequence signal. Use bullets (`-`) for parallel tasks; reserve numbering for true sequential workflows. |
| D3 | **Implicit formats** | No explicit tone, structure, or verbosity specified | GPT 5.2 will infer these and may produce inconsistent results. Specify the output profile (`Tone: Professional`) and format explicitly. |
| D4 | **Weak Markdown hierarchy** | Mixed list types, unclear headers, inconsistent nesting | GPT 5.2 uses structure as a control signal. Clean up: `##` for sections, `-` for parallel items, `Step N:` for sequences. |
| D5 | **No validation step** | Instructions end without a self-check gate | GPT 5.2 may choose faster reasoning and return incomplete output. Add: "Before finalizing, confirm: [checklist]" |
| D6 | **Verbose tone instructions** | 5+ lines describing desired tone and style | Replace with a single output profile reference: `Tone: Professional` or `Tone: Efficient`. GPT 5.2 maps these to built-in profiles. |
| D7 | **Vague verbs** | "Verify", "process", "handle", "clean" without specifying observable actions | Replace with precise verbs: "search", "compare", "list", "call [function]", "ask the user for" |
| D8 | **Missing stabilizing header** | Agent that previously worked on GPT 5.0 now shows drift (reordered steps, added reasoning, tone changes) | Add a literal-execution header at the top as an interim fix (see Stabilizing Header section below) |

### E. API Plugin Instruction Anti-Patterns

These apply specifically to agents with API plugins (actions).

| # | Anti-pattern | What it looks like | Fix |
|---|---|---|---|
| E1 | **No function-level WHEN clauses** | Instructions mention the plugin but don't say when to call each function | List every function with a WHEN clause: "`getRepairs` — use when user asks to find or list repairs" |
| E2 | **No chaining instructions** | Agent has multiple plugins or plugin + capability but no guidance on combining them | Add chaining rules: "After calling `getWeather`, use the result to call `createTask` with the temperature in the title" |
| E3 | **No multi-turn collection** | Function requires 3+ parameters but instructions don't say to collect them before calling | Add: "Before calling `createRepair`, collect title, description, and assignee. Ask for missing values." |
| E4 | **Missing confirmation for writes** | POST/PATCH/DELETE functions have no confirmation gate in instructions | Add: "Before calling `deleteRepair`, confirm: 'Are you sure you want to delete repair #[id]?'" |
| E5 | **Negative/contrasting instructions** | "Don't call getWeather for indoor temperatures" instead of defining valid cases | Rewrite as positive: "Call `getWeather` only for outdoor weather queries with a location." |
| E6 | **No cross-capability chaining** | Agent has SharePoint knowledge + API plugin but instructions treat them as isolated | Add chaining: "Search SharePoint for project statuses, then call `createTask` for each project needing follow-up" |

---

## Capability Reference (v1.6)

Use this table to understand what each built-in capability provides and to detect intent mismatches between instructions and the manifest. This is a **guide for understanding intent**, not a keyword checklist.

> **⚠️ IMPORTANT: M365 Copilot uses internal names for built-in capabilities** that differ from the manifest identifiers (e.g., `OneDriveAndSharePoint`). The orchestrator already knows which capabilities are configured — instructions do NOT need to use the exact cap
...<truncated>
````


### `references/localization.md`

````markdown
# Localization Workflow

## Quick Reference — Complete Localization Checklist

**For adding localization to an agent (Workflow A):**

1. ⛔ Tokenize `declarativeAgent.json` → replace `name`, `description`, all `conversation_starters[].title` and `.text` with `[[token]]` syntax
2. ⛔ Create language files → `en.json` (default) + one per additional language, each with `name.short`, `name.full`, `description.short`, `description.full`, and `localizationKeys` mapping EVERY token
3. ⛔ Update `manifest.json` → add `localizationInfo` with `defaultLanguageTag`, `defaultLanguageFile`, `additionalLanguages`
4. ⛔ Deploy → `npx -y --package @microsoft/m365agentstoolkit-cli atk provision --env local --interactive false`

**For adding a language to an already-localized agent (Workflow B):**

1. Read existing default language file to get the list of `localizationKeys`
2. Create new `{lang}.json` with the SAME set of keys, translated values
3. Add new entry to `additionalLanguages` in `manifest.json`
4. ⛔ Deploy

---

This document provides step-by-step instructions for localizing an M365 Copilot declarative agent into multiple languages.

Localization spans two layers: the **app manifest** (`manifest.json`) and the **declarative agent manifest** (`declarativeAgent.json`). Both use the same set of language files, but reference strings differently.

> **Important:** If an agent supports more than one language, you must provide a separate language file for **every** supported language, including the default language. Single-language agents do not require language files.

---

## ⛔ STOP — READ THIS FIRST

### Two Localization Scenarios

| Scenario | What to do |
|----------|------------|
| **Agent has NO localization yet** (no `localizationInfo` in `manifest.json`, no `[[tokens]]` in `declarativeAgent.json`) | Follow **Workflow A: Add Localization to an Agent** — you must tokenize, externalize instructions, create ALL language files, and update `manifest.json` |
| **Agent is ALREADY localized** (`localizationInfo` exists, manifests use `[[tokens]]`) | Follow **Workflow B: Add a Language to an Already-Localized Agent** — you only create a new language file and update `localizationInfo` |

**⛔ MANDATORY:** You MUST check which scenario applies BEFORE making any changes. Read `manifest.json` to see if `localizationInfo` exists, and read `declarativeAgent.json` to see if it already uses `[[token]]` syntax.

### ⛔ Anti-Patterns — NEVER Do These

| ❌ Anti-Pattern | Why It Fails | ✅ Correct Approach |
|----------------|-------------|---------------------|
| Replacing strings directly with translated text in `declarativeAgent.json` | Hardcoded translations don't support multi-language switching. Only one language works at a time. | Use `[[token]]` syntax and create language files with `localizationKeys`. |
| Leaving instructions inline in `declarativeAgent.json` when localizing | Instructions must be separated from localizable content. Inline instructions block proper tokenization. | Create `appPackage/instructions.txt` and set `"instructions": "$[file]('instructions.txt')"`. |
| Creating language files without tokenizing `declarativeAgent.json` first | Language file `localizationKeys` are only resolved when the manifest uses `[[token]]` syntax. Without tokens, the language files have no effect. | Always tokenize the manifest BEFORE creating language files. |
| Using `[[token]]` for the instructions field | Instructions are NOT localizable. The LLM consumes them in a single language. | Use `$[file]('instructions.txt')` for instructions. Never tokenize them. |
| Setting `defaultLanguageTag` to a non-English language without an `en.json` fallback | The default language must have the default language file. English should typically be the default. | Set `defaultLanguageTag: "en"` and `defaultLanguageFile: "en.json"`. Add other languages as `additionalLanguages`. |

### How Localization Works

| Layer | Key style | Example |
|-------|-----------|---------|
| App manifest (`manifest.json`) | JSONPath expressions | `name.short`, `description.full` |
| Agent / plugin manifests | Double-bracket tokens resolved via `localizationKeys` | `[[agent_name]]`, `[[plugin_description]]` |

Both types of localized strings live in the **same** language file per locale.

---

## Workflow A: Add Localization to an Agent

Use this workflow when localizing an agent for the **first time** — the agent currently has hardcoded strings and no `localizationInfo`.

### Step A1: Tokenize Agent Manifests — MANDATORY

Replace ALL user-facing strings in `declarativeAgent.json` (and `plugin.json`, if applicable) with tokenized keys wrapped in double brackets (`[[key_name]]`).

**You MUST tokenize ALL of these fields:**

| Field | Token example |
|-------|---------------|
| `name` | `[[agent_name]]` |
| `description` | `[[agent_description]]` |
| Every `conversation_starters[].title` | `[[starter_travel_title]]`, `[[starter_remote_title]]`, etc. |
| Every `conversation_starters[].text` | `[[starter_travel_text]]`, `[[starter_remote_text]]`, etc. |
| `disclaimer.text` (if present) | `[[disclaimer_text]]` |

**Token key rules:**
- Must match the pattern: `^[a-zA-Z_][a-zA-Z0-9_]*$`
- Use descriptive, snake_case names (e.g., `starter_vpn_title` not `key1`)
- Keep names consistent across agent and plugin manifests

**Example — tokenized `declarativeAgent.json`:**

```json
{
  "$schema": "https://developer.microsoft.com/json-schemas/copilot/declarative-agent/v1.6/schema.json",
  "version": "v1.6",
  "name": "[[agent_name]]",
  "description": "[[agent_description]]",
  "instructions": "$[file]('instructions.txt')",
  "conversation_starters": [
    {
      "title": "[[starter_vpn_title]]",
      "text": "[[starter_vpn_text]]"
    },
    {
      "title": "[[starter_password_title]]",
      "text": "[[starter_password_text]]"
    }
  ],
  "disclaimer": {
    "text": "[[disclaimer_text]]"
  }
}
```

**If API plugin exists — tokenize `plugin.json` too:**

```json
{
  "schema_version": "v2.4",
  "name_for_human": "[[plugin_name]]",
  "description_for_human": "[[plugin_description]]",
  "description_for_model": "[[plugin_model_description]]"
}
```

**⛔ NEVER skip tokenization.** Writing localization files without first tokenizing the manifests makes localization non-functional. The manifests MUST use `[[token]]` syntax for the language files to take effect.

**✅ POST-TOKENIZATION CHECKPOINT — Verify before proceeding to Step A2:**
- [ ] `name` field uses `[[agent_name]]` or similar token
- [ ] `description` field uses `[[agent_description]]` or similar token
- [ ] Every `conversation_starters[].title` uses a `[[token]]`
- [ ] Every `conversation_starters[].text` uses a `[[token]]`
- [ ] `instructions` field is NOT tokenized (it should remain as `$[file]('instructions.txt')` or inline text — never `[[token]]`)

**If any box is unchecked, STOP and fix it before continuing.**

### Step A2: Create Language Files — MANDATORY

Create one JSON file per language in `appPackage/`, named `{languageTag}.json` (e.g., `en.json`, `fr.json`, `ja.json`).

**Every language file MUST contain:**

1. **`$schema`** — The localization schema reference
2. **App manifest strings** — `name.short`, `name.full`, `description.short`, `description.full` (all four are REQUIRED)
3. **`localizationKeys` object** — One entry per `[[token]]` used in `declarativeAgent.json` and `plugin.json`, using the token name WITHOUT brackets

**Default language file (`en.json`) — use the ORIGINAL English values from the agent:**

```json
{
  "$schema": "https://developer.microsoft.com/json-schemas/teams/vDevPreview/MicrosoftTeams.Localization.schema.json",
  "name.short": "IT Help Desk",
  "name.full": "IT Help Desk Agent",
  "description.short": "Resolve common IT issues",
  "description.full": "Helps employees resolve common IT issues using internal knowledge bases and ticketing systems.",
  "localizationKeys": {
    "agent_name": "IT Help Desk Agent",
    "agent_description": "Helps employees resolve common IT issues using internal knowledge bases and ticketing systems.",
    "starter_vpn_title": "VPN issues",
    "starter_vpn_text": "I can't connect to the corporate VPN. What should I try?",
    "starter_password_title": "Password reset",
    "starter_password_text": "How do I reset my password?",
    "disclaimer_text": "This agent provides general IT guidance. For urgent issues, contact the helpdesk directly."
  }
}
```

**Additional language file (`fr.json`) — use translations provided by the user:**

```json
{
  "$schema": "https://developer.microsoft.com/json-schemas/teams/vDevPreview/MicrosoftTeams.Localization.schema.json",
  "name.short": "Support informatique",
  "name.full": "Agent de support informatique",
  "description.short": "Résoudre les problèmes informatiques courants",
  "description.full": "Aide les employés à résoudre les problèmes informatiques courants à l'aide des bases de connaissances internes.",
  "localizationKeys": {
    "agent_name": "Agent de support informatique",
    "agent_description": "Aide les employés à résoudre les problèmes informatiques courants à l'aide des bases de connaissances internes.",
    "starter_vpn_title": "Problèmes de VPN",
    "starter_vpn_text": "Je n'arrive pas à me connecter au VPN de l'entreprise. Que dois-je essayer ?",
    "starter_password_title": "Réinitialisation du mot de passe",
    "starter_password_text": "Comment réinitialiser mon mot de passe ?",
    "disclaimer_text": "Cet agent fournit des conseils informatiques généraux. Pour les problèmes urgents, contactez le service d'assistance directement."
  }
}
```

**⛔ CRITICAL:** The `localizationKeys` in EVERY language file must have the EXACT SAME set of keys. If `en.json` has `agent_name`, `agent_description`, `starter_vpn_title`, etc., then `fr.json` must also have ALL of those same keys. Missing keys cause runtime resolution failures.

**If the user did not provide translations for some strings** (e.g., conversation starters), you MUST ask the user for them. Do NOT invent translations.

### Step A3: Add `localizationInfo` to `manifest.json` — MANDATORY

Add the `localizationInfo` section to `manifest.json`:

```json
{
  "localizationInfo": {
    "defaultLanguageTag": "en",
    "defaultLanguageFile": "en.json",
    "additionalLanguages": [
      {
        "languageTag": "fr",
        "file": "fr.json"
      },
      {
        "languageTag": "es",
        "file": "es.json"
      }
    ]
  }
}
```

**Rules:**
- `defaultLanguageTag` and `defaultLanguageFile` are always required
- Each additional language needs an entry in `additionalLanguages`
- Language files live in `appPackage/` alongside the manifests
- Use language-only tags (e.g., `en` rather than `en-us`) for top-level translations; add region-specific overrides only when needed

### Step A4: Deploy — MANDATORY

After completing ALL localization changes, deploy the agent:

```bash
npx -y --package @microsoft/m365agentstoolkit-cli atk provision --env local --interactive false
```

Then read `M365_TITLE_ID` from `env/.env.local` and present the test link. **⛔ Never skip deployment after localization changes.**

---

## Workflow B: Add a Language to an Already-Localized Agent

Use this workflow when the agent is ALREADY localized (manifests use `[[tokens]]`, `localizationInfo` exists, language files exist) and the user wants to add another language.

**⛔ Do NOT re-tokenize manifests or recreate existing language files.** Only create the NEW language file and update `localizationInfo`.

### Step B1: Read Existing Localization Setup

1. Read `manifest.json` — note the `localizationInfo` section (default language, existing additional languages)
2. Read the default language file (e.g., `en.json`) — note ALL keys in `localizationKeys` (these are the keys the new file must also have)
3. Read `declarativeAgent.json` — confirm it uses `[[token]]` syntax (if not, switch to Workflow A)

### Step B2: Create the New Language File

Create `appPackage/{languageTag}.json` with:

1. `$schema` — same as existing language files
2. `name.short`, `name.full`, `description.short`, `description.full` — translated values from the user
3. `localizationKeys` — one entry per key from the default language file, with translated values from the user

**⛔ CRITICAL:** The new file MUST have the EXACT SAME set of `localizationKeys` as the default language file. Copy the key names from the existing default language file and fill in translated values.

### Step B3: Update `localizationInfo` in `manifest.json`

Add the new language to the `additionalLanguages` array:

```json
{
  "languageTag": "pt-BR",
  "file": "pt-BR.json"
}
```

**⛔ Do NOT modify existing language files or the `defaultLanguageFile` entry.** Only add to `additionalLanguages`.

### Step B4: Deploy — MANDATORY

Deploy the agent:

```bash
npx -y --package @microsoft/m365agentstoolkit-cli atk provision --env local --interactive false
```

Then present the test link. **⛔ Never skip deployment.**

---

## Localizable Fields Reference

**Declarative agent manifest:**

| Field | Description | Max length | Required |
|---|---|---|---|
| `name` | Display name of the agent | 100 chars | ✔️ |
| `description` | Description shown to users | 1,000 chars | ✔️ |
| `conversation_starters[].title` | Short title for a conversation starter | — | |
| `conversation_starters[].text` | Full prompt text for a conversation starter | — | |
| `disclaimer.text` | Disclaimer shown at conversation start | — | |

**API plugin manifest:**

| Field | Description | Max length | Required |
|---|---|---|---|
| `name_for_human` | Short, human-readable plugin name | 20 chars | ✔️ |
| `description_for_human` | Human-readable description | 100 chars | ✔️ |
| `description_for_model` | Description provided to the model | 2,048 chars | |
| `conversation_starters[].title` | Title for plugin conversation starters | — | |
| `conversation_starters[].text` | Text for plugin conversation starters | — | |

### Required App Manifest Keys in Every Language File

| Key | Description | Max length | Required |
|---|---|---|---|
| `name.short` | Short app name | 30 chars | ✔️ |
| `name.full` | Full app name | 100 chars | ✔️ |
| `description.short` | Short app description | 80 chars | ✔️ |
| `description.full` | Full app description | 4,000 chars | ✔️ |

---

## Language Resolution Order

The Microsoft 365 host resolves strings in the following order:

1. Start with the **default language** strings
2. Overwrite with the user's **language-only** file (e.g., `en`)
3. Overwrite with the user's **language + region** file (e.g., `en-gb`), if available

For example, if the default language is `fr`, and you provide `en` and `en-gb` files, a user with locale `en-gb` sees: `fr` → overwritten by `en` → overwritten by `en-gb`.

> **Tip:** Provide top-level, language-only translations (e.g., `en` rather than `en-us`). Add region-specific overrides only for the few strings that need them.

---

## Project Structure

A localized app package includes the language files alongside the manifests:

```text
my-agent/
├── appPackage/
│   ├── manifest.json
│   ├── declarativeAgent.json
│   ├── instructions.txt            # externalized instructions (NOT tokenized)
│   ├── plugin.json                 # optional
│   ├── en.json                     # default language file
│   ├── fr.json                     # French language file
│   ├── es.json                     # Spanish language file
│   ├── color.png
│   └── outline.png
├── env/
│   └── .env.dev
└── m365agents.yml
```

---

## Critical Rules

1. **Every `[[token]]` must have a matching `localizationKeys` entry** in every language file. Missing keys cause runtime failures.
2. **Keep token names descriptive** — use `starter_vpn_title` not `key1`.
3. **Do NOT localize instructions** — externalize to `instructions.txt` via `$[file]('instructions.txt')`. Never use `[[tokens]]` for instructions.
4. **Schema version consistency** — `$schema` in language files must match `manifest.json`.
5. **Always deploy after localization changes.**
6. **Do NOT invent translations** — ask the user for translated strings. Never machine-translate without confirmation.
7. **Tokenization is MANDATORY** — language files have no effect without `[[token]]` syntax in the manifests.

---

## ⛔ FINAL GATE — Before Responding to the User

**STOP.** Before writing your response, verify ALL of the following:

- [ ] `declarativeAgent.json` uses `[[token]]` syntax for ALL localizable fields (name, description, every conversation starter title/text, disclaimer)
- [ ] `instructions` field is NOT tokenized (never use `[[token]]` for instructions)
- [ ] A default language file exists (e.g., `en.json`) with `name.short`, `name.full`, `description.short`, `description.full`, and ALL `localizationKeys`
- [ ] Every additional language file has the EXACT SAME set of `localizationKeys` as the default
- [ ] `manifest.json` has `localizationInfo` with `defaultLanguageTag`, `defaultLanguageFile`, and `additionalLanguages`
- [ ] I deployed with `npx -y --package @microsoft/m365agentstoolkit-cli atk provision --env local --interactive false`
- [ ] I presented the test link

**If you cannot check ALL boxes, you are NOT done.** Go back and complete the missing steps.

---

## Error Handling

| Error | Action |
|-------|--------|
| Token in manifest has no matching `localizationKeys` entry | **Stop.** List the missing keys and the language files that need them. Ask the user to provide the translations. |
| Language file referenced in `localizationInfo` doesn't exist | **Stop.** List the missing files. Ask the user to provide them or remove the language from `additionalLanguages`. |
| `localizationInfo.defaultLanguageFile` is missing | **Stop.** Inform the user that a default language file is required when `localizationInfo` is present. |
| Agent has only one language | Inform the user that language files are not required for single-language agents. Ask if they want to add more languages. |

---

## Learn More

- [Localize your agent](https://learn.microsoft.com/en-us/microsoft-365-copilot/extensibility/localize-agents) — Official Microsoft localization guide for agents
- [Localize your app (Microsoft Teams)](https://learn.microsoft.com/en-us/microsoftteams/platform/concepts/build-and-test/apps-localization) — General Teams app localization reference
- [Localization schema reference](https://learn.microsoft.com/en-us/microsoftteams/platform/resources/schema/localization-schema) — JSON schema for localization files

````


### `references/mcp-plugin.md`

````markdown
# MCP Server Plugin Integration

This guide explains how to integrate Model Context Protocol (MCP) servers as actions in your Microsoft 365 Copilot agent using JSON manifests. It covers both unauthenticated and OAuth-authenticated MCP servers.

> **⛔ SINGLE FILE ONLY:** MCP plugins require exactly **ONE file** — the plugin manifest (`{name}-plugin.json`). Tool descriptions are inlined directly in the manifest's `mcp_tool_description.tools` array. **Do NOT create a separate `{name}-mcp-tools.json` file.** There is no `"file"` property — only `"tools": [...]`.

## Overview

MCP servers expose tools that can be consumed by your agent. Unlike OpenAPI-based plugins, MCP plugins use a `RemoteMCPServer` runtime type and embed the tool descriptions directly in the plugin manifest.

> **⚠️ IMPORTANT:** `npx -y --package @microsoft/m365agentstoolkit-cli atk add action` does NOT support MCP servers — it only supports `--api-plugin-type api-spec` for OpenAPI plugins. MCP plugins MUST be created manually following the steps below. This is NOT a violation of the "Always Use `npx -y --package @microsoft/m365agentstoolkit-cli atk add action`" rule — that rule applies only to OpenAPI/REST API plugins.

## Prerequisites

- MCP server URL (must be accessible via HTTP/HTTPS)
- Node.js installed (for `mcp-remote` authentication helper)
- Logo images for the agent (color.png 192×192 and outline.png 32×32) — optional, see [Step 5: Logo Images](#step-5-logo-images-optional)

---

## Scaffold the Agent Project First

Before adding an MCP plugin, you **must** have a scaffolded agent project. Run `npx -y --package @microsoft/m365agentstoolkit-cli atk new` if you haven't already:

```bash
npx -y --package @microsoft/m365agentstoolkit-cli atk new \
  -n my-agent \
  -c declarative-agent \
  -i false
```

This creates `m365agents.yml` (and `m365agents.local.yml`) with the **5 required lifecycle steps**:

| Step | Lifecycle Action | What it does |
|------|-----------------|--------------|
| 1 | `teamsApp/create` | Registers the Teams app |
| 2 | `teamsApp/zipAppPackage` | Packages manifest + icons into a zip |
| 3 | `teamsApp/validateAppPackage` | Validates the package (icons, schema, etc.) |
| 4 | `teamsApp/update` | Uploads the package to Teams |
| 5 | `teamsApp/extendToM365` | **Extends the app to M365 Copilot** — generates `M365_TITLE_ID` |

**What breaks without `extendToM365`:** If this step is missing, `npx -y --package @microsoft/m365agentstoolkit-cli atk provision` will register the Teams app and generate `TEAMS_APP_ID`, but the agent will **never appear in Copilot Chat** because no `M365_TITLE_ID` is generated. This is the most common reason for "provision succeeded but agent not found" failures.

> **If you already have a project** but are missing `teamsApp/extendToM365`, add it to the `provision` lifecycle in `m365agents.yml` after `teamsApp/update`. See [deployment.md](deployment.md) for the full provisioning reference.

---

## Step-by-Step Integration

### Step 1: Get MCP Server URL

Ask the user for the MCP server URL. Example: `https://learn.microsoft.com/api/mcp`

Derive the **server root** (scheme + host only): e.g., `https://learn.microsoft.com`

### Step 2: Detect Authentication Requirements

Before discovering tools, determine if the MCP server requires OAuth authentication.

**Probe both well-known endpoints in parallel:**

```bash
curl -s <SERVER_ROOT>/.well-known/oauth-authorization-server
curl -s <SERVER_ROOT>/.well-known/openid-configuration
```

**Decision:**
- **OAuth metadata found** (either endpoint returns valid JSON with `authorization_endpoint`) → the server requires authentication. Follow [authentication.md](authentication.md) Steps 1-3 to discover endpoints, obtain credentials, and configure `oauth/register` in both `m365agents.yml` and `m365agents.local.yml`. Then continue to [Step 3](#step-3-discover-mcp-tools-mandatory) below for authenticated tool discovery.
- **No OAuth metadata** (both return 404 or non-JSON) → the server is unauthenticated. Skip directly to [Step 3](#step-3-discover-mcp-tools-mandatory) for unauthenticated tool discovery.

### Step 3: Discover MCP Tools (MANDATORY)

🚨 **THIS STEP IS MANDATORY — DO NOT SKIP**

You MUST discover tools via the MCP protocol directly. Tool discovery uses HTTP POST requests to the MCP server URL.

#### 3a. Authenticate (OAuth servers only)

If the server requires OAuth (detected in Step 2), perform a one-time authentication:

Tell the user:
> "I need to authenticate with [name]'s MCP server. A browser window will open — please sign in."

Run the command **interactively** (NOT backgrounded — do NOT append `&` or redirect to files):

```bash
npx -p mcp-remote@latest mcp-remote-client <MCP_SERVER_URL> --port 3334
```

Wait for it to complete. The command will open a browser for OAuth sign-in and then exit once authentication succeeds.

> **WSL / headless environments:** `mcp-remote` starts a local HTTP server for the OAuth callback and tries to open a browser. In WSL, the browser opens on the Windows host but the `http://127.0.0.1:3334/...` callback URL may not route back to WSL. If the browser opens but authentication seems stuck:
> 1. After signing in, copy the full callback URL from the browser (it will show an error or blank page)
> 2. Run `curl '<callback-url>'` inside WSL to deliver the auth code to mcp-remote
> 3. Alternatively, run `export BROWSER=wslview` before the command so WSL's browser opener is used, which handles the redirect correctly

**⛔ IMPORTANT:** Do NOT look for tokens in `/tmp/` or log files. Tokens are ONLY stored at `~/.mcp-auth/`.

**Read the cached access token from `~/.mcp-auth`:**

```bash
ls ~/.mcp-auth/mcp-remote-*/
```

Find the token file (pattern: `{url-hash}_tokens.json`), read it, and extract `access_token`.

**⛔ Security:** Do NOT print the token value in your output. Extract it silently and use it only in subsequent HTTP calls. Do NOT write it to any file or create copies.

#### 3b. MCP Session Handshake

Run three sequential HTTP calls to discover tools.

**⛔ Security:** Suppress raw HTTP responses that may contain tokens. Only extract the fields you need (`mcp-session-id`, tool definitions). Do NOT display Authorization headers or token values to the user.

**Call 1 — Initialize:**

```bash
curl -s -X POST <MCP_SERVER_URL> \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  [-H "Authorization: Bearer <access_token>"] \
  -D - \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"m365-agent-skill","version":"1.0.0"}}}'
```

Extract `mcp-session-id` from the response headers. Omit the `Authorization` header for unauthenticated servers.

**Call 2 — Initialized notification:**

```bash
curl -s -X POST <MCP_SERVER_URL> \
  -H "Content-Type: application/json" \
  -H "mcp-session-id: <session_id>" \
  [-H "Authorization: Bearer <access_token>"] \
  -d '{"jsonrpc":"2.0","method":"notifications/initialized"}'
```

**Call 3 — List tools (with pagination):**

```bash
curl -s -X POST <MCP_SERVER_URL> \
  -H "Content-Type: application/json" \
  -H "mcp-session-id: <session_id>" \
  [-H "Authorization: Bearer <access_token>"] \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'
```

If the response contains `nextCursor`, repeat with `{"params":{"cursor":"<nextCursor>"}}` until no cursor remains. Collect all tools.

**Extracting tools from the response:**

Save the raw tools/list response to a file, then use this script to extract the tools array:

```bash
python3 << 'EXTRACT_TOOLS'
import json, sys

with open("/tmp/mcp-tools-response.json") as f:
    data = json.load(f)

tools = data.get("result", {}).get("tools", [])
with open("/tmp/mcp-tools.json", "w") as out:
    json.dump(tools, out, indent=2)

print(f"Extracted {len(tools)} tools")
for t in tools:
    print(f"  - {t['name']}: {t.get('description', '')[:80]}")
EXTRACT_TOOLS
```

> **⛔ Do NOT use inline Python inside command substitutions** (e.g., `$(python3 -c '...')`). The shell security policy blocks nested command substitutions. Always use heredoc scripts (`<< 'EOF'`) or standalone `.py` files instead.

**Expected output structure:**
```json
{
  "result": {
    "tools": [
      {
        "name": "tool_name",
        "description": "Tool description",
        "inputSchema": {
          "type": "object",
          "properties": { ... },
          "required": [...]
        },
        "annotations": {
          "readOnlyHint": true
        },
        "execution": {
          "taskSupport": "forbidden"
        },
        "_meta": {
          "ui": {
            "resourceUri": "ui://namespace/view-name.html"
          }
        }
      }
    ]
  }
}
```

> **⚠️ IMPORTANT:** The example above shows commonly seen fields (`annotations`, `execution`, `_meta`), but MCP servers may return **any** additional properties on tool objects. You **MUST** preserve every property returned by tools/list — copy each tool object in its entirety into `mcp_tool_description.tools[]`. Do NOT cherry-pick known fields; treat the tools/list output as the source of truth and inline it verbatim.

#### 3c. Use All Discovered Tools

**Include ALL tools** returned by `tools/list` in the plugin manifest. Do NOT filter or exclude tools unless the developer explicitly asks to limit the tool set.

**Copy each tool object verbatim** — every property the server returns (`name`, `description`, `inputSchema`, `annotations`, `execution`, `_meta`, `outputSchema`, `title`, or any other field) must be preserved in `mcp_tool_description.tools[]`. Do NOT maintain a hardcoded list of "known" fields — the MCP protocol evolves and servers may return new properties at any time.

Tell the user how many tools were discovered and confirm they will all be included.

### Step 4: Create the Plugin Manifest

Create `{name}-plugin.json` in the `appPackage` folder:

```json
{
  "$schema": "https://developer.microsoft.com/json-schemas/copilot/plugin/v2.4/schema.json",
  "schema_version": "v2.4",
  "name_for_human": "{NAME-FOR-HUMAN}",
  "description_for_human": "{DESCRIPTION-FOR-HUMAN}",
  "namespace": "simplename",
  "functions": [],
  "runtimes": []
}
```

**Required fields:**
| Field | Description |
|-------|-------------|
| `name_for_human` | Display name shown to users (max 20 characters) |
| `description_for_human` | Brief description of the plugin (max 100 characters) |
| `namespace` | Unique identifier, lowercase alphanumeric only (no hyphens, no underscores) |

### Step 4a: Add Functions from Discovered Tools

For EACH discovered tool from Step 3, add a function entry with `name`, `description`, and `capabilities` only. Do **NOT** duplicate `parameters`/`inputSchema` in the function — all tool schema data lives exclusively in `mcp_tool_description.tools[]` (see Step 6).

```json
{
  "functions": [
    {
      "name": "microsoft_docs_search",
      "description": "Search official Microsoft/Azure documentation to find the most relevant content for a user's query."
    }
  ]
}
```

**🚨 CRITICAL: Preserve ALL tool properties when creating function entries:**

| MCP tools/list Output | Plugin Manifest (`functions[]`) |
|---|---|
| `name` | `name` — copy EXACTLY, do not rename |
| `description` | `description` — use the **full** description text, do NOT abbreviate or summarize |
| `inputSchema` | Do NOT add to `functions[]` — this goes in `mcp_tool_description.tools[]` only |
| **Any other property** (`annotations`, `execution`, `_meta`, `outputSchema`, `title`, or any future field) | Do NOT add to `functions[]` — this goes in `mcp_tool_description.tools[]` only |

> **🔑 Full-fidelity rule:** Every tool object in `mcp_tool_description.tools[]` must be a **verbatim copy** of the corresponding tool from the tools/list response. Copy the entire object as-is — do NOT strip, rename, reorder, or omit any property. The MCP protocol evolves and servers may return fields not listed above. If the server returns it, the plugin must include it.

**Why this matters:** The model uses `description` from functions to decide when to invoke each tool. The runtime uses the full tool definitions from `mcp_tool_description.tools[]` to actually call the MCP server. These definitions include `inputSchema` (parameters), `annotations` (read-only/destructive hints), `execution` (task support behavior), `_meta` (UI widget resources), and potentially other properties. Stripping any of them breaks runtime behavior or loses capabilities. Do not duplicate schema data in both places — only `name` and `description` go into `functions[]`.

### Step 4b: Add Response Semantics

**ALWAYS** add `capabilities.response_semantics` to every function — even if no title or URL fields can be identified. Never omit it.

For each tool:
1. Check the tool's `outputSchema` field (optional in MCP — present on some servers). If present, read field names from it directly.
2. If `outputSchema` is absent (common), reason from the tool's `description` text to identify which fields are returned. Look for mentions of URL fields (`url`, `link`, `href`) and title fields (`title`, `name`, `label`).
3. If you can confidently identify BOTH a title-like field AND a navigable URL field → use the **rich pattern**.
4. Otherwise → use the **default pattern**.

**Rich pattern** (when title + URL field are identified):
```json
{
  "name": "tool_name",
  "description": "...",
  "capabilities": {
    "response_semantics": {
      "data_path": "$.items",
      "properties": {
        "title": "$.title",
        "url": "$.url"
      },
      "static_template": {
        "type": "AdaptiveCard",
        "$schema": "https://adaptivecards.io/schemas/adaptive-card.json",
        "version": "1.6",
        "body": [
          {
            "type": "TextBlock",
            "text": "[${title}](${url})",
            "wrap": true,
            "maxLines": 2
          }
        ]
      }
    }
  }
}
```

**Default pattern** (when title or URL cannot be confidently identified):
```json
{
  "name": "tool_name",
  "description": "...",
  "capabilities": {
    "response_semantics": {
      "data_path": "$",
      "properties": {},
      "static_template": {
        "type": "AdaptiveCard",
        "$schema": "https://adaptivecards.io/schemas/adaptive-card.json",
        "version": "1.6",
        "body": [
          {
            "type": "TextBlock",
            "text": "${if(title, title, description)}",
            "wrap": true
          }
        ]
      }
    }
  }
}
```

**Response semantics rules:**
- `$schema`: always `https://adaptivecards.io/schemas/adaptive-card.json` (not `http://`)
- `version`: always `"1.6"`
- Rich template body is always `"[${title}](${url})"` — the title IS the hyperlink
- Source name comes from `name_for_human` automatically — do NOT add it as a TextBlock
- `data_path` and field paths are connector-specific — derive them from the tool's actual response structure

### Step 5: Logo Images (Optional)

Logos are **not mandatory**. The default logos from `npx -y --package @microsoft/m365agentstoolkit-cli atk new` work fine. Ask the user casually:

> "Would you like to use a custom logo for [name], or is the default fine?"

If the user **does not** have a logo or says to skip → **move on**. Do NOT block the workflow for logos.

If the user **does** want a custom logo, they need two **PNG** files (no JPG, SVG, or other formats):

- **`color.png`** — 192×192 px, full colour
- **`outline.png`** — 32×32 px, white-on-transparent

**Resolving logo inputs — check in this order:**

1. **URL**: If the user provides a URL, download the image with `curl -L -o <tempfile> <url>`.
2. **Local file path**: If the user provides a path, use it directly.

**If the provided image is not PNG, convert it to PNG before processing.**

**Handling missing formats:**
- If the user provides only one image, ask: "I have your [color/outline] logo. For the [other format], would you like to provide it, or shall I generate it automatically?"
- If the user says to generate it, derive it from the provided image using jimp.
- If the user says the provided images already meet the size requirements, skip processing and use them directly.

**Processing with jimp** (only when resizing or conversion is needed):

```javascript
// Install: npm install jimp (in a temp directory)
// Import: const { Jimp } = require('jimp');

// color.png: resize to 192x192
// outline.png: resize to 32x32, convert all non-transparent pixels to white on transparent background
```

Output files: `appPackage/color.png` (192×192) and `appPackage/outline.png` (32×32 white-on-transparent).

Show the resulting icon(s) to the user for approval before proceeding. If the user rejects, ask them to provide their own images and do NOT proceed until approved.

### Step 6: Configure the Runtime

Add the `RemoteMCPServer` runtime with the tools inlined in `mcp_tool_description.tools`:

**For authenticated servers** (see [authentication.md](authentication.md)):
```json
{
  "runtimes": [
    {
      "type": "RemoteMCPServer",
      "auth": {
        "type": "OAuthPluginVault",
        "reference_id": "${{<PREFIX>_MCP_AUTH_ID}}"
      },
      "spec": {
        "url": "{MCP_SERVER_URL}",
        "mcp_tool_description": {
          "tools": [
            {
              "name": "function_name_1",
              "description": "Full tool description from tools/list output",
              "inputSchema": {
                "type": "object",
                "properties": { "..." : "..." },
                "required": ["..."]
              },
              "annotations": { "readOnlyHint": true },
              "execution": { "taskSupport": "forbidden" },
              "_meta": { "ui": { "resourceUri": "ui://namespace/view.html" } }
            }
          ]
        }
      },
      "run_for_functions": [
        "function_name_1",
        "function_name_2"
      ]
    }
  ]
}
```

**For unauthenticated servers:**
```json
{
  "runtimes": [
    {
      "type": "RemoteMCPServer",
      "auth": {
        "type": "None"
      },
      "spec": {
        "url": "{MCP_SERVER_URL}",
        "mcp_tool_description": {
          "tools": [ ... ]
        }
      },
      "run_for_functions": [ ... ]
    }
  ]
}
```

> **⚠️ IMPORTANT:**
> - The `mcp_tool_description.tools` array must be a **verbatim copy** of the tools from the tools/list output (Step 3). Copy every property on each tool object exactly as returned — `inputSchema`, `annotations`, `execution`, `_meta`, and any other field the server includes. Do NOT strip, rename, or omit any property. Do NOT use a `file` reference — inline the tools directly.
> - Do NOT fabricate properties that the server did not return. Only include what tools/list actually gives you.
> - For authenticated servers, both `m365agents.yml` and `m365agents.local.yml` must include the `oauth/register` step — see [authentication.md](authentication.md).

### Step 7: Register Plugin in Agent Manifest

Add the plugin to your `declarative-agent.json`:

```json
{
  "actions": [
    {
      "id": "mcpPlugin",
      "file": "{name}-plugin.json"
    }
  ]
}
```

---

## Complete Workflow Checklist

```
□ Step 0: Scaffold agent project with `npx -y --package @microsoft/m365agentstoolkit-cli atk new` (if not already scaffolded)      ← MANDATORY
□ Step 1: Get MCP server URL from user
□ Step 2: Detect authentication requirements (probe well-known endpoints)
□       → If OAuth: follow authentication.md (discover endpoints, get creds, configure oauth/register)
□ Step 3: Discover tools via MCP protocol (initialize → tools/list)               ← MANDATORY
□       → Include ALL tools (do not filter unless developer explicitly requests it)
□ Step 4: Create {name}-plugin.json with functions + response_semantics
□ Step 
...<truncated>
````


### `references/scaffolding-workflow.md`

`````markdown
# Scaffolding Workflow

Step-by-step instructions for scaffolding a new M365 Copilot agent project.

## ⛔ STOP — READ THIS FIRST

### ATK CLI Setup

Check if ATK CLI is available by running `npx -y --package @microsoft/m365agentstoolkit-cli atk --version`. If the command is not found, **STOP and tell the user** that the ATK CLI is required but not installed. Do NOT attempt to install it yourself — the user must install ATK separately before you can proceed.

### The Only Valid Command

Copy this command EXACTLY. Replace `<project-name>` with the user's project name:

```bash
npx -y --package @microsoft/m365agentstoolkit-cli atk new -n <project-name> -c declarative-agent -with-plugin no -i false
```

### Forbidden Commands — These Do Not Exist

| ❌ Invalid Command | Why It Fails |
|-------------------|--------------|
| `npx -y --package @microsoft/m365agentstoolkit-cli atk init` | DOES NOT EXIST — there is no init command |
| `npx -y --package @microsoft/m365agentstoolkit-cli atk init --template` | DOES NOT EXIST — there is no init or --template flag |
| `npx -y --package @microsoft/m365agentstoolkit-cli atk create` | DOES NOT EXIST — there is no create command |
| `npx -y --package @microsoft/m365agentstoolkit-cli atk scaffold` | DOES NOT EXIST — there is no scaffold command |
| `--template anything` | DOES NOT EXIST — there is no --template flag |

---

## Workflow

### Step 1: Understand the Request

**Action:** Verify the user wants to create a NEW M365 Copilot agent project.

**Check for:**
- Keywords: "new project", "create agent", "scaffold", "start from scratch", "M365 Copilot", "M365 agent", "declarative agent"
- Confirmation this is NOT an existing project

**If existing project:** Stop and use the editing workflow instead.

### Step 2: Verify Empty Directory and Collect Project Name

**Action:** Check if the current directory is empty, then ask for the project name.

**Directory check (CRITICAL):**
- Use `ls -A` to check if the current directory is empty
- **Ignore hidden folders** (starting with `.`) — these are meta-configuration folders (`.claude`, `.copilot`, `.github`) and should not block scaffolding
- If ONLY hidden folders exist, treat the directory as empty and proceed
- If directory has non-hidden files/folders, **ERROR OUT immediately**:
  ```
  ❌ Error: Current directory is not empty!

  This skill requires an empty directory to scaffold a new M365 Copilot agent project.
  Please navigate to an empty directory or create a new one first.
  ```
- Do NOT ask for a project name until the directory check passes

**Project naming rules:**
- Use **kebab-case** (lowercase with hyphens): `customer-support-agent`, `expense-tracker`
- Keep it concise: 2–4 words maximum
- No spaces, underscores, or special characters
- ✅ Good: `sales-dashboard`, `document-finder`, `hr-faq-agent`
- ❌ Bad: `agent1`, `test`, `ExpenseTrackerAgent`, `my project`

### Step 3: Run ATK CLI Command and Move Files

**Action:** Execute the scaffolding command, then move files from the ATK-created subfolder to the current directory.

Always use `-i false` (non-interactive mode) to prevent unexpected prompts.

**Commands to execute sequentially:**

1. **Create the project:**
```bash
npx -y --package @microsoft/m365agentstoolkit-cli atk new -n <project-name> -c declarative-agent -with-plugin no -i false
```

2. **Move all files from the subfolder to current directory:**
```bash
mv <project-name>/* <project-name>/.* . 2>/dev/null || true
```

3. **Delete the now-empty subfolder:**
```bash
rmdir <project-name>
```

4. **Verify success:**
- Check that key files exist in the current directory (`package.json`, `m365agents.yml`)
- Confirm the ATK-created subfolder was removed
- If the command fails, report the error and stop — do NOT retry automatically

### Step 4: Add Agent Context Files

**Action:** Ensure the project has context files that tell coding agents which skills are available and how to invoke them. This is critical for future sessions — without these files, agents won't know to use the `declarative-agent-developer` skill.

**Detection logic — check for existing files in this order:**

1. `.github/copilot-instructions.md` — if it exists, **edit it** to append the skill context block below
2. `AGENTS.md` — if it exists, **edit it** to add the skill context block below
3. `CLAUDE.md` (that is NOT a symlink) — if it exists, **edit it** to add the skill context block below
4. **If NONE of the above exist** — create both:
   - `AGENTS.md` — with the full content below
   - `CLAUDE.md` — as a **symlink** to `AGENTS.md` (`ln -sf AGENTS.md CLAUDE.md`)

**Content to add** (when editing an existing file, append this section; when creating `AGENTS.md`, use this as the full content):

````markdown
# M365 Declarative Agent Project

This is an M365 Copilot declarative agent project managed by the ATK CLI.

## Available Skills

When working on this project, you MUST use the appropriate skill for the task. **Do NOT work directly on files without invoking a skill first.**

| Skill | When to Use |
|-------|-------------|
| **declarative-agent-developer** | Any task involving this agent (see scenarios below). **This is the primary skill for this project.** |
| **ui-widget-developer** | Only when adding an MCP server that renders rich interactive widgets (HTML) in Copilot Chat using the OpenAI Apps SDK. |
| **install-atk** | Only when the ATK CLI is not installed or needs updating. |

## ⛔ MANDATORY: Invoke `declarative-agent-developer` Skill First

**This is a declarative agent project. For ANY task in this workspace — regardless of what it is — you MUST invoke the `declarative-agent-developer` skill BEFORE doing any work.** This is not optional. Do not attempt to handle any task yourself. Always delegate to the skill.

### Scenarios handled by this skill

- Creating a new agent project from scratch
- Editing manifests (declarativeAgent.json, manifest.json, m365agents.yml)
- Adding or removing capabilities (web search, Graph connectors, etc.)
- Adding API plugins from OpenAPI specs
- Adding MCP server plugins
- Adding OAuth authentication to plugins
- Localizing an agent into multiple languages
- Adding a new language to an already-localized agent
- Writing or updating agent instructions
- Deploying and provisioning with `atk provision`
- Validating the project with `atk validate`
- Fixing manifest errors or validation failures

**Do NOT:**
- Edit `declarativeAgent.json` or other manifest files directly without the skill
- Run `npx -y --package @microsoft/m365agentstoolkit-cli atk` commands without the skill
- "Help" by manually making changes — always delegate to the skill
````

**Rules:**
- When editing an existing file, preserve all existing content — append the skill context block at the end
- When creating new files, use the content above as-is
- Always create the `CLAUDE.md` symlink when creating a new `AGENTS.md` — both GitHub Copilot CLI and Claude Code read these files automatically
- This step is NOT optional — every scaffolded project must have agent context files

### Step 5: Confirm and Continue

**Action:** Provide a brief confirmation and immediately continue to the editing workflow.

```
✅ Project scaffolded in current directory: <absolute-current-directory-path>

Your M365 Copilot agent project structure is ready (JSON-based).
Agent context files have been added for future skill invocation.

🚀 Continuing to help you design and implement your agent...
```

Then invoke the editing workflow — do NOT wait for user input.

---

## Scope Boundaries

This workflow **only** handles project creation and agent context setup. After scaffolding:

- ✅ Confirm creation and hand off to the editing workflow automatically
- ❌ Do NOT discuss architecture, capability selection, or API plugin design
- ❌ Do NOT write JSON manifests, instructions, or configuration
- ❌ Do NOT create TODO files, open VS Code workspaces, or run extra commands
- ❌ Do NOT provide implementation guidance — that's for the editing workflow

---

## Error Handling

| Error | Action |
|-------|--------|
| ATK CLI not installed | Stop. Tell the user to install ATK first. |
| Directory not empty | Stop. Show error message. Do not proceed. |
| Invalid project name | Warn and suggest a corrected name. |
| `npx -y --package @microsoft/m365agentstoolkit-cli atk new` command fails | Report the error with full output. Do not retry. |
| File move fails | Report the error. Files may still be in the subfolder. |

`````


### `references/schema.md`

```markdown
# JSON Schema Reference for M365 Copilot Agents

This document provides schema version information, compatibility rules, and links to the official documentation for M365 Copilot declarative agent and API plugin manifests.

For full property details, examples, and JSON structures, refer to the linked GitHub documentation below.

## Schema Resources

### Declarative Agent Manifest Versions

| Version | JSON Schema | Documentation |
|---------|-------------|---------------|
| **v1.6** | [schema.json](https://developer.microsoft.com/json-schemas/copilot/declarative-agent/v1.6/schema.json) | [declarative-agent-manifest-1.6.md](https://raw.githubusercontent.com/MicrosoftDocs/m365copilot-docs/main/docs/declarative-agent-manifest-1.6.md) |
| v1.5 | [schema.json](https://developer.microsoft.com/json-schemas/copilot/declarative-agent/v1.5/schema.json) | [declarative-agent-manifest-1.5.md](https://raw.githubusercontent.com/MicrosoftDocs/m365copilot-docs/main/docs/declarative-agent-manifest-1.5.md) |
| v1.4 | [schema.json](https://developer.microsoft.com/json-schemas/copilot/declarative-agent/v1.4/schema.json) | [declarative-agent-manifest-1.4.md](https://raw.githubusercontent.com/MicrosoftDocs/m365copilot-docs/main/docs/declarative-agent-manifest-1.4.md) |
| v1.3 | [schema.json](https://developer.microsoft.com/json-schemas/copilot/declarative-agent/v1.3/schema.json) | [declarative-agent-manifest-1.3.md](https://raw.githubusercontent.com/MicrosoftDocs/m365copilot-docs/main/docs/declarative-agent-manifest-1.3.md) |
| v1.2 | [schema.json](https://developer.microsoft.com/json-schemas/copilot/declarative-agent/v1.2/schema.json) | [declarative-agent-manifest-1.2.md](https://raw.githubusercontent.com/MicrosoftDocs/m365copilot-docs/main/docs/declarative-agent-manifest-1.2.md) |
| v1.0 | [schema.json](https://developer.microsoft.com/json-schemas/copilot/declarative-agent/v1.0/schema.json) | [declarative-agent-manifest-1.0.md](https://raw.githubusercontent.com/MicrosoftDocs/m365copilot-docs/main/docs/declarative-agent-manifest-1.0.md) |

### API Plugin Manifest Versions

| Version | JSON Schema | Documentation |
|---------|-------------|---------------|
| **v2.4** | [schema.json](https://aka.ms/json-schemas/copilot/plugin/v2.4/schema.json) | [plugin-manifest-2.4.md](https://raw.githubusercontent.com/MicrosoftDocs/m365copilot-docs/main/docs/plugin-manifest-2.4.md) |
| v2.3 | [schema.json](https://aka.ms/json-schemas/copilot/plugin/v2.3/schema.json) | [plugin-manifest-2.3.md](https://raw.githubusercontent.com/MicrosoftDocs/m365copilot-docs/main/docs/plugin-manifest-2.3.md) |
| v2.2 | [schema.json](https://aka.ms/json-schemas/copilot/plugin/v2.2/schema.json) | [plugin-manifest-2.2.md](https://raw.githubusercontent.com/MicrosoftDocs/m365copilot-docs/main/docs/plugin-manifest-2.2.md) |
| v2.1 | [schema.json](https://aka.ms/json-schemas/copilot/plugin/v2.1/schema.json) | [plugin-manifest-2.1.md](https://raw.githubusercontent.com/MicrosoftDocs/m365copilot-docs/main/docs/plugin-manifest-2.1.md) |

---

## How to Use These References

When building or editing an agent manifest, **fetch the documentation for the version you are using** from the links above. The linked docs contain:

- Complete property definitions with types, descriptions, and constraints
- JSON examples for every object type (capabilities, actions, runtimes, etc.)
- Capability configuration details (WebSearch sites, OneDriveAndSharePoint items_by_url, Email shared_mailbox, etc.)
- API plugin function and runtime structures
- Response semantics and Adaptive Card templates
- MCP Server (RemoteMCPServer) runtime configuration

```


### `references/workspace-gates.md`

````markdown
# Workspace Detection & Gate Rules

This document contains detailed rules for workspace detection, gate scenarios, and error handling behavior.

---

## 🚨 CRITICAL — Read Before Anything Else

**The #1 eval failure pattern is creating files that should not exist.** These rules are HARD BLOCKS:

1. **If `declarativeAgent.json` does NOT exist and the user asked to edit/modify/add/deploy → REJECT.** Respond with text only. Do NOT create the file. Do NOT create `appPackage/`. Do NOT look at other directories for examples to copy.
2. **If `declarativeAgent.json` has malformed JSON → DETECT first, then INFORM, then ASK.** You must parse the file and report errors to the user BEFORE making any edits. Never edit a broken file without first telling the user it's broken.
3. **If validation finds errors → NEVER run `npx -y --package @microsoft/m365agentstoolkit-cli atk provision`.** There are zero exceptions. Report errors and ask the user.

**The "Detect → Inform → Ask" protocol is mandatory for ALL error states:**
- **Detect**: Identify the problem (missing file, parse error, validation error)
- **Inform**: Tell the user what you found BEFORE taking action
- **Ask**: Wait for instructions before modifying anything

---

## Gate Definitions

### Gate 1: Wrong Project Type — STOP

The workspace contains a non-agent project (Express, React, Django, plain Node.js, Python, Go, etc.) with NO `appPackage/` folder and NO `m365agents.yml`.

**Your entire response must be text-only. No edits, no commands, no files.**

**Example rejection:**
```
This workspace contains an Express.js application (I can see package.json with
express as a dependency, src/index.js, etc.) but it is NOT an M365 declarative
agent project — there is no `appPackage/` folder and no `m365agents.yml`.

I cannot create agent files inside an existing non-agent project. To create a
declarative agent:
1. Navigate to a separate, empty directory
2. Ask me to create a new agent from scratch
```

### Gate 2: No Manifest, User Wants to Edit/Deploy — STOP

No `appPackage/declarativeAgent.json` exists but user implies an existing agent ("add X to this agent", "deploy this", "update instructions").

**Your entire response must be text-only. No edits, no commands, no files.**

**Anti-patterns that WILL cause eval failure:**
- ❌ Creating `declarativeAgent.json` from scratch to "help" the user
- ❌ Creating the `appPackage/` directory
- ❌ Looking at other directories/fixtures for examples and copying them
- ❌ Running `npx -y --package @microsoft/m365agentstoolkit-cli atk new` when the user asked to edit (editing ≠ scaffolding)
- ❌ Running ANY `npx -y --package @microsoft/m365agentstoolkit-cli atk` command — the project is not an agent project

**Example rejection:**
```
This workspace does not appear to be an M365 declarative agent project —
I could not find `appPackage/declarativeAgent.json`.

To get started with a new agent, please:
1. Navigate to an empty directory
2. Ask me to create a new agent from scratch

I cannot add capabilities or plugins to a project that doesn't have an agent manifest.
```

### Gate 3: No Manifest, User Wants New Project — Scaffold

User explicitly says "create a new agent", "scaffold", "start from scratch". The workspace should be empty — if it has a different project, go to Gate 1 instead.

→ Proceed to [Scaffolding Workflow](scaffolding-workflow.md)

### Gate 4: Manifest Exists with Errors — Fix First

`declarativeAgent.json` exists but has validation errors.

**Rules:**
- Parse and check `declarativeAgent.json` against the expected schema
- Report ALL errors to the user with specific details
- ASK the user before making changes
- **Do NOT** run `npx -y --package @microsoft/m365agentstoolkit-cli atk provision` — fix errors first, no exceptions
- **Do NOT** silently rewrite the entire file — surgical fixes only
- **Do NOT** invent placeholder values for missing required fields

**Special case — mostly empty manifest** (has `$schema` and `version` but no `name`, `description`, or `instructions`): This is Gate 4. Report missing fields, ASK the user. Do NOT invent values.

**Malformed JSON handling — STRICT ORDER (do NOT skip steps or reorder):**
1. **DETECT**: Read the file and attempt to parse it. Identify all syntax errors.
2. **INFORM**: Tell the user the file has malformed JSON BEFORE making any edits. List every syntax issue you found (missing commas, unclosed brackets, trailing commas, etc.) with line numbers.
3. **ASK**: Ask the user if you should fix the syntax errors. Wait for their response.
4. **FIX** (only after user confirms): Fix with surgical edits (not a rewrite — if you're changing >20% of lines, stop and reconsider)
5. **VALIDATE**: Check the manifest against the schema after fixing
6. **DO NOT DEPLOY**: Even after fixing, do NOT run `npx -y --package @microsoft/m365agentstoolkit-cli atk provision` until the user's original request is also addressed and validation passes cleanly

**⛔ Malformed JSON anti-patterns that WILL cause eval failure:**
- ❌ Reading the file and immediately editing it without telling the user it's broken
- ❌ Fixing JSON errors as part of a larger edit (fix syntax → inform → ask, THEN handle the user's request separately)
- ❌ Running `npx -y --package @microsoft/m365agentstoolkit-cli atk provision` after fixing syntax errors
- ❌ Validating AFTER editing instead of detecting errors BEFORE editing
- ❌ Mentioning malformed JSON only in a summary at the end instead of upfront

### Gate 5: Valid Project, User Reports Behavior Issues — Review

`declarativeAgent.json` exists and is valid, but the user reports that the agent "doesn't work well", "gives generic answers", "doesn't use the right tool", "ignores capabilities", or shows behavior changes after a model update.

**Trigger phrases:** "review instructions", "improve instructions", "my agent doesn't work", "agent gives generic answers", "agent doesn't follow the process", "agent changed after update"

→ Proceed to [Instruction Review](instruction-review.md) — run the full 5-phase review workflow (Inventory → Comprehension Check → Diagnose → Report → Rewrite).

**Rules:**
- Follow the Detect → Inform → Ask protocol — diagnose first, present findings, wait for approval before rewriting
- Do NOT skip directly to editing the instructions — run the diagnostic checklist first
- Do NOT deploy until the review is complete and the user has approved changes

### Gate 6: Valid Agent Project — Edit

→ Proceed to [Editing Workflow](editing-workflow.md)

---

## STOP Scenarios Quick Reference

| Scenario | What you see | What you MUST do | What you MUST NOT do |
|----------|-------------|-----------------|---------------------|
| Express/React/Node app | `package.json` + `src/index.js` but NO `appPackage/` | Text-only: tell user this is NOT an agent project | ❌ Create `appPackage/` ❌ Run `npx -y --package @microsoft/m365agentstoolkit-cli atk new` ❌ Create ANY files |
| No manifest, edit request | No `declarativeAgent.json`, user says "add capability" | Text-only: explain manifest is missing | ❌ Create files ❌ Scaffold ❌ "Help" by creating missing files |
| Manifest missing fields | `declarativeAgent.json` missing `name`/`description`/`instructions` | List ALL missing fields, ASK user | ❌ Invent placeholders ❌ Auto-fill ❌ Run `npx -y --package @microsoft/m365agentstoolkit-cli atk provision` |
| Manifest has errors | Manifest has structural/schema errors | Report ALL errors, suggest fixes, ask user | ❌ Silently fix ❌ Deploy ❌ Auto-correct |
| Valid project, behavior issues | Valid manifest, user says "agent doesn't work well" | Run Instruction Review workflow (5 phases) | ❌ Jump to editing without diagnosis ❌ Deploy without review ❌ Rewrite without user approval |

---

## Anti-Patterns (Gate 4)

These will cause eval failure:

**File creation violations:**
- ❌ Creating `declarativeAgent.json` when it doesn't exist (this is Gate 2, not Gate 4)
- ❌ Scaffolding a new project to "fix" an incomplete manifest
- ❌ Creating `appPackage/` directory in a non-agent project

**Content invention violations:**
- ❌ Inventing placeholder values (generic names, boilerplate instructions)
- ❌ Auto-completing missing fields without asking

**Malformed JSON violations:**
- ❌ Editing a malformed file without first telling the user it's broken
- ❌ Combining syntax fixes with other edits in one step (fix syntax first, THEN handle the request)
- ❌ Validating JSON only AFTER editing — you must detect errors BEFORE editing
- ❌ Mentioning "the file had malformed JSON" only in a final summary

**Deployment violations:**
- ❌ Running `npx -y --package @microsoft/m365agentstoolkit-cli atk provision` when validation found errors — not even "to test"
- ❌ Running `npx -y --package @microsoft/m365agentstoolkit-cli atk provision` "to see what happens"
- ❌ Auto-correcting errors and deploying without asking
- ❌ Deploying "for educational purposes" to show error output

**"EVEN IF..." — No exceptions to the deploy block:**
- EVEN IF deploying would "demonstrate the error" — just report errors
- EVEN IF the user says "deploy this" — if you know there are errors, explain why you can't
- EVEN IF you fixed errors yourself — verify the JSON is valid before deploying

````
