Skip to main content

Documentation Index

Fetch the complete documentation index at: https://adcp-docs-ja.pier1.co.jp/llms.txt

Use this file to discover all available pages before exploring further.

Model Context Protocol を使って AdCP を統合するためのトランスポート別ガイドです。タスク処理、ステータス管理、ワークフローパターンは Task Lifecycle を参照してください。

MCP 経由で AdCP をテスト

CLI ツール を使うか、AgenticAdvertising.org のアシスタント Addie とチャットして AdCP タスクをテストできます。

ツールコールパターン

基本のツール呼び出し

// Standard MCP tool call
const response = await mcp.call('get_products', {
  brand: {
    domain: "premiumpetfoods.com"
  },
  brief: "Video campaign for pet owners"
});

// All responses include status field (AdCP 1.6.0+)
console.log(response.status);   // "completed" | "input-required" | "working" | etc.
console.log(response.message);  // Human-readable summary

フィルター付きツール呼び出し

// Structured parameters
const response = await mcp.call('get_products', {
  brand: {
    domain: "betnow.com"
  },
  brief: "Sports betting app for March Madness",
  filters: {
    format_types: ["video"],
    delivery_type: "guaranteed",
    max_cpm: 50
  }
});

アプリケーションレベルのコンテキスト付き呼び出し

// Pass opaque application-level context; agents must carry it back
const response = await mcp.call('build_creative', {
  target_format_id: { agent_url: 'https://creative.agent', id: 'premium_bespoke_display' },
  creative_manifest: { /* ... */ },
  context: { ui: 'buyer_dashboard', session: '123' }
});

// Response includes the same context at the top level
console.log(response.context); // { ui: 'buyer_dashboard', session: '123' }

MCP レスポンス形式

AdCP 1.6.0 の新機能: すべてのレスポンスに統一ステータスフィールドが含まれます。 MCP レスポンスは フラット構造 で、タスク固有フィールドがプロトコルフィールドと同じトップレベルに配置されます:
{
  "status": "completed",           // Unified status (see Core Concepts)
  "message": "Found 5 products",   // Human-readable summary
  "context_id": "ctx-abc123",      // MCP session continuity
  "context": { "ui": "buyer_dashboard" }, // Application-level context echoed back
  "products": [...],               // Task-specific data (flat, not nested)
  "errors": [...]                  // Task-level errors/warnings
}

MCP 固有フィールド

  • context_id: 手動で管理するセッション ID
  • context: 呼び出し元が提供し、エージェントがそのまま返す不透明メタデータ
  • status: 一貫性のため A2A と同じ値
  • タスク固有フィールド(例: products, media_buy_id, creatives)は data にラップされずトップレベルに配置
ステータス処理: 完全なパターンは Task Lifecycle を参照してください。

利用可能なツール

すべての AdCP タスクは MCP ツールとして利用できます:

プロトコルツール

await mcp.call('get_adcp_capabilities', {...});  // Discover agent capabilities (start here)

Media Buy ツール

await mcp.call('get_products', {...});           // Discover inventory
await mcp.call('list_creative_formats', {...});  // Get format specs
await mcp.call('create_media_buy', {...});       // Create campaigns
await mcp.call('update_media_buy', {...});       // Modify campaigns
await mcp.call('sync_creatives', {...});         // Manage creative assets
await mcp.call('get_media_buy_delivery', {...}); // Performance metrics
await mcp.call('provide_performance_feedback', {...}); // Share outcomes

タスク管理ツール

await mcp.call('tasks/list', {...});          // List and filter async tasks
await mcp.call('tasks/get', {...});           // Poll specific task status

Signals ツール

await mcp.call('get_signals', {...});      // Discover audience signals
await mcp.call('activate_signal', {...});  // Deploy signals to platforms
タスクパラメータ: Media Buy および Signals セクションの各タスクドキュメントを参照。 タスク管理: 非同期追跡、ポーリングパターン、Webhook 連携の詳細は Webhooks を参照。

コンテキスト管理(MCP 固有)

重要: MCP はコンテキストを手動管理する必要があります。会話状態を保つには context_id を渡してください。

コンテキストセッションパターン

class McpAdcpSession {
  constructor(mcpClient) {
    this.mcp = mcpClient;
    this.contextId = null;
    this.activeTasks = new Map(); // Track async operations
  }
  
  async call(tool, params, options = {}) {
    // Build request with protocol-level fields
    const request = {
      tool: tool,
      arguments: params
    };
    
    // Include context from previous calls
    if (this.contextId) {
      request.context_id = this.contextId;
    }
    
    // Include webhook configuration (protocol-level, A2A-compatible)
    if (options.push_notification_config) {
      request.push_notification_config = options.push_notification_config;
    }
    
    const response = await this.mcp.call(request);
    
    // Save context for next call
    this.contextId = response.context_id;
    
    // Track async operations
    if (response.task_id) {
      this.activeTasks.set(response.task_id, {
        tool,
        params,
        startTime: new Date(),
        status: response.status
      });
    }
    
    return response;
  }
  
  reset() {
    this.contextId = null;
    this.activeTasks.clear();
  }
  
  // Poll specific task
  async pollTask(taskId, includeResult = false) {
    return this.call('tasks/get', { 
      task_id: taskId, 
      include_result: includeResult 
    });
  }
  
  // List pending tasks
  async listPendingTasks() {
    return this.call('tasks/list', {
      filters: {
        statuses: ["submitted", "working", "input-required"]
      }
    });
  }
  
  // State reconciliation helper
  async reconcileState() {
    const pending = await this.listPendingTasks();
    const serverTasks = new Set(pending.tasks.map(t => t.task_id));
    const clientTasks = new Set(this.activeTasks.keys());
    
    return {
      missing_from_client: [...serverTasks].filter(id => !clientTasks.has(id)),
      missing_from_server: [...clientTasks].filter(id => !serverTasks.has(id)),
      total_pending: pending.tasks.length
    };
  }
}

使用例

基本的なコンテキスト付きセッション

const session = new McpAdcpSession(mcp);

// First call - no context needed
const products = await session.call('get_products', {
  brief: "Sports campaign"
});

// Follow-up - context automatically included
const refined = await session.call('get_products', {
  brief: "Focus on premium CTV"
});
// Session remembers previous interaction

Webhook を用いた非同期処理

MCP はプッシュ通知を定義していません。AdCP は Webhook 設定(pushNotificationConfig)とペイロード形式(mcp-webhook-payload.json)を規定することでこのギャップを埋めます。Webhook を設定すると、サーバーはポーリング不要でタスク更新を URL に POST します。 Webhook Envelope: mcp-webhook-payload.json
ベストプラクティス: URL ベースのルーティング
推奨: ルーティング情報(task_type, operation_id)はペイロードではなく Webhook URL に含めます。 理由
  • 業界標準パターン - 多くの API で採用
  • 関心の分離 - ルーティングは URL、データはペイロード
  • プロトコル非依存 - MCP/A2A/REST などで共通に利用可能
  • ハンドラー簡素化 - ペイロード解析ではなく URL でルーティング
URL Pattern Options:
// Option 1: Path parameters (recommended)
url: `https://buyer.com/webhooks/adcp/${taskType}/${operationId}`
// Example: /webhooks/adcp/create_media_buy/op_nike_q1_2025

// Option 2: Query parameters
url: `https://buyer.com/webhooks/adcp?task=${taskType}&op=${operationId}`

// Option 3: Subdomain routing
url: `https://${taskType}.webhooks.buyer.com/${operationId}`
Example Configuration:
const operationId = "op_nike_q1_2025";
const taskType = "create_media_buy";

// Configure webhook with routing in URL
const response = await session.call('create_media_buy',
  {
    packages: [...],
    budget: { total: 150000, currency: "USD" }
  },
  {
    pushNotificationConfig: {
      url: `https://buyer.com/webhooks/adcp/${taskType}/${operationId}`,
      authentication: {
        schemes: ["HMAC-SHA256"],  // or ["bearer"] for simple auth
        credentials: "shared_secret_32_chars"
      }
    }
  }
);

if (response.status === 'submitted') {
  console.log(`Task ${response.task_id} submitted for long-running execution`);
  // Server will POST status updates to your webhook URL
} else if (response.status === 'completed') {
  console.log(`Media buy created: ${response.media_buy_id}`);
}
Webhook POST format:
{
  "task_id": "task_456",
  "status": "completed",
  "timestamp": "2025-01-22T10:30:00Z",
  "result": {
    "media_buy_id": "mb_12345",
    "packages": [...]
  }
}
Note: この例は task_typeoperation_id を URL に載せる推奨パターン(例: /webhooks/adcp/create_media_buy/op_456)に従っています。後方互換のためペイロード内のフィールドもスキーマ上はサポートしていますが非推奨です。 result フィールドには AdCP のデータペイロードが入ります。completed/failed ではタスクレスポンス全体(例: create-media-buy-response.json)、それ以外のステータスではステータス別スキーマ(例: create-media-buy-async-response-working.json)を使用します。

MCP Webhook のエンベロープフィールド

mcp-webhook-payload.json には以下が含まれます: 必須フィールド:
  • task_id — 相関用の一意なタスク ID
  • status — 現在のタスクステータス(completed, failed, working, input-required など)
  • timestamp — Webhook 生成時の ISO 8601 タイムスタンプ
任意フィールド:
  • domain — AdCP ドメイン(“media-buy” または “signals”)
  • context_id — 会話/セッション ID
  • message — ステータス変更に関する人間向けコンテキスト
非推奨フィールド(サポートはするが推奨しない):
  • task_type — タスク名(例: “create_media_buy”, “sync_creatives”)- ⚠️ 非推奨: URL ベースのルーティング を参照
  • operation_id — 同一オペレーションの更新を関連付ける - ⚠️ 非推奨: 同上
Data フィールド:
  • result — タスク固有の AdCP ペイロード(下記のデータスキーマ検証を参照)

Webhook が送信される条件

Webhook は次の すべて を満たす場合に送信されます:
  1. タスクが非同期をサポート(例: create_media_buy, sync_creatives, get_products
  2. リクエストに pushNotificationConfig が指定 されています
  3. タスクが非同期実行 — 初回レスポンスが working または submitted
初回レスポンスがすでに終端(completed, failed, rejected)なら、結果が手元にあるため Webhook は送信されません。 Webhook を送るステータス変化:
  • working → 進捗更新(処理中)
  • input-required → 人による入力が必要
  • completed → 最終結果
  • failed → エラー詳細

データスキーマの検証

MCP Webhook の result フィールドはステータス別スキーマを使用します:
StatusSchemaContents
completed[task]-response.json成功ブランチの完全なタスクレスポンス
failed[task]-response.jsonエラーブランチの完全なタスクレスポンス
working[task]-async-response-working.json進捗情報(percentage, step
input-required[task]-async-response-input-required.json必要事項、承認情報
submitted[task]-async-response-submitted.json受領通知(通常は最小限)
スキーマ参照: async-response-data.json

Webhook Handler Example

const express = require('express');
const app = express();

app.post('/webhooks/adcp/:task_type/:agent_id/:operation_id', async (req, res) => {
  const { task_type, agent_id, operation_id } = req.params;
  const webhook = req.body;

  // Verify webhook authenticity (HMAC-SHA256 example)
  const signature = req.headers['x-adcp-signature'];
  const timestamp = req.headers['x-adcp-timestamp'];
  if (!verifySignature(webhook, signature, timestamp)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  // Handle status changes
  switch (webhook.status) {
    case 'input-required':
      // Alert human that input is needed
      await notifyHuman({
        operation_id,
        message: webhook.message,
        context_id: webhook.context_id,
        data: webhook.result
      });
      break;

    case 'completed':
      // Process the completed operation
      if (task_type === 'create_media_buy') {
        await handleMediaBuyCreated({
          media_buy_id: webhook.result.media_buy_id,
          packages: webhook.result.packages
        });
      }
      break;

    case 'failed':
      // Handle failure
      await handleOperationFailed({
        operation_id,
        error: webhook.result?.errors,
        message: webhook.message
      });
      break;

    case 'working':
      // Update progress UI
      await updateProgress({
        operation_id,
        percentage: webhook.result?.percentage,
        message: webhook.message
      });
      break;

    case 'canceled':
      await handleOperationCanceled(operation_id, webhook.message);
      break;
  }

  // Always return 200 for successful processing
  res.status(200).json({ status: 'processed' });
});

function verifySignature(payload, signature, timestamp) {
  const crypto = require('crypto');
  const expectedSig = crypto
    .createHmac('sha256', process.env.WEBHOOK_SECRET)
    .update(timestamp + JSON.stringify(payload))
    .digest('hex');
  return signature === `sha256=${expectedSig}`;
}

タスク管理とポーリング

// Check status of specific task
const taskStatus = await session.pollTask('task_456', true);
if (taskStatus.status === 'completed') {
  console.log('Result:', taskStatus.result);
}

// State reconciliation
const reconciliation = await session.reconcileState();
if (reconciliation.missing_from_client.length > 0) {
  console.log('Found orphaned tasks:', reconciliation.missing_from_client);
  // Start tracking these tasks
}

// List all pending operations
const pending = await session.listPendingTasks();
console.log(`${pending.tasks.length} operations in progress`);

コンテキスト期限切れの扱い

async function handleContextExpiration(session, tool, params) {
  try {
    return await session.call(tool, params);
  } catch (error) {
    if (error.message?.includes('context not found')) {
      // Context expired - start fresh
      session.reset();
      return session.call(tool, params);
    }
    throw error;
  }
}
主な違い: コンテキストを自動管理する A2A と異なり、MCP は context_id を明示的に扱う必要があります。

非同期処理の扱い

タスクが working または submitted を返す場合、更新を受け取る方法は 2 つあります:
ApproachBest ForTrade-offs
Pollingシンプルな統合、短時間タスク実装が簡単だが長時間待機に非効率
Webhooks本番システム、長時間タスク効率的だが公開エンドポイントが必要

オプション 1: ポーリング

tasks/get を使って定期的にステータスを確認します:
async function waitForCompletion(session, initialResponse) {
  if (!initialResponse.task_id) {
    return initialResponse; // Already completed
  }
  
  // 'working' は短時間で終わるため高頻度にポーリング
  // 'submitted' は数時間かかる場合があるため低頻度にポーリング
  let pollInterval = initialResponse.status === 'working' ? 5000 : 30000;
  
  while (true) {
    const response = await session.pollTask(initialResponse.task_id, true);
    
    if (['completed', 'failed', 'canceled'].includes(response.status)) {
      return response;
    }
    
    if (response.status === 'input-required') {
      const input = await promptUser(response.message);
      return session.call('create_media_buy', {
        context_id: response.context_id,
        additional_info: input
      });
    }
    
    pollInterval = response.status === 'working' ? 5000 : 30000;
    await new Promise(resolve => setTimeout(resolve, pollInterval));
  }
}

オプション 2: Webhook

Webhook URL を設定すると、サーバーが直接更新を POST します。ポーリング不要なので長時間タスクで効率的です。
const response = await session.call('create_media_buy',
  {
    packages: [...],
    budget: { total: 150000, currency: "USD" }
  },
  {
    pushNotificationConfig: {
      url: "https://buyer.com/webhooks/adcp",
      authentication: {
        schemes: ["HMAC-SHA256"],
        credentials: "shared_secret_32_chars"
      }
    }
  }
);

// ステータスが 'submitted' の場合、サーバーが Webhook に更新を送る
// ポーリング不要。Webhook を待つだけでよい
Webhooks でペイロード形式と処理例を参照。

ステータス別の扱い

const initial = await session.call('create_media_buy', {
  packages: [...],
  budget: { total: 100000, currency: "USD" }
});

switch (initial.status) {
  case 'completed':
    // 即時完了 - 非同期処理不要
    console.log('Created:', initial.media_buy_id);
    break;
    
  case 'working':
    // 2 分以内に完了見込み - ポーリングまたは待機
    console.log('Processing...');
    const final = await waitForCompletion(session, initial);
    console.log('Created:', final.result.media_buy_id);
    break;
    
  case 'submitted':
    // 長時間(数時間〜数日) - Webhook か低頻度ポーリング
    console.log(`Task ${initial.task_id} queued for approval`);
    // Webhook will notify when complete, or poll manually
    break;
    
  case 'input-required':
    // ユーザー入力待ち
    console.log('Need more info:', initial.message);
    break;
}

統合の例

// コンテキスト管理付きで MCP セッションを初期化
const session = new McpAdcpSession(mcp);

// 統一ステータスで処理(Core Concepts を参照)
async function handleAdcpCall(tool, params, options = {}) {
  const response = await session.call(tool, params, options);
  
  switch (response.status) {
    case 'input-required':
      // 追加情報を処理(パターンは Core Concepts 参照)
      const input = await promptUser(response.message);
      return session.call(tool, { ...params, additional_info: input });
      
    case 'working':
      // 短時間の非同期を処理 
      return waitForCompletion(session, response);
      
    case 'submitted':
      // 長時間の非同期を処理
      if (options.webhook_url) {
        console.log(`Task ${response.task_id} submitted, webhook will notify`);
        return { pending: true, task_id: response.task_id };
      } else {
        console.log(`Task ${response.task_id} submitted, polling...`);
        return waitForCompletion(session, response);
      }
      
    case 'completed':
      return response; // タスク固有フィールドはトップレベル
      
    case 'failed':
      throw new Error(response.message);
  }
}

// Example usage
const products = await handleAdcpCall('get_products', {
  brief: "CTV campaign for luxury cars"
});

MCP 固有の考慮点

ツールディスカバリー

// List available AdCP tools
const tools = await mcp.listTools();
const adcpTools = tools.filter(t => t.name.startsWith('adcp_') || 
  ['get_products', 'create_media_buy'].includes(t.name));

MCP サーバーカードによる AdCP 拡張

推奨: 実行時の機能発見には get_adcp_capabilities を使用してください。サーバーカード拡張はツールカタログやレジストリ向けの静的メタデータを提供します。
MCP サーバーは /.well-known/mcp.json(または /.well-known/server.json)のサーバーカードで AdCP 対応を宣言できます。AdCP 固有メタデータは adcontextprotocol.org 名前空間の _meta フィールドに記載します。
{
  "name": "io.adcontextprotocol/media-buy-agent",
  "version": "1.0.0",
  "title": "AdCP Media Buy Agent",
  "description": "AI-powered media buying agent implementing AdCP",
  "tools": [
    { "name": "get_products" },
    { "name": "create_media_buy" },
    { "name": "list_creative_formats" }
  ],
  "_meta": {
    "adcontextprotocol.org": {
      "adcp_version": "2.6.0",
      "protocols_supported": ["media_buy"],
      "extensions_supported": ["sustainability"]
    }
  }
}
AdCP 対応の検出:
// Check both possible locations for MCP server card
const serverCard = await fetch('https://sales.example.com/.well-known/mcp.json')
  .then(r => r.ok ? r.json() : null)
  .catch(() => null)
  || await fetch('https://sales.example.com/.well-known/server.json')
    .then(r => r.json());

// Check for AdCP metadata
const adcpMeta = serverCard?._meta?.['adcontextprotocol.org'];

if (adcpMeta) {
  console.log('AdCP Version:', adcpMeta.adcp_version);
  console.log('Supported domains:', adcpMeta.protocols_supported);
  // ["media_buy", "creative", "signals"]
  console.log('Typed extensions:', adcpMeta.extensions_supported);
  // ["sustainability"]
}
メリット:
  • テストコールなしで AdCP の対応状況を把握できます
  • 実装しているプロトコルドメイン(media_buy, creative, signals)を宣言できます
  • サポートする拡張を宣言できる(Context & Sessions 参照)
  • バージョンに基づく互換性チェックが可能
Note: _meta フィールドは MCP server.json spec に従い逆 DNS の名前空間を使用します。/.well-known/mcp.json/.well-known/server.json の両方をサポートしてください。

パラメータバリデーション

// MCP provides tool schemas for validation
const toolSchema = await mcp.getToolSchema('get_products');
// 呼び出し前にスキーマでバリデーション

エラーハンドリング

AdCP エラーは isError: true のツールレベルレスポンスとして structuredContent.adcp_error にエラーが格納されて返されます。完全な抽出ロジックと JSON-RPC トランスポートコードは Transport Error Mapping を参照してください。
try {
  const response = await session.call('get_products', params);

  // AdCP アプリケーションエラーを確認(isError: true かつ構造化データあり)
  if (response.isError) {
    const adcpError = response.structuredContent?.adcp_error;
    if (adcpError) {
      // code, recovery, retry_after などを含む構造化エラー
      console.log('AdCP error:', adcpError.code, adcpError.recovery);
    }
  }
} catch (mcpError) {
  // MCP トランスポートエラー(接続、認証など)
  // AdCP 構造化トランスポートエラーを確認
  const adcpError = mcpError.data?.adcp_error;
  if (adcpError) {
    console.log('Transport error:', adcpError.code);
  } else {
    console.error('MCP Error:', mcpError);
  }
}

ベストプラクティス

  1. セッションラッパーを利用 してコンテキストを自動管理
  2. レスポンス処理前に status フィールド を確認
  3. コンテキスト期限切れ はリトライで丁寧に処理
  4. ステータス処理パターンは Core Concepts を参照
  5. 利用可能なら MCP ツールスキーマで パラメータ検証

次のステップ

ステータス処理、非同期オペレーション、確認フローについては Task Lifecycle を参照してください。このガイドは MCP トランスポート固有の内容に絞っています。