はじめに
2026年10月29日、AWS Step Functionsのワークフローを、AIエージェントのツールとして使えるようになりました。Amazon Bedrock AgentCore Gatewayの組み込みコネクタを経由します。
エージェントは「何をどの順で行うか」を自分で決めるのが持ち味です。一方で、返金やオンボーディングのように「毎回同じ手順で実行し、何が実行されたかの記録も残したい」業務があります。このコネクタは、その手順をステートマシンに持たせ、エージェントには「いつ起動するか、何を入力にするか」だけを任せる仕組みです。
ただ、エージェントに実行系のAPIを渡すとなると、気になるのは機能紹介よりも「どこまで許すか」です。エージェントが呼べる範囲を絞る仕組み、つまり統制には、ピン留め・IAM・タスクトークンの3つがあります。本記事では公式ドキュメントを一次情報として、コネクタが公開する10個のオペレーションを整理し、この3つの統制をどう使い分けるかを、実機で確かめた結果とあわせて解説します。
検証は8項目です。コネクタに固有の5項目は、tools/list、ピンの拒否、includedData、実行ARNのIAM拒否、waitForTaskTokenによる承認フローです。これに、Standard / Expressの制約、公開していないステートマシンのトークン、CloudTrailでの呼び出し元の見え方の3項目を加えました。結果は「実際に検証した内容と結果」にまとめています。
前提:アップデートで、統制はどこに移ったか
3つの統制のうち、ピンは、ゲートウェイのターゲット設定で、オペレーションのstateMachineArnをあらかじめ固定しておく設定です。ピンしたパラメータはツールの入力スキーマから消え、エージェントは値を変えられません。
公式ドキュメントは、このコネクタの利点を「Lambda関数もMCPサーバーも書かず、ツールも自分で定義しなくてよい」と説明しています(出典:AWS Step Functions as Connector Target)。MCP(Model Context Protocol)は、エージェントがツールを探して呼び出すための共通の取り決めです。裏返すと、アップデート前は、Lambda関数や自作のMCPサーバーを用意して、Step Functionsをツール化していたと読めます(筆者の解釈)。その場合、エージェントに許す範囲を絞る統制は、次のようにコードとロールの側にあったと考えられます。
| 統制 | アップデート前(Lambdaや自作MCPサーバーでツール化)(筆者の解釈) | アップデート後(コネクタ) |
|---|---|---|
| 対象の固定(ピン) | コードにステートマシンのARNを書く | ターゲット設定のparameterValuesで指定する。ゲートウェイが、違う値を渡す呼び出しを拒否する |
| IAM | Lambdaの実行ロールなど、自分で用意したロールで許可範囲を絞る | ゲートウェイのサービスロールで絞る。同一ゲートウェイのターゲット間で共有する |
| タスクトークン | 自分のコードで扱う。検証ロジックなどを挟める |
SendTask*をそのまま公開する。IAMでは絞れず、トークンだけが制御になる |
アップデート後の列も、同じドキュメントの記載にもとづいています。
つまり、コードで書いていた制御が、「ターゲットの設定」と「サービスロール」に置き換わりました。コードを書かなくてよい代わりに、設定どおりに効くか、効かない所はどこかを、自分で把握しておく必要があります。本記事で確かめたいのは、次の3点です。
| 統制 | 確かめたいこと | 対応する検証(後述の表の番号) |
|---|---|---|
| ピン | 違うstateMachineArnを渡すと、設定どおり拒否されるか。エージェントには何が見えるか |
1、2 |
| IAM | 実行ARNで絞ったポリシーで、許可していない実行の参照・停止が拒否されるか。includedDataで何が渡るか |
3、4 |
| タスクトークン | IAMで絞れない前提で、承認フローがどう動き、トークンが何を制御するか | 5 |
結論
| 論点 | 結論 |
|---|---|
| 何が使えるようになったか | AgentCore Gatewayの組み込みコネクタ(connectorId: "states")で、既存のStandard / Expressステートマシンを、Lambdaも自作MCPサーバーも書かずにMCPツールとして公開できる |
| 役割分担 | エージェントは「いつ・どの入力で実行するか」を決め、ステートマシンが「どのステップをどの順で実行するか」を決める。エージェントはステップを省略・並べ替えできない |
| 統制は3層 | ①ステートマシンのピン留め、②ゲートウェイのサービスロールのIAMポリシー、③タスクトークンの秘匿。①と②は補完関係で、③はIAMで絞れない |
| 選ぶ基準 | 後から実行を参照・停止したい、監査証跡が要る業務はStandard。同期で結果が欲しいだけならExpress(検証した制約から導いた、筆者の解釈) |
| 検証で確認できたこと | ピンしたstateMachineArnを渡すと拒否され、実行は作られない。許可していない実行ARNへの参照・停止はIAMで拒否される。includedData: ALL_DATAで入出力が応答に入る。タスクトークンはResource: "*"で動き、使用済みトークンの再送は失敗する。ゲートウェイに公開していないステートマシンの実行も、トークンがあれば完了できる。Standard / Expressの制約エラーは公式の表どおりに発生する |
「何が使えるようになったか」「役割分担」「統制は3層」の出典は、前提の節で挙げた公式ドキュメントです。
検証環境
| 項目 | 内容 |
|---|---|
| リージョン | ap-northeast-1(東京) |
| ゲートウェイ | AWS_IAM認可。主なターゲットはhello、approval。Express検証用にexpress、wrongtypeを追加 |
| ステートマシン | Standard 4つ(AgentHelloWorld、AgentOtherWorkflow、ApprovalWorkflow、UnexposedApproval)、Express 1つ(AgentExpressWorkflow) |
| 呼び出し側 | awscurl(SigV4署名。AWSのリクエスト署名方式)。エージェントが送るのと同じMCPのJSON-RPCリクエストを直接送った |
| ツール | AWS CLI 2.34.24(検証の途中で2.39.3に更新。後述)、Python 3.12、awscurl 0.44 |
前提として、IAMロール、Step Functionsの基本、MCPのやり取りに使われるJSON-RPC(ツール一覧を取るtools/listと、ツールを呼ぶtools/call)を理解している読者を想定します。公式チュートリアルと同じ構成をベースにしています。そこに、IAM拒否の確認用に公開しないステートマシン、承認フロー用のSQSとワークフロー、Standard / Expressの違いを確かめるExpressのステートマシンを追加しました。
全体像
公式ドキュメントによると、処理の流れは次のとおりです。
| # | 内容 |
|---|---|
| 1 | ゲートウェイにconnectorId: "states"のターゲットを追加し、ツール化するオペレーションを選ぶ。対応するものにはstateMachineArnを固定(ピン)する。ゲートウェイがツールスキーマのスナップショットを取る |
| 2 | エージェントがtools/listを呼ぶと、設定したオペレーションごとに1ツールが見える。ピンしたパラメータはスキーマに出ない(例:StartExecutionはinputのみ) |
| 3 | エージェントがtools/callを呼ぶと、ゲートウェイがピンしたパラメータと引数をマージし、サービスロールでStep Functionsを呼ぶ |
| 4 | Step Functions APIのレスポンスが、JSON文字列としてcontent[].textに入って返る。エラー時はisError: trueで、Step Functionsのエラー名が入る(検証では、本文の先頭にエラー名が付いた形で返った。後述) |
| 5 | エージェントがツールを連鎖する。典型例はStartExecution → DescribeExecution → GetExecutionHistory
|
出典:AWS Step Functions as Connector Target
tools/listを呼ぶと、設定した6オペレーション分のツールが、アルファベット順(設定順ではない)で返りました。ツール名はhello___DescribeExecutionのように、ターゲット名に___(アンダースコア3つ)が付く形です。hello___StartExecutionの入力スキーマはinputのみで、stateMachineArnは見えませんでした。
コネクタが公開する10のオペレーション
公式ドキュメントによると、コネクタ(version 1.0.0)は10のオペレーションを公開します。大きく「ステートマシンにピンできる4つ」と「エージェントが識別子を渡す6つ」に分かれます。
ピンできる4つ
| オペレーション | 役割 | 既定でエージェントに見えるフィールド | 追加で公開できるフィールド | 対応するワークフロー |
|---|---|---|---|---|
StartExecution |
ステートマシンを起動する。返ってくるexecutionArnで、あとから状態を追える |
input(JSON文字列) |
name, traceHeader
|
Standard / Express |
StartSyncExecution |
Expressを起動し、完了まで待って、結果を同じ呼び出しで返す | input |
name, traceHeader
|
Expressのみ |
ListExecutions |
ステートマシンの実行の一覧を返す。ステータスで絞れる | statusFilter |
maxResults, nextToken
|
Standardのみ |
DescribeStateMachine |
ステートマシンの定義・ロール・設定を返す | includedData |
なし | Standard / Express |
エージェントが識別子を渡す6つ(ピン不可)
| オペレーション | 役割 | 渡す識別子 | 既定フィールド | 追加で公開できるフィールド | 対応するワークフロー |
|---|---|---|---|---|---|
DescribeExecution |
実行の状態(成功・失敗など)と、開始・終了時刻を返す。ALL_DATAなら入出力も返す |
executionArn |
executionArn, includedData
|
なし | Standardの実行のみ |
StopExecution |
実行中の実行を止める | executionArn |
executionArn |
error, cause
|
Standardの実行のみ |
GetExecutionHistory |
実行のイベント履歴(各ステップの出来事)を返す | executionArn |
executionArn |
includeExecutionData, maxResults, reverseOrder, nextToken
|
Standardの実行のみ |
SendTaskSuccess |
承認待ちのタスクを、成功として再開する | taskToken |
taskToken, output
|
なし | Standard / Express |
SendTaskFailure |
承認待ちのタスクを、失敗として終わらせる | taskToken |
taskToken, error, cause
|
なし | Standard / Express |
SendTaskHeartbeat |
長い作業中であることを伝え、タイムアウトを防ぐ | taskToken |
taskToken |
なし | Standard / Express |
追加フィールドを見せたい場合は、そのオペレーションの設定にparameterOverridesでvisible: trueを指定します。stateMachineArnだけはparameterOverridesでは公開できず、ピンでのみ指定できます。
DescribeExecutionとDescribeStateMachineのincludedDataは、既定がMETADATA_ONLYです。ALL_DATAを指定すると、実行の入出力やステートマシンの定義が含まれます。
試してみると、includedDataを省略した応答は、METADATA_ONLYを指定した応答と同一でした。DescribeStateMachineをMETADATA_ONLYで呼ぶと、definitionは"{}"(空)で返り、ALL_DATAで定義の全文が入りました。
Standard / Expressの違い(ハマりどころ)
次のエラーはStep Functions側の制約で、コネクタの不具合ではありません。
| オペレーション | 使えるタイプ | 違うタイプで使うと |
|---|---|---|
StartSyncExecution |
Expressのみ | Standardでは失敗する。StartExecutionとDescribeExecutionのポーリングを使う |
ListExecutions |
Standardのみ | ExpressではStateMachineTypeNotSupported
|
DescribeExecution / GetExecutionHistory / StopExecution
|
Standardの実行のみ | Expressの実行ARNではInvalidArn(開始後はARNで扱えないため) |
StartExecution / DescribeStateMachine / SendTask*
|
両方 | - |
- 1つのターゲットに両タイプを同居させたい場合は、
StartSyncExecutionをExpressに、他のピン可能なオペレーションをStandardに固定します。 - コールバックパターン(
.waitForTaskToken)はStandardのみです。Expressは非対応です。 -
tools/listで返るツール説明文に、これらの制約が書かれているため、エージェントが適切なツールを選べます。
出典:AWS Step Functions as Connector Target
tools/listの説明文を見ると、DescribeExecution・GetExecutionHistory・ListExecutions・StopExecutionの4つに、「Standard workflows only」とありました。ListExecutionsには、ExpressではStateMachineTypeNotSupportedで失敗するとも書かれています。StartExecutionとDescribeStateMachineには制約の記載がなく、両タイプで使えることと対応します。
さらに、Expressのステートマシンを用意して、上の表の動きを実際に発生させました。
| 操作 | 結果 |
|---|---|
StandardのステートマシンにStartSyncExecution
|
StateMachineTypeNotSupported(This operation is not supported by this type of state machine) |
ExpressのステートマシンにStartSyncExecution
|
成功。応答にstatus、output、billingDetailsが入る |
ExpressのステートマシンにListExecutions
|
StateMachineTypeNotSupported |
ExpressのStartExecutionで得た実行ARNをDescribeExecution・GetExecutionHistory・StopExecutionに渡す |
3つともInvalidArn(Resource type not valid in this context: express) |
Expressのステートマシンを、.waitForTaskTokenを含む定義で作成 |
作成時にInvalidDefinition(Express state machine does not support '.waitForTaskToken' service integration) |
公式の表と一致しました。公式はStartSyncExecutionをStandardに向けた場合を「失敗する」としか書いていませんが、実際のエラー名はStateMachineTypeNotSupportedでした。また、Expressの実行ARNはexpress:<ステートマシン名>:...という、Standardのexecution:とは別のリソースタイプで返ります。今回はexpress:AgentExpressWorkflow:*もIAMで許可しておき、IAM拒否ではなくStep Functions側のInvalidArnが返ることを確認しました。
Expressを公開すると、エージェントは起動後に状態確認・履歴参照・停止ができません(上の検証のとおり、3つともInvalidArnになります)。「あとから実行を追える」ことが要件なら、Standardを選ぶのが無難です(筆者の解釈)。
統制設計:エージェントに何を許すか
ここが本記事の中心です。公式ドキュメントは、統制を「ピン」と「IAM」の2層で説明しています。両者は代替ではなく補完関係です(出典:AWS Step Functions as Connector Target)。
図1:エージェントへの権限の絞り方(1:ピン留め、2:サービスロールのIAM、3:タスクトークン)
| 統制 | 効く範囲 | 備考 |
|---|---|---|
ピン(stateMachineArnの固定) |
StartExecution, StartSyncExecution, ListExecutions, DescribeStateMachine
|
エージェントはこの対象を変更できない。ゲートウェイが拒否する |
| ゲートウェイのサービスロールのIAMポリシー | ピンできない6つ |
DescribeExecution / StopExecution / GetExecutionHistoryは、公開するステートマシンの実行ARNに絞れる |
| タスクトークンの秘匿 |
SendTaskSuccess / SendTaskFailure / SendTaskHeartbeat
|
IAMにリソースレベル権限がなく"*"で許可する。有効なトークンを持つエージェントは誰でもそのタスクを完了できる |
① ピン留め
ピンしたパラメータはツールスキーマから取り除かれ、すべての呼び出しで強制されます。エージェントがstateMachineArnを渡すと、次のエラーで拒否されます。
Invalid request parameters: cannot set parameter(s): [/stateMachineArn]
別のステートマシン(AgentOtherWorkflow)のARNをstateMachineArnとして渡してみました。isError: trueで次の内容が返り、実行は作られませんでした。公式の記載と同じ文言で、先頭にエラー名が付きます。
IllegalArgumentException - Invalid request parameters: cannot set parameter(s): [/stateMachineArn]
エラー名はIllegalArgumentExceptionで、後述のIAM拒否(DependencyFailedExceptionで包まれたStep Functionsのエラー)とは形式が異なります。ピン違反は、Step Functionsを呼ぶ前にゲートウェイが拒否していると読めます(筆者の解釈)。
公式ドキュメントには、次の注意点が書かれています。
- ピンはオペレーションごとに1つです。複数のステートマシンを同じオペレーションで公開するなら、ターゲットを分けます。
- ピンすべきオペレーションで
parameterValues.stateMachineArnを設定し忘れても、ターゲット自体は作成できます。ただし、ステートマシンが渡されないため、ツールは毎回失敗します。
② サービスロールのIAMポリシー
ピンできないDescribeExecutionなど3つは、エージェントが渡す実行ARNに対して動きます。ここを絞るのはサービスロールのIAMポリシーです。
実行ARNはステートマシンARNとは別のリソースタイプなので、公式チュートリアルのポリシーは2つのStatementに分かれています(出典:チュートリアル)。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "StateMachineOperations",
"Effect": "Allow",
"Action": [
"states:StartExecution",
"states:ListExecutions",
"states:DescribeStateMachine"
],
"Resource": "arn:aws:states:<region>:<account-id>:stateMachine:AgentHelloWorld"
},
{
"Sid": "ExecutionOperations",
"Effect": "Allow",
"Action": [
"states:DescribeExecution",
"states:GetExecutionHistory",
"states:StopExecution"
],
"Resource": "arn:aws:states:<region>:<account-id>:execution:AgentHelloWorld:*"
}
]
}
信頼ポリシーは、bedrock-agentcore.amazonaws.comを信頼しつつ、aws:SourceAccountとaws:SourceArn(gateway/...)で絞ります。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "Service": "bedrock-agentcore.amazonaws.com" },
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": { "aws:SourceAccount": "<account-id>" },
"ArnLike": {
"aws:SourceArn": "arn:aws:bedrock-agentcore:<region>:<account-id>:gateway/step-functions-gateway-*"
}
}
}
]
}
上のポリシー(AgentHelloWorldの実行ARNのみ許可)を適用した状態で、許可していないAgentOtherWorkflowの実行ARNを、DescribeExecution・GetExecutionHistory・StopExecutionに渡しました。3つとも拒否されました。許可しているAgentHelloWorldの実行は参照できました。DescribeExecutionの拒否は次のとおりです(アカウントIDなどは伏せています)。
DependencyFailedException - {"errorType":"com.amazon.coral.service#AccessDeniedException","errorMessage":"User: arn:aws:sts::<account-id>:assumed-role/AgentCoreGatewayToStepFunctions/gateway-session-invocation is not authorized to perform: states:DescribeExecution on resource: arn:aws:states:<region>:<account-id>:execution:AgentOtherWorkflow:<execution-id> because no identity-based policy allows the states:DescribeExecution action","statusCode":400,"service":"states","operation":"DescribeExecution"}
読み取れる点は次の2つです。
- 拒否はStep FunctionsのIAM評価で起きている(
"service":"states")。エラー名はDependencyFailedExceptionで包まれるため、エージェント側は中のJSONを読む必要がある - 呼び出し主体は、サービスロールの
assumed-role/.../gateway-session-invocation。セッション名は固定で、どのエージェントや利用者の呼び出しかは、このメッセージからは分からない
次の2枚の画面は、検証5で承認ワークフロー(ApprovalWorkflow)を追加したあとのものです。そのため、ResourceにはAgentHelloWorldとApprovalWorkflowの2つが入っています。
図2:サービスロールのインラインポリシー(StateMachineOperations:ステートマシンARNで許可)
図3:サービスロールのインラインポリシー(ExecutionOperations:実行ARNで許可)

同一ゲートウェイのターゲットは、サービスロールを共有します。ワークフローごとにIAM境界を変えたい場合は、ゲートウェイを分けます。
③ タスクトークン
SendTaskSuccess・SendTaskFailure・SendTaskHeartbeatの3つは、IAMで特定の実行に絞れません(出典:AWS Step Functions as Connector Target)。そのため、タスクトークンそのものが唯一の制御になります。トークンは秘密情報として扱い、処理すべきエージェントにだけ届けます。
サービスロールにstates:SendTaskSuccessなど3つをResource: "*"で許可すると(下のスクリーンショット)、ゲートウェイ経由のSendTaskSuccessは成功しました。一度完了したタスクのトークンを再送すると、TaskTimedOut(Provided task does not exist anymore)で失敗しました。
図4:サービスロールのインラインポリシー(TaskTokenOperations:SendTask*をResource: "*"で許可)
また、ゲートウェイのどのターゲットにも公開していないステートマシン(UnexposedApproval)で、実行をCLIから直接起動しました。そのトークンをゲートウェイ経由のSendTaskSuccessに渡すと、実行はSUCCEEDEDになりました。でたらめなトークンはInvalidTokenで拒否されました。ピンでもIAMでも絞れないため、トークンを持っていれば、ゲートウェイに公開していないステートマシンの実行でも完了できます。
図5:ゲートウェイに公開していないステートマシン(UnexposedApproval)のグラフビュー(ゲートウェイ経由のSendTaskSuccessの後。全ステップが成功)
図6:同じ実行のイベント履歴(TaskSucceededからExecutionSucceededまで)
統制設計のまとめ
| 要件 | 使う統制 |
|---|---|
| 起動できるワークフローを限定したい | ピン留め |
| 参照・停止できる実行の範囲を限定したい | サービスロールのIAMポリシー(実行ARNでスコープ) |
| 人手承認など、後から再開するタスクを渡したい | タスクトークンの配送先を限定(IAMでは絞れない) |
| ワークフローごとに権限境界を分けたい | ゲートウェイを分ける(サービスロールが共有のため) |
運用上の観点
| 観点 | 内容 |
|---|---|
| 呼び出し元の記録 | ゲートウェイ経由の実行は、CloudTrail上でゲートウェイのサービスロールが呼び出し元になる(出典:チュートリアル)。実際のイベントでも、StartExecutionイベントのuserIdentityがAssumedRole(assumed-role/AgentCoreGatewayToStepFunctions/gateway-session-invocation)で、invokedByはbedrock-agentcore.amazonaws.comだった |
| 誰が起動したかの識別 | 呼び出し元がゲートウェイのロールなので、「どのエージェント/利用者が起動したか」はCloudTrailだけでは区別できない。確認した範囲では、セッション名はgateway-session-invocationで固定で、ゲートウェイIDや、呼び出したエージェント・利用者を示すフィールドは見当たらなかった。また、requestParameters.inputはHIDDEN_DUE_TO_SECURITY_REASONSに置き換えられ、入力の中身はCloudTrailに残らない。そのため、実行のinputに利用者IDを入れても、CloudTrailからは追えない。一方、responseElements.executionArnは記録されるので、CloudTrailのイベントから実行を特定し、Step Functionsの実行履歴で入力を確認する形になる |
| 入出力の露出 |
includedData: ALL_DATAを許すと、実行の入出力がエージェント(LLMのコンテキスト)に渡る。個人情報を含むワークフローでは扱いに注意。試した範囲では、入力約400文字の実行で、応答が674文字(省略時・METADATA_ONLY)から1410文字(ALL_DATA)になり、入力のJSON(顧客ID・住所を含めた模擬データ)と出力がそのまま入った |
ゲートウェイ経由のStartExecutionイベントは次のとおりです(アカウントIDなどは伏せています)。
{
"eventVersion": "1.11",
"userIdentity": {
"type": "AssumedRole",
"principalId": "AROA****************:gateway-session-invocation",
"arn": "arn:aws:sts::<account-id>:assumed-role/AgentCoreGatewayToStepFunctions/gateway-session-invocation",
"accountId": "<account-id>",
"accessKeyId": "ASIA****************",
"sessionContext": {
"sessionIssuer": {
"type": "Role",
"principalId": "AROA****************",
"arn": "arn:aws:iam::<account-id>:role/AgentCoreGatewayToStepFunctions",
"accountId": "<account-id>",
"userName": "AgentCoreGatewayToStepFunctions"
},
"attributes": {
"creationDate": "2026-11-01T11:28:30Z",
"mfaAuthenticated": "false"
}
},
"invokedBy": "bedrock-agentcore.amazonaws.com"
},
"eventTime": "2026-11-01T11:28:30Z",
"eventSource": "states.amazonaws.com",
"eventName": "StartExecution",
"awsRegion": "ap-northeast-1",
"sourceIPAddress": "bedrock-agentcore.amazonaws.com",
"userAgent": "bedrock-agentcore.amazonaws.com",
"requestParameters": {
"stateMachineArn": "arn:aws:states:ap-northeast-1:<account-id>:stateMachine:AgentHelloWorld",
"input": "HIDDEN_DUE_TO_SECURITY_REASONS"
},
"responseElements": {
"executionArn": "arn:aws:states:ap-northeast-1:<account-id>:execution:AgentHelloWorld:<execution-id>",
"startDate": "2026-11-01T11:28:30Z"
},
"requestID": "<request-id>",
"eventID": "<event-id>",
"readOnly": false,
"eventType": "AwsApiCall",
"managementEvent": true,
"recipientAccountId": "<account-id>",
"eventCategory": "Management"
}
userIdentityのセッション名はgateway-session-invocationで固定され、invokedByがbedrock-agentcore.amazonaws.comです。どのエージェントや利用者の呼び出しかを示すフィールドは、このイベントにはありません。
エージェントとワークフローの2つの連携パターン
公式ドキュメントによると、エージェントとワークフローの協働は2通りです(出典:AWS Step Functions as Connector Target)。
パターンA:エージェントがワークフローを起動・監視する
StartExecution → DescribeExecution(ポーリング)→ GetExecutionHistory →(必要なら)StopExecution。同期でよければ、ExpressにStartSyncExecutionを使います。
パターンB:ワークフローがエージェントの作業を待つ(Human-in-the-loop)
| # | 内容 |
|---|---|
| 1 | Standardのタスクを.waitForTaskTokenで一時停止し、Step Functionsがタスクトークンを発行する |
| 2 | ワークフロー側で、トークンとタスク詳細をエージェントのアプリへ届ける(例:SQSキュー、アプリがポーリングするActivity) |
| 3 | エージェントが作業し、SendTaskSuccess(結果)またはSendTaskFailureで再開させる。長い作業はSendTaskHeartbeatで生存を伝える |
トークンの配送はワークフロー側の責任です。ゲートウェイは配送を行いません。
図7:人手承認(コールバック)パターンの流れ(ステートマシンがSQSにトークンを送って停止し、エージェントがSendTask*で再開する)
.waitForTaskTokenでSQSにトークンを送って待つStandardワークフロー(ApprovalWorkflow)で、次の流れを確認しました。
| # | 操作 | 結果 |
|---|---|---|
| 1 |
approval___StartExecutionで起動(入力は{"requestId":"refund-1001","amount":3000}) |
実行はRUNNINGで停止。TaskSubmitted(sqs:sendMessage)まで進んでいる |
| 2 | SQSからメッセージを受信 | BodyはRequest(実行の入力)とTaskToken(656文字)の2キー |
| 3 |
approval___SendTaskSuccessにtaskTokenとoutput(JSON文字列)を渡す |
成功(応答は{})。引数は2つとも必須 |
| 4 |
approval___DescribeExecution(ALL_DATA)で確認 |
SUCCEEDED。outputは送った{"approved":true,"approver":"agent"}
|
ワークフローがSQSにトークンを送り、アプリ側がそれを読んでエージェントに渡す部分は、ゲートウェイが関与しません。今回は、筆者がSQSからトークンを取り出してエージェント役の呼び出しに渡しました。
図8:ApprovalWorkflowのイベント履歴(待機中。sqs:sendMessageのTaskSubmittedで停止)
図9:ApprovalWorkflowのグラフビュー(SendTaskSuccessの後。全ステップが成功)
図10:ApprovalWorkflowのイベント履歴(完了後。TaskSucceededからExecutionSucceededまで)
設定の最小構成
公式チュートリアルの流れを要約します。詳細はチュートリアルを参照してください。
# ゲートウェイ作成(AWS_IAM認可)
aws bedrock-agentcore-control create-gateway \
--name "step-functions-gateway" \
--protocol-type MCP \
--authorizer-type AWS_IAM \
--role-arn "arn:aws:iam::<account-id>:role/AgentCoreGatewayToStepFunctions" \
--region <region>
# ターゲット追加(READYになってから)
aws bedrock-agentcore-control create-gateway-target \
--gateway-identifier "<GATEWAY_ID>" \
--name "hello" \
--target-configuration '{
"mcp": {
"connector": {
"source": { "connectorId": "states" },
"configurations": [
{ "name": "StartExecution",
"parameterValues": { "stateMachineArn": "arn:aws:states:<region>:<account-id>:stateMachine:AgentHelloWorld" } },
{ "name": "ListExecutions",
"parameterValues": { "stateMachineArn": "arn:aws:states:<region>:<account-id>:stateMachine:AgentHelloWorld" } },
{ "name": "DescribeStateMachine",
"parameterValues": { "stateMachineArn": "arn:aws:states:<region>:<account-id>:stateMachine:AgentHelloWorld" } },
{ "name": "DescribeExecution" },
{ "name": "GetExecutionHistory" },
{ "name": "StopExecution" }
]
}
}
}' \
--credential-provider-configurations '[{"credentialProviderType": "GATEWAY_IAM_ROLE"}]' \
--region <region>
本記事の検証環境(AWS CLI 2.34.24)では、上のターゲット追加コマンドが次のエラーで失敗しました。connector型のtargetConfigurationを、CLIが認識していません。
Unknown parameter in targetConfiguration.mcp: "connector", must be one of: openApiSchema, smithyModel, lambda, mcpServer, apiGateway
AWS CLIを2.39.3に更新して、同じコマンドをもう一度実行すると、connector型のターゲットが作成できました。get-gateway-targetの出力にも、connectorId: "states"とversion: "1.0.0"が表示されました。2.34.24のget-gateway-targetは、connectorをSDK_UNKNOWN_MEMBERとしか表示しませんでした。コネクタを使う場合は、AWS CLIを更新しておくのが安全です(boto3 1.43.91でも、同じAPIが通りました)。
コンソールでは、このターゲットは「ターゲットタイプ: Step Functions」と表示され、ステータスはReadyでした。一方、ピンしたstateMachineArnなどのコネクタの設定は、コンソールには表示されません(「追加設定」を展開しても認証付与タイプのみ)。ピンの設定は、CLIのget-gateway-target(2.39.3)で確認できます。
図11:ゲートウェイのターゲット一覧(helloとapproval。ターゲットタイプは「Step Functions」、インバウンド認証は「IAM 許可」)

図12:ゲートウェイの詳細(名前・ID・URL・ステータス「Ready」)
- ゲートウェイとステートマシンは、同一リージョンに作ります。ゲートウェイは自リージョンのStep Functionsを呼びます。
-
AWS_IAM認可では、呼び出し側のIDにbedrock-agentcore:InvokeGateway権限とSigV4署名が必要です。 - ツール名には、ターゲット名がプレフィックスで付きます。チュートリアルの例は
hello___StartExecution(アンダースコア3つ)です。
実際に検証した内容と結果
公式ドキュメントから読み取れない点、または自分で確かめたい点のうち、8項目を検証しました。1〜5はStep Functionsコネクタに固有の確認、6〜8は追加の確認です。リクエストは、awscurlでゲートウェイにJSON-RPCを直接送っています(エージェントのフレームワークは使っていません)。
| # | 確認したいこと | 確認方法 | 結果 |
|---|---|---|---|
| 1 |
tools/listで見えるツールの説明文・スキーマの実物 |
awscurlでtools/listを呼ぶ |
6ツール。stateMachineArnは非公開。4ツールの説明文に「Standard workflows only」の記載 |
| 2 | ピンしたパラメータを渡したときの拒否 |
tools/callで別のステートマシンのARNをstateMachineArnに指定 |
IllegalArgumentException - ... cannot set parameter(s): [/stateMachineArn]で拒否。実行は作られない |
| 3 |
includedData: ALL_DATAで、エージェントに渡る情報量が変わるか |
DescribeExecutionを省略・METADATA_ONLY・ALL_DATAで呼ぶ |
省略とMETADATA_ONLYは同一(674文字)。ALL_DATAで入出力が加わり1410文字 |
| 4 | 実行ARNを絞ったIAMポリシーで、別ステートマシンの実行が拒否されるか | 許可していない実行ARNでDescribeExecution・GetExecutionHistory・StopExecutionを呼ぶ |
3つともAccessDeniedException(DependencyFailedExceptionで包まれる)で拒否 |
| 5 |
.waitForTaskTokenとSQSを組み合わせた承認フロー |
Standardでコールバックを組み、SendTaskSuccessで再開 |
RUNNINGで停止 → SQSからトークン取得 → SendTaskSuccessでSUCCEEDED。使用済みトークンの再送はTaskTimedOut
|
| 6 | Standard / Expressの制約エラー | Expressのステートマシンと、型違いのターゲットを用意して呼ぶ |
StateMachineTypeNotSupported、InvalidArn、Expressの.waitForTaskTokenは作成時にInvalidDefinition。公式の表どおり |
| 7 | 別の実行(ゲートウェイに公開していないステートマシン)のトークンでも通るか | 公開していないステートマシンの実行をCLIで起動し、そのトークンをSendTaskSuccessに渡す |
SUCCEEDED。でたらめなトークンはInvalidToken
|
| 8 | CloudTrailでの呼び出し元の見え方 |
lookup-eventsでStartExecutionイベントを確認 |
AssumedRole、セッション名gateway-session-invocationで固定。エージェントや利用者を区別するフィールドは見当たらない。inputはHIDDEN_DUE_TO_SECURITY_REASONSに置き換わり記録されない。executionArnは記録される |
検証後は、チュートリアルのStep 6の手順(ターゲット → ゲートウェイ → ステートマシン → IAMロールの順に削除)でクリーンアップしてください。本記事の検証では、SQSキューと承認用の実行ロールも作ったので、あわせて削除しました。
料金
公式ドキュメントに専用の追加料金の記載はなく、Step Functions(ステート遷移)とAgentCore Gateway(ツール呼び出し)の標準料金が適用されます。単価は本記事では割愛します。最新の単価は各料金ページで確認してください。
さいごに
今回のコネクタは、「手順は固定し、起動の判断だけをエージェントに任せる」という切り分けを、追加コードなしで実現するものでした。一方で、ツール化した瞬間に、サービスロールという1つの権限の入口にエージェントの操作が集まります。ピン・IAM・タスクトークンのどこで何を絞れて、どこは絞れないのかを把握したうえで、公開するオペレーションを最小限に選ぶことが、この機能を実務で使う鍵になりそうです。
ここまでの検証では、公式の記載どおりに動きました。ピンは、違うstateMachineArnを渡すとゲートウェイ側で拒否され、実行は作られません。サービスロールのIAMは、許可していない実行ARNへの参照・停止をStep Functions側のAccessDeniedで拒否します。一方、タスクトークンはResource: "*"で許可する必要があり、IAMでは絞れません。実際、ゲートウェイに公開していないステートマシンの実行でも、トークンがあれば完了できました。ただし、拒否の仕方はピンとIAMで異なります(IllegalArgumentExceptionと、DependencyFailedExceptionで包まれたAccessDenied)。呼び出し主体もgateway-session-invocationで固定されます。この2点は、エージェント側のエラー処理や監査の設計で意識しておく必要があります。
これから使う場合は、次の順で進めるのが安全そうです(筆者の解釈)。
- まず、起動と参照のオペレーション(
StartExecution、DescribeExecutionなど)だけを公開し、ステートマシンはピンで固定する -
SendTask*はResource: "*"が必要になるため、人手承認のように必要なワークフローだけを、別のゲートウェイに分けて公開する(同一ゲートウェイのターゲットはサービスロールを共有するため) - 入出力に個人情報を含むワークフローは、
includedData: ALL_DATAでエージェントに渡る内容を確認してから公開する
参考文献
発表
ドキュメント
関連(ワークフローからエージェントを呼ぶ逆方向の連携)
英語版
[AWS] Using Step Functions as an agent tool: Testing how PINs, IAM, and tokens work [AgentCore]









