Awesome-copilot flowstudio-power-automate-mcp
git clone https://github.com/github/awesome-copilot
T=$(mktemp -d) && git clone --depth=1 https://github.com/github/awesome-copilot "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/flowstudio-power-automate-mcp" ~/.claude/skills/github-awesome-copilot-flowstudio-power-automate-mcp-483b8e && rm -rf "$T"
skills/flowstudio-power-automate-mcp/SKILL.mdPower Automate via FlowStudio MCP
This skill lets AI agents read, monitor, and operate Microsoft Power Automate cloud flows programmatically through a FlowStudio MCP server — no browser, no UI, no manual steps.
Real debugging examples: Expression error in child flow | Data entry, not a flow bug | Null value crashes child flow
Requires: A FlowStudio MCP subscription (or compatible Power Automate MCP server). You will need:
- MCP endpoint:
(same for all subscribers)https://mcp.flowstudio.app/mcp- API key / JWT token (
header — NOT Bearer)x-api-key- Power Platform environment name (e.g.
)Default-<tenant-guid>
Source of Truth
| Priority | Source | Covers |
|---|---|---|
| 1 | Real API response | Always trust what the server actually returns |
| 2 | | Tool names, parameter names, types, required flags |
| 3 | SKILL docs & reference files | Response shapes, behavioral notes, workflow recipes |
Start every new session with
. It returns the authoritative, up-to-date schema for every tool — parameter names, types, and required flags. The SKILL docs cover whattools/listcannot tell you: response shapes, non-obvious behaviors, and end-to-end workflow patterns.tools/listIf any documentation disagrees with
or a real API response, the API wins.tools/list
Recommended Language: Python or Node.js
All examples in this skill and the companion build / debug skills use Python
with
(stdlib — no urllib.request
pip install needed). Node.js is an
equally valid choice: fetch is built-in from Node 18+, JSON handling is
native, and the async/await model maps cleanly onto the request-response pattern
of MCP tool calls — making it a natural fit for teams already working in a
JavaScript/TypeScript stack.
| Language | Verdict | Notes |
|---|---|---|
| Python | ✅ Recommended | Clean JSON handling, no escaping issues, all skill examples use it |
| Node.js (≥ 18) | ✅ Recommended | Native + /; async/await fits MCP call patterns well; no extra packages needed |
| PowerShell | ⚠️ Avoid for flow operations | silently truncates nested definitions; quoting and escaping break complex payloads. Acceptable for a quick discovery call but not for building or updating flows. |
| cURL / Bash | ⚠️ Possible but fragile | Shell-escaping nested JSON is error-prone; no native JSON parser |
TL;DR — use the Core MCP Helper (Python or Node.js) below. Both handle JSON-RPC framing, auth, and response parsing in a single reusable function.
What You Can Do
FlowStudio MCP has two access tiers. FlowStudio for Teams subscribers get both the fast Azure-table store (cached snapshot data + governance metadata) and full live Power Automate API access. MCP-only subscribers get the live tools — more than enough to build, debug, and operate flows.
Live Tools — Available to All MCP Subscribers
| Tool | What it does |
|---|---|
| List flows in an environment directly from the PA API (always current) |
| List all Power Platform environments visible to the service account |
| List all connections in an environment from the PA API |
| Fetch the complete flow definition (triggers, actions, parameters) |
| Inspect the JSON body schema and response schemas of an HTTP-triggered flow |
| Get the current signed callback URL for an HTTP-triggered flow |
| POST to an HTTP-triggered flow's callback URL (AAD auth handled automatically) |
| Create a new flow or patch an existing definition in one call |
| Migrate a non-solution flow into a solution |
| List recent run history with status, start/end times, and errors |
| Get structured error details (per-action) for a failed run |
| Inspect inputs/outputs of any action (or every foreach iteration) in a run |
| Re-run a failed or cancelled run using its original trigger payload |
| Cancel a currently running flow execution |
Store Tools — FlowStudio for Teams Subscribers Only
These tools read from (and write to) the FlowStudio Azure table — a monitored snapshot of your tenant's flows enriched with governance metadata and run statistics.
| Tool | What it does |
|---|---|
| Search flows from the cache with governance flags, run failure rates, and owner metadata |
| Get full cached details for a single flow including run stats and governance fields |
| Get the trigger URL from the cache (instant, no PA API call) |
| Cached run history for the last N days with duration and remediation hints |
| Cached failed-only runs with failed action names and remediation hints |
| Aggregated stats: success rate, failure count, avg/max duration |
| Start or stop a flow via the PA API and sync the result back to the store |
| Update governance metadata (description, tags, monitor flag, notification rules, business impact) |
| List all environments from the cache |
| List all makers (citizen developers) from the cache |
| Get a maker's flow/app counts and account status |
| List all Power Apps canvas apps from the cache |
| List all Power Platform connections from the cache |
Which Tool Tier to Call First
| Task | Tool | Notes |
|---|---|---|
| List flows | | Always current — calls PA API directly |
| Read a definition | | Always fetched live — not cached |
| Debug a failure | → | Use live run data |
⚠️
returns a wrapper object with alist_live_flowsarray — access viaflows.result["flows"]
Store tools (
,list_store_flows, etc.) are available to FlowStudio for Teams subscribers and provide cached governance metadata. Use live tools when in doubt — they work for all subscription tiers.get_store_flow
Step 0 — Discover Available Tools
Always start by calling
tools/list to confirm the server is reachable and see
exactly which tool names are available (names may vary by server version):
import json, urllib.request TOKEN = "<YOUR_JWT_TOKEN>" MCP = "https://mcp.flowstudio.app/mcp" def mcp_raw(method, params=None, cid=1): payload = {"jsonrpc": "2.0", "method": method, "id": cid} if params: payload["params"] = params req = urllib.request.Request(MCP, data=json.dumps(payload).encode(), headers={"x-api-key": TOKEN, "Content-Type": "application/json", "User-Agent": "FlowStudio-MCP/1.0"}) try: resp = urllib.request.urlopen(req, timeout=30) except urllib.error.HTTPError as e: raise RuntimeError(f"MCP HTTP {e.code} — check token and endpoint") from e return json.loads(resp.read()) raw = mcp_raw("tools/list") if "error" in raw: print("ERROR:", raw["error"]); raise SystemExit(1) for t in raw["result"]["tools"]: print(t["name"], "—", t["description"][:60])
Core MCP Helper (Python)
Use this helper throughout all subsequent operations:
import json, urllib.request TOKEN = "<YOUR_JWT_TOKEN>" MCP = "https://mcp.flowstudio.app/mcp" def mcp(tool, args, cid=1): payload = {"jsonrpc": "2.0", "method": "tools/call", "id": cid, "params": {"name": tool, "arguments": args}} req = urllib.request.Request(MCP, data=json.dumps(payload).encode(), headers={"x-api-key": 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'])}") text = raw["result"]["content"][0]["text"] return json.loads(text)
Common auth errors:
- HTTP 401/403 → token is missing, expired, or malformed. Get a fresh JWT from mcp.flowstudio.app.
- HTTP 400 → malformed JSON-RPC payload. Check
and body structure.Content-Type: application/json → wrong or missing tool arguments.MCP error: {"code": -32602, ...}
Core MCP Helper (Node.js)
Equivalent helper for Node.js 18+ (built-in
fetch — no packages required):
const TOKEN = "<YOUR_JWT_TOKEN>"; const MCP = "https://mcp.flowstudio.app/mcp"; async function mcp(tool, args, cid = 1) { const payload = { jsonrpc: "2.0", method: "tools/call", id: cid, params: { name: tool, arguments: args }, }; const res = await fetch(MCP, { method: "POST", headers: { "x-api-key": TOKEN, "Content-Type": "application/json", "User-Agent": "FlowStudio-MCP/1.0", }, body: JSON.stringify(payload), }); if (!res.ok) { const body = await res.text(); throw new Error(`MCP HTTP ${res.status}: ${body.slice(0, 200)}`); } const raw = await res.json(); if (raw.error) throw new Error(`MCP error: ${JSON.stringify(raw.error)}`); return JSON.parse(raw.result.content[0].text); }
Requires Node.js 18+. For older Node, replace
withfetchfrom the stdlib or installhttps.request.node-fetch
List Flows
ENV = "Default-<tenant-guid>" result = mcp("list_live_flows", {"environmentName": ENV}) # Returns wrapper object: # {"mode": "owner", "flows": [{"id": "0757041a-...", "displayName": "My Flow", # "state": "Started", "triggerType": "Request", ...}], "totalCount": 42, "error": null} for f in result["flows"]: FLOW_ID = f["id"] # plain UUID — use directly as flowName print(FLOW_ID, "|", f["displayName"], "|", f["state"])
Read a Flow Definition
FLOW = "<flow-uuid>" flow = mcp("get_live_flow", {"environmentName": ENV, "flowName": FLOW}) # Display name and state print(flow["properties"]["displayName"]) print(flow["properties"]["state"]) # List all action names actions = flow["properties"]["definition"]["actions"] print("Actions:", list(actions.keys())) # Inspect one action's expression print(actions["Compose_Filter"]["inputs"])
Check Run History
# Most recent runs (newest first) runs = mcp("get_live_flow_runs", {"environmentName": ENV, "flowName": FLOW, "top": 5}) # Returns direct array: # [{"name": "08584296068667933411438594643CU15", # "status": "Failed", # "startTime": "2026-02-25T06:13:38.6910688Z", # "endTime": "2026-02-25T06:15:24.1995008Z", # "triggerName": "manual", # "error": {"code": "ActionFailed", "message": "An action failed..."}}, # {"name": "08584296028664130474944675379CU26", # "status": "Succeeded", "error": null, ...}] for r in runs: print(r["name"], r["status"]) # Get the name of the first failed run run_id = next((r["name"] for r in runs if r["status"] == "Failed"), None)
Inspect an Action's Output
run_id = runs[0]["name"] out = mcp("get_live_flow_run_action_outputs", { "environmentName": ENV, "flowName": FLOW, "runName": run_id, "actionName": "Get_Customer_Record" # exact action name from the definition }) print(json.dumps(out, indent=2))
Get a Run's Error
err = mcp("get_live_flow_run_error", { "environmentName": ENV, "flowName": FLOW, "runName": run_id }) # Returns: # {"runName": "08584296068...", # "failedActions": [ # {"actionName": "HTTP_find_AD_User_by_Name", "status": "Failed", # "code": "NotSpecified", "startTime": "...", "endTime": "..."}, # {"actionName": "Scope_prepare_workers", "status": "Failed", # "error": {"code": "ActionFailed", "message": "An action failed..."}} # ], # "allActions": [ # {"actionName": "Apply_to_each", "status": "Skipped"}, # {"actionName": "Compose_WeekEnd", "status": "Succeeded"}, # ... # ]} # The ROOT cause is usually the deepest entry in failedActions: root = err["failedActions"][-1] print(f"Root failure: {root['actionName']} → {root['code']}")
Resubmit a Run
result = mcp("resubmit_live_flow_run", { "environmentName": ENV, "flowName": FLOW, "runName": run_id }) print(result) # {"resubmitted": true, "triggerName": "..."}
Cancel a Running Run
mcp("cancel_live_flow_run", { "environmentName": ENV, "flowName": FLOW, "runName": run_id })
⚠️ Do NOT cancel a run that shows
because it is waiting for an adaptive card response. That status is normal — the flow is paused waiting for a human to respond in Teams. Cancelling it will discard the pending card.Running
Full Round-Trip Example — Debug and Fix a Failing Flow
# ── 1. Find the flow ───────────────────────────────────────────────────── result = mcp("list_live_flows", {"environmentName": ENV}) target = next(f for f in result["flows"] if "My Flow Name" in f["displayName"]) FLOW_ID = target["id"] # ── 2. Get the most recent failed run ──────────────────────────────────── runs = mcp("get_live_flow_runs", {"environmentName": ENV, "flowName": FLOW_ID, "top": 5}) # [{"name": "08584296068...", "status": "Failed", ...}, ...] RUN_ID = next(r["name"] for r in runs if r["status"] == "Failed") # ── 3. Get per-action failure breakdown ────────────────────────────────── err = mcp("get_live_flow_run_error", {"environmentName": ENV, "flowName": FLOW_ID, "runName": RUN_ID}) # {"failedActions": [{"actionName": "HTTP_find_AD_User_by_Name", "code": "NotSpecified",...}], ...} root_action = err["failedActions"][-1]["actionName"] print(f"Root failure: {root_action}") # ── 4. Read the definition and inspect the failing action's expression ─── defn = mcp("get_live_flow", {"environmentName": ENV, "flowName": FLOW_ID}) acts = defn["properties"]["definition"]["actions"] print("Failing action inputs:", acts[root_action]["inputs"]) # ── 5. Inspect the prior action's output to find the null ──────────────── out = mcp("get_live_flow_run_action_outputs", { "environmentName": ENV, "flowName": FLOW_ID, "runName": RUN_ID, "actionName": "Compose_Names" }) nulls = [x for x in out.get("body", []) if x.get("Name") is None] print(f"{len(nulls)} records with null Name") # ── 6. Apply the fix ───────────────────────────────────────────────────── acts[root_action]["inputs"]["parameters"]["searchName"] = \ "@coalesce(item()?['Name'], '')" conn_refs = defn["properties"]["connectionReferences"] result = mcp("update_live_flow", { "environmentName": ENV, "flowName": FLOW_ID, "definition": defn["properties"]["definition"], "connectionReferences": conn_refs }) assert result.get("error") is None, f"Deploy failed: {result['error']}" # ⚠️ error key is always present — only fail if it is NOT None # ── 7. Resubmit and verify ─────────────────────────────────────────────── mcp("resubmit_live_flow_run", {"environmentName": ENV, "flowName": FLOW_ID, "runName": RUN_ID}) import time; time.sleep(30) new_runs = mcp("get_live_flow_runs", {"environmentName": ENV, "flowName": FLOW_ID, "top": 1}) print(new_runs[0]["status"]) # Succeeded = done
Auth & Connection Notes
| Field | Value |
|---|---|
| Auth header | — not |
| Token format | Plain JWT — do not strip, alter, or prefix it |
| Timeout | Use ≥ 120 s for (large outputs) |
| Environment name | (find it via or response) |
Reference Files
- MCP-BOOTSTRAP.md — endpoint, auth, request/response format (read this first)
- tool-reference.md — response shapes and behavioral notes (parameters are in
)tools/list - action-types.md — Power Automate action type patterns
- connection-references.md — connector reference guide
More Capabilities
For diagnosing failing flows end-to-end → load the
flowstudio-power-automate-debug skill.
For building and deploying new flows → load the
flowstudio-power-automate-build skill.