mcpField
V
vercel

vercel

github.com/vercel-labsテスト・品質2.0k204 回インストール1 日前に更新
リポジトリ

vercel について

vercel はアプリケーションのテスト向けの、状態を保持する Vercel REST API エミュレーターです。Vercel 形式の JSON 応答とカーソルベースのページネーションを備え、プロジェクト、デプロイ、ドメイン、Blob、OAuth などを扱います。

主な機能

  1. 01Vercel REST API の模擬
  2. 02カーソルベースのページネーション
  3. 03プロジェクト、デプロイ、ドメイン管理に対応
  4. 04環境変数と Blob API の模擬
  5. 05チームスコープと OAuth に対応

活用シーン

  1. 01Vercel API クライアントのテスト
  2. 02プロジェクト作成とデプロイ手順の検証
  3. 03Blob のファイル操作のテスト
  4. 04OAuth とチームスコープ付きリクエストの確認

インストール

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

$npx skills add https://github.com/vercel-labs/emulate --skill vercel

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

詳細

Vercel API エミュレーター

Vercel 形式の JSON レスポンスとカーソルベースのページネーションに対応した、完全な状態管理機能を備える Vercel REST API エミュレーションです。

起動

ローカルリスナーは、デフォルトで 127.0.0.1 にバインドされます。コンテナーやほかのマシンからアクセスするには、CLI に --host 0.0.0.0 を渡すか、createEmulator に hostname: '0.0.0.0' を渡します。これらのクライアントからアクセスできる URL を通知するには、--base-url または baseUrl を設定してください。

# Vercel only
npx emulate --service vercel

# Default port
# http://localhost:4000

または、プログラムから起動します。

import { createEmulator } from 'emulate'

const vercel = await createEmulator({ service: 'vercel', port: 4000 })
// vercel.url === 'http://localhost:4000'

認証

トークンは Authorization: Bearer <token> として渡します。すべてのエンドポイントで、チームのスコープを指定するための teamId または slug クエリパラメーターを使用できます。

curl http://localhost:4000/v2/user \
  -H "Authorization: Bearer test_token_admin"

チームスコープのリクエストでは、teamId または slug クエリパラメーターからアカウントを解決します。ユーザースコープのリクエストでは、認証済みユーザーからアカウントを解決します。

アプリの接続先をエミュレーターに変更する

環境変数
VERCEL_EMULATOR_URL=http://localhost:4000
Vercel SDK /カスタム Fetch
const VERCEL_API = process.env.VERCEL_EMULATOR_URL ?? 'https://api.vercel.com'

const res = await fetch(`${VERCEL_API}/v10/projects`, {
  headers: { Authorization: `Bearer ${token}` },
})
OAuth URL のマッピング
実際の Vercel URLエミュレーター URL
https://vercel.com/integrations/oauth/authorize$VERCEL_EMULATOR_URL/oauth/authorize
https://api.vercel.com/login/oauth/token$VERCEL_EMULATOR_URL/login/oauth/token
https://api.vercel.com/login/oauth/userinfo$VERCEL_EMULATOR_URL/login/oauth/userinfo
Auth.js / NextAuth.js
{
  id: 'vercel',
  name: 'Vercel',
  type: 'oauth',
  authorization: {
    url: `${process.env.VERCEL_EMULATOR_URL}/oauth/authorize`,
  },
  token: {
    url: `${process.env.VERCEL_EMULATOR_URL}/login/oauth/token`,
  },
  userinfo: {
    url: `${process.env.VERCEL_EMULATOR_URL}/login/oauth/userinfo`,
  },
  clientId: process.env.VERCEL_CLIENT_ID,
  clientSecret: process.env.VERCEL_CLIENT_SECRET,
  profile(profile) {
    return {
      id: profile.sub,
      name: profile.name,
      email: profile.email,
      image: profile.picture,
    }
  },
}

シード設定

tokens:
  test_token_admin:
    login: admin
    scopes: []

vercel:
  users:
    - username: developer
      name: Developer
      email: [email protected]
  teams:
    - slug: my-team
      name: My Team
      description: Engineering team
  projects:
    - name: my-app
      team: my-team
      framework: nextjs
      buildCommand: next build
      outputDirectory: .next
      rootDirectory: null
      nodeVersion: "20.x"
      envVars:
        - key: DATABASE_URL
          value: postgres://localhost/mydb
          type: encrypted
          target: [production, preview]
  integrations:
    - client_id: oac_abc123
      client_secret: secret_abc123
      name: My Vercel App
      redirect_uris:
        - http://localhost:3000/api/auth/callback/vercel

ページネーション

limit、since、until クエリパラメーターを使用するカーソルベースのページネーションです。レスポンスには pagination オブジェクトが含まれます。

curl "http://localhost:4000/v10/projects?limit=10" \
  -H "Authorization: Bearer $TOKEN"

API エンドポイント

ユーザーとチーム
# Registration check
curl http://localhost:4000/registration

# Authenticated user
curl http://localhost:4000/v2/user -H "Authorization: Bearer $TOKEN"

# Update user
curl -X PATCH http://localhost:4000/v2/user \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "New Name", "email": "[email protected]"}'

# List teams (cursor paginated)
curl http://localhost:4000/v2/teams -H "Authorization: Bearer $TOKEN"

# Get team (by ID or slug)
curl http://localhost:4000/v2/teams/my-team -H "Authorization: Bearer $TOKEN"

# Create team
curl -X POST http://localhost:4000/v2/teams \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"slug": "new-team", "name": "New Team"}'

# Update team (name, slug, description)
curl -X PATCH http://localhost:4000/v2/teams/my-team \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "Updated Team", "description": "New description"}'

# List members
curl http://localhost:4000/v2/teams/my-team/members -H "Authorization: Bearer $TOKEN"

# Add member (by uid or email, with role)
curl -X POST "http://localhost:4000/v2/teams/team_id/members" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"email": "[email protected]", "role": "MEMBER"}'

ロール:OWNER、MEMBER、DEVELOPER、VIEWER。

プロジェクト
# Create project (with optional env vars, git, and build config)
curl -X POST http://localhost:4000/v11/projects \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "my-app", "framework": "nextjs", "buildCommand": "next build", "outputDirectory": ".next", "nodeVersion": "20.x", "environmentVariables": [{"key": "API_KEY", "value": "secret", "type": "encrypted", "target": ["production"]}]}'

# List projects (search, cursor pagination)
curl "http://localhost:4000/v10/projects?search=my-app" \
  -H "Authorization: Bearer $TOKEN"

# Get project (includes env vars)
curl http://localhost:4000/v9/projects/my-app \
  -H "Authorization: Bearer $TOKEN"

# Update project (framework, buildCommand, devCommand, installCommand,
#   outputDirectory, rootDirectory, nodeVersion, serverlessFunctionRegion,
#   publicSource, autoAssignCustomDomains, gitForkProtection,
#   commandForIgnoringBuildStep)
curl -X PATCH http://localhost:4000/v9/projects/my-app \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"framework": "remix"}'

# Delete project (cascades deployments, domains, env vars, protection bypasses)
curl -X DELETE http://localhost:4000/v9/projects/my-app \
  -H "Authorization: Bearer $TOKEN"

# Promote aliases status
curl http://localhost:4000/v1/projects/my-app/promote/aliases \
  -H "Authorization: Bearer $TOKEN"

# Protection bypass: generate, revoke, regenerate
curl -X PATCH http://localhost:4000/v1/projects/my-app/protection-bypass \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"generate": {"note": "CI preview", "scope": "deployment"}}'

# Revoke protection bypass secrets
curl -X PATCH http://localhost:4000/v1/projects/my-app/protection-bypass \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"revoke": ["secret_to_revoke"]}'

# Regenerate protection bypass secrets
curl -X PATCH http://localhost:4000/v1/projects/my-app/protection-bypass \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"regenerate": ["old_secret"]}'
デプロイ
# Create deployment (auto-transitions to READY)
curl -X POST http://localhost:4000/v13/deployments \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "my-app", "target": "production", "meta": {"commit": "abc123"}, "regions": ["iad1"], "gitSource": {"type": "github", "ref": "main", "sha": "abc123", "repoId": "123", "org": "my-org", "repo": "my-app", "message": "Deploy", "authorName": "dev", "commitAuthorName": "dev"}}'

# Targets: "production", "preview", "staging"

# Get deployment (by ID or URL)
curl http://localhost:4000/v13/deployments/dpl_abc123 \
  -H "Authorization: Bearer $TOKEN"

# List deployments (filter by projectId, app, target, state; cursor paginated)
curl "http://localhost:4000/v6/deployments?projectId=my-app&target=production&limit=10" \
  -H "Authorization: Bearer $TOKEN"

# List deployments (filter by commit SHA)
curl "http://localhost:4000/v7/deployments?sha=abc123" \
  -H "Authorization: Bearer $TOKEN"

# Delete deployment
curl -X DELETE http://localhost:4000/v13/deployments/dpl_abc123 \
  -H "Authorization: Bearer $TOKEN"

# Cancel building deployment
curl -X PATCH http://localhost:4000/v12/deployments/dpl_abc123/cancel \
  -H "Authorization: Bearer $TOKEN"

# List deployment aliases
curl http://localhost:4000/v2/deployments/dpl_abc123/aliases \
  -H "Authorization: Bearer $TOKEN"

# Get build events/logs (supports direction, limit)
curl "http://localhost:4000/v3/deployments/dpl_abc123/events?direction=forward&limit=50" \
  -H "Authorization: Bearer $TOKEN"

# List deployment files
curl http://localhost:4000/v6/deployments/dpl_abc123/files \
  -H "Authorization: Bearer $TOKEN"

# Upload file (by SHA digest)
curl -X POST http://localhost:4000/v2/files \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/octet-stream" \
  -H "x-vercel-digest: sha256hash" \
  --data-binary @file.txt
ドメイン
# Add domain (with optional redirect, gitBranch, customEnvironmentId)
curl -X POST http://localhost:4000/v10/projects/my-app/domains \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "example.com", "redirect": null, "redirectStatusCode": null, "gitBranch": null}'

# *.vercel.app domains are auto-verified

# List domains (cursor paginated)
curl http://localhost:4000/v9/projects/my-app/domains \
  -H "Authorization: Bearer $TOKEN"

# Get, update, remove domain
curl http://localhost:4000/v9/projects/my-app/domains/example.com \
  -H "Authorization: Bearer $TOKEN"

# Verify domain
curl -X POST http://localhost:4000/v9/projects/my-app/domains/example.com/verify \
  -H "Authorization: Bearer $TOKEN"

リダイレクトのステータスコード:301、302、307、308。

環境変数
# List env vars (with decrypt option; filter by gitBranch, customEnvironmentId)
curl "http://localhost:4000/v10/projects/my-app/env?decrypt=true" \
  -H "Authorization: Bearer $TOKEN"

# Create env vars (single, batch, or upsert)
curl -X POST "http://localhost:4000/v10/projects/my-app/env?upsert=true" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"key": "API_KEY", "value": "secret123", "type": "encrypted", "target": ["production", "preview"], "comment": "API key for service"}'

# Get env var
curl http://localhost:4000/v10/projects/my-app/env/env_abc123 \
  -H "Authorization: Bearer $TOKEN"

# Update env var
curl -X PATCH http://localhost:4000/v9/projects/my-app/env/env_abc123 \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"value": "newsecret"}'

# Delete env var
curl -X DELETE http://localhost:4000/v9/projects/my-app/env/env_abc123 \
  -H "Authorization: Bearer $TOKEN"

環境変数のタイプ:system、encrypted、plain、secret、sensitive。

Blob

@vercel/blob SDK が使用する Vercel Blob API(put、head、list、del)を実装しています。2 つの環境変数を使って、SDK の接続先をエミュレーターに設定します。

VERCEL_BLOB_API_URL=http://localhost:4000/api/blob
BLOB_READ_WRITE_TOKEN=vercel_blob_rw_mystore_secret

vercel_blob_rw_<storeId>_<secret> 形式のトークンはすべて受け付けます。トークンからストア ID を解析します。

import { put, head, list, del } from '@vercel/blob'

const blob = await put('avatars/user.png', data, { access: 'public' })
// blob.url serves the bytes from the emulator
await head(blob.url)
await list({ prefix: 'avatars/' })
await del(blob.url)

HTTP による直接操作:

BLOB_TOKEN="vercel_blob_rw_mystore_secret"

# Upload (honors x-add-random-suffix, x-allow-overwrite, x-content-type,
#   x-cache-control-max-age, x-if-match headers)
curl -X PUT "http://localhost:4000/api/blob?pathname=docs/readme.txt" \
  -H "Authorization: Bearer $BLOB_TOKEN" \
  --data-binary @readme.txt

# Copy (x-if-match is compared with the source blob's ETag;
#   an existing destination is always replaced)
curl -X PUT "http://localhost:4000/api/blob?pathname=docs/copy.txt&fromUrl=docs/readme.txt" \
  -H "Authorization: Bearer $BLOB_TOKEN"

# Metadata (head)
curl "http://localhost:4000/api/blob?url=docs/readme.txt" \
  -H "Authorization: Bearer $BLOB_TOKEN"

# List (prefix, limit, cursor, mode=folded; folded pages share one
#   sorted cursor across folders and root blobs)
curl "http://localhost:4000/api/blob?prefix=docs/" \
  -H "Authorization: Bearer $BLOB_TOKEN"

# Delete
curl -X POST http://localhost:4000/api/blob/delete \
  -H "Authorization: Bearer $BLOB_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"urls": ["docs/readme.txt"]}'

# Start a multipart upload (returns { key, uploadId })
curl -X POST "http://localhost:4000/api/blob/mpu?pathname=docs/large.bin" \
  -H "Authorization: Bearer $BLOB_TOKEN" \
  -H "x-mpu-action: create"

# Serve content (public, no auth; ?download=1 forces attachment)
curl http://localhost:4000/blob/mystore/docs/readme.txt

アップロードと削除のルール:

  • パス名の先頭にあるスラッシュは 1 つ削除されます。そのため、/docs/readme.txt は docs/readme.txt として保存されます。実際のサービスと同様に、HTTP ではそれ以外の // は受け付けるため、//a.txt は /a.txt として保存されます(@vercel/blob SDK はリクエスト送信前に // を拒否します)。このルールはコピー先とマルチパートアップロードにも適用されます。
  • コンテンツタイプは、x-content-type、既知のタイプに一致するパス名の拡張子、リクエストの Content-Type ヘッダー、application/octet-stream の順に取得されます。
  • x-cache-control-max-age は最小値が 60 秒です(0 は public, max-age=60 として保存されます)。値が指定されていない場合、または無効な場合は、デフォルトで 30 日になります。
  • urls 配列がない場合、または空の場合、削除リクエストは 400 Missing urls in request body を返します。不正な JSON の場合は 400 Cannot delete more than 1,000 urls at once or body is malformed、1,000 件を超える URL が含まれる場合は 400 Cannot delete more than 1,000 urls at once、文字列以外の値が含まれる場合は 400 Some urls are malformed を返します。これらの場合、削除は行われません。

マルチパートアップロードは作成できますが、パーツのアップロードとアップロードの完了にはまだ対応しておらず、400 が返されます。クライアント(ブラウザー)からのアップロードにはまだ対応していません。

API キー
# Create API key (optional teamId scope)
curl -X POST "http://localhost:4000/v1/api-keys?teamId=team_abc123" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "CI Deploy Key"}'

# List API keys (optional teamId filter)
curl "http://localhost:4000/v1/api-keys?teamId=team_abc123" \
  -H "Authorization: Bearer $TOKEN"

# Delete API key
curl -X DELETE http://localhost:4000/v1/api-keys/ak_abc123 \
  -H "Authorization: Bearer $TOKEN"

作成された API キーは自動的にトークンマップに登録され、すべてのエンドポイントで Bearer トークンとして使用できます。

OAuth /インテグレーション
# Authorize (browser flow, shows user picker)
# GET /oauth/authorize?client_id=...&redirect_uri=...&scope=...&state=...

# Token exchange (supports PKCE; accepts JSON or form-urlencoded)
curl -X POST http://localhost:4000/login/oauth/token \
  -H "Content-Type: application/json" \
  -d '{"client_id": "oac_abc123", "client_secret": "secret_abc123", "code": "<code>", "redirect_uri": "http://localhost:3000/api/auth/callback/vercel"}'

# User info (returns sub, email, email_verified, name, preferred_username, picture)
curl http://localhost:4000/login/oauth/userinfo \
  -H "Authorization: Bearer $TOKEN"

よくあるパターン

プロジェクトを作成してデプロイする
TOKEN="test_token_admin"
BASE="http://localhost:4000"

# Create project
curl -X POST $BASE/v11/projects \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "my-app", "framework": "nextjs"}'

# Add env var
curl -X POST $BASE/v10/projects/my-app/env \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"key": "DATABASE_URL", "value": "postgres://...", "type": "encrypted", "target": ["production"]}'

# Create deployment
curl -X POST $BASE/v13/deployments \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "my-app", "target": "production"}'
OAuth 統合フロー
  1. ユーザーを $VERCEL_EMULATOR_URL/oauth/authorize?client_id=...&redirect_uri=...&state=... にリダイレクトします。
  2. ユーザーがエミュレーターの UI でシード済みユーザーを選択します。
  3. エミュレーターが ?code=...&state=... を付けてリダイレクトします。
  4. POST /login/oauth/token でコードをトークンに交換します。
  5. GET /login/oauth/userinfo でユーザー情報を取得します。

PKCE に対応しています。認可リクエストに code_challenge と code_challenge_method を渡し、トークン交換時に code_verifier を渡します。

チームスコープのリクエスト

すべてのエンドポイントで、クエリパラメーター teamId または slug を指定できます。

curl "http://localhost:4000/v10/projects?teamId=team_abc123" \
  -H "Authorization: Bearer $TOKEN"

curl "http://localhost:4000/v10/projects?slug=my-team" \
  -H "Authorization: Bearer $TOKEN"

組み込みエミュレーターと併せてカスタムエミュレーターを使用する

npx emulate init --custom inventory を使用して、サードパーティ API エミュレーターをひな形から作成し、テストします。emulate の defineConfig を使い、{ emulator: "vercel" } などの組み込みエントリと併せて、emulate.config.ts に登録します。npx emulate start --watch を実行すると、インポートが再読み込みされ、表示された /_emulate URL でカスタムの状態を確認できます。再読み込みが成功すると、実行状態はシードにリセットされます。既存のフラットなシード設定も引き続き使用できます。明示的なファイルを選択するには --config を指定します。サードパーティ API エミュレーターの作成とテストについては、https://emulate.dev/docs/custom-emulators を参照してください。

コメント