npx skills add https://github.com/nextlevelbuilder/ui-ux-pro-max-skill --skill ui-ux-pro-maxHow Flowstudio Power Automate Build fits into a Paperclip company.
Flowstudio Power Automate Build drops into any Paperclip agent that handles this kind of work. Assign it to a specialist inside a pre-configured PaperclipOrg company and the skill becomes available on every heartbeat — no prompt engineering, no tool wiring.
Pre-configured AI company — 18 agents, 18 skills, one-time purchase.
SKILL.md479 linesExpandCollapse
---name: flowstudio-power-automate-builddescription: >- Build, scaffold, and deploy Power Automate cloud flows using the FlowStudio MCP server. Your agent constructs flow definitions, wires connections, deploys, and tests — all via MCP without opening the portal. Load this skill when asked to: create a flow, build a new flow, deploy a flow definition, scaffold a Power Automate workflow, construct a flow JSON, update an existing flow's actions, patch a flow definition, add actions to a flow, wire up connections, or generate a workflow definition from scratch. Requires a FlowStudio MCP subscription — see https://mcp.flowstudio.appmetadata: openclaw: requires: env: - FLOWSTUDIO_MCP_TOKEN primaryEnv: FLOWSTUDIO_MCP_TOKEN homepage: https://mcp.flowstudio.app--- # Build & Deploy Power Automate Flows with FlowStudio MCP Step-by-step guide for constructing and deploying Power Automate cloud flowsprogrammatically through the FlowStudio MCP server. **Prerequisite**: A FlowStudio MCP server must be reachable with a valid JWT.See the `flowstudio-power-automate-mcp` skill for connection setup. Subscribe at https://mcp.flowstudio.app --- ## Source of Truth > **Always call `tools/list` first** to confirm available tool names and their> parameter schemas. Tool names and parameters may change between server versions.> This skill covers response shapes, behavioral notes, and build patterns —> things `tools/list` cannot tell you. If this document disagrees with `tools/list`> or a real API response, the API wins. --- ## Python Helper ```pythonimport json, urllib.request MCP_URL = "https://mcp.flowstudio.app/mcp"MCP_TOKEN = "<YOUR_JWT_TOKEN>" def mcp(tool, **kwargs): payload = json.dumps({"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": tool, "arguments": kwargs}}).encode() req = urllib.request.Request(MCP_URL, data=payload, headers={"x-api-key": MCP_TOKEN, "Content-Type": "application/json", "User-Agent": "FlowStudio-MCP/1.0"}) try: resp = urllib.request.urlopen(req, timeout=120) except urllib.error.HTTPError as e: body = e.read().decode("utf-8", errors="replace") raise RuntimeError(f"MCP HTTP {e.code}: {body[:200]}") from e raw = json.loads(resp.read()) if "error" in raw: raise RuntimeError(f"MCP error: {json.dumps(raw['error'])}") return json.loads(raw["result"]["content"][0]["text"]) ENV = "<environment-id>" # e.g. Default-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx``` --- ## Step 1 — Safety Check: Does the Flow Already Exist? Always look before you build to avoid duplicates: ```pythonresults = mcp("list_live_flows", environmentName=ENV) # list_live_flows returns { "flows": [...] }matches = [f for f in results["flows"] if "My New Flow".lower() in f["displayName"].lower()] if len(matches) > 0: # Flow exists — modify rather than create FLOW_ID = matches[0]["id"] # plain UUID from list_live_flows print(f"Existing flow: {FLOW_ID}") defn = mcp("get_live_flow", environmentName=ENV, flowName=FLOW_ID)else: print("Flow not found — building from scratch") FLOW_ID = None``` --- ## Step 2 — Obtain Connection References Every connector action needs a `connectionName` that points to a key in theflow's `connectionReferences` map. That key links to an authenticated connectionin the environment. > **MANDATORY**: You MUST call `list_live_connections` first — do NOT ask the> user for connection names or GUIDs. The API returns the exact values you need.> Only prompt the user if the API confirms that required connections are missing. ### 2a — Always call `list_live_connections` first ```pythonconns = mcp("list_live_connections", environmentName=ENV) # Filter to connected (authenticated) connections onlyactive = [c for c in conns["connections"] if c["statuses"][0]["status"] == "Connected"] # Build a lookup: connectorName → connectionName (id)conn_map = {}for c in active: conn_map[c["connectorName"]] = c["id"] print(f"Found {len(active)} active connections")print("Available connectors:", list(conn_map.keys()))``` ### 2b — Determine which connectors the flow needs Based on the flow you are building, identify which connectors are required.Common connector API names: | Connector | API name ||---|---|| SharePoint | `shared_sharepointonline` || Outlook / Office 365 | `shared_office365` || Teams | `shared_teams` || Approvals | `shared_approvals` || OneDrive for Business | `shared_onedriveforbusiness` || Excel Online (Business) | `shared_excelonlinebusiness` || Dataverse | `shared_commondataserviceforapps` || Microsoft Forms | `shared_microsoftforms` | > **Flows that need NO connections** (e.g. Recurrence + Compose + HTTP only)> can skip the rest of Step 2 — omit `connectionReferences` from the deploy call. ### 2c — If connections are missing, guide the user ```pythonconnectors_needed = ["shared_sharepointonline", "shared_office365"] # adjust per flow missing = [c for c in connectors_needed if c not in conn_map] if not missing: print("✅ All required connections are available — proceeding to build")else: # ── STOP: connections must be created interactively ── # Connections require OAuth consent in a browser — no API can create them. print("⚠️ The following connectors have no active connection in this environment:") for c in missing: friendly = c.replace("shared_", "").replace("onlinebusiness", " Online (Business)") print(f" • {friendly} (API name: {c})") print() print("Please create the missing connections:") print(" 1. Open https://make.powerautomate.com/connections") print(" 2. Select the correct environment from the top-right picker") print(" 3. Click '+ New connection' for each missing connector listed above") print(" 4. Sign in and authorize when prompted") print(" 5. Tell me when done — I will re-check and continue building") # DO NOT proceed to Step 3 until the user confirms. # After user confirms, re-run Step 2a to refresh conn_map.``` ### 2d — Build the connectionReferences block Only execute this after 2c confirms no missing connectors: ```pythonconnection_references = {}for connector in connectors_needed: connection_references[connector] = { "connectionName": conn_map[connector], # the GUID from list_live_connections "source": "Invoker", "id": f"/providers/Microsoft.PowerApps/apis/{connector}" }``` > **IMPORTANT — `host.connectionName` in actions**: When building actions in> Step 3, set `host.connectionName` to the **key** from this map (e.g.> `shared_teams`), NOT the connection GUID. The GUID only goes inside the> `connectionReferences` entry. The engine matches the action's> `host.connectionName` to the key to find the right connection. > **Alternative** — if you already have a flow using the same connectors,> you can extract `connectionReferences` from its definition:> ```python> ref_flow = mcp("get_live_flow", environmentName=ENV, flowName="<existing-flow-id>")> connection_references = ref_flow["properties"]["connectionReferences"]> ``` See the `flowstudio-power-automate-mcp` skill's **connection-references.md** referencefor the full connection reference structure. --- ## Step 3 — Build the Flow Definition Construct the definition object. See [flow-schema.md](references/flow-schema.md)for the full schema and these action pattern references for copy-paste templates:- [action-patterns-core.md](references/action-patterns-core.md) — Variables, control flow, expressions- [action-patterns-data.md](references/action-patterns-data.md) — Array transforms, HTTP, parsing- [action-patterns-connectors.md](references/action-patterns-connectors.md) — SharePoint, Outlook, Teams, Approvals ```pythondefinition = { "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", "contentVersion": "1.0.0.0", "triggers": { ... }, # see trigger-types.md / build-patterns.md "actions": { ... } # see ACTION-PATTERNS-*.md / build-patterns.md}``` > See [build-patterns.md](references/build-patterns.md) for complete, ready-to-use> flow definitions covering Recurrence+SharePoint+Teams, HTTP triggers, and more. --- ## Step 4 — Deploy (Create or Update) `update_live_flow` handles both creation and updates in a single tool. ### Create a new flow (no existing flow) Omit `flowName` — the server generates a new GUID and creates via PUT: ```pythonresult = mcp("update_live_flow", environmentName=ENV, # flowName omitted → creates a new flow definition=definition, connectionReferences=connection_references, displayName="Overdue Invoice Notifications", description="Weekly SharePoint → Teams notification flow, built by agent") if result.get("error") is not None: print("Create failed:", result["error"])else: # Capture the new flow ID for subsequent steps FLOW_ID = result["created"] print(f"✅ Flow created: {FLOW_ID}")``` ### Update an existing flow Provide `flowName` to PATCH: ```pythonresult = mcp("update_live_flow", environmentName=ENV, flowName=FLOW_ID, definition=definition, connectionReferences=connection_references, displayName="My Updated Flow", description="Updated by agent on " + __import__('datetime').datetime.utcnow().isoformat()) if result.get("error") is not None: print("Update failed:", result["error"])else: print("Update succeeded:", result)``` > ⚠️ `update_live_flow` always returns an `error` key.> `null` (Python `None`) means success — do not treat the presence of the key as failure.>> ⚠️ `description` is required for both create and update. ### Common deployment errors | Error message (contains) | Cause | Fix ||---|---|---|| `missing from connectionReferences` | An action's `host.connectionName` references a key that doesn't exist in the `connectionReferences` map | Ensure `host.connectionName` uses the **key** from `connectionReferences` (e.g. `shared_teams`), not the raw GUID || `ConnectionAuthorizationFailed` / 403 | The connection GUID belongs to another user or is not authorized | Re-run Step 2a and use a connection owned by the current `x-api-key` user || `InvalidTemplate` / `InvalidDefinition` | Syntax error in the definition JSON | Check `runAfter` chains, expression syntax, and action type spelling || `ConnectionNotConfigured` | A connector action exists but the connection GUID is invalid or expired | Re-check `list_live_connections` for a fresh GUID | --- ## Step 5 — Verify the Deployment ```pythoncheck = mcp("get_live_flow", environmentName=ENV, flowName=FLOW_ID) # Confirm stateprint("State:", check["properties"]["state"]) # Should be "Started"# If state is "Stopped", use set_live_flow_state — NOT update_live_flow# mcp("set_live_flow_state", environmentName=ENV, flowName=FLOW_ID, state="Started") # Confirm the action we added is thereacts = check["properties"]["definition"]["actions"]print("Actions:", list(acts.keys()))``` --- ## Step 6 — Test the Flow > **MANDATORY**: Before triggering any test run, **ask the user for confirmation**.> Running a flow has real side effects — it may send emails, post Teams messages,> write to SharePoint, start approvals, or call external APIs. Explain what the> flow will do and wait for explicit approval before calling `trigger_live_flow`> or `resubmit_live_flow_run`. ### Updated flows (have prior runs) — ANY trigger type > **Use `resubmit_live_flow_run` first.** It works for EVERY trigger type —> Recurrence, SharePoint, connector webhooks, Button, and HTTP. It replays> the original trigger payload. Do NOT ask the user to manually trigger the> flow or wait for the next scheduled run. ```pythonruns = mcp("get_live_flow_runs", environmentName=ENV, flowName=FLOW_ID, top=1)if runs: # Works for Recurrence, SharePoint, connector triggers — not just HTTP result = mcp("resubmit_live_flow_run", environmentName=ENV, flowName=FLOW_ID, runName=runs[0]["name"]) print(result) # {"resubmitted": true, "triggerName": "..."}``` ### HTTP-triggered flows — custom test payload Only use `trigger_live_flow` when you need to send a **different** payloadthan the original run. For verifying a fix, `resubmit_live_flow_run` isbetter because it uses the exact data that caused the failure. ```pythonschema = mcp("get_live_flow_http_schema", environmentName=ENV, flowName=FLOW_ID)print("Expected body:", schema.get("requestSchema")) result = mcp("trigger_live_flow", environmentName=ENV, flowName=FLOW_ID, body={"name": "Test", "value": 1})print(f"Status: {result['responseStatus']}")``` ### Brand-new non-HTTP flows (Recurrence, connector triggers, etc.) A brand-new Recurrence or connector-triggered flow has **no prior runs** toresubmit and no HTTP endpoint to call. This is the ONLY scenario where youneed the temporary HTTP trigger approach below. **Deploy with a temporaryHTTP trigger first, test the actions, then swap to the production trigger.** #### 7a — Save the real trigger, deploy with a temporary HTTP trigger ```python# Save the production trigger you built in Step 3production_trigger = definition["triggers"] # Replace with a temporary HTTP triggerdefinition["triggers"] = { "manual": { "type": "Request", "kind": "Http", "inputs": { "schema": {} } }} # Deploy (create or update) with the temp triggerresult = mcp("update_live_flow", environmentName=ENV, flowName=FLOW_ID, # omit if creating new definition=definition, connectionReferences=connection_references, displayName="Overdue Invoice Notifications", description="Deployed with temp HTTP trigger for testing") if result.get("error") is not None: print("Deploy failed:", result["error"])else: if not FLOW_ID: FLOW_ID = result["created"] print(f"✅ Deployed with temp HTTP trigger: {FLOW_ID}")``` #### 7b — Fire the flow and check the result ```python# Trigger the flowtest = mcp("trigger_live_flow", environmentName=ENV, flowName=FLOW_ID)print(f"Trigger response status: {test['status']}") # Wait for the run to completeimport time; time.sleep(15) # Check the run resultruns = mcp("get_live_flow_runs", environmentName=ENV, flowName=FLOW_ID, top=1)run = runs[0]print(f"Run {run['name']}: {run['status']}") if run["status"] == "Failed": err = mcp("get_live_flow_run_error", environmentName=ENV, flowName=FLOW_ID, runName=run["name"]) root = err["failedActions"][-1] print(f"Root cause: {root['actionName']} → {root.get('code')}") # Debug and fix the definition before proceeding # See flowstudio-power-automate-debug skill for full diagnosis workflow``` #### 7c — Swap to the production trigger Once the test run succeeds, replace the temporary HTTP trigger with the real one: ```python# Restore the production triggerdefinition["triggers"] = production_trigger result = mcp("update_live_flow", environmentName=ENV, flowName=FLOW_ID, definition=definition, connectionReferences=connection_references, description="Swapped to production trigger after successful test") if result.get("error") is not None: print("Trigger swap failed:", result["error"])else: print("✅ Production trigger deployed — flow is live")``` > **Why this works**: The trigger is just the entry point — the actions are> identical regardless of how the flow starts. Testing via HTTP trigger> exercises all the same Compose, SharePoint, Teams, etc. actions.>> **Connector triggers** (e.g. "When an item is created in SharePoint"):> If actions reference `triggerBody()` or `triggerOutputs()`, pass a> representative test payload in `trigger_live_flow`'s `body` parameter> that matches the shape the connector trigger would produce. --- ## Gotchas | Mistake | Consequence | Prevention ||---|---|---|| Missing `connectionReferences` in deploy | 400 "Supply connectionReferences" | Always call `list_live_connections` first || `"operationOptions"` missing on Foreach | Parallel execution, race conditions on writes | Always add `"Sequential"` || `union(old_data, new_data)` | Old values override new (first-wins) | Use `union(new_data, old_data)` || `split()` on potentially-null string | `InvalidTemplate` crash | Wrap with `coalesce(field, '')` || Checking `result["error"]` exists | Always present; true error is `!= null` | Use `result.get("error") is not None` || Flow deployed but state is "Stopped" | Flow won't run on schedule | Call `set_live_flow_state` with `state: "Started"` — do **not** use `update_live_flow` for state changes || Teams "Chat with Flow bot" recipient as object | 400 `GraphUserDetailNotFound` | Use plain string with trailing semicolon (see below) | ### Teams `PostMessageToConversation` — Recipient Formats The `body/recipient` parameter format depends on the `location` value: | Location | `body/recipient` format | Example ||---|---|---|| **Chat with Flow bot** | Plain email string with **trailing semicolon** | `"user@contoso.com;"` || **Channel** | Object with `groupId` and `channelId` | `{"groupId": "...", "channelId": "..."}` | > **Common mistake**: passing `{"to": "user@contoso.com"}` for "Chat with Flow bot"> returns a 400 `GraphUserDetailNotFound` error. The API expects a plain string. --- ## Reference Files - [flow-schema.md](references/flow-schema.md) — Full flow definition JSON schema- [trigger-types.md](references/trigger-types.md) — Trigger type templates- [action-patterns-core.md](references/action-patterns-core.md) — Variables, control flow, expressions- [action-patterns-data.md](references/action-patterns-data.md) — Array transforms, HTTP, parsing- [action-patterns-connectors.md](references/action-patterns-connectors.md) — SharePoint, Outlook, Teams, Approvals- [build-patterns.md](references/build-patterns.md) — Complete flow definition templates (Recurrence+SP+Teams, HTTP trigger) ## Related Skills - `flowstudio-power-automate-mcp` — Core connection setup and tool reference- `flowstudio-power-automate-debug` — Debug failing flows after deploymentAdd Educational Comments
Takes any code file and transforms it into a teaching resource by adding educational comments that explain syntax, design choices, and language concepts. Automa
Agent Governance
When your AI agents start calling APIs, touching databases, or executing shell commands, you need guardrails before something goes sideways. This gives you comp
Agentic Eval
Implements self-critique loops where Claude generates output, evaluates it against your criteria, then refines based on its own feedback. Includes evaluator-opt