mcpField
U
upstash-box-remote-work

upstash-box-remote-work

github.com/upstashクラウド・DevOps34486 回インストール2 日前に更新
リポジトリ

upstash-box-remote-work について

UpstashのリモートクラウドBoxで開発作業を進めるワークフロースキルです。分離されたLinux環境、プルリクエスト、公開プレビュー、スクリーンショットが必要な作業向けに、BoxやGit、実行、後片付けの手順を説明します。

主な機能

  1. 01クラウドサンドボックスBoxの作成と管理
  2. 02リモートでのシェルコマンドとGit操作
  3. 03ポート経由のプレビューURL公開
  4. 04ブラウザー画面の撮影とBlobへのアップロード
  5. 05スナップショットと並列Boxによる独立タスクの拡張

活用シーン

  1. 01生成コードや未信頼コードの隔離環境での構築とテスト
  2. 02公開プレビュー付きプルリクエストの作成
  3. 03複数の独立した開発タスクの並列実行

MCP 対応

このワークフローは https://mcp.upstash.com/mcp のUpstashリモートMCPサーバーを介して動作します。ツールでBox管理、シェルやGit操作、プレビュー作成、ブラウザーセッション、スナップショット、Blobへのアップロードを行います。

インストール

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

$npx skills add https://github.com/upstash/skills --skill upstash-box-remote-work

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

詳細

ボックスは、シェル、ファイルシステム、git、オプションのヘッドレス Chromium、およびそのポート用の公開 URL を備えた、クラウド上のサンドボックス化された Linux コンテナです。ここでのすべては、リモートの Upstash MCP サーバーを経由します。インストールする SDK も、環境内の API キーもありません(動画の録画は例外です。以下を参照してください)。サーバーはセッションの OAuth トークンを Box API に転送し、スクリーンショットのバイト列はサーバーを経由せずにボックスから Blob へと送られます。

いつ作業をボックスに持ち込むか

  • ユーザーがそれを求めている場合: リモートで、サンドボックスで、クラウドで、ボックスで、自分のマシンではなく。
  • 成果物がプルリクエスト、公開プレビュー URL、または実行結果のスクリーンショットである場合。ボックスには GitHub の認証情報、公開ポート、ブラウザーがあり、ローカルマシンにはしばしばその 3 つのどれもありません。
  • 作業に分離が必要な場合: 信頼できないコードや生成されたコード、重い依存関係のインストール、クリーンなチェックアウト、ローカルのツリーが抱えるべきでないブランチ。
  • 作業をスケールアウトする場合: 複数の独立したタスクを、それぞれ 1 つのボックスで、並行して。

タスクがボックスに入ったら、そのすべてをそこで行ってください。ボックスのファイルシステムはローカルのものではないため、ローカルで行った編集とボックスで実行したビルドは 2 つの異なるチェックアウトに作用し、どちらの側も不一致を報告しません。

接続

プラグインはすでに https://mcp.upstash.com/mcp を登録しており、ボックスと blob のツールはそのデフォルトのツールセットに含まれています。サーバーを手動で追加するには:

claude mcp add --scope user --transport http upstash "https://mcp.upstash.com/mcp"

初回使用時に、クライアントが Upstash の同意ページを開きます。ボックスとバケットを配置するアカウントまたはチームを選び、読み取り専用スイッチをオフにしてください: 一覧表示を除く以下のすべてのステップは、読み取り専用の許可では 403 で拒否されます。

ツール

ツールアクション / 目的
box_managecreate、list、get、delete、pause、resume、fork
box_execボックスでシェルコマンドを実行します(command は argv 配列、folder は作業ディレクトリです)
box_gitclone、status、diff、commit、checkout、push、create_pr
box_previewボックス内のポート用の公開 URL を create、list、delete
box_browsergoto、content、screenshot、tabs、tab_new、tab_close、live_view
box_snapshotscreate、list、list_all、delete、restore(スナップショットからの新しいボックス)
box_logs、box_runsボックス内で何が起きたか、およびその実行履歴
box_apikeyデプロイされたアプリまたは CI 用の Box API キーを list、create、delete(キーは OAuth の許可より長く存続するため、完了したら取り消すようユーザーに伝えてください)
blob_bucketlist、create(create のデフォルトは visibility: public)
blob_upload_urlバケット内のパス用の署名付き PUT URL、および公開バケットでの public_url

フロー:1 タスク、1 ボックス

  1. 作成。 box_manage の create です。スクリーンショットを撮ったりページを確認したりする場合は browser: true を設定します。使い捨ての作業には ttl とともに ephemeral: true を使用します(有料プランは不要です)。プレビュー URL をセッションより長く存続させる必要がある場合は keep_alive: true を使用します(有料プラン)。返された id を控えておきます。
  2. box_git の clone でクローンします。box_exec で git を使うことは決してしないでください。クローンこそがアカウントの GitHub 認証情報をボックスに書き込む処理であり、これを行わないと push や create_pr が素っ気ない 500 で失敗します。チェックアウト先は /workspace/home/<リポジトリ名> になります。以降のすべての box_exec と box_git 呼び出しでは、これを folder として渡してください。デフォルトはワークスペースのルートであり、リポジトリではありません。
  3. 作業。 インストール、ビルド、テスト、アプリ自体には box_exec を使用します。各呼び出しはコマンドの完了を待つため、サーバーはデタッチします: ["sh", "-c", "( pnpm preview --host 0.0.0.0 --port 4321 > /workspace/home/app.log 2>&1 & )"]、 その後、2 回目の呼び出しで curl を使ってポートをポーリングします。ファイルはローカルのファイルツールではなく、box_exec 内のシェルコマンドまたは短いスクリプトで編集してください。
  4. プレビュー URL。 box_preview の create を port とともに実行します。アプリは 0.0.0.0 でリッスンする必要があります。127.0.0.1 にバインドされたサーバーはボックス内の curl には応答しますが、プレビュー経由では 502 を返します。URL は https://<box-id>-<port>.preview.box.upstash.com という形式です。リンクを知っている誰にでもページを開放したくない場合は basic_auth または bearer_token を追加します。認証情報は一度だけ返されます。URL がどれだけ存続するかを返信で伝えてください。エフェメラルボックスでは expires_at に URL が停止します。
  5. スクリーンショット。 box_browser の goto で http://localhost:<port> のページに移動し、次に screenshot を実行します。path を指定しない場合、PNG は確認できる画像として返されます。path を指定した場合はボックス内に書き込まれ、{saved, bytes} だけが返されます。作業中に確認し、残すべきものは保存し、長いページには full_page: true を渡します。
  6. スクリーンショットの公開。 GitHub の添付エンドポイントはボックスのトークンを拒否するため、画像は Blob を経由します。blob_bucket の list を実行し、合うものがなければ public のバケットを create します(プライベートバケットは短命の署名付き URL しか提供しません)。bucket_id と files: [{path: "<repo>/<branch>/after.png", content_type: "image/png", size: <bytes>}] を指定して blob_upload_url を、使用直前に発行します(URL の有効期間は最大 10 分です)。次に、結果の curl_example を box_exec で実行し、返されたヘッダーをそのまま送信します(それらは署名の一部です)。バイト列はボックスからストレージへ直接送られます。
  7. プルリクエスト。 box_git の checkout でブランチを作成し、commit、ブランチ名を指定して push し、次に base、title、そしてプレビュー URL と各スクリーンショットの ![after](<public_url>) を含む body を指定して create_pr を実行します。create_pr 自体は何もプッシュしません。PR の URL、プレビュー URL、スクリーンショットの URL を返信で伝えてください。
  8. クリーンアップ。 ユーザーがボックスやそのプレビューの保持を希望しない限り、box_manage の delete(または pause)を実行します。エフェメラルボックスは自動的に期限切れになります。

スケールアウト

  • 独立したタスクごとに 1 つのボックス。共有の labels エントリで作成し、並行して操作し、最後に label を指定して box_manage の list で見つけて削除します。1 つのボックスで同時に 2 つのタスクを実行しないでください。
  • クライアントにサブエージェントがある場合は、それぞれに専用のボックスを与えて並行して実行します。ボックスを共有するエージェントは互いのファイルとプロセスを上書きしてしまいます。
  • すべてのタスクが同じ高コストなセットアップ(クローン、依存関係のインストール、ビルドキャッシュ)を必要とする場合は、一度だけ実行し、box_snapshots の create を行い、次にスナップショットからタスクごとに新しいボックスを 1 つ restore します。fork はアイドル状態または一時停止中の非エフェメラルボックスに対して同じことを行います。
  • size は small、medium、large です。すべてを過大に指定するのではなく、タスクごとに選んでください。
  • live_view URL を使うと、人がボックスのブラウザタブが動作する様子を観察できます(フレームは出力されますが、入力はできません)。長時間の実行ではこれを渡してください。

ボックスでの動画録画

録画はまだ MCP ツールではないため、これが SDK を必要とする唯一のフローです。 box_apikey の create でキーを作成し、それを box 内のファイルに保管し、 そこから @upstash/box で box を操作し(box.browser.recordings、および Playwright 用の box.browser.cdpUrl())、完了したらキーを削除します。

何が録画されるか。 box のブラウザです。すべてのタブを、フォアグラウンドの タブに追従しながら、固定解像度で録画します。それ以外はブラウザに入れる必要が あります。ターミナルプログラムは ttyd(GitHub リリースの静的バイナリ。 Debian にはパッケージがありません)を介して、固定サイズの tmux セッションに 接続します。入力は tmux send-keys -l で小さなチャンクに分けて送信して ください。TUI はブラケットペーストを潰してしまうためです。tmux set -g status off にしないと、ステータスバーがすべてのフレームに映り込みます。可能な限り 1 つのタブにとどまってください。新しく開かれたタブは確実には追従されません。

box 内でのエージェントや CLI の実行。 手動で起動した CLI エージェントには 独自のモデルキーが必要です。box の管理キーは、Box ランナーが起動するプロンプト にしか届きません。チャットに貼り付けずにキーを box に入れるには、box から ワンショットのフォームを提供します(1 回の POST を受け付ける小さな HTTP サーバー)。box_preview で公開し、ユーザーにリンクを送ると、そのフォームが 値をファイルに追記して終了します。localhost の OAuth コールバックを待つ ログインはトンネルできません(Box SSH は -L を拒否します)。CLI が リダイレクト URI を設定できる場合(OpenCode: opencode.json の oauth.redirect_uri)、Box のプレビュー URL を指すようにします。コールバック サーバーは依然として URI のポート(https の場合は 443、非特権で許可されます) で 127.0.0.1 にバインドし、プレビューは 0.0.0.0 にしか到達しないため、付与の 間だけプレビューポートをそこにリレーします。そうでなければ、box 内でログインを 実行し、ユーザーが自分のブラウザで承認し、CLI が待機をやめる前に、失敗した http://127.0.0.1:<port>/... URL を box 内の curl に貼り付けてもらいます。 同意画面でアカウントが選ばれるため、録画が依存するリソースはそのアカウントに 存在している必要があります。無人でのエージェント実行では、明確化の質問を 排除するワークスペース指示があれば、テイクが停滞するのを防げます。

制限。 録画は最長 600 秒で、ピクセルの変化が 3 分間ないと自動的に停止します。 1 つの box は 1 つのアクティブな録画を保持します。残った録画があると、次の start が 409 を返すため、クラッシュハンドラー内および毎回の start の前に 停止してください。加速する予定の待機も含め、見せたいものはすべて録画して ください。ギャップを後から編集で戻すことはできません。

タイミング。 目分量ではなくタイムスタンプから切り出します。スクリプトが 実行するすべてのアクションの実時間をログに記録し、録画されたプログラム自身の タイムラインは画面からではなく構造化出力(セッションのエクスポート、ログ、 API レスポンス)から取得し、両方をビデオ時間に変換します。開始ではなく終了を 基準にします。キャプチャは startedAt の約 2 秒後に始まるため、最初のフレームは endedAt - <file duration>(ffprobe)であり、startedAt を使うとすべてのカットが 早くなります。ターミナルからスクレイピングしたテキストは行の折り返しで途切れます。 大量の CLI 出力はファイルにリダイレクトしてください。パイプはそれを切り詰める ことがあります。

編集(ffmpeg):

  • 長い待ち時間はカットするのではなく高速化します。ばっさりカットすると 不具合のように見えます。丸い係数(5、10、20、50 倍)を使い、セグメントの 長さはその係数から決まるようにし、係数は画面上の隅に濃い文字で 不透明な緑のピル(box=1:boxcolor=0x00d48a)として表示します。半透明の バッジではいけません。どんな背景の上でも一目で読める必要があります。
  • 何が起きているかを画面上のテキストで示します。各ステップに短いキャプションを 付け、読めるだけの長さ(およそ 0.7 秒以上)表示し、ステップの進行が速いときは 間引きます。
  • ヘッドレス録画にはポインターもアドレスバーもありません。入力が行われる場所に カーソルとクリックマーカーを描画し、URL が重要な場合は URL を描画します。 位置は Playwright のバウンディングボックスから取得します(まず要素を ビュー内にスクロールしてください)。ターミナルの場合は ttyd の window.term から取得します。セルサイズは .xterm-screen の矩形を cols と rows で 割ったもので、行と列は term.buffer.active を検索して求めます。
  • カーソルは手で動かしたように見せます。小さく細いアンチエイリアス済みの矢印 (1280x800 で高さ約 25 px)に柔らかい影を付けます。各移動はなめらかに 加減速し(smootherstep)、わずかな曲線(制御点を横に押し出した二次ベジェ) に沿わせ、クリックの少し前に到着させ、停止中はかすかな揺らぎを加えます。 クリックは短い押し込み(約 0.15 秒だけ少し小さくなるスプライト)と、約 0.4 秒かけて広がって消える細い波紋で示します。塗りつぶしたリングでは いけません。各セグメントの境界ではカーソルを保持し、次のセグメントの位置へ 滑っていかないようにします。
  • 波紋は PNG シーケンスとしてレンダリングし、クリックごとに 1 つのコピーを tpad で遅延させます。フィルターグラフは -filter_complex_script に 入れます。引用符で囲んだ式の中のカンマには依然として \, が必要です (以下のスケッチでは省略しています)。
[0:v]trim=T0:T1,setpts=(PTS-STARTPTS)/20,fps=30,
  drawtext=text='20x':fontcolor=0x062b1f:box=1:boxcolor=0x00d48a:boxborderw=10:x=w-tw-30:y=26,
  drawtext=text='<step>':enable='between(t,2.0,2.8)'[fast]
[ripple]format=rgba,tpad=start_duration=<click t>:color=0x00000000[r]
[v][r]overlay=x=<cx-36>:y=<cy-36>:eof_action=pass[v2]
[v2][cursor]overlay=eval=frame:enable='between(t,A,B)':
  x='st(0,clip((t-T0)/D,0,1));st(1,ld(0)^3*(ld(0)*(6*ld(0)-15)+10));
     (1-ld(1))^2*X0+2*(1-ld(1))*ld(1)*XC+ld(1)^2*X1':y='...'

カットは ffmpeg ... tile=3x3 のコンタクトシートで確認します。これは python3 -m http.server で配信し、box_browser で開きます。録画の実行中には 決して行いません(同じブラウザーだからです)。動画は blob_upload_url で 公開します。

テイクの合間。 box ツールで録画したエージェントは、何かを作成する前に 既存のものを一覧表示します。前のテイクが残した box があると、クローンを スキップしてその box を再利用します。先に残置物を削除するか、プロンプトに 「新しい box」と書いてください。録画されたエージェントが作成する box は、 そのエージェントが同意したアカウント内に存在し、それがあなた自身のセッション から見えるアカウントとは限りません。

複数の録画。 録画ごとに 1 つの box を使い、クライアントにサブエージェントが ある場合はそれらで並列に操作します。録画用の box をエージェント間で共有しては いけません。tmux サーバー、ブラウザー、録画スロットは box ごとに独立しており、 2 つのエージェントは互いのセッションを黙って殺し、互いのファイルを上書きします。

注意点

  • 読み取り専用の付与 → 作成、実行、パスへのスクリーンショット、git への書き込み、アップロード URL で 403 になります。読み取り専用をオフにして再同意してください。
  • box_git の create_pr は、アカウントが対象リポジトリをカバーする GitHub インストールを持っている場合に失敗します。Upstash コンソールの Box 設定で GitHub を接続するよう依頼してください。
  • box_manage の create では clone_repro よりも box_git の clone を優先してください。エフェメラルな box ではそのオプションは無効にでき、永続的な box ではバックグラウンドで実行され、box_logs 以外に完了シグナルはありません。
  • box 内のファイルは絶対パスでない限り workspace/home からの相対パスになります。ls/workspace は root 所有、モード 711 です。
  • node イメージには corepack がなく、box では npm i -g ができないため、packageManager で固定された pnpm は自己インストールに失敗します。npx -y pnpm@<version> を使うか、export npm_config_manage_package_versions=false を設定してください(自己インストール型の pnpm)。sudo npm i -g pnpm@<version> も動作します。sudo はパスワードなしです。
  • blob_upload_url のヘッダーは重要です。content-type や cache-control を変更するとストレージで失敗します(SignatureDoesNotMatch)。期限切れの URL は同様に失敗します。
  • バケットはアカウント全体で共有されます。リポジトリごとに作成するのではなく、agent-proof のような 1 つを再利用してください。
  • box_b は box が browser: true で作成されていない限り「browser is not enabled for this box」で失敗します。後から有効にする方法はありません。
  • パブリックな Blob ホストはオブジェクトをキャッシュします(cache-control ヘッダーに従います)。同じパスに再アップロードしても古いオブジェクトが配信され続けます。置き換える場合は新しい名前で公開し、上書きしたばかりのものを読み戻すときはクエリ文字列を追加してください。
  • box_exec のリクエストは 60 秒でタイムアウトします。長時間のジョブは setsid ... & でデタッチし、後続の呼び出しでポーリングし、プロセスがまだ生きているか確認してください。
  • box_exec の pkill -f <pattern> は呼び出し元シェル自身のコマンドラインにも一致して呼び出しを kill します(exit 143)。pkill -f 'patter[n]' と書いてください。
  • tmux kill-server の直後に tmux new-session を実行すると「server exited unexpectedly」で失敗することがあります。先に古いサーバーの終了を待ってください。
  • node イメージには PIL も ImageMagick もありません。必要なものを sudo apt-get install するか、Python の zlib で小さな PNG を書き出してください。

コメント