mcpField
P
power-automate-debug

power-automate-debug

github.com/ninihen1開発3440 回インストール8 週間前に更新
リポジトリ

power-automate-debug について

FlowStudio MCPを通じて、Power Automateのクラウドフローの失敗を調べる診断スキルです。エージェント向けに、実行データから原因を特定し、修正と再検証を進める手順を示します。

主な機能

  1. 01フローと失敗実行の特定
  2. 02アクションの入出力確認
  3. 03上流データと式の追跡
  4. 04フロー修正と検証

活用シーン

  1. 01null値や誤ったフィールドの診断
  2. 02HTTPアクション失敗の原因調査
  3. 03失敗実行を再送して修正をテスト

MCP 対応

有効なJWTで接続できるFlowStudio MCPサーバーが必要です。フローや実行に関するツールを使い、失敗の調査、フローの更新、実行の再送を行います。

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/ninihen1/power-automate-mcp-skills --skill power-automate-debug

インストール後、Claude Code で/power-automate-debugを入力して読み込まれたか確認します。

同梱ファイルreferences/common-errors.mdreferences/debug-workflow.md

詳細

FlowStudio MCP を使用した Power Automate のデバッグ

FlowStudio MCP サーバーを通じて、失敗した Power Automate クラウドフローを調査するための、段階的な診断プロセスです。

実際のデバッグ例:子フローの式エラー | フローのバグではなくデータ入力の問題 | null 値が子フローをクラッシュさせる

前提条件:有効な JWT で FlowStudio MCP サーバーに接続できる必要があります。 接続の設定については、power-automate-mcp スキルを参照してください。 https://mcp.flowstudio.app で登録してください


信頼できる情報源

まず必ず list_skills / tool_search を呼び出し、利用可能なツール名とパラメーターのスキーマを確認してください。ツール名とパラメーターは、サーバーのバージョンによって変わる場合があります。 このスキルでは、ツールのスキーマからは分からない応答の形式、動作に関する注意事項、診断パターンを説明します。このドキュメントの内容が tool_search または実際の API 応答と異なる場合は、API の情報を優先してください。


Python ヘルパー

import 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

ステップ 1 — フローを特定する

result = mcp("list_live_flows", environmentName=ENV)
# Returns a wrapper object: {mode, flows, totalCount, error}
target = next(f for f in result["flows"] if "My Flow Name" in f["displayName"])
FLOW_ID = target["id"]   # plain UUID — use directly as flowName
print(FLOW_ID)

ステップ 2 — 失敗した実行を見つける

runs = mcp("get_live_flow_runs", environmentName=ENV, flowName=FLOW_ID, top=5)
# Returns direct array (newest first):
# [{"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": "...", "status": "Succeeded", "error": null, ...}]

for r in runs:
    print(r["name"], r["status"], r["startTime"])

RUN_ID = next(r["name"] for r in runs if r["status"] == "Failed")

ステップ 3 — 最上位のエラーを取得する

重要:get_live_flow_run_error は、どのアクションが失敗したかを示します。 get_live_flow_run_action_outputs は、なぜ失敗したかを示します。両方を呼び出す必要があります。 エラーだけで調査を終えてはいけません。ActionFailed、NotSpecified、InternalServerError などのエラーコードは汎用的なラッパーです。根本原因(誤ったフィールド、null 値、HTTP 500 の応答本文、スタックトレース)は、アクションの入力と出力にしか表示されません。

err = mcp("get_live_flow_run_error",
    environmentName=ENV, flowName=FLOW_ID, runName=RUN_ID)
# Returns:
# {
#   "runName": "08584296068667933411438594643CU15",
#   "failedActions": [
#     {"actionName": "Apply_to_each_prepare_workers", "status": "Failed",
#      "error": {"code": "ActionFailed", "message": "An action failed..."},
#      "startTime": "...", "endTime": "..."},
#     {"actionName": "HTTP_find_AD_User_by_Name", "status": "Failed",
#      "code": "NotSpecified", "startTime": "...", "endTime": "..."}
#   ],
#   "allActions": [
#     {"actionName": "Apply_to_each", "status": "Skipped"},
#     {"actionName": "Compose_WeekEnd", "status": "Succeeded"},
#     ...
#   ]
# }

# failedActions is ordered outer-to-inner. The ROOT cause is the LAST entry:
root = err["failedActions"][-1]
print(f"Root action: {root['actionName']} → code: {root.get('code')}")

# allActions shows every action's status — useful for spotting what was Skipped
# See common-errors.md to decode the error code.

ステップ 4 — 失敗したアクションの入力と出力を調べる

これが最も重要なステップです。 get_live_flow_run_error から分かるのは、汎用的なエラーコードだけです。実際のエラーの詳細(HTTP ステータスコード、応答本文、スタックトレース、null 値)は、アクションの実行時の入力と出力にあります。失敗したアクションを特定したら、直ちに必ず調べてください。

# Get the root failing action's full inputs and outputs
root_action = err["failedActions"][-1]["actionName"]
detail = mcp("get_live_flow_run_action_outputs",
    environmentName=ENV,
    flowName=FLOW_ID,
    runName=RUN_ID,
    actionName=root_action)

if len(detail) > 1:
    print(f"{root_action} returned {len(detail)} repetitions; inspect iteration indexes")
out = detail[0] if detail else {}
print(f"Action: {out.get('actionName')}")
print(f"Status: {out.get('status')}")

# For HTTP actions, the real error is in outputs.body
if isinstance(out.get("outputs"), dict):
    status_code = out["outputs"].get("statusCode")
    body = out["outputs"].get("body", {})
    print(f"HTTP {status_code}")
    print(json.dumps(body, indent=2)[:500])

    # Error bodies are often nested JSON strings — parse them
    if isinstance(body, dict) and "error" in body:
        err_detail = body["error"]
        if isinstance(err_detail, str):
            err_detail = json.loads(err_detail)
        print(f"Error: {err_detail.get('message', err_detail)}")

# For expression errors, the error is in the error field
if out.get("error"):
    print(f"Error: {out['error']}")

# Also check inputs — they show what expression/URL/body was used
if out.get("inputs"):
    print(f"Inputs: {json.dumps(out['inputs'], indent=2)[:500]}")
エラーコードだけでは分からない、アクションの出力から分かること
get_live_flow_run_error のエラーコードget_live_flow_run_action_outputs から分かること
ActionFailed実際に失敗した入れ子のアクションと、その HTTP 応答
NotSpecified実際のエラーを含む HTTP ステータスコードと応答本文
InternalServerErrorサーバーのエラーメッセージ、スタックトレース、または API エラーの JSON
InvalidTemplate失敗した正確な式と、null または型が誤った値
BadRequest送信されたリクエスト本文と、サーバーがそれを拒否した理由
Foreach の反復

actionName が foreach 内のアクションを指している場合、出力ツールはそのアクションのすべての反復を返すことがあります。各項目には、ループ名とゼロ始まりの itemIndex を含む repetitionIndexes が含まれる場合があります。疑わしい項目を見つけたら、iterationIndex を使って該当する反復を調べてください。

all_reps = mcp("get_live_flow_run_action_outputs",
    environmentName=ENV,
    flowName=FLOW_ID,
    runName=RUN_ID,
    actionName=root_action)

for rep in all_reps[:10]:
    print(rep.get("repetitionIndexes"), rep.get("status"), rep.get("error"))

one_rep = mcp("get_live_flow_run_action_outputs",
    environmentName=ENV,
    flowName=FLOW_ID,
    runName=RUN_ID,
    actionName=root_action,
    iterationIndex=3)
証拠となる Compose の前後アクション

コネクタの動作が不確かな場合は、リスクのあるアクションの前に Compose_*_Request を、後に Compose_*_Result を追加し、結果アクションが Succeeded と Failed のどちらでも実行されるようにします。これにより、再デプロイせずに、今後のデバッグに役立つ明確なペイロードのスナップショットを取得できます。これらの前後アクションには、シークレットや長いバイナリペイロードを含めないでください。

例:HTTP アクションが 500 を返す場合
Error code: "InternalServerError" ← this tells you nothing

Action outputs reveal:
  HTTP 500
  body: {"error": "Cannot read properties of undefined (reading 'toLowerCase')
    at getClientParamsFromConnectionString (storage.js:20)"}
  ← THIS tells you the Azure Function crashed because a connection string is undefined
例:null による式エラー
Error code: "BadRequest" ← generic

Action outputs reveal:
  inputs: "body('HTTP_GetTokenFromStore')?['token']?['access_token']"
  outputs: ""   ← empty string, the path resolved to null
  ← THIS tells you the response shape changed — token is at body.access_token, not body.token.access_token

ステップ 5 — フロー定義を読む

defn = mcp("get_live_flow", environmentName=ENV, flowName=FLOW_ID)
actions = defn["properties"]["definition"]["actions"]
print(list(actions.keys()))

定義内で失敗したアクションを見つけます。想定されるデータを理解するため、inputs の式を確認してください。


ステップ 6 — 失敗箇所からさかのぼる

失敗したアクションの入力が上流のアクションを参照している場合は、 それらも調べます。問題のあるデータの発生元が見つかるまで、チェーンを さかのぼってください。

# Inspect multiple actions leading up to the failure
for action_name in [root_action, "Compose_WeekEnd", "HTTP_Get_Data"]:
    result = mcp("get_live_flow_run_action_outputs",
        environmentName=ENV,
        flowName=FLOW_ID,
        runName=RUN_ID,
        actionName=action_name)
    out = result[0] if result else {}
    print(f"\n--- {action_name} ({out.get('status')}) ---")
    print(f"Inputs:  {json.dumps(out.get('inputs', ''), indent=2)[:300]}")
    print(f"Outputs: {json.dumps(out.get('outputs', ''), indent=2)[:300]}")

⚠️ 配列を処理するアクションの出力ペイロードは、非常に大きくなることがあります。 出力する前に、必ずスライスしてください(例:[:500])。

ヒント:問題のあるデータを生成したアクションが分からない場合は、actionName を省略すると、トップレベルのアクションが一覧表示されます。foreach 内のアクションを選んだら、iterationIndex を指定して、すべての反復をコンテキストに読み込まないようにしてください。


ステップ 7 — 根本原因を特定する

式のエラー(例:null に対する split)

エラーに InvalidTemplate または関数名が含まれている場合:

  1. 定義内で該当するアクションを見つけます
  2. そのアクションが読み取る上流のアクションまたは式を確認します
  3. その上流アクションの出力に null やフィールドの欠落がないか調べます
# Example: action uses split(item()?['Name'], ' ')
# → null Name in the source data
result = mcp("get_live_flow_run_action_outputs", ..., actionName="Compose_Names")
if not result:
    print("No outputs returned for Compose_Names")
    names = []
else:
    names = result[0].get("outputs", {}).get("body") or []
nulls = [x for x in names if x.get("Name") is None]
print(f"{len(nulls)} records with null Name")
フィールドパスの誤り

式 triggerBody()?['fieldName'] が null を返す場合、fieldName が誤っています。 トリガーの出力を調べて、実際のフィールド名を確認してください:

result = mcp("get_live_flow_run_action_outputs", ..., actionName="<trigger-action-name>")
print(json.dumps(result[0].get("outputs"), indent=2)[:500])
エラーを返す HTTP アクション

エラーコードが InternalServerError または NotSpecified の場合は、実際の HTTP ステータスとレスポンス本文を確認するために、必ずアクションの出力を調べてください:

result = mcp("get_live_flow_run_action_outputs", ..., actionName="HTTP_Get_Data")
out = result[0]
print(f"HTTP {out['outputs']['statusCode']}")
print(json.dumps(out['outputs']['body'], indent=2)[:500])
接続/認証の失敗

ConnectionAuthorizationFailed がないか確認してください。接続の所有者は、フローの実行に使われているサービスアカウントと一致する必要があります。API 経由では修正できません。PA デザイナーで修正してください。

Outlook のユーザー選択失敗(DynamicListValuesUndefinedOrInvalid)

GetEmailsV3 などの Outlook アクションでは、mailboxAddress、to、cc、from といったパラメーターのドロップダウンが builtInOperation:AadGraph.GetUsers によって提供されます。しかし、この操作は PA の listEnum レイヤーで壊れており、常に DynamicListValuesUndefinedOrInvalid を返します。エージェントが update_live_flow 経由で Outlook アクションを再構築または変更し、動的なオプションからユーザーを解決しようとすると、この問題が発生します。AadGraph を再試行して解決しようとしないでください。代わりに shared_office365users.SearchUserV2 に切り替えてください(同じ AAD ユーザー形式が返されます)。describe_live_connector を使って、該当するパラメーターに構造化された fallback があるかどうかを確認し、壊れている AadGraph 操作ではなく shared_office365users.SearchUserV2 に対して get_live_dynamic_options を呼び出してください。ドロップダウンのオプションではなく動的なフィールドスキーマを取得する場合は、describe_live_connector が返すメタデータを使って get_live_dynamic_properties を呼び出してください。


ステップ 8 — 修正を適用する

式/データの問題の場合:

defn = mcp("get_live_flow", environmentName=ENV, flowName=FLOW_ID)
acts = defn["properties"]["definition"]["actions"]

# Example: fix split on potentially-null Name
acts["Compose_Names"]["inputs"] = \
    "@coalesce(item()?['Name'], 'Unknown')"

conn_refs = defn["properties"]["connectionReferences"]
result = mcp("update_live_flow",
    environmentName=ENV,
    flowName=FLOW_ID,
    definition=defn["properties"]["definition"],
    connectionReferences=conn_refs)

print(result.get("error"))  # None = success

⚠️ update_live_flow は常に error キーを返します。 値が null(Python の None)の場合は成功です。


ステップ 9 — 修正を検証する

HTTP トリガーに限らず、どのフローのテストにも resubmit_live_flow_run を使ってください。 resubmit_live_flow_run は、以前の実行を元のトリガーペイロードで再生します。これは、Recurrence、SharePoint の「アイテムが作成されたとき」、コネクタの Webhook、Button トリガー、HTTP トリガーなど、すべてのトリガーの種類で機能します。ユーザーに手動でフローをトリガーするよう依頼したり、次のスケジュール実行を待ったりする必要はありません。

resubmit を利用できないのは、一度も実行されていない新規フローだけです。再生できる過去の実行がありません。

# Resubmit the failed run — works for ANY trigger type
resubmit = mcp("resubmit_live_flow_run",
    environmentName=ENV, flowName=FLOW_ID, runName=RUN_ID)
print(resubmit)   # {"resubmitted": true, "triggerName": "..."}

# Wait ~30 s then check
import time; time.sleep(30)
new_runs = mcp("get_live_flow_runs", environmentName=ENV, flowName=FLOW_ID, top=3)
print(new_runs[0]["status"])   # Succeeded = done
resubmit と trigger の使い分け
シナリオ使用するもの理由
どのフローでも修正をテストする場合resubmit_live_flow_run失敗の原因となったトリガーペイロードをそのまま再生するため、検証に最適です
Recurrence/スケジュールされたフローtrigger_live_flow(body なし)ポータルの「フローを実行」ボタンのように、今すぐ実行します。resubmit は過去の実行データを再生します
SharePoint/コネクタのトリガーresubmit_live_flow_run実際の SP アイテムを作成せずにトリガーすることはできません
独自のテストペイロードを使う HTTP、Button、または PowerApps トリガーtrigger_live_flow元の実行とは異なるデータを送信する必要がある場合に使用します
一度も実行されていない新規フローtrigger_live_flowresubmit できる過去の実行がありません
カスタムペイロードを使用した HTTP、Button、PowerApps フローのテスト

Request トリガー(HTTP 要求、手動の Button、PowerApps)を使用するフローで、元の実行とは異なるペイロードを送信する必要がある場合は、trigger_live_flow を使用します。すべての種類で、トリガーの入力を body として渡します。

# First inspect what the trigger expects — read directly from the flow definition
defn = mcp("get_live_flow", environmentName=ENV, flowName=FLOW_ID)
triggers = defn["properties"]["definition"]["triggers"]
manual = next(iter(triggers.values()))   # usually the only trigger on HTTP flows
request_schema = manual.get("inputs", {}).get("schema")
print("Expected body schema:", request_schema)

# Response schemas live on Response action(s) in the actions block
for name, act in defn["properties"]["definition"]["actions"].items():
    if act.get("type") == "Response":
        print(f"Response {name}:", act.get("inputs", {}).get("schema"))

# Trigger with a test payload
result = mcp("trigger_live_flow",
    environmentName=ENV,
    flowName=FLOW_ID,
    body={"name": "Test User", "value": 42})
print(f"Status: {result['responseStatus']}, Body: {result.get('responseBody')}")
print(f"Kind: {result['triggerKind']}, via: {result['invocation']}, run: {result.get('runName')}")
if result.get("warning"):
    print(result["warning"])   # required trigger inputs you left out

trigger_live_flow は、AAD 認証されたトリガーを自動的に処理します。 Request トリガー(HTTP 要求、Button、PowerApps)と、スケジュールされた(Recurrence)フローに対応しており、スケジュールされたフローは即座に実行されます。スケジュールされたトリガーは入力を受け取らないため、body は指定しません(指定すると拒否されます)。自動化されたコネクタートリガーは、ソースイベントからのみ起動します。

Power Automate は、トリガーの required 入力を強制しません。入力を省略しても実行は開始され、その入力は null になり、結果には不足しているキーを示す warning が含まれます。それが問題になる場合は、実行をキャンセルし、すべての入力を含む body を指定して再度呼び出してください。

runName が返されるのは、Button と PowerApps の実行のみです。HTTP トリガーの場合は、get_live_flow_runs で実行を探してください。

ブラウザー拡張機能のキーを使用する場合、Button と PowerApps のトリガーは空の body でのみ実行されます。ツールはその旨を伝え、回避方法を示します。過去の実行を再送信する、フロー内で coalesce(triggerBody()?['x'], 'value') を使って入力に既定値を設定する、または標準の API キーを使用する方法があります。


クイックリファレンス:診断の判断ツリー

症状最初のツール次に必ず呼び出すツール確認する内容
フローが Failed と表示されるget_live_flow_run_error失敗したアクションに対して get_live_flow_run_action_outputsoutputs 内の HTTP ステータスとレスポンス body
エラーコードが汎用的(ActionFailed、NotSpecified)—get_live_flow_run_action_outputsoutputs.body に実際のエラーメッセージ、スタックトレース、または API エラーが含まれている
HTTP アクションが 500 を返す—get_live_flow_run_action_outputsサーバーエラーの詳細を示す outputs.statusCode と outputs.body
式のクラッシュ—前のアクションに対して get_live_flow_run_action_outputs出力 body 内の null フィールド、または型が誤っているフィールド
フローが開始されないget_live_flow—properties.state = "Started" であることを確認
アクションが誤ったデータを返すget_live_flow_run_action_outputs—実際の出力 body と期待される出力を比較
修正を適用しても失敗する再送信後に get_live_flow_runs—新しい実行の status フィールド

ルール:エラーコードだけで原因を診断してはいけません。 get_live_flow_run_error は失敗したアクションを特定します。get_live_flow_run_action_outputs は 実際の原因を明らかにします。必ず両方を呼び出してください。


参照ファイル

関連スキル

  • power-automate-mcp — 基礎スキル:接続のセットアップ、MCP ヘルパー、ツールの検出
  • power-automate-build — 新しいフローの構築とデプロイ

コメント