mcpField
C
connector-x

connector-x

github.com/caffeinelabs開発28.0k 回インストール1 週間前に更新
リポジトリ

connector-x について

connector-x は、X API 仕様から生成された Motoko バインディングを提供し、X API v2 への投稿方法を説明します。ユーザーに代わって投稿する canister の開発者向けで、OAuth 2.0 PKCE を利用します。

主な機能

  1. 01X API v2 の Motoko バインディング
  2. 02TweetsApi.createPosts による投稿
  3. 03OAuth 2.0 PKCE のベアラートークン設定
  4. 04既定で非レプリケートの HTTP リクエスト
  5. 05JSON から null の任意フィールドを除外
  6. 06トークン更新とレート制限の説明

活用シーン

  1. 01Motoko canister から投稿
  2. 02OAuth 2.0 PKCE でユーザーを認証
  3. 03送信前に投稿フィールドを設定

インストール

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

$npx skills add https://github.com/caffeinelabs/skills --skill connector-x

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

詳細

x-clientでXに投稿する

XのOpenAPI仕様から生成された、X API v2向けのMotokoバインディングです。書き込みには TweetsApi.createPosts (POST /2/tweets)を使用します。リクエストモデルは TweetCreateRequest です。

バックエンド

OAuth 2.0のベアラートークンを持つユーザーに代わってツイートを投稿する、最小限の canister です(トークンの取得と更新は canister 側で行います。詳細は後述)。デフォルトでは非レプリケートなので、トークンを渡すだけで使えます。すべてのオプションフィールドを指定する必要があり、nullは「指定なし」を意味します。

import { createPosts } "mo:x-client/Apis/TweetsApi";
import { type TweetCreateRequest } "mo:x-client/Models/TweetCreateRequest";
import { defaultConfig } "mo:x-client/Config";

actor {
  // Post a tweet on behalf of a user holding an OAuth 2.0 bearer token.
  public func postTweet(accessToken : Text, body : Text) : async () {
    let cfg = { defaultConfig with auth = ?#bearer accessToken };
    let req : TweetCreateRequest = {
      text_ = ?body;
      for_super_followers_only = null;
      poll = null;
      reply = null;
      reply_settings = null;
      media = null;
      geo = null;
      quote_tweet_id = null;
      nullcast = null;
      direct_message_deep_link = null;
      community_id = null;
      card_uri = null;
      edit_options = null;
      made_with_ai = null;
      paid_partnership = null;
      share_with_followers = null;
    };
    ignore await* createPosts(cfg, req);
  };
}

テキストフィールドはtext_ : ?Textです(末尾のアンダースコアはMotokoのキーワードとの衝突を避けるためのもので、JSONキー"text"にシリアライズされます)。

OAuth 2.0の設定 — PKCE、クライアントシークレット不要

すべての書き込みエンドポイント(特に/2/tweets)には、ユーザーごとのOAuth 2.0ベアラートークンが必要です。x-clientはPKCEフロー向けに構築されているため、クライアントシークレットは不要で、公開情報であるClient IDだけを使用します。

  1. X Developer Portalにアクセスし、プロジェクト(無料プランでは月1,500件の投稿)とアプリを作成します。

  2. アプリ → 設定 → ユーザー認証設定 → 編集を開き、OAuth 2.0をオンにします。アプリの種類はWeb App, Automated App or Bot(PKCE)を選択します。Native Appや「Confidential Client」は選択しないでください。これらは、このクライアントが出力しないクライアントシークレットフローを強制します。

  3. コールバックURI:?code=…を受け取る canister のHTTPSエンドポイントを指定します。完全一致が必要です(例:https://<canister-id>.ic0.app/oauth/x/callback)。

  4. 認証時にリクエストするスコープ:

    スコープ理由
    tweet.writecreatePosts/投稿に必須
    tweet.readUIに「@…として接続済み」と表示
    users.read認証済みユーザーを特定
    offline.accessリフレッシュトークンを発行(アクセストークンの有効期間は約2時間)
  5. 保存し、OAuth 2.0 Client ID(約30文字の公開文字列)をコピーします。これはシークレットではないため、コミット、ログ出力、ハードコードをしても安全です。

デプロイモデル — どちらか一方を選ぶか、両方に対応します。管理者が一度設定する canister 全体共通のClient ID(デフォルト)、またはレート制限のクォータを共有すべきでないマルチテナントアプリ向けのユーザーごとのClient IDです。

スコープは認証時にリクエストしますが、チェックを外していると発行されたトークンに含まれません。createPostsで「Insufficient OAuth scope」が表示される場合、ほとんどはtweet.writeが不足しています。

呼び出しはデフォルトで非レプリケート

x-clientの呼び出しはすべて、IC上のhttp_requestです。このパッケージではdefaultConfigにis_replicated = ?falseを設定しています。Xは副作用があり(投稿によって状態が変更され)、レート制限ヘッダーや応答のタイムスタンプもリクエストごとに異なります。そのため、レプリケートされたアウトコールでは、すべてのサブネットノードがリクエストを発行し、ICが完全に同一の応答を要求するうえ、サイクルも約13倍必要になるため、投稿が重複し、コンセンサスにも失敗します。自分で設定する必要はありません。デフォルトのままで問題ありません。コンセンサスが特に必要な場合に限り、is_replicated = ?trueに上書きしてください。

オプションフィールドはnullのままにする

x-clientはserde-coreのskip_null_fieldsオプションを使い、値がnullのオプションフィールドを送信するJSONから除外します。そのため、/2/tweetsには設定したフィールドだけが渡されます。text_ = ?"…"を設定し、ほかのすべてのフィールドをnullにしたTweetCreateRequestを構築すれば(上記のスニペットのように)、リクエストボディは検証に通ります。Motokoでは値を指定する際にレコードの全フィールドが必要です。nullは「指定なし」を表します。

null以外のオプションフィールドのサブオブジェクト規則

poll、reply、geo、media、edit_optionsのいずれかを?Someに設定すると、Xはそのサブオブジェクトに必要なフィールドを検証します。空のオブジェクトは送信できないため、フィールドをnullのままにするか、必要な値をすべて設定してください。

  • poll — options(2つ以上)とduration_minutes。
  • reply — in_reply_to_tweet_id。
  • media — media_ids(事前にアップロードしておく必要があります)。
  • geo — place_id。

トークンの更新

アクセストークンは期限切れになります(約2時間)。各呼び出しの前に、expires_atまでの安全バッファ内であれば、保存済みのrefresh_tokenとClient IDを使用して https://api.x.com/2/oauth2/tokenにgrant_type=refresh_tokenを指定してPOSTし、canisterでトークンを更新してください。 Xは更新のたびにリフレッシュトークンをローテーションします。新しいaccess_token とrefresh_tokenの両方を保存してください。古いリフレッシュトークンを再利用すると400が返され、再認証が必要になります。x-clientは更新を認識しません。更新処理はcanister側で行います。標準的なコード形式については、posting-to-x拡張機能を参照してください。

レート制限

無料プランでは、アプリあたり月間1500件の投稿、月間500件の読み取りが可能です。本番環境ではHTTP 429を受け取ったらバックオフしてください。投稿を黙って再試行してはいけません(再試行するとツイートが重複する可能性があります)。Xのレート制限ヘッダーはレスポンス本文に含まれますが、このパッケージでは解釈されません。

コメント