@ericrisco/rsc 0.1.36 → 0.1.38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +9 -3
  2. package/manifest.json +153 -4
  3. package/package.json +1 -1
  4. package/scripts/lib/domains.js +2 -1
  5. package/scripts/lib/recommend.js +6 -0
  6. package/skills/automation-flows/SKILL.md +1 -1
  7. package/skills/automation-strategy/SKILL.md +131 -0
  8. package/skills/automation-strategy/evals/README.md +16 -0
  9. package/skills/automation-strategy/evals/cases.yaml +48 -0
  10. package/skills/automation-strategy/references/automate-decision-and-roi.md +118 -0
  11. package/skills/automation-strategy/references/orchestration-and-antipatterns.md +107 -0
  12. package/skills/automation-strategy/references/platform-selection-matrix.md +86 -0
  13. package/skills/fable-operator/SKILL.md +170 -0
  14. package/skills/fable-operator/evals/README.md +8 -0
  15. package/skills/fable-operator/evals/cases.yaml +52 -0
  16. package/skills/harness/references/providers.yaml +100 -0
  17. package/skills/make/SKILL.md +255 -0
  18. package/skills/make/evals/README.md +14 -0
  19. package/skills/make/evals/cases.yaml +45 -0
  20. package/skills/make/references/api-v2-cheatsheet.md +114 -0
  21. package/skills/make/references/blueprint-round-trip.md +128 -0
  22. package/skills/make/references/make-mcp-and-auth.md +101 -0
  23. package/skills/n8n/SKILL.md +205 -0
  24. package/skills/n8n/evals/README.md +14 -0
  25. package/skills/n8n/evals/cases.yaml +45 -0
  26. package/skills/n8n/references/auth-and-selfhost.md +62 -0
  27. package/skills/n8n/references/n8n-mcp-tools.md +84 -0
  28. package/skills/n8n/references/rest-api-cheatsheet.md +94 -0
  29. package/skills/power-automate/SKILL.md +169 -0
  30. package/skills/power-automate/evals/README.md +17 -0
  31. package/skills/power-automate/evals/cases.yaml +43 -0
  32. package/skills/power-automate/references/dataverse-web-api.md +187 -0
  33. package/skills/power-automate/references/entra-auth-setup.md +75 -0
  34. package/skills/power-automate/references/flowstudio-mcp-and-limits.md +62 -0
  35. package/skills/roast-me/tools/adapters/__pycache__/__init__.cpython-312.pyc +0 -0
  36. package/skills/roast-me/tools/adapters/__pycache__/base.cpython-312.pyc +0 -0
  37. package/skills/roast-me/tools/adapters/__pycache__/claude.cpython-312.pyc +0 -0
  38. package/skills/roast-me/tools/adapters/__pycache__/codex.cpython-312.pyc +0 -0
  39. package/skills/roast-me/tools/adapters/__pycache__/gemini.cpython-312.pyc +0 -0
  40. package/skills/roast-me/tools/adapters/__pycache__/registry.cpython-312.pyc +0 -0
  41. package/skills/zapier/SKILL.md +145 -0
  42. package/skills/zapier/evals/README.md +5 -0
  43. package/skills/zapier/evals/cases.yaml +50 -0
  44. package/skills/zapier/references/programmatic-limits-and-partner-api.md +48 -0
  45. package/skills/zapier/references/when-to-use-vs-n8n-make.md +44 -0
  46. package/skills/zapier/references/zapier-mcp-tools.md +72 -0
@@ -0,0 +1,169 @@
1
+ ---
2
+ name: power-automate
3
+ description: "Use when operating Microsoft Power Automate cloud flows programmatically — creating, enabling, updating, listing or deleting flows through the Dataverse Web API (the workflow table, category 5) with Entra ID / OAuth2 auth, or reading action-level run history via the third-party FlowStudio MCP. Triggers: 'create a Power Automate cloud flow with the Dataverse API', 'enable my flow by code / set statecode to 1', 'why can't I create or edit a My Flow programmatically', 'PATCH the workflow clientdata', 'automatizar el despliegue de un flujo de Power Automate desde código'. NOT designing the flow or authoring its definition (automation-flows), NOT choosing a platform by billing model (automation-strategy), NOT a generic typed REST client with auth/pagination/backoff (api-connector-builder), NOT the endpoint receiving an HTTP-trigger POST in your app (webhooks), NOT scripting Microsoft 365 / Graph mail & calendar directly (no first-party skill — api-connector-builder, or google-workspace)."
4
+ tags: [power-automate, dataverse, microsoft, flows, entra, oauth, mcp, m365, automation]
5
+ recommends: [automation-flows, automation-strategy, api-connector-builder, webhooks]
6
+ profiles: [full]
7
+ origin: risco
8
+ ---
9
+
10
+ # Power Automate — operate cloud flows through Dataverse, honestly
11
+
12
+ Drive Microsoft Power Automate **cloud flows** from code: create, enable, update, list, and delete them, plus pull run history for debugging. This skill operates the *live* surface. Deciding what the flow should do and shaping its trigger→actions→error definition is design — that lives in `../automation-flows/SKILL.md`; this skill wraps that definition, ships it, and manages it.
13
+
14
+ Read these three facts before you write a line — they set the boundary of what is even possible:
15
+
16
+ 1. **Only flows inside a Dataverse *solution* are code-manageable.** Classic personal **"My Flows"** cannot be created or edited by code — the Microsoft docs say so explicitly. If the target is a My Flow, the honest answer is: move it into a solution first, or drive it by hand. There is no API workaround.
17
+ 2. **`api.flow.microsoft.com` is unsupported.** Microsoft's own words: use it "at your own risk," it is subject to breaking changes. The supported programmatic surface is the **Dataverse Web API** (or the .NET SDK) against the `workflow` table. For admin-style operations the **Power Automate Management** connector is the other supported path.
18
+ 3. **The unified Power Platform API (`api.powerplatform.com`) is maturing fast.** It already lists cloud flows (api-version `2024-10-01`) and its Inventory API went GA in early 2026. It may eventually supersede the Dataverse path for flow management. Treat the Dataverse-vs-Power-Platform-API split as fast-moving — verify the current recommendation at author time.
19
+
20
+ ## Connect first — API vs MCP
21
+
22
+ Everything runs against your org's Dataverse Web API. **Base URL:** `https://{org}.{region}.dynamics.com/api/data/v9.2` (find yours under Power Platform admin → your environment → developer resources). Auth is **OAuth2 / Entra ID** — a user token or, for CI, a **service principal** (app registration). For a service principal the org needs a **Dataverse application user** mapped to that app plus a security role; the token audience is the Dataverse URL. Full setup, including the delegated `user_impersonation` vs app `.default` scope split and the 401/403 causes, is in `references/entra-auth-setup.md`.
23
+
24
+ Put these in `.env` (never inline a secret in a flow or a script):
25
+
26
+ ```
27
+ PA_DATAVERSE_URL=https://contoso.crm.dynamics.com # no trailing slash, no /api/...
28
+ PA_TENANT_ID=<entra-tenant-guid>
29
+ PA_CLIENT_ID=<app-registration-client-id>
30
+ PA_CLIENT_SECRET=<app-registration-secret> # service-principal flow only
31
+ ```
32
+
33
+ **When to use the REST API vs the MCP:**
34
+
35
+ | You need to… | Use | Why |
36
+ |---|---|---|
37
+ | Create / enable / update / delete / list flows | **Dataverse Web API** | The only supported CRUD surface; scriptable, CI-friendly, service-principal auth. |
38
+ | Read a flow's **action-level** run inputs/outputs to debug a failure | **FlowStudio MCP** (third-party) | Dataverse exposes run *records* (`flowrun` table) but not per-action I/O; the MCP does. See `references/flowstudio-mcp-and-limits.md`. |
39
+ | Admin-scope operations (turn on/off across an environment) | Power Automate **Management** connector | Supported management surface when raw Dataverse is awkward. |
40
+
41
+ **FlowStudio MCP is NOT Microsoft-affiliated** — it is a de-facto third-party server (`mcp.flowstudio.app/mcp`). Microsoft's own MCP story is Copilot Studio *consuming* MCP servers and a Dataverse MCP, neither of which authors Power Automate flows. Flag the third-party dependency to anyone before wiring it into a pipeline.
42
+
43
+ ## The `workflow` table — the data model
44
+
45
+ Cloud flows are rows in the Dataverse **Process (`workflow`)** table. The columns that matter:
46
+
47
+ | Column | Meaning | Values you use |
48
+ |---|---|---|
49
+ | `category` | Kind of process | **`5` = modern cloud flow** (automated / instant / scheduled). (0 classic workflow, 4 business process flow, 6 desktop flow.) |
50
+ | `type` | Definition vs template | **`1` = Definition** (a runnable flow). |
51
+ | `statecode` | On/off state | **`0` = Draft (Off)**, **`1` = Activated (On)**, `2` = Suspended. |
52
+ | `name` | Display name | your string |
53
+ | `primaryentity` | Bound table | **`"none"`** for automated/instant/scheduled flows |
54
+ | `clientdata` | The flow itself | **string-encoded JSON** (see below) |
55
+ | `workflowid` | GUID key | returned on create; used in `workflows({id})` |
56
+
57
+ List the cloud flows that are on:
58
+
59
+ ```bash
60
+ curl -s "$PA_DATAVERSE_URL/api/data/v9.2/workflows?\$filter=category eq 5 and statecode eq 1&\$select=name,statecode,type,workflowid" \
61
+ -H "Authorization: Bearer $TOKEN" -H "OData-Version: 4.0" -H "Accept: application/json"
62
+ ```
63
+
64
+ ## `clientdata` — the payload, and the trap that bites everyone
65
+
66
+ `clientdata` is **a JSON string, not a nested JSON object.** It is the serialized form of:
67
+
68
+ ```json
69
+ {
70
+ "properties": {
71
+ "connectionReferences": { "shared_commondataserviceforapps": { "runtimeSource": "embedded", "connection": {}, "api": { "name": "shared_commondataserviceforapps" } } },
72
+ "definition": { "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", "contentVersion": "1.0.0.0", "triggers": { }, "actions": { } }
73
+ },
74
+ "schemaVersion": "1.0.0.0"
75
+ }
76
+ ```
77
+
78
+ Two load-bearing parts:
79
+
80
+ - **`definition`** — a Logic Apps workflow definition: `triggers` (exactly one) then `actions`. This is the *design* artifact. Do not invent it from scratch here — get the trigger→actions→branch→error shape from `../automation-flows/SKILL.md`, then drop it into `definition`. Fastest reliable way to get a real one: build the flow once in the maker portal, export the solution, and copy its `clientdata`.
81
+ - **`connectionReferences`** — the map from the definition's connectors to actual connections. **A flow whose connection references are not authorized will not turn on.** In a solution these are connection-reference records the target environment must resolve; unresolved references are the #1 reason a `PATCH statecode=1` "succeeds" but the flow never runs.
82
+
83
+ **The trap:** `clientdata` must be *escaped into a string* before it goes in the request body — a nested object is rejected. In a script, `JSON.stringify(clientDataObject)` and assign the result; do not paste the object raw. Full annotated example in `references/dataverse-web-api.md`.
84
+
85
+ ## Dynamic lifecycle — token → create → validate → enable → manage → delete
86
+
87
+ **1. Get a token** (service-principal / client-credentials shown):
88
+
89
+ ```bash
90
+ TOKEN=$(curl -s -X POST "https://login.microsoftonline.com/$PA_TENANT_ID/oauth2/v2.0/token" \
91
+ -d "grant_type=client_credentials" -d "client_id=$PA_CLIENT_ID" \
92
+ -d "client_secret=$PA_CLIENT_SECRET" -d "scope=$PA_DATAVERSE_URL/.default" \
93
+ | python3 -c "import sys,json;print(json.load(sys.stdin)['access_token'])")
94
+ ```
95
+
96
+ **2. Create the flow** (comes up `statecode=0`, Off — expected):
97
+
98
+ ```bash
99
+ curl -s -i -X POST "$PA_DATAVERSE_URL/api/data/v9.2/workflows" \
100
+ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
101
+ -H "OData-Version: 4.0" \
102
+ -d '{ "category": 5, "type": 1, "name": "Nightly sync", "primaryentity": "none", "clientdata": "<string-encoded JSON>" }'
103
+ # → 204 No Content. The workflowid is in the OData-EntityId response header:
104
+ # OData-EntityId: .../workflows(00aa00aa-bb11-cc22-dd33-44ee44ee44ee)
105
+ ```
106
+
107
+ **3. Validate before enabling.** Read it back, confirm `category`/`type` are right, and confirm every `connectionReferences` entry resolves to an authorized connection in this environment. Enabling a flow with dangling connections is the classic silent failure.
108
+
109
+ **4. Enable** — flip `statecode` to `1` (use `If-Match: *` for the update):
110
+
111
+ ```bash
112
+ curl -s -X PATCH "$PA_DATAVERSE_URL/api/data/v9.2/workflows(<workflowid>)" \
113
+ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
114
+ -H "OData-Version: 4.0" -H "If-Match: *" \
115
+ -d '{ "statecode": 1 }'
116
+ # → 204 No Content
117
+ ```
118
+
119
+ **5. Manage** — update the definition or owner with the same `PATCH` (send only the fields you change; to reassign use `"ownerid@odata.bind": "systemusers(<id>)"`). To turn a flow off, `PATCH statecode=0`.
120
+
121
+ **6. Delete — irreversible; export first.** There is no undo on `DELETE`. **Before deleting, export the containing solution** (`POST /api/data/v9.2/ExportSolution` → base64 zip you save to source control), so the flow can be reimported:
122
+
123
+ ```bash
124
+ curl -s -X DELETE "$PA_DATAVERSE_URL/api/data/v9.2/workflows(<workflowid>)" \
125
+ -H "Authorization: Bearer $TOKEN"
126
+ # → 204 No Content
127
+ ```
128
+
129
+ **Debug a run** — Dataverse's `flowrun` table lists run records but not per-action I/O. To see *which action failed and with what payload*, use FlowStudio MCP: `list_live_flows` → `get_live_flow_runs` → `get_live_flow_run_action_outputs`. See `references/flowstudio-mcp-and-limits.md`.
130
+
131
+ ## Microsoft 365 ecosystem fit
132
+
133
+ Power Automate is Microsoft's iPaaS across M365 and Dynamics; flows live in **Dataverse** and glue Outlook, Teams, SharePoint, Dynamics, and hundreds of connectors. That means the boundary is sharp: if the ask is *"send this email / read this calendar via Microsoft Graph from my own backend code,"* that is not a flow at all — build a typed Graph client with `../api-connector-builder/SKILL.md` (there is no first-party MS-Graph skill). If the equivalent is on Google, that is `../google-workspace/SKILL.md`. This skill is for when the automation genuinely *is* a Power Automate flow you must operate by code.
134
+
135
+ ## Honesty / gotchas
136
+
137
+ | Trap | Why it bites | Do instead |
138
+ |---|---|---|
139
+ | Trying to CRUD a **My Flow** by code | Unsupported — silently impossible, not a bug you can fix | Move it into a Dataverse solution, or drive it by hand |
140
+ | `clientdata` sent as a nested object | Request rejected; the column expects an escaped **string** | Serialize (`JSON.stringify`) the definition+connectionReferences before sending |
141
+ | `PATCH statecode=1` "worked" but the flow never runs | Connection references unresolved/unauthorized in the target environment | Authorize every connection reference before enabling; validate on read-back |
142
+ | Building against `api.flow.microsoft.com` | Unsupported; breaks without warning | Dataverse Web API, or the Power Automate Management connector |
143
+ | `DELETE` with no export | No undo; the flow and its history are gone | `ExportSolution` first, save the zip, then delete |
144
+ | Hardcoding the Dataverse URL / secret | Env-specific, leaks in source | `.env`: `PA_DATAVERSE_URL`, `PA_TENANT_ID`, `PA_CLIENT_ID`, `PA_CLIENT_SECRET` |
145
+ | Assuming the Dataverse path is permanent | Power Platform API is superseding surfaces piecemeal | Re-check `api.powerplatform.com` coverage at author time; the split is fast-moving |
146
+
147
+ ## Related skills
148
+
149
+ - **`../automation-flows/SKILL.md`** — design side: what the trigger→actions→branch→error should be, the importable definition. This skill *consumes* that shape as `clientdata.definition`. Design there, operate here.
150
+ - **`../automation-strategy/SKILL.md`** — choosing Power Automate vs n8n/Make/Zapier by billing and constraints. Decide there before you commit to operating here.
151
+ - **`../api-connector-builder/SKILL.md`** — a generic typed REST client with auth/pagination/backoff (including a Dataverse or MS Graph client). This skill uses the Dataverse API surgically; it does not build a general client.
152
+ - **`../webhooks/SKILL.md`** — building the inbound endpoint in *your* app that receives a flow's HTTP-trigger POST. This skill triggers/operates flows; it does not build the receiver.
153
+
154
+ ## Checklist
155
+
156
+ - [ ] `.env` set: `PA_DATAVERSE_URL` (no trailing slash), `PA_TENANT_ID`, `PA_CLIENT_ID`, `PA_CLIENT_SECRET`.
157
+ - [ ] Confirmed the target is a **solution-aware** flow, not a My Flow.
158
+ - [ ] Token acquired against scope `{PA_DATAVERSE_URL}/.default` (or delegated `user_impersonation`).
159
+ - [ ] Create payload has `category:5`, `type:1`, `primaryentity:"none"`, and `clientdata` as an **escaped string**.
160
+ - [ ] Every `connectionReferences` entry maps to an authorized connection before enabling.
161
+ - [ ] Flow enabled via `PATCH statecode=1` and verified on read-back.
162
+ - [ ] Solution **exported** before any `DELETE`.
163
+ - [ ] No secrets or org URLs hardcoded; FlowStudio MCP (if used) flagged as third-party.
164
+
165
+ ## References
166
+
167
+ - `references/dataverse-web-api.md` — endpoint cheat-sheet (token, list/filter, create, enable, update, delete, ExportSolution, share), the full annotated `clientdata`, OData headers, and error handling.
168
+ - `references/entra-auth-setup.md` — app registration, service principal vs delegated user, the Dataverse application user + security role, `.default` vs `user_impersonation`, and common 401/403 causes.
169
+ - `references/flowstudio-mcp-and-limits.md` — FlowStudio MCP tools and auth (third-party), what it exposes that Dataverse doesn't, and the platform limits (My Flows, `api.flow.microsoft.com`, Power Platform API evolution).
@@ -0,0 +1,17 @@
1
+ # Evals — power-automate
2
+
3
+ `cases.yaml` has three groups. `should_trigger` covers the programmatic operate cases: create+enable
4
+ via the Dataverse Web API, the My-Flows honesty question, enable-via-statecode, a Spanish
5
+ solution-aware CI deploy, FlowStudio-MCP run debugging, and a clientdata update. `should_not_trigger`
6
+ lists adjacent prompts owned by a named sibling — flow *design*/definition (automation-flows),
7
+ platform selection by billing (automation-strategy), generic typed-client engineering
8
+ (api-connector-builder), the inbound webhook receiver (webhooks), and direct Microsoft Graph
9
+ scripting (api-connector-builder, no first-party MS-Graph skill). `capability` is one end-to-end
10
+ lifecycle scenario whose `must_include` rubric checks the concrete API calls (token, POST, PATCH
11
+ statecode, DELETE), the string-encoded `clientdata`, the My-Flows/`api.flow.microsoft.com` honesty
12
+ limits, the ExportSolution-before-DELETE irreversibility rule, and the third-party FlowStudio-MCP
13
+ flag.
14
+
15
+ No automated runner. Score by judgement: feed each `should_trigger` / `should_not_trigger` prompt to
16
+ the routing layer and confirm it activates (or routes to the listed sibling); for `capability`, have
17
+ the skill produce the lifecycle walkthrough and check every `must_include` bullet by hand.
@@ -0,0 +1,43 @@
1
+ skill: power-automate
2
+
3
+ should_trigger:
4
+ - prompt: "Create a Power Automate cloud flow via the Dataverse Web API and turn it on from my deploy script."
5
+ why: Canonical programmatic operate — POST /workflows (category 5) then enable via statecode; the core deliverable.
6
+ - prompt: "Why can't I edit my flow under 'My Flows' with the API? Every call I make fails."
7
+ why: The load-bearing honesty limit — My Flows are not code-manageable; must route to the solution-aware answer.
8
+ - prompt: "Enable my Power Automate flow by code — set statecode to 1 after I create it."
9
+ why: Enable-via-statecode is the explicit lifecycle step new flows come up Off (0) and need PATCH to 1.
10
+ - prompt: "Automatizar el despliegue de un flujo de Power Automate dentro de una solución de Dataverse desde CI."
11
+ why: Spanish; solution-aware programmatic deployment with a service principal — squarely this skill.
12
+ - prompt: "Use FlowStudio MCP to pull the action-level inputs and outputs of my flow's last failed run."
13
+ why: MCP run-history debugging that the Dataverse API doesn't expose per action — the MCP path this skill owns.
14
+ - prompt: "PATCH the clientdata of an existing workflow row to update the flow's definition."
15
+ why: Update lifecycle against the workflow table, including the clientdata string-encoding concern.
16
+
17
+ should_not_trigger:
18
+ - prompt: "Design a 'new Stripe payment → post to Teams' automation and give me the importable definition."
19
+ route_to: automation-flows
20
+ why: Designing what the flow does and authoring its definition is the design skill; this one operates a definition, it doesn't invent it.
21
+ - prompt: "Should I use Power Automate or n8n for about 50k runs a month?"
22
+ route_to: automation-strategy
23
+ why: Platform selection by billing/constraints, not operating a live Power Automate flow.
24
+ - prompt: "Write a typed TypeScript client for the Dataverse Web API with token refresh, pagination, and exponential backoff."
25
+ route_to: api-connector-builder
26
+ why: Generic client engineering (auth/pagination/retry) even though the API is Dataverse; this skill uses endpoints surgically, it doesn't build the client.
27
+ - prompt: "Build the Express endpoint that receives my flow's HTTP-trigger POST and verifies it."
28
+ route_to: webhooks
29
+ why: Building the inbound receiver in your own app; this skill operates/triggers flows, it does not build the receiver.
30
+ - prompt: "Send an email and read a calendar via Microsoft Graph from my Node backend."
31
+ route_to: api-connector-builder
32
+ why: Scripting Microsoft 365 via Graph directly is not a Power Automate flow; no first-party MS-Graph skill, so build a typed Graph client.
33
+
34
+ capability:
35
+ - scenario: "User has an Entra app registration and wants to create an automated cloud flow inside a Dataverse solution, enable it, later update its definition, and finally remove it safely. Walk the full programmatic lifecycle."
36
+ must_include:
37
+ - "Acquire an Entra OAuth2 token from https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token (client_credentials) with scope {PA_DATAVERSE_URL}/.default."
38
+ - "POST /api/data/v9.2/workflows with category=5, type=1, primaryentity=\"none\", name, and clientdata as a STRING-encoded JSON (definition + connectionReferences), not a nested object."
39
+ - "State the honesty limit: only flows inside a Dataverse solution are code-manageable; classic My Flows cannot be created or edited by code."
40
+ - "New flow comes up statecode=0 (Off); enable with PATCH workflows({workflowid}) statecode=1 using If-Match: *; validate connection references resolve before enabling."
41
+ - "Update via PATCH (send only changed fields), and note api.flow.microsoft.com is unsupported — use the Dataverse Web API or the Power Automate Management connector; flag api.powerplatform.com as an evolving alternative."
42
+ - "Irreversibility rule: ExportSolution to a saved base64 zip BEFORE DELETE workflows({workflowid}) — deletion has no undo."
43
+ - "For action-level run debugging use the third-party FlowStudio MCP (get_live_flow_run_action_outputs), flagged as NOT Microsoft-affiliated."
@@ -0,0 +1,187 @@
1
+ # Dataverse Web API — cloud-flow cheat-sheet
2
+
3
+ The supported programmatic surface for Power Automate cloud flows. Base URL:
4
+ `https://{org}.{region}.dynamics.com/api/data/v9.2` (your `PA_DATAVERSE_URL` + `/api/data/v9.2`).
5
+ Cloud flows are rows in the **Process (`workflow`)** table, exposed as the `workflows` entity set.
6
+
7
+ All examples assume `$TOKEN` holds an Entra bearer token (see `entra-auth-setup.md`) and
8
+ `$PA_DATAVERSE_URL` is set without a trailing slash.
9
+
10
+ ## Standard OData headers
11
+
12
+ Send these on requests (the API is OData v4):
13
+
14
+ ```
15
+ Authorization: Bearer <token>
16
+ Content-Type: application/json # on POST/PATCH
17
+ Accept: application/json
18
+ OData-MaxVersion: 4.0
19
+ OData-Version: 4.0
20
+ If-Match: * # on PATCH (update)
21
+ Prefer: odata.include-annotations="*" # optional; adds FormattedValue / lookuplogicalname to reads
22
+ ```
23
+
24
+ `$` in query options must be escaped as `\$` in a shell to stop the shell expanding it.
25
+
26
+ ## Column reference (workflow table)
27
+
28
+ | Column | Type | Notes |
29
+ |---|---|---|
30
+ | `category` | choice | `0` classic workflow · `1` dialog · `2` business rule · `3` classic action · `4` business process flow · **`5` modern cloud flow** · `6` desktop flow |
31
+ | `type` | choice | **`1` Definition** (runnable) · `2` Activation · `3` Template |
32
+ | `statecode` | choice | **`0` Draft/Off** · **`1` Activated/On** · `2` Suspended |
33
+ | `name` | string | display name |
34
+ | `description` | string | optional |
35
+ | `primaryentity` | string | `"none"` for automated/instant/scheduled flows |
36
+ | `clientdata` | string | escaped-JSON: `properties.definition` + `properties.connectionReferences` |
37
+ | `ismanaged` | bool | true if installed from a managed solution |
38
+ | `workflowid` | guid | key used in `workflows({id})` |
39
+ | `_ownerid_value` | lookup | owner; set via `ownerid@odata.bind` on write |
40
+
41
+ ## List / read
42
+
43
+ ```bash
44
+ # All cloud flows that are currently on
45
+ curl -s "$PA_DATAVERSE_URL/api/data/v9.2/workflows?\$filter=category eq 5 and statecode eq 1&\$select=name,statecode,type,workflowid&\$top=50" \
46
+ -H "Authorization: Bearer $TOKEN" -H "OData-Version: 4.0" -H "Accept: application/json"
47
+
48
+ # One flow with its clientdata
49
+ curl -s "$PA_DATAVERSE_URL/api/data/v9.2/workflows(<workflowid>)?\$select=name,statecode,clientdata" \
50
+ -H "Authorization: Bearer $TOKEN" -H "OData-Version: 4.0"
51
+ ```
52
+
53
+ ## Create
54
+
55
+ Required properties for automated/instant/scheduled flows: `category`, `name`, `type`,
56
+ `primaryentity`, `clientdata`.
57
+
58
+ ```bash
59
+ curl -s -i -X POST "$PA_DATAVERSE_URL/api/data/v9.2/workflows" \
60
+ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -H "OData-Version: 4.0" \
61
+ -d '{
62
+ "category": 5,
63
+ "type": 1,
64
+ "name": "Sample flow name",
65
+ "description": "Reads some data from Dataverse.",
66
+ "primaryentity": "none",
67
+ "clientdata": "<escaped JSON string>"
68
+ }'
69
+ ```
70
+
71
+ Response is `204 No Content`. The new `workflowid` is in the **`OData-EntityId`** response header:
72
+ `OData-EntityId: .../workflows(00aa00aa-bb11-cc22-dd33-44ee44ee44ee)`. New flows are always
73
+ `statecode=0` (Off) — you must enable them.
74
+
75
+ ### The `clientdata` string, unescaped for reading
76
+
77
+ `clientdata` is the *string* form of this object. Serialize it (`JSON.stringify`) before assigning
78
+ it to the property — a nested object is rejected.
79
+
80
+ ```json
81
+ {
82
+ "properties": {
83
+ "connectionReferences": {
84
+ "shared_commondataserviceforapps": {
85
+ "runtimeSource": "embedded",
86
+ "connection": {},
87
+ "api": { "name": "shared_commondataserviceforapps" }
88
+ }
89
+ },
90
+ "definition": {
91
+ "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
92
+ "contentVersion": "1.0.0.0",
93
+ "parameters": {
94
+ "$connections": { "defaultValue": {}, "type": "Object" },
95
+ "$authentication": { "defaultValue": {}, "type": "SecureObject" }
96
+ },
97
+ "triggers": {
98
+ "manual": {
99
+ "type": "Request", "kind": "Button",
100
+ "inputs": { "schema": { "type": "object", "properties": {}, "required": [] } }
101
+ }
102
+ },
103
+ "actions": {
104
+ "List_rows": {
105
+ "runAfter": {},
106
+ "type": "OpenApiConnection",
107
+ "inputs": {
108
+ "host": {
109
+ "apiId": "/providers/Microsoft.PowerApps/apis/shared_commondataserviceforapps",
110
+ "connectionName": "shared_commondataserviceforapps",
111
+ "operationId": "ListRecords"
112
+ },
113
+ "parameters": { "entityName": "accounts", "$select": "name", "$top": 1 },
114
+ "authentication": "@parameters('$authentication')"
115
+ }
116
+ }
117
+ }
118
+ }
119
+ },
120
+ "schemaVersion": "1.0.0.0"
121
+ }
122
+ ```
123
+
124
+ - `definition` — a Logic Apps workflow definition (`triggers`, exactly one; then `actions`). Design
125
+ this in `../../automation-flows/SKILL.md`, or export a hand-built flow's solution and copy its
126
+ `clientdata`. Do not hand-write a complex definition blind.
127
+ - `connectionReferences` — maps each connector the definition uses to a connection. **Unresolved /
128
+ unauthorized references mean the flow cannot turn on** even though `PATCH statecode=1` returns 204.
129
+
130
+ ## Enable / update
131
+
132
+ ```bash
133
+ # Enable (turn on)
134
+ curl -s -X PATCH "$PA_DATAVERSE_URL/api/data/v9.2/workflows(<workflowid>)" \
135
+ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
136
+ -H "OData-Version: 4.0" -H "If-Match: *" \
137
+ -d '{ "statecode": 1 }'
138
+
139
+ # Update definition + reassign owner (send only changed fields)
140
+ curl -s -X PATCH "$PA_DATAVERSE_URL/api/data/v9.2/workflows(<workflowid>)" \
141
+ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
142
+ -H "OData-Version: 4.0" -H "If-Match: *" \
143
+ -d '{ "clientdata": "<escaped JSON string>", "ownerid@odata.bind": "systemusers(<systemuserid>)" }'
144
+ ```
145
+
146
+ To turn a flow off: `PATCH { "statecode": 0 }`.
147
+
148
+ ## Export (do this before delete)
149
+
150
+ ```bash
151
+ curl -s -X POST "$PA_DATAVERSE_URL/api/data/v9.2/ExportSolution" \
152
+ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -H "OData-Version: 4.0" \
153
+ -d '{ "SolutionName": "FlowContainer", "Managed": false }'
154
+ # → 200 with { "ExportSolutionFile": "<base64 zip>" }. Save it to source control.
155
+ ```
156
+
157
+ Reimport later with the `ImportSolution` action (`CustomizationFile` = the base64 zip,
158
+ `OverwriteUnmanagedCustomizations: true`).
159
+
160
+ ## Delete (irreversible)
161
+
162
+ ```bash
163
+ curl -s -X DELETE "$PA_DATAVERSE_URL/api/data/v9.2/workflows(<workflowid>)" \
164
+ -H "Authorization: Bearer $TOKEN"
165
+ # → 204 No Content. No undo — always ExportSolution first.
166
+ ```
167
+
168
+ ## Run history
169
+
170
+ The `flowrun` table holds run *records* (status, start/end) but not per-action inputs/outputs.
171
+ Query it for a status overview; for action-level payloads use FlowStudio MCP
172
+ (`flowstudio-mcp-and-limits.md`).
173
+
174
+ ## Sharing
175
+
176
+ `RetrieveSharedPrincipalsAndAccess` (who a flow is shared with), `GrantAccess` / `ModifyAccess` /
177
+ `RevokeAccess` — standard Dataverse record-sharing messages, called as Web API actions.
178
+
179
+ ## Error handling
180
+
181
+ - `401` — token missing/expired or wrong audience/scope. Re-acquire; confirm scope is
182
+ `{PA_DATAVERSE_URL}/.default`.
183
+ - `403` — authenticated but the (application) user lacks a Dataverse security role, or the flow is a
184
+ My Flow / outside a solution you can touch.
185
+ - `404` on `workflows({id})` — wrong id, or the flow isn't in Dataverse (My Flow).
186
+ - `400` on create — almost always `clientdata` sent as an object instead of an escaped string, or a
187
+ missing required property.
@@ -0,0 +1,75 @@
1
+ # Entra ID / OAuth2 setup for the Dataverse Web API
2
+
3
+ The Dataverse Web API accepts only OAuth2 bearer tokens from **Microsoft Entra ID**. Two flows:
4
+
5
+ - **Delegated (a user signs in)** — the token carries a real user's permissions. Scope:
6
+ `{PA_DATAVERSE_URL}/user_impersonation`. Good for local/interactive work.
7
+ - **Application / service principal (client credentials)** — no user; a headless identity for CI.
8
+ Scope: `{PA_DATAVERSE_URL}/.default`. This is the CI-friendly path and what the SKILL curl uses.
9
+
10
+ The **token audience is your org's Dataverse URL** (`PA_DATAVERSE_URL`), not Graph, not
11
+ `api.flow.microsoft.com`.
12
+
13
+ ## App registration (both flows)
14
+
15
+ 1. Entra admin center → **App registrations → New registration**. Note the **Application (client)
16
+ ID** (`PA_CLIENT_ID`) and **Directory (tenant) ID** (`PA_TENANT_ID`).
17
+ 2. **API permissions → Add → Dynamics CRM → `user_impersonation`** (delegated). Grant admin consent.
18
+ 3. For the service-principal flow, create a **client secret** (`PA_CLIENT_SECRET`) under
19
+ *Certificates & secrets*. (A certificate is preferable in production; a secret is fine to start.)
20
+
21
+ ## Dataverse application user (service-principal flow only)
22
+
23
+ A service principal cannot touch Dataverse until it exists there as an **application user**:
24
+
25
+ 1. Power Platform admin center → your **environment → Settings → Users + permissions →
26
+ Application users → New app user**.
27
+ 2. Add the app registration by its **client ID**.
28
+ 3. Assign a **security role** with privileges on the Process (`workflow`) table (e.g. a role that can
29
+ read/create/write/delete processes, such as *System Customizer* scoped appropriately, or a custom
30
+ role). Without a role every call returns `403`.
31
+
32
+ ## Get a token
33
+
34
+ Delegated (device code / auth code — pattern shown as client-credentials substitute for headless):
35
+
36
+ ```bash
37
+ # Service principal (client credentials)
38
+ curl -s -X POST "https://login.microsoftonline.com/$PA_TENANT_ID/oauth2/v2.0/token" \
39
+ -d "grant_type=client_credentials" \
40
+ -d "client_id=$PA_CLIENT_ID" \
41
+ -d "client_secret=$PA_CLIENT_SECRET" \
42
+ -d "scope=$PA_DATAVERSE_URL/.default"
43
+ # → { "access_token": "...", "expires_in": 3599, "token_type": "Bearer" }
44
+ ```
45
+
46
+ For the delegated flow use the same `/oauth2/v2.0/token` endpoint with `grant_type=authorization_code`
47
+ (after an interactive `/authorize` redirect) and `scope={PA_DATAVERSE_URL}/user_impersonation
48
+ offline_access`. Tokens last ~1 hour; cache and refresh — do not mint one per request.
49
+
50
+ ## `.env`
51
+
52
+ ```
53
+ PA_DATAVERSE_URL=https://contoso.crm.dynamics.com # no trailing slash, no /api/data/...
54
+ PA_TENANT_ID=<tenant-guid>
55
+ PA_CLIENT_ID=<app-client-id>
56
+ PA_CLIENT_SECRET=<app-secret> # service-principal flow only
57
+ ```
58
+
59
+ Never commit the secret; never inline it in a flow definition or a script.
60
+
61
+ ## Common failures
62
+
63
+ | Symptom | Cause | Fix |
64
+ |---|---|---|
65
+ | `401 Unauthorized` | wrong scope/audience, or expired token | scope must be `{PA_DATAVERSE_URL}/.default` (app) or `/user_impersonation` (delegated); re-acquire |
66
+ | `403 Forbidden` on every call | app user missing, or no security role | create the Dataverse application user and assign a role with `workflow` privileges |
67
+ | `403` only on some flows | those are My Flows / outside your solution | only solution-aware flows are code-manageable |
68
+ | `AADSTS700016` | client ID not found in tenant | check `PA_TENANT_ID` / `PA_CLIENT_ID` |
69
+ | token works for Graph, `401` here | audience is Graph, not Dataverse | request the token for the Dataverse resource, not Graph |
70
+
71
+ ## Generic client engineering
72
+
73
+ Token caching/refresh, retry/backoff on `429`, and pagination via `@odata.nextLink` are generic
74
+ concerns — build them once with `../../api-connector-builder/SKILL.md` rather than re-inventing per
75
+ script. This skill uses the endpoints; that skill engineers the client around them.
@@ -0,0 +1,62 @@
1
+ # FlowStudio MCP + platform limits
2
+
3
+ ## FlowStudio MCP — third-party, not Microsoft
4
+
5
+ There is **no Microsoft first-party MCP server that authors Power Automate flows.** Microsoft's MCP
6
+ story is Copilot Studio *consuming* MCP servers, and a Dataverse MCP — neither builds/edits flows.
7
+
8
+ The de-facto tool is **FlowStudio MCP**, an independent third party. Flag this to anyone before it
9
+ enters a pipeline: it is **not Microsoft-affiliated**, it is a paid hosted service, and it sits
10
+ between your tenant and your agent.
11
+
12
+ - **Endpoint:** `https://mcp.flowstudio.app/mcp` (remote MCP).
13
+ - **Auth:** an `x-api-key` header. The key is issued after you sign in with your Microsoft account
14
+ and grant Power Platform scopes to FlowStudio (delegated). Verify the exact scope list and pricing
15
+ at author time — the free tier is a small monthly call allowance.
16
+ - **Why use it:** it exposes what the Dataverse Web API does **not** — **action-level run inputs and
17
+ outputs**, so you can see which step in a live run failed and with what payload.
18
+
19
+ ### Representative tools (verify the live list — 30+ tools, evolving)
20
+
21
+ | Tool | Does |
22
+ |---|---|
23
+ | `list_live_flows` | List flows in the tenant, filterable by name |
24
+ | `get_live_flow` | Read a flow's configuration/definition |
25
+ | `get_live_flow_runs` | Recent run history for a flow (status, timing) |
26
+ | `get_live_flow_run_action_outputs` | **Per-action inputs/outputs of a run** — the debugging payload |
27
+ | `update_live_flow` | Modify a live flow |
28
+
29
+ Typical debug loop: `list_live_flows` (find it) → `get_live_flow_runs` (find the failed run) →
30
+ `get_live_flow_run_action_outputs` (see the failing action's payload). For plain CRUD and enablement,
31
+ prefer the supported Dataverse Web API — reserve the MCP for the run-history depth it uniquely gives.
32
+
33
+ ## Platform limits — teach these loudly
34
+
35
+ 1. **My Flows are not code-manageable.** Only flows inside a **Dataverse solution** can be created or
36
+ edited by code. Microsoft's docs state managing **My Flows** is not supported programmatically. If
37
+ the target is a My Flow: move it into a solution in the maker portal first, or drive it by hand.
38
+ There is no API that lets you CRUD a classic personal flow.
39
+
40
+ 2. **`api.flow.microsoft.com` is unsupported.** Microsoft's exact stance: usable "at your own risk,"
41
+ subject to breaking changes. Do not build on it. Supported alternatives:
42
+ - **Dataverse Web API** (this skill's default) — full CRUD on the `workflow` table.
43
+ - **Power Automate Management** connector (or **Power Automate for Admins**) — the supported
44
+ management surface for turning flows on/off and admin operations across an environment.
45
+
46
+ 3. **The Power Platform API is superseding surfaces — fast-moving.** `api.powerplatform.com` is a
47
+ unified surface with its own Entra scopes. As of early 2026 it lists cloud flows
48
+ (`GET https://api.powerplatform.com/powerautomate/environments/{environmentId}/cloudFlows`,
49
+ api-version `2024-10-01`) and its **Inventory API is GA**. It may eventually be the recommended
50
+ management path over Dataverse. **Re-check its coverage at author time** before committing a new
51
+ project to either surface — this is the single most likely fact to have moved since this was
52
+ written.
53
+
54
+ ## What lives where
55
+
56
+ | Need | Surface |
57
+ |---|---|
58
+ | Create / enable / update / delete / list flows | Dataverse Web API (`workflow` table) |
59
+ | Turn on/off, admin ops across an environment | Power Automate Management connector |
60
+ | Action-level run inputs/outputs for debugging | FlowStudio MCP (third-party) |
61
+ | Emerging unified management (evolving) | Power Platform API (`api.powerplatform.com`) |
62
+ | Anything on `api.flow.microsoft.com` | unsupported — avoid |