microsoft/new-topic
Create a new Copilot Studio topic YAML file. Use when the user asks to create a new topic, conversation flow, or dialog for their agent.
npx skills add https://github.com/microsoft/skills-for-copilot-studio --skill new-topic
Generate a new Copilot Studio topic YAML file based on user requirements.
Glob: **/agent.mcs.yml
If multiple agents found, ask which one. NEVER hardcode an agent name.
${CLAUDE_SKILL_DIR}/../../templates/topics/ first:greeting.topic.mcs.yml — OnConversationStart greetingfallback.topic.mcs.yml — OnUnknownIntent fallback with escalationarithmeticsum.topic.mcs.yml — Topic with inputs/outputs and computationquestion-topic.topic.mcs.yml — Question with branching logicsearch-topic.topic.mcs.yml — Generative answers from knowledgeauth-topic.topic.mcs.yml — Authentication flowerror-handler.topic.mcs.yml — Error handlingdisambiguation.topic.mcs.yml — Multiple topics matchedIf a template matches, use it as the starting point.
kind: values against the schema before writing them: node ${CLAUDE_SKILL_DIR}/../../scripts/schema-lookup.bundle.js kinds # List all valid kind values
node ${CLAUDE_SKILL_DIR}/../../scripts/schema-lookup.bundle.js resolve AdaptiveDialog # Resolve trigger structure
node ${CLAUDE_SKILL_DIR}/../../scripts/schema-lookup.bundle.js resolve <TriggerType> # Resolve specific trigger
node ${CLAUDE_SKILL_DIR}/../../scripts/schema-lookup.bundle.js search <ActionKind> # Verify an action kind exists
NEVER write a kind: value you haven't verified exists in the schema. This is the #1 source of hallucination errors. If schema-lookup.bundle.js search <kind> returns no results, the kind does NOT exist — do not use it.
OnRecognizedIntent — For topics triggered by user phrases (most common)OnConversationStart — For welcome/greeting topicsOnUnknownIntent — For fallback topicsOnEscalate — For escalation to human agentOnError — For error handling# Name: comment at the topkind: AdaptiveDialogbeginDialog with correct trigger<nodeType>_<6-8 random alphanumeric>)_REPLACE placeholders with unique IDsGenerativeActionsEnabled. Read the agent's settings.mcs.yml to check.topics/<topic-name>.topic.mcs.yml directoryStep A: Schema validation — always run this first:
node ${CLAUDE_SKILL_DIR}/../../scripts/schema-lookup.bundle.js validate <saved-file.yml>
Step B: LSP-based validation — also run this if the agent has .mcs/conn.json:
Read .mcs/conn.json to get connection details, then:
node ${CLAUDE_SKILL_DIR}/../../scripts/manage-agent.bundle.js validate \
--workspace "<path-to-agent-folder>" \
--tenant-id "<tenantId>" \
--environment-id "<envId>" \
--environment-url "<envUrl>" \
--agent-mgmt-url "<mgmtUrl>"
Both validations are complementary: schema validation checks structural correctness (action kinds, property placement), while LSP validation checks Power Fx expressions, cross-file references, and environment-specific rules. If either fails, fix the issues before reporting success to the user.
When the agent has GenerativeActionsEnabled: true in settings:
Use Topic Inputs (AutomaticTaskInput) instead of Question nodes to auto-collect user info.
Place inputs at the AdaptiveDialog root level (NOT inside beginDialog):
kind: AdaptiveDialog
inputs: # <-- at AdaptiveDialog root, NOT inside beginDialog
- kind: AutomaticTaskInput
propertyName: userName
description: "The user's name"
entity: StringPrebuiltEntity
shouldPromptUser: true
beginDialog:
kind: OnRecognizedIntent
id: main
actions:
- ...
Use Topic Outputs instead of SendActivity for final results.
Use outputType at the root level and SetVariable to set output values — do NOT use TaskOutput (which is only valid in TaskDialog connector actions):
kind: AdaptiveDialog
inputs:
- ...
beginDialog:
kind: OnRecognizedIntent
id: main
actions:
- kind: SetVariable
id: setVar_abc123
variable: Topic.result
value: ="computed value"
outputType: # <-- at AdaptiveDialog root
properties:
result:
displayName: result
description: The computed result
type: String
Include inputType/outputType schemas at the AdaptiveDialog root level when using inputs/outputs:
inputType:
properties:
userName:
displayName: userName
description: "The user's name"
type: String
outputType:
properties:
result:
displayName: result
type: String
When a topic exists alongside other topics or other actions (i.e. TaskDialog), think carefully about the topic outputs — it directly affects whether the orchestrator will chain to an action.
Two scenarios:
Ask yourself: Does this topic complete the task on its own, or does it prepare data for an action/other topic? If the latter, output the data, not a status message.
Alternative approaches for data-gathering topics:
=: value: =Text(Topic.num1 + Topic.num2){}: activity: "Hello {Topic.UserName}"Text(), Now(), IsBlank(), !IsBlank(), DateTimeFormat.UTCvariable: init:Topic.MyVar (first assignment uses init:)SetTextVariable instead of SetVariable to convert non-text types (Number, DateTime, etc.) to text via template interpolation: value: "Guests: {Topic.NumberOfGuests}"Take microsoft/new-topic from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.