Copilot Studio Agent Flows from the CLI
Build solution-aware Power Automate agent flows and attach them to Copilot Studio agents without depending on the portal designer.
Prerequisites
- Power Platform CLI (
pac) authenticated to the target environment. - Azure CLI authenticated to the same tenant.
- Dataverse org URL and environment GUID.
dataverseMCP for targeted metadata/table work.- Existing
copilot-studio-agentskill for agent creation and publishing. - Playwright MCP as a fallback when an undocumented designer-generated shape must be captured.
Golden Rules
- Clone before inventing. Query a working
workflow.clientdataand flow-toolbotcomponent.datafrom the same environment when possible. - An agent flow must be a solution flow with a
Requesttrigger usingkind: Skillsand aResponseaction usingkind: Skills. - Bind connector references with
runtimeSource: embedded.invokercan send an empty bearer token when the agent calls Dataverse-backed AI Builder actions. - In the agent tool YAML, use
connectionProperties.mode: Makerfor headless/private agents. - Write
modelDescriptionas a single-line scalar. A folded>-description can deploy but render as empty in Copilot Studio, causing the orchestrator to skip the tool. - Never trust the chat answer as proof. Verify a flow run and its expected side effect.
Workflow
1. Preflight
pac auth who
az account show --query "{tenant:tenantId,user:user.name}" -o json
Confirm both commands point to the target tenant and environment.
2. Inspect Working Shapes
Query:
workflow:workflowid,name,clientdata,category,statecodebotcomponent:schemaname,data, filtered to an existing flow action
Use the returned JSON/YAML shape exactly. Do not guess connector operation IDs.
3. Create Connector References
For every connector used by the flow:
- Confirm a live user connection exists.
- Create a solution-aware
connectionreferencerow. - Add this shape to
clientdata.properties.connectionReferences:
"shared_connector": {
"api": { "name": "shared_connector" },
"connection": { "connectionReferenceLogicalName": "prefix_reference_name" },
"runtimeSource": "embedded"
}
An action does not exist merely because an email address or another input exists. If the flow must send email, create an explicit Office 365 Outlook action and connection reference.
Email notification pattern
Add shared_office365 to connectionReferences with runtimeSource: embedded, then add:
"Send_an_email_(V2)": {
"type": "OpenApiConnection",
"inputs": {
"parameters": {
"emailMessage/To": "@triggerBody()?['Reviewer_Email']",
"emailMessage/Subject": "Your review sentiment analysis",
"emailMessage/Body": "<p>...</p>",
"emailMessage/Importance": "Normal"
},
"host": {
"apiId": "/providers/Microsoft.PowerApps/apis/shared_office365",
"operationId": "SendEmailV2",
"connectionName": "shared_office365"
}
},
"runAfter": { "Previous_action": ["Succeeded"] }
}
Run Respond_to_the_agent only after the email action succeeds if the agent is going to claim the
email was sent.
4. Author the Agent Flow
Required trigger shape:
"manual": {
"type": "Request",
"kind": "Skills",
"inputs": { "schema": { "type": "object", "properties": {}, "required": [] } }
}
Required response shape:
"Respond_to_the_agent": {
"type": "Response",
"kind": "Skills",
"inputs": {
"schema": { "type": "object", "properties": {} },
"statusCode": 200,
"body": {}
}
}
Use runAfter to ensure the response only runs after all required side effects succeed.
5. Create and Activate the Workflow
Create a workflow row with:
category: 5(Modern Flow)type: 1primaryentity: "none"clientdata: the serialized definitionMSCRM.SolutionUniqueName: target unmanaged solution
Create it in Draft, patch the definition while Draft, then activate with:
{"statecode":1,"statuscode":2}
6. Attach It to the Agent
Create agents/topic.<flow-name>.mcs.yml:
kind: TaskDialog
modelDescription: Call this tool to perform the specific flow operation and persist the result.
action:
kind: InvokeFlowTaskAction
flowId: <workflow-guid>
connectionProperties:
$kind: ConnectionProperties
diagnostics:
mode: Maker
outputMode: All
Then:
pac copilot push
pac copilot publish --bot <agent-guid>
7. Verify
- Start a fresh agent test session.
- Use a prompt that explicitly supplies every required input.
- Query flow runs and require
status: Succeeded. - Verify the expected side effect: Dataverse row, email action success, file creation, or other durable output.
- Inspect each failed action before changing the definition.
Error Handling
| Symptom | Cause | Recovery |
|---|---|---|
| Agent claims success but no flow run exists | Tool was not selected | Check single-line modelDescription, tool enabled state, and instructions |
| Connection-manager card | Tool uses mode: Invoker |
Change to Maker, push, and publish |
| AI Builder returns Dataverse 401 | Connection reference uses runtimeSource: invoker |
Change to embedded, publish the flow |
| Tool page shows no description | Folded/multiline YAML was not parsed | Use a single-line modelDescription |
| Flow runs but a connector action is absent | The action was never authored | Add the explicit action plus its connection reference |
| Reviewer email is stored but no email arrives | Email input was treated only as data | Add SendEmailV2; verify its run status and the mailbox |
| Standalone flow test does nothing | Skills trigger requires an agent caller |
Test through the agent |
Output Format
Report:
- Environment, solution, flow ID, agent ID/schema
- Connector references and binding mode
- Published status
- Test prompt
- Flow run status
- Durable side-effect evidence
- Any portal fallback used and the reusable shape captured from it
Post-Run Reflection
After a multi-step run, identify any undocumented schema, connector, or verification friction. Update this skill when a repeatable rule would prevent the failure in future runs.