mcpField
O
openhands-api

openhands-api

github.com/openhandsエージェントワークフロー17564 回インストール今日更新
リポジトリ

openhands-api について

openhands-apiは、OpenHands CloudとエージェントサーバーAPIを扱う開発者向けのスキルです。会話の操作、実行監視、タスク委任、ローカルAgent Canvasの手順を、コピー可能なクライアント例とともに説明します。

主な機能

  1. 01OpenHands Cloud V1 REST APIのワークフロー
  2. 02会話の作成と確認
  3. 03非同期起動と実行の監視
  4. 04並列・バックグラウンド会話への委任
  5. 05サンドボックス管理とイベント取得
  6. 06ローカルAgent Canvasバックエンドの会話

活用シーン

  1. 01コードからCloud会話を開始・監視
  2. 02独立タスクや並列タスクの委任
  3. 03サンドボックスのエージェントサーバーAPIへのアクセス
  4. 04ローカルAgent Canvasバックエンドの会話確認

インストール

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

$npx skills add https://github.com/openhands/extensions --skill openhands-api

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

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

README.md
.plugin/
plugin.json
references/
example_prompt.md
README.md
TROUBLESHOOTING.md
windows.md
scripts/
openhands_api.py
openhands_api.ts

詳細

このスキルでは、OpenHands Cloud API(V1)、一般的に使用されるエージェントサーバー API、および簡単にコピーして使える小規模なクライアントについて説明します。 このスキルのシェルの例に対応する Windows PowerShell の例は、references/windows.md にあります。

このスキルでは、一般的な OpenHands API のワークフローに焦点を当てています。

  • OpenHands Cloud(https://app.all-hands.dev)をデフォルトで使用します。
  • /api/v1/... 配下のV1 アプリサーバー REST APIを対象とします。
  • X-Session-API-Key を使用する、サンドボックス内のいくつかのエージェントサーバーエンドポイントを含みます。
  • 複数の会話への委任パターンを扱います。新しいコンテキストウィンドウやバックグラウンド作業が必要な場合は、別々の Cloud 会話を開始します。
  • ローカル Agent Canvas バックエンドの会話を扱います。ローカルエージェントサーバーを直接呼び出して、会話を開始または確認します。

このスキルを使用する場面

次のような場合に、このスキルを使用します。

  • コードから OpenHands Cloud の会話を開始または確認する
  • 開始タスクのポーリングで非同期の起動を監視する
  • 長時間実行されるジョブの実行ステータスを監視する
  • 並列作業やバックグラウンド作業のために、別々の Cloud 会話を作成する
  • 会話の実行開始後に、サンドボックスのエージェントサーバーエンドポイントにアクセスする
  • ローカル Agent Canvas バックエンドまたはローカルエージェントサーバーで会話を開始または確認する

認証

アプリサーバー(Cloud)

Bearer 認証を使用します。

  • ヘッダー:Authorization: Bearer <OPENHANDS_CLOUD_API_KEY>
  • 推奨環境変数:OPENHANDS_CLOUD_API_KEY
  • 後方互換性のための環境変数:OPENHANDS_API_KEY
エージェントサーバー(サンドボックス内)

セッション認証を使用します。

  • ヘッダー:X-Session-API-Key: <session_api_key>

agent_server_url と session_api_key の取得方法:

  1. アプリサーバー(Bearer 認証)経由でアプリの会話を開始または取得します。例:
    • POST /api/v1/app-conversations
    • または GET /api/v1/app-conversations?ids=<conversation_id>
  2. 返された JSON から、サンドボックス/ランタイム接続フィールドを探します(名前はデプロイ環境やバージョンによって多少異なります)。一般的なパターン:
    • agent_server_url(または類似の名前)を含むサンドボックスオブジェクト
    • session_api_key(または類似の名前)などのセッションキー
  3. これらの値を使って、エージェントサーバーを直接呼び出します。
    • ベース:{agent_server_url}/api/...
    • ヘッダー:X-Session-API-Key: <session_api_key>

例(一般的なフィールド名。デプロイ環境に合わせて調整してください):

# using the minimal Python client (`OpenHandsAPI`)
conv = api.app_conversation_get(app_conversation_id)

session_api_key = conv.get("session_api_key")
conversation_url = conv.get("conversation_url", "")

# `conversation_url` often looks like: https://<runtime-host>/api/conversations/<id>
agent_server_url = conversation_url.rsplit("/api/conversations", 1)[0]

これらのフィールドが会話レコードにない場合は、サンドボックスを一覧表示/検索(GET /api/v1/sandboxes/search)し、その会話で参照されているサンドボックスを使って、エージェントサーバーの URL とセッションキーを特定します。

ローカル Agent Canvas バックエンド

agent-canvas、agent-canvas --backend-only、または ingress が http://localhost:8000 の npm run dev など、ローカルの Agent Canvas/agent-server の開発に限り、ローカルバックエンドのフローを使用してください。このフローでは、X-Session-API-Key を使ってエージェントサーバーを直接呼び出します。これは自動化ではありません。また、Cloud アプリ API に対して Bearer 認証を使用し、非同期の start-task レコードを返すことがある POST /api/v1/app-conversations を通じた OpenHands Cloud への委任とは異なります。

Agent Canvas をローカルで実行すると、LOCAL_BACKEND_API_KEY が設定されている場合、ランチャーはその値を使用します。設定されていない場合は、~/.openhands/agent-canvas/api-key.txt にセッション API キーを生成して保存します。保存先のキーのパスを上書きするには、OH_SESSION_API_KEY_PATH を設定してください。実際のキーを表示、ログ出力、貼り付けしないでください。例やスクリプトでは、コマンド置換または環境変数を使用してください。

LOCAL_AGENT_SERVER_URL="${LOCAL_AGENT_SERVER_URL:-http://localhost:8000}"
SESSION_API_KEY="${LOCAL_BACKEND_API_KEY:-$(cat "${OH_SESSION_API_KEY_PATH:-$HOME/.openhands/agent-canvas/api-key.txt}")}"

バックエンドの会話を作成する前に、ローカルサーバーを確認してください。

curl -sS "${LOCAL_AGENT_SERVER_URL}/server_info" \
  -H "X-Session-API-Key: ${SESSION_API_KEY}"

POST /api/conversations でバックエンドの会話を開始します。そのバックエンドで必要なエージェント設定とワークスペースを指定してください。ローカルのエージェントサーバー呼び出しでは、{"kind": "LocalWorkspace", "working_dir": "/workspace"} のように workspace を明示的に指定します。一方、Cloud アプリ会話への委任では、selected_repository や selected_branch などの app-server フィールドを使用します。既存の Agent Canvas セッションから会話を開始する場合は、スクリプトにシークレットをハードコードせず、現在設定されている設定または暗号化された設定を引き継いでください。

CONVERSATION_JSON=$(curl -sS -X POST "${LOCAL_AGENT_SERVER_URL}/api/conversations" \
  -H "X-Session-API-Key: ${SESSION_API_KEY}" \
  -H "Content-Type: application/json" \
  -d @- <<'JSON'
{
  "agent": {
    "kind": "Agent",
    "llm": {
      "model": "your-model-provider/your-model-name",
      "api_key": "**********"
    },
    "tools": [
      {"name": "terminal"},
      {"name": "file_editor"},
      {"name": "task_tracker"}
    ]
  },
  "workspace": {"kind": "LocalWorkspace", "working_dir": "/workspace"},
  "initial_message": {
    "content": [{"text": "Summarize the current workspace."}],
    "run": true
  }
}
JSON
)
CONVERSATION_ID=$(python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])' <<<"${CONVERSATION_JSON}")
printf 'Conversation: %s/api/conversations/%s\n' "${LOCAL_AGENT_SERVER_URL}" "${CONVERSATION_ID}"

ステータスをポーリングし、最近のイベントを確認します。

curl -sS "${LOCAL_AGENT_SERVER_URL}/api/conversations/${CONVERSATION_ID}" \
  -H "X-Session-API-Key: ${SESSION_API_KEY}"

curl -sS "${LOCAL_AGENT_SERVER_URL}/api/conversations/${CONVERSATION_ID}/events/search?limit=20&sort_order=TIMESTAMP_DESC" \
  -H "X-Session-API-Key: ${SESSION_API_KEY}"

同じベース URL で Agent Canvas UI も提供している場合、ブラウザーのルートは次のとおりです。

printf '%s/conversations/%s\n' "${LOCAL_AGENT_SERVER_URL}" "${CONVERSATION_ID}"

一般的な V1 アプリサーバーのエンドポイント

以下は、最小クライアントに実装されている主なエンドポイントです。

  • GET /api/v1/users/me — 認証を検証し、現在のアカウントを確認する
  • GET /api/v1/app-conversations/search?limit=... — 最近の会話を一覧表示する
  • GET /api/v1/app-conversations?ids=... — ID で会話レコードを取得する(一括)
  • GET /api/v1/app-conversations/count — 会話数を取得する
  • POST /api/v1/app-conversations — 新しい会話を開始する(サンドボックスを作成)
  • GET /api/v1/app-conversations/start-tasks?ids=... — 非同期の start-task のステータスを確認する
  • GET /api/v1/conversation/{app_conversation_id}/events/search?limit=... — 会話イベントを読み取る
  • GET /api/v1/conversation/{app_conversation_id}/events/count — イベント数を取得する
  • GET /api/v1/sandboxes/search?limit=... — サンドボックスを一覧表示する
  • POST /api/v1/sandboxes/{sandbox_id}/pause / .../resume — サンドボックスのライフサイクルを管理する
  • GET /api/v1/app-conversations/{app_conversation_id}/download — 軌跡の zip をダウンロードする

Cloud の会話を追加して作業を委任する

独立した新しいコンテキストウィンドウを持つ別の OpenHands 会話を開始する場合は、Cloud API を使用してください。 次のような場合に便利です。

  • 独立して実行できるバックグラウンドジョブ
  • 並行して進める調査や実装タスク
  • 現在の会話に集中しながら進めたい、長時間実行される作業
  • 一方の会話でコンポーネントを構築し、もう一方でテストを実行するなど、タスクごとに分けたコンテキスト
委任チェックリスト

Cloud の会話を委任するときは、次の手順に従ってください。

  1. 自己完結したタスクの説明を書きます。新しい会話には現在の会話のコンテキストが引き継がれないことを前提にしてください。
  2. リポジトリ、ブランチ、関連するファイルパス、制約、期待する出力を含めます。
  3. POST /api/v1/app-conversations で新しい会話を開始します。
  4. status が READY になり、app_conversation_id を取得できるまで、start-task をポーリングします。
  5. GET /api/v1/app-conversations?ids=... で委任した会話の状態を監視します。
  6. レスポンスに含まれる会話の conversation_ui_url を共有するか、保存します。この URL を開くと、会話が Agent Canvas に表示されます。サーバーから返されない場合は、<base_url>/canvas/conversations/<app_conversation_id> を使用します。ここで、<base_url> は API を呼び出したサーバーです。conversation_url は共有しないでください。これはサンドボックスの agent-server API であり、ユーザーが開けるページではありません。
最小限の cURL フロー
curl -X POST "https://app.all-hands.dev/api/v1/app-conversations" \
  -H "Authorization: Bearer ${OPENHANDS_CLOUD_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "initial_message": {
      "content": [{"type": "text", "text": "Investigate flaky tests in tests/test_api.py. Report the root cause and propose a fix."}]
    },
    "selected_repository": "owner/repo"
  }'

レスポンスに app_conversation_id が含まれていない場合は、start-task をポーリングします。

curl -s "https://app.all-hands.dev/api/v1/app-conversations/start-tasks?ids=${START_TASK_ID}" \
  -H "Authorization: Bearer ${OPENHANDS_CLOUD_API_KEY}"

次に、実行ステータスを確認し、共有するリンク(conversation_ui_url)を取得します。

curl -s "https://app.all-hands.dev/api/v1/app-conversations?ids=${APP_CONVERSATION_ID}" \
  -H "Authorization: Bearer ${OPENHANDS_CLOUD_API_KEY}"
最小限の Python フロー
from openhands_api import OpenHandsAPI

api = OpenHandsAPI()  # prefers OPENHANDS_CLOUD_API_KEY

start = api.app_conversation_start(
    initial_message=(
        "Implement the requested dashboard component in src/dashboard.tsx. "
        "Update any related tests and summarize the changes."
    ),
    selected_repository="owner/repo",
    selected_branch="main",
    title="Dashboard component task",
)

ready = start
if not ready.get("app_conversation_id"):
    ready = api.poll_start_task_until_ready(start["id"])

conversation_id = ready["app_conversation_id"]
print(f"Delegated conversation: {api.app_conversation_ui_url(conversation_id)}")

status = api.app_conversation_get(conversation_id)
print(status.get("sandbox_status"), status.get("execution_status"))

api.close()
並列実行のガイダンス
  • 同時に実行する委任先の会話は、5 件以下にすることをおすすめします。
  • それ以上を開始する前に、最近の会話を確認し、execution_status == "running" のものがいくつあるか数えます。
  • ID がすでにわかっている場合は、GET /api/v1/app-conversations?ids=... で会話をまとめて検索します。

例:

items = api.app_conversations_search(limit=50).get("items", [])
running = [item for item in items if item.get("execution_status") == "running"]
if len(running) >= 5:
    print("Wait for some delegated conversations to finish before starting more.")
start-task と app_conversation_id(よくある落とし穴)

多くのデプロイ環境では、POST /api/v1/app-conversations は非同期で、start-task オブジェクトを返します。

  • id は start_task_id です。
  • app_conversation_id は、次のような会話操作で使用する ID です。
    • GET /api/v1/app-conversations/{app_conversation_id}/download
    • GET /api/v1/conversation/{app_conversation_id}/events/...

初回のレスポンスに app_conversation_id が含まれていない場合は、次のエンドポイントから取得します。

  • GET /api/v1/app-conversations/start-tasks?ids=<start_task_id>

start_task_id を /download に渡すと、404 Not Found が返されます。

一般的な agent server エンドポイント

これらは app server ではなく、agent_server_url に対して実行されます。

  • POST {agent_server_url}/api/bash/execute_bash_command
  • GET {agent_server_url}/api/file/download/<absolute_path>
  • POST {agent_server_url}/api/file/upload/<absolute_path> (multipart)
  • GET {agent_server_url}/api/conversations/{conversation_id}/events/search
  • GET {agent_server_url}/api/conversations/{conversation_id}/events/count
イベント数のカウント(推奨方法)

会話のイベント数を確認する必要がある場合は、次の方法を使用できます。

  1. App server でのカウント(動作している場合は最速)
    • GET /api/v1/conversation/{app_conversation_id}/events/count
  2. Agent server でのカウント(信頼性の高い代替手段)
    • GET {agent_server_url}/api/conversations/{app_conversation_id}/events/count
  3. Trajectory zip による代替手段(負荷は高めですが、1 回の呼び出しで完全なペイロードを取得できます)
    • GET /api/v1/app-conversations/{app_conversation_id}/download
    • 展開して、event_*.json ファイルを数えます。

イベントの総数を推測するために、最後のイベントの id に依存しないでください。 agent-server API では、イベント ID は UUID(単調増加する整数ではありません)です。

トラブルシューティング

よくある問題と解決策については、TROUBLESHOOTING.md を参照してください。

イベントの構造(デバッグ用)

次のエンドポイントから返されるイベントは、

  • アプリサーバー:GET /api/v1/conversation/{id}/events/search
  • エージェントサーバー:GET {agent_server_url}/api/conversations/{id}/events/search

…同じ大まかな構造を共有します。

各イベントには通常、次の情報が含まれます。

  • id(UUID)
  • timestamp
  • kind
  • source

一般的な kind の値:

kindsource(一般的な値)主なフィールド(一般的な値)目的
ActionEventagenttool_name、tool_call_id、actionエージェントが要求したツール呼び出し
ObservationEventenvironmenttool_name、tool_call_id、action_id、observationサンドボックス/環境が生成したツールの結果
MessageEventuser / assistantmessage(または同様のもの)ユーザー/アシスタントのチャットメッセージ
ConversationStateUpdateEventenvironmentkey、value状態遷移/メタデータ

ツール呼び出しの関連付け:

  • ActionEvent.tool_call_id == ObservationEvent.tool_call_id
  • ObservationEvent.action_id == ActionEvent.id

例(簡略版):

{
  "id": "<action-event-uuid>",
  "kind": "ActionEvent",
  "source": "agent",
  "tool_name": "terminal",
  "tool_call_id": "toolu_...",
  "action": {"command": "ls"}
}
{
  "id": "<observation-event-uuid>",
  "kind": "ObservationEvent",
  "source": "environment",
  "tool_name": "terminal",
  "tool_call_id": "toolu_...",
  "action_id": "<action-event-uuid>",
  "observation": {"exit_code": 0, "stdout": "..."}
}

デバッグ用のワンライナー(イベント)

ここでは、アプリサーバーのエンドポイントに問い合わせることを前提としています。エージェントサーバーに問い合わせる場合は、URL のベースを置き換え、X-Session-API-Key を使用してください。

簡単なタイムラインを表示する
curl -s "${BASE_URL:-https://app.all-hands.dev}/api/v1/conversation/${APP_CONVERSATION_ID}/events/search?limit=100" \
  -H "Authorization: Bearer ${OPENHANDS_CLOUD_API_KEY:-$OPENHANDS_API_KEY}" \
  -H "Accept: application/json" | \
python3 - <<'PY'
import json, sys
items = (json.load(sys.stdin) or {}).get("items", [])
for i, e in enumerate(items):
    print(f"{i:04d}  {e.get('timestamp','')}  {e.get('source','')}  {e.get('kind','')}")
PY
エラーらしきイベントを見つける
curl -s "${BASE_URL:-https://app.all-hands.dev}/api/v1/conversation/${APP_CONVERSATION_ID}/events/search?limit=200" \
  -H "Authorization: Bearer ${OPENHANDS_CLOUD_API_KEY:-$OPENHANDS_API_KEY}" \
  -H "Accept: application/json" | \
python3 - <<'PY'
import json, sys
items = (json.load(sys.stdin) or {}).get("items", [])
for i, e in enumerate(items):
    if e.get("kind") == "ErrorEvent" or ("code" in e and "detail" in e):
        print(i, e.get("kind"), e.get("code"), str(e.get("detail", ""))[:400])
PY
ツール呼び出しの対応関係を確認する(一致しないアクション/重複する観測)
curl -s "${BASE_URL:-https://app.all-hands.dev}/api/v1/conversation/${APP_CONVERSATION_ID}/events/search?limit=200" \
  -H "Authorization: Bearer ${OPENHANDS_CLOUD_API_KEY:-$OPENHANDS_API_KEY}" \
  -H "Accept: application/json" | \
python3 - <<'PY'
import json, sys
from collections import Counter
items = (json.load(sys.stdin) or {}).get("items", [])
action_ids = {e.get("id") for e in items if e.get("kind") == "ActionEvent"}
obs_action_ids = [e.get("action_id") for e in items if e.get("kind") == "ObservationEvent" and e.get("action_id")]
observed = set(obs_action_ids)
print("actions:", len(action_ids))
print("observations:", len(observed))
unmatched = action_ids - observed
print("unmatched actions:", list(unmatched)[:20] if unmatched else "none")
dups = [aid for aid, c in Counter(obs_action_ids).items() if c > 1]
print("duplicate observation action_ids:", list(dups)[:20] if dups else "none")
PY

クイックスタート(Python)

# Copy `skills/openhands-api/scripts/openhands_api.py` into your project (e.g. as `openhands_api.py`),
# then import it normally:
from openhands_api import OpenHandsAPI

api = OpenHandsAPI()  # prefers OPENHANDS_CLOUD_API_KEY

me = api.users_me()
print(me)

recent = api.app_conversations_search(limit=5)
print(recent)

api.close()

CLI の例

会話を検索する:

export OPENHANDS_CLOUD_API_KEY="..."
python skills/openhands-api/scripts/openhands_api.py search-conversations --limit 5

プロンプトファイルから会話を開始する:

python skills/openhands-api/scripts/openhands_api.py start-conversation \
  --prompt-file skills/openhands-api/references/example_prompt.md \
  --repo owner/repo \
  --branch main

このクライアントを拡張する AI エージェント向けの注意事項

  • .../search エンドポイントを使用し、limit は小さな値にしてください。
  • 多数の API 呼び出しが発生する可能性のあるループは避けてください。
  • 会話を開始するのは、要求された場合に限ってください。サンドボックスが作成され、費用が発生する可能性があります。
  • サンドボックスのファイル操作とコマンド実行には、X-Session-API-Key を使用してエージェントサーバーのエンドポイントを利用してください。

こちらも参照してください:

  • skills/openhands-api/scripts/openhands_api.py
  • 元になったクライアント:enyst/llm-playground → openhands-api-client-v1/scripts/cloud_api_v1.py
  • トラブルシューティングの内容と実際の使用状況に関するフィードバック → https://github.com/jpshackelford/.openhands/tree/main/skills/openhands-cloud-api

信頼できる情報源

このスキルは、最新の OpenHands API ドキュメントおよび実装に準拠しています。

  • OpenHands/docs/openhands/usage/cloud/cloud-api.mdx
  • OpenHands/docs/openhands/usage/agent-canvas/backend-setup/local.mdx
  • OpenHands/docs/sdk/arch/agent-server.mdx
  • OpenHands/docs/openhands/usage/api/v1.mdx
  • OpenHands/OpenHands/openhands/app_server/v1_router.py
  • OpenHands/OpenHands/openhands/app_server/app_conversation/app_conversation_router.py
  • OpenHands/OpenHands/openhands/app_server/app_conversation/app_conversation_models.py

コメント