mcpField
B
bluesky-mcp-server

bluesky-mcp-server

リモート
github.com/cyanheadsソーシャルメディア26 日前に更新
リポジトリ

bluesky-mcp-server について

bluesky-mcp-serverは、MCPクライアントにBlueskyの公開プロフィール、投稿、フィード、スレッド、トレンドへのアクセスを提供し、任意で認証付き全文投稿検索にも対応します。stdioとStreamable HTTPをサポートし、公開ホスト型エンドポイントも利用できます。

主な機能

  1. 01投稿とアカウントの検索
  2. 02プロフィールとユーザーフィードの取得
  3. 03スレッドと引用投稿の取得
  4. 04フィードとトレンドの閲覧
  5. 05公開プロフィールリソースへのアクセス

活用シーン

  1. 01投稿を作成者・話題・日付で調査
  2. 02会話と引用投稿の確認
  3. 03トレンドと関連フィードの探索
  4. 04エージェント向けプロフィール情報の取得

MCP 対応

Blueskyデータの検索・読み取り用に10個のツールと、アクターで指定できるプロフィールリソースを公開します。stdioとStreamable HTTPに対応し、全文投稿検索はアプリパスワードの認証情報を設定した場合にのみ利用できます。

設定

リモートサーバーはインストール不要です。クライアントを選んで設定をコピーするか、ワンクリックで導入できます。

エンドポイントhttps://bluesky.caseyjhand.com/mcp
インストールコマンドをコピー
Claude CodeTerminal
Cursor.cursor/mcp.json
VS Code.vscode/mcp.json
Claude Desktopclaude_desktop_config.json
Windsurf~/.codeium/windsurf/mcp_config.json

コマンドはターミナルで実行し、JSON は各クライアントの MCP 設定に貼り付けて再起動します。認可が必要なサーバーは初回接続時にログインを求めます。

パッケージ
npm@cyanheads/bluesky-mcp-server0.6.0

ツール

8
パラメーター型必須説明
actorstringはい検索するアクターのハンドル(例:"bsky.app"、"alice.bsky.social")または DID(例:"did:plc:z72i7hdynmk6r22z27h6tvur")。先頭に "@" が付いた形式("@bsky.app")やアカウントの bsky.app ページ("https://bsky.app/profile/bsky.app")も受け付け、含まれるハンドルまたは DID として読み取ります。ドットのない名前だけではハンドルではありません。bsky_search_actors を使って解決してください。

詳細

@cyanheads/bluesky-mcp-server

Bluesky の投稿やフィードを検索し、プロフィール、スレッド、トレンドを読み取ります。STDIO または Streamable HTTP に対応しています。

10 個のツール • 1 個のリソース

バージョン ライセンス Docker MCP SDK npm TypeScript Bun

Claude Desktop にインストール Cursor にインストール VS Code にインストール

フレームワーク

公開ホストサーバー: https://bluesky.caseyjhand.com/mcp


概要

アカウントなしで Bluesky の情報を読み取れます。プロフィール、フィードカタログの検索、トレンドトピックとそれに関連するフィード、カスタムフィードや投稿者フィード、スレッド、引用投稿に対応しています。bsky.app のリンクまたは @handle を、入力を求められた箇所に貼り付けてください。任意のアプリパスワードを追加すると、投稿の全文検索も利用できます。

ツール
ツール説明
bsky_search_posts公開Bluesky投稿を全文検索します。投稿者、メンション、言語、タグ、ドメイン、URL、日付、並べ替えのフィルターを利用できます。アプリパスワードが設定されている場合のみ提供されます
bsky_get_profileハンドルまたはDIDでBlueskyアクターの公開プロフィールを取得します。ハンドルとDIDの解決に使います
bsky_get_feedAT-URIまたはbsky.appのURLを指定して、フィードジェネレーターの投稿を読み取ります。トレンドのフィード、Discover、または任意のカスタムフィードを指定できます
bsky_get_author_feedユーザーの最近の投稿を新しい順に取得します。投稿タイプで絞り込めます
bsky_get_post_threadAT-URIまたはbsky.appのURLを指定して、投稿の会話を取得します。親投稿をさかのぼるチェーンと、返信をたどるツリーに加えて、Blueskyがカウントしたものの返さなかった情報も含まれます
bsky_get_post_quotes投稿のquoteCountに含まれる引用投稿を、新しい順またはいいね数順で読み取ります。投稿への反応の多くがここに集まります
bsky_search_actors名前またはハンドルの一部からBlueskyアカウントを検索します
bsky_search_feeds公開フィードカタログの名前と説明を検索し、返されたfeedUriをbsky_get_feedで読み取ります
bsky_get_followsページ分割されたソーシャルグラフのエッジを取得します。ユーザーがフォローしている相手、またはユーザーをフォローしている相手を確認できます
bsky_get_trendingBlueskyのリアルタイムのトレンドトピックを取得します。ストーリーの概要、投稿数、カテゴリ、ステータス、各トピックを牽引するアカウント、投稿を集約するフィードが含まれます
リソース
リソース説明
bsky://profile/{actor}ハンドルまたはDIDで指定できる、Blueskyアクターの公開プロフィール

すべてのリソースデータはツールからも取得できます。プログラムからアクセスするにはbsky_get_profileを、プロフィールのコンテキストを直接挿入するにはbsky://profile/{actor}を使用してください。

機能のリファレンス

bsky_search_posts ツール
  • Bluesky ではサインイン済みアカウントがないと投稿検索が拒否されるため、このツールは BLUESKY_IDENTIFIER と BLUESKY_APP_PASSWORD が設定されている場合にのみ登録されます。設定されていない場合は tools/list に表示されません(投稿検索を参照)
  • 検索はそのアカウントとして PDS 経由で実行されます。最初の検索時にログインし、セッションは再利用されて更新されます。ログインが拒否された場合、再試行は行われません。失敗時には、search_auth_failed(ログインまたは更新が拒否された)、search_login_limited(アカウントの1日のログイン上限に達した。このエラーには検索を再開できる時刻が示されます)、または search_refused(Bluesky が検索を拒否した)が返されます
  • フィルター:投稿者とメンションされたアカウント(ハンドルまたは DID。@handle または bsky.app のプロフィール URL も指定可能。mentions はリッチテキストのメンションのみを対象とします)、2文字の言語コード、ハッシュタグ(# の有無を問いません)、リンク先の domain(ホスト名のみ。先頭の www. は削除されます)または完全一致する url(テキスト内のリンクまたはリンクカードが対象)、since/until、および top/latest の並べ替え。カーソルによる不透明なページネーションを使って、1回の呼び出しにつき最大100件の結果を取得できます
  • since/until は、投稿の並べ替え時刻(createdAt と indexedAt のうち早い方)を秒単位で比較し、指定時刻を含みます。日付のみを指定した場合は 00:00:00 UTC となるため、until: "2026-01-01" は 2025-12-31 の全投稿を対象とします
  • 識別子、言語、ドメイン、URL、日付の入力は、上流への呼び出し前にローカルでパターン検証されます。3文字の言語コード("fil"、"eng")は拒否されます。Bluesky の検索ではこれらのコードが無視され、フィルターなしの結果が返されるためです。コードは小文字にして送信され、後続のサブタグは受け付けられますが、上流では無視されます(en-US は en としてフィルターされます)
  • Bluesky がパラメーターを拒否した場合、単なるステータスコードではなく、upstream_rejected_filter エラー理由を通じて Bluesky 自身の説明が返されます。デコードできないカーソルは、1回のリクエスト後に invalid_cursor として失敗します
  • hitsTotal は Bluesky による推定値であり、正確な件数ではありません。10,000未満の場合、ページネーションで返される件数の上限です(Bluesky は返却対象から除外する投稿も含めてカウントします)。ちょうど10,000の場合は上限値であり、「少なくともその件数」を意味します。truncated/shown/cap は返されたカーソルに基づきます。これ以上一致する投稿がない場合、Bluesky はカーソルを省略します
  • いずれかの出力先でページが48,000バイトの応答予算を超える場合、収まる limit で再検索し、ページ全体を返します。budgetCapped: true となり、hitsTotal とカーソルにはその1回の応答の値が使われます
  • 埋め込みは type で判別するユニオン(images、external、record、video、unknown)に正規化されます。引用投稿は最大3段階までネストして独自の添付ファイルを保持します。それより深い階層の件数は omittedEmbeds に記録され、削除済み、ブロック済み、切り離し済み、または投稿ではない引用は recordKind に示されます
  • モデレーションラベルはフィルターせず、そのまま返されます

投稿ツールでは、任意のリッチテキスト facets を元のテキストとは別に保持します。内容は UTF-8 のバイト範囲(byteStart は含み、byteEnd は含みません)と、完全なリンク URI、メンションの DID、タグです。認識されない機能バリアントや上流からの追加フィールドは、両方の出力先でリテラルなメタデータとして引き続き表示されます。引用投稿は、既存の埋め込み深度の上限内で独自の facets を保持します。両方の応答先では、このメタデータも応答予算に含まれます。bsky_get_post_quotes では、照会対象のテキストを再掲する際に、その facets も省略されます。

bsky_get_feed tool
  • フィードジェネレーターの AT-URI(at://<handle-or-did>/app.bsky.feed.generator/<rkey>)または bsky.app のページ(https://bsky.app/profile/<handle-or-did>/feed/<rkey>。末尾の /、?…、#… は無視されます)を指定します。トレンドの feedUri はそのまま使用でき、Discover は at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/whats-hot です
  • ハンドルの所有者の場合は追加の検索(resolveHandle)が1回必要ですが、DID の所有者の場合は不要です。投稿の AT-URI など、ほかのコレクションは上流への呼び出し前に拒否されます
  • フィードの先頭に固定された投稿には pinned: true が付き、リポストには repostedBy/repostedAt が付きます
  • 1回の呼び出しで最大100件の投稿を取得し、カーソルでページネーションします。フィードは limit 件未満でも後続の投稿を返す場合があるため、取得の打ち切りはカーソルに示されます
  • 48,000バイトのレスポンス予算を超えるページは、収まる limit で再度要求してページ全体を返し、budgetCapped: true として示します。ランキング付きフィードはリクエストごとに再ランキングされるため、投稿とカーソルは同じレスポンスから取得されます
  • feed_not_found(該当するフィードまたはハンドルがありません)、feed_unavailable(フィードのジェネレーターが応答しません。再試行されないため、停止中のフィードはすぐに失敗します)、feed_requires_login(パーソナライズされたフィードです)
  • 検索の認証情報が設定されていても、常に未認証で動作します

bsky_get_profile tool
  • ハンドルまたはDIDを指定します。@handle またはアカウントの bsky.app ページ(https://bsky.app/profile/<handle-or-did>)も使用できます。displayName、handle、DID、bio、pronouns、website、フォロワー数/フォロー数/投稿数、アバター、モデレーションラベル、固定された投稿の AT-URI を返します
  • アカウントでどちらも設定されていない場合、website と pronouns は含まれません。省略可能な links には、website とは別にプロフィールボタンが順番どおりに格納されます。各レコードには URI/CID、完全な遷移先 URL、任意のタイトル/アイコン、モデレーションラベル(val、src?、cts?)が含まれます。省略された配列は省略されたまま、明示的な空配列は空のままです。プロフィールリソースも同じフィールドを返します
  • verification には、Bluesky の完全な認証状態が含まれます。verifiedStatus と trustedVerifierStatus(valid、invalid、none のいずれかで、そのまま渡されます)に加え、信頼された検証者が発行したすべての認証、その発行者、有効性、日付、レコードの AT-URI が含まれます。Bluesky から何も送信されなかった場合は含まれません
  • bio は、アカウント作成者が記述したテキストで独自の Markdown 構造を含む場合があるため、content[] 内で Markdown のブロック引用として表示されます
  • ハンドルを解決できない場合は actor_not_found が返されます。まず bsky_search_actors でハンドルを解決してください
  • ハンドルしかわからず、DID または AT-URI が必要なツールを使う場合に使用する、主要なハンドル↔DID リゾルバーです

bsky_get_author_feed tool
  • ハンドルまたはDIDを指定します。@handle またはアカウントの bsky.app ページも使用できます
  • filter:posts_with_replies、posts_no_replies(デフォルト。返信を除外)、posts_and_author_threads はリポストを含みます。posts_with_media(画像または動画を含む、そのアクター自身の投稿。リンクカードは除く)と posts_with_video は何も返しません
  • include_pins: true を指定すると、プロフィールの固定投稿が追加され、最初のページの先頭に pinned: true として表示されます。これは limit とは別枠で、filter に一致するかどうかにかかわらず追加されます
  • リポストには repostedBy と repostedAt が付き、author には常に実際に投稿を書いた人が示されます
  • リポストを含むフィルターでは、リポストも limit に数えられるため、リポストの多いアカウントでは、上限から想定されるよりも自身の投稿が大幅に少なく返されることがあります。リポストが含まれる場合は、originalPosts/reposts に実際の内訳が示されます
  • 1回の呼び出しで最大100件の投稿を取得し、カーソルでページネーションします。48,000バイトのレスポンス予算を超えるページは、収まる limit で再度要求し(固定投稿はカウントされません)、budgetCapped: true として示します。カーソルは除外された最初の投稿から再開します
  • ハンドルまたはDIDを解決できない場合は actor_not_found が返されます。Bluesky が渡されたカーソルをデコードできない場合は、1回のリクエスト後に invalid_cursor が返されます。Bluesky はこれに HTTP 500 で応答しますが、通常は再試行されます

bsky_get_post_thread tool
  • 投稿の AT-URI または bsky.app のページ(https://bsky.app/profile/<handle-or-did>/post/<rkey>。末尾の /、?…、#… は無視されます)を受け取ります
  • 返信のみを対象とします。引用投稿は bsky_get_post_quotes で取得します
  • depth(返信の階層数。デフォルトは 6、最大は 10 — リクエストでどれだけ深く指定しても、Bluesky 独自の上限です)と parent_height(親チェーンの高さ。デフォルトは 80、最大は 100)
  • ノードが自身の replyCount より少ない返信しか返さない場合、truncated: true、unreturnedReplies(正確な不足数ではなく上限値)、truncationReason("depth" — 続きを取得するには、そのノードの AT-URI を指定します。または "unavailable" — リクエストでは不足分を補えません)が付与されます
  • 親チェーンが会話のルートに達する前に parent_height で停止した場合、最上位のノードに parentChainTruncated: true が付与されます。そのノードの AT-URI を自身のスレッドとして取得すれば、続きにアクセスできます
  • スレッドが 48,000 バイトのレスポンス上限を超える場合、対象投稿、最も近い親から順に、次に階層ごとの返信の順で保持し、投稿全体の区切りで切り詰めます。エンリッチメントには budgetCapped と budgetOmitted(省略された投稿数)、対象投稿には budgetOmittedReplyUris、保持された返信には budgetOmittedReplies、保持された最上位の親には budgetOmittedParents が付与されます。これらの AT-URI を取得すると、省略された内容をすべて読めます
  • 設定されている場合は投稿者の返信制限(返信できるユーザー)と、投稿者が非表示にした返信の AT-URI を返します。削除された投稿は notFound: true、ブロックされた投稿は blocked: true を返します
  • 返信の階層はインデントではなく投稿者の見出し(### ↳2 Name)に表示されるため、深くネストされた返信が Markdown のコードブロックに入ることはありません
  • AT-URI が解決できない場合は invalid_at_uri、post_not_found エラーが発生します。AT-URI は返された投稿の uri フィールドから取得できます
  • フィードジェネレーターの AT-URI(app.bsky.feed.generator)は、リクエストを行う前に uri_is_feed として拒否され、bsky_get_feed が案内されます

bsky_get_post_quotes tool
  • 投稿の AT-URI または bsky.app のページを受け取ります。それ以外のコレクションは、上流への呼び出し前に拒否されます
  • DID オーソリティの投稿ではリクエストは 1 回です。ハンドルの場合は、getQuotes がハンドルオーソリティに対して空のリストを返すため、追加で 1 回の検索(resolveHandle)が必要です
  • 各結果はすべて同じ投稿を引用するため、各結果の埋め込みには、その投稿の AT-URI と CID、読み取れない場合の recordKind、および引用投稿に添付されたメディアのみが保持されます。照会対象の投稿のテキストや添付ファイルが各項目に重複して含まれることはありません
  • 1 回の呼び出しで最大 100 件の引用を取得できます。sort: latest(デフォルト)は新しい順、sort: top は Bluesky の並び順に従って「いいね」数の降順です。適用された sort は両方のサーフェスで返されます。カーソルは同じ投稿とソート順で渡してください。ページは limit 件に満たなくても続きがあることが多いため、切り詰めの情報はカーソルで通知されます
  • ページが 48,000 バイトのレスポンス上限を超える場合、収まる limit で再度取得し、budgetCapped: true を付与します。カーソルは省略された最初の引用から再開します
  • quoteCount は、返される件数の上限値です。Bluesky のカウンターには、インデックスから外れた引用も残っています
  • 投稿が存在しない場合、またはハンドルを解決できない場合は post_not_found が発生します(最初のページが空の場合は、getPosts を 1 回呼び出して確認します)。Bluesky がデコードできないカーソルを指定した場合は、1 回リクエストした後に invalid_cursor が発生します

bsky_search_actors ツール
  • handle、DID、displayName、bio、設定されている場合はpronounsを含む、ランキング順のプロフィールを返します。フォロワー数、フォロー数、投稿数、websiteは含まれません。websiteを返すのはbsky_get_profileのみです
  • Blueskyから送信された場合、各アカウントには2種類の認証ステータス(verification.verifiedStatus、verification.trustedVerifierStatus)が含まれます。これにより、認証済みアカウントと、似たハンドルを持つアカウントを区別できます。認証を発行した相手はbsky_get_profileで確認できます
  • bioはアカウントが作成したテキストであるため、content[]内でMarkdownの引用ブロックとして表示されます
  • 1回の呼び出しにつき最大100件の結果を返し、cursorでページネーションします。各ページはlimitより少ない件数になることがよくありますが、その場合も続きがあります。最後のページにはcursorがありません
  • Blueskyが渡されたcursorをデコードできない場合、1回のリクエスト後にinvalid_cursorを返します(BlueskyはHTTP 400で応答します)
  • 名前は分かっているものの、確認済みのハンドルが分からない場合は、bsky_get_profileまたはbsky_get_author_feedの前に使用してください

bsky_search_feeds ツール
  • 投稿のテキストではなく、プロバイダーの公開フィードカタログのメタデータを検索します。feedUri、displayName、任意のdescription、および任意の表示名を含む作成者のDID/ハンドルを、プロバイダーの順序で返します
  • 投稿検索の認証情報を設定していても、常に認証情報は不要です。返されたfeedUriをbsky_get_feedに直接渡すと、その投稿を取得できます
  • queryは必須です。limitのデフォルトは25で、共通の上限設定ポリシーにより最大100に制限されます。同じqueryとともに不透明なcursorを渡してください。cursorがあるだけで、空のページでも続きがあることを示します
  • 不安定なapp.bsky.unspecced.getPopularFeedGeneratorsを使用するため、変更または廃止される可能性があります。catalog_unavailableの場合はbsky_get_trendingで代替できます。これはフィードの発見機能であり、網羅的な一覧取得ではありません
  • フィード名、説明、作成者のIDは、引用されたサードパーティーデータとして表示されます。空の値が指定された場合と、フィールドが省略された場合は区別されます

bsky_get_follows ツール
  • direction:followers(そのアクターをフォローしているアカウント)またはfollowing(そのアクターがフォローしているアカウント)
  • sort:latest(Blueskyのデフォルト。フォローした日時が新しい順)またはtop(Bluesky独自の、著名なアカウントのランキング順であり、フォロワー数順ではありません)。cursorは同じsortとともに渡してください
  • handleまたはDIDを指定します。@handleや、アカウントのbsky.appページも指定できます
  • ページネーションされたプロフィール(handle、DID、displayName、bio、設定されている場合はpronouns、認証ステータス)と、ステータスも含む対象アカウント自身のプロフィール概要を返します
  • リストと対象アカウントのどちらにも、フォロワー数、フォロー数、投稿数、websiteは含まれません。必要な場合はbsky_get_profileで取得してください
  • 1ページにつき最大100件を返し、cursorでページネーションします。残りのアカウントを解決できなくなった場合、cursorが空のページを返すことがあり、その際はレスポンスでリストの末尾であることが示されます
  • handleまたはDIDを解決できない場合はactor_not_foundを返します

bsky_get_trending ツール
  • トピックの表示名、Blueskyによる1文のストーリー概要(description)、投稿数、カテゴリ、ステータス(例:hot、cooling、stale)、開始時刻、および各トピックの話題を牽引する代表的なアカウントを最大5件返します
  • 各トレンドにはフィードジェネレーターが紐付いています。feedUriはそのフィードのAT-URIで、トレンドのlinkから解析されます。bsky_get_feedでその投稿を取得できます。topicはフィードのレコードキーであり、検索語ではありません
  • cursorはありません。現在のスナップショットをlimitまで返します(デフォルトは10、最大25。Bluesky独自の最大値)。truncatedはlimitより多くのトピックがトレンド入りしていることを示すため、25では表示されません
  • 不安定なエンドポイントapp.bsky.unspecced.getTrendsを使用します。Blueskyは予告なく変更する可能性があります

bsky://profile/{actor} リソース
  • bsky_get_profile と同じフィールド(displayName、handle、DID、bio、pronouns、website、フォロワー数/フォロー中の数/投稿数、アバター、モデレーションラベル、固定された投稿の AT-URI、完全な認証状態)を、コンテキストに挿入できる形式で返します
  • {actor} にはハンドルまたは DID を指定でき、先頭に @ を付けても付けなくてもかまいません(bsky://profile/@bsky.app)
  • ハンドルを解決できない場合は actor_not_found が返されます

機能

@cyanheads/mcp-ts-core を基盤としており、stdio と Streamable HTTP のトランスポート、プラグイン可能な認証(none/jwt/oauth)、切り替え可能なストレージ(in-memory、filesystem、Supabase、Cloudflare KV/R2/D1)、オプションの OpenTelemetry トレースを備えた構造化ログを提供します。

Bluesky 固有の機能:

  • 10 個のツールのうち 9 個は認証情報なしで api.bsky.app を読み取ります。bsky_search_posts はオプションのアプリパスワードセッションを通して実行され、セッションがない場合は利用できません
  • 入力として共有リンクを利用できます。アカウントを指定する箇所では @handle と bsky.app のプロフィール URL、投稿またはフィードを指定する箇所では bsky.app の投稿 URL またはフィード URL を受け付け、リクエスト前にリンクが示すハンドル、DID、AT-URI に書き換えます
  • AT Protocol AppView をラップする単一の BlueskyService を使用します。リクエストごとのタイムアウトは 15 秒で、一時的な上流の障害時には最大 3 回、500ms の基本遅延で再試行します。ただし、ログイン時や、ツールエラーにすでに変換された障害時には再試行しません。また、バージョン付きの User-Agent を使用します
  • エラーには HTML を含めません。Bluesky のエッジから返されたブロックページはエラーデータから削除され、ステータスのみが残ります
  • 埋め込みデータの正規化 — 生のネストされた AT Protocol 埋め込みオブジェクトを、type で判別できるクリーンな共用体にフラット化します
  • モデレーションラベルは、フィルタリングせずそのまま表示します
  • AT Protocol の識別子型(ハンドル、DID、AT-URI)は、各ツールの説明で初めて登場した際に解説します

エージェントに適した出力:

  • すべての投稿に AT-URI を付与します。bsky_search_feeds または bsky_get_trending → bsky_get_feed → bsky_get_post_thread と、追加手順なしでつなげられます
  • エージェントが選択または引用するすべてのアカウントについて認証情報を提供します。5 つすべての投稿ツール、アクター検索、フォローグラフでは、投稿者の DID 行に Bluesky の verifiedStatus/trustedVerifierStatus が含まれます。bsky_get_profile では、各認証を発行した主体も示します
  • 判別可能な埋め込み共用体 — type: "images" | "external" | "record" | "video" | "unknown" により、呼び出し側は $type 文字列を解析せずにデータに応じた分岐ができます。対応付けられていない lexicon 型は消失せず、生の $type を保持した unknown として返されます
  • サードパーティーのテキストは Markdown の引用ブロックとしてレンダリングされます。投稿本文、プロフィール文、代替テキスト、リンクカードのテキストは、content[] 内で > を先頭に付けた引用ブロックとして表示されるため、投稿独自の見出しやコードフェンスがサーバーの構造と結合しません。同じ理由から、インラインで表示される値(表示名、代名詞、トピック名)の改行はスペースに変換されます。どちらの場合も、CommonMark レンダラーが処理する Markdown と生の HTML(強調、リンク、画像、コード、リスト、見出し、タグ、エンティティ)はエスケープされるため、ユーザーのテキストは入力どおりに表示されます。URL はコピー可能なままで、structuredContent にはすべての文字列が変更されずに保持されます。各引用ブロックは、サーバーが次の行を書き込む前に終了するため、レンダリングクライアントが投稿の件数やラベルを引用ブロックに取り込むことはありません
  • 上限付きの切り詰め情報 — スレッドとページネーションの不足分(truncated、unreturnedReplies、parentChainTruncated、10,000 件の上限に達した hitsTotal)は、実測値ではなく上限として報告されます
  • 投稿を返すすべてのツールでは、各出力面のレスポンス予算を 48,000 バイトとし、レンダリング後の出力を基準に計測して、投稿全体の境界でのみ切り詰めます。ページは収まる limit で再リクエストされるため、カーソルは最後の位置から引き続き使用できます。スレッドでは省略された投稿を AT-URI で示し、いずれかが発生した場合は budgetCapped で通知します。レスポンスが予算内に収まる場合、内容は変更されません

はじめに

公開ホストインスタンス

インストールせずに直接接続できます:

{
  "mcpServers": {
    "bluesky-mcp-server": {
      "type": "streamable-http",
      "url": "https://bluesky.caseyjhand.com/mcp"
    }
  }
}
セルフホスト/ローカル

以下をMCPクライアントの設定ファイルに追加してください。APIキーは不要です。投稿検索を有効にするには、envにBLUESKY_IDENTIFIERとBLUESKY_APP_PASSWORDを追加してください。

{
  "mcpServers": {
    "bluesky-mcp-server": {
      "type": "stdio",
      "command": "bunx",
      "args": ["@cyanheads/bluesky-mcp-server@latest"],
      "env": {
        "MCP_TRANSPORT_TYPE": "stdio",
        "MCP_LOG_LEVEL": "info"
      }
    }
  }
}

またはnpxを使用します(Bunは不要です)。

{
  "mcpServers": {
    "bluesky-mcp-server": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@cyanheads/bluesky-mcp-server@latest"],
      "env": {
        "MCP_TRANSPORT_TYPE": "stdio",
        "MCP_LOG_LEVEL": "info"
      }
    }
  }
}

またはDockerを使用します。

{
  "mcpServers": {
    "bluesky-mcp-server": {
      "type": "stdio",
      "command": "docker",
      "args": [
        "run", "-i", "--rm",
        "-e", "MCP_TRANSPORT_TYPE=stdio",
        "ghcr.io/cyanheads/bluesky-mcp-server:latest"
      ]
    }
  }
}

Streamable HTTPの場合は、トランスポートを設定してサーバーを起動します。

MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
# Server listens at http://localhost:3010/mcp
前提条件
  • Bun v1.4.0以降(またはNode.js v24以降)。
  • 投稿検索以外はAPIキーもアカウントも不要です。有効にするには投稿検索をご覧ください。
インストール
  1. リポジトリをクローンします。
git clone https://github.com/cyanheads/bluesky-mcp-server.git
  1. ディレクトリに移動します。
cd bluesky-mcp-server
  1. 依存関係をインストールします。
bun install
  1. 環境を設定します(任意)。
cp .env.example .env
# edit .env to override any framework defaults

設定

このサーバーにAPIキーは不要です。以下はすべて任意です。

変数説明デフォルト
BLUESKY_IDENTIFIER投稿検索で使用するアカウントのハンドル、DID、またはメールアドレスです。BLUESKY_APP_PASSWORDと一緒に設定するか、どちらも設定しないでください。—
BLUESKY_APP_PASSWORDそのアカウントのアプリパスワードです。この変数とBLUESKY_IDENTIFIERの両方が設定されていない場合、bsky_search_postsは提供されません。—
BLUESKY_CLAMP_READ_LIMITS読み取り制限ポリシーです。上限を超えるリクエストを既存の上限に合わせ、要求値と適用値を明示します。falseにすると、再びリクエストを拒否します。true
MCP_TRANSPORT_TYPEトランスポート:stdioまたはhttpstdio
MCP_SESSION_MODEHTTPセッションモード:stateful、stateless、またはauto。未設定または空の場合、サーバーは独自のcreateApp({ sessionMode })宣言に基づいてstatelessに設定します。明示的な値を指定した場合は、その値が優先されます。stateless
MCP_HTTP_PORTHTTPサーバーのポート3010
MCP_AUTH_MODE認証モード:none、jwt、またはoauthnone
MCP_LOG_LEVELログレベル(RFC 5424)info
LOGS_DIRログファイルのディレクトリ(Node.jsのみ)<project-root>/logs
STORAGE_PROVIDER_TYPEストレージバックエンドin-memory
OTEL_ENABLEDOpenTelemetryのインストルメンテーションを有効にしますfalse

任意のオーバーライドの全一覧は.env.exampleをご覧ください。

投稿検索

Blueskyでは、サインイン済みアカウントなしでapp.bsky.feed.searchPostsを利用できません。そのため、BLUESKY_IDENTIFIERとBLUESKY_APP_PASSWORDの両方が設定されている場合に限り、bsky_search_postsが登録されます。一方だけを設定すると、不足している変数名を示すメッセージとともに起動に失敗します。

  • 専用アカウントに、標準の(特権のない)アプリパスワードを使用してください。Settings → Privacy and security → App passwordsで作成できます。サーバーがDMへのアクセスを必要とすることはありません。
  • すべての呼び出し元の検索は、そのアカウントとして実行されます。 AppViewは、そのアカウントとのブロック関係がどちらかの方向にあるアカウントの投稿を除外します。ミュートは適用されません。共有インスタンスでは、誰でもそのアカウントをブロックして、その投稿を検索結果から除外できます。
  • ログイン回数には上限があります。 Blueskyでは、アカウントごとのcreateSessionが1日あたり約10回に制限されています。そのため、サーバーは起動時ではなく初回検索時にログインし、セッションを再利用して、アクセストークンの有効期限が切れたときに更新し、更新が拒否された場合に限って再度ログインします。ログインが拒否された場合、プロセスを再起動するまで再試行しません。Blueskyからログインまたはトークン更新に対して429が返された場合、指定されたratelimit-resetの時刻まで(指定がない場合は15分間)、リクエストを送らずにすべての検索がsearch_login_limitedとして失敗し、その後に再度ログインします。
  • 認証情報とトークンはメモリ内に保持されます。ログ、エラー、ツールの出力に現れることはなく、ほかのツールから送信されることもありません。

サーバーの実行

ローカル開発
  • ビルドして実行します。

    # One-time build
    bun run rebuild
    
    # Run the built server
    bun run start:stdio
    # or
    bun run start:http
    
  • チェックとテストを実行します。

    bun run devcheck   # Lint, format, typecheck, security
    bun run test       # Vitest test suite
    bun run lint:mcp   # Validate MCP definitions against spec
    
Docker
docker build -t bluesky-mcp-server .
docker run --rm -p 3010:3010 bluesky-mcp-server

Dockerfileのデフォルト設定では、HTTPトランスポート、ステートレスセッションモードを使用し、/var/log/bluesky-mcp-serverにログを出力します。OpenTelemetryのピア依存関係はデフォルトでインストールされます。省略するには、--build-arg OTEL_ENABLED=falseを指定してビルドしてください。

プロジェクト構成

ディレクトリ用途
src/index.tscreateApp() エントリーポイント — 設定を読み込み、ツールとリソースを登録し、サービスを初期化します。
src/config読み取り制限ポリシーと、投稿検索を有効にする任意のアプリパスワードのペアです。
src/services/blueskyAT Protocol HTTP クライアント — 公開 AppView の読み取り、アプリパスワードによる検索セッション、再試行、タイムアウト、User-Agent を扱います。
src/mcp-server/toolsツール定義(*.tool.ts)— 読み取り専用の Bluesky ツールを 10 個定義します。
src/mcp-server/resourcesリソース定義(*.resource.ts)— bsky://profile/{actor} です。
tests/src/ に対応する単体テストと統合テストです。

開発ガイド

開発ガイドラインとアーキテクチャのルールについては、CLAUDE.md を参照してください。要点は次のとおりです。

  • ハンドラーは例外をスローし、フレームワークが捕捉します — ツールのロジックで try/catch を使わないでください
  • リクエストスコープのログには ctx.log を、テナントスコープのストレージには ctx.state を使用します
  • ツールは src/mcp-server/server-surface.ts に、リソースは src/index.ts に登録します
  • 外部 API 呼び出しは、未加工の値を検証 → ドメイン型に正規化 → 出力スキーマを返す、という流れでラップします。不足しているフィールドを捏造しないでください

コントリビューション

Issue を歓迎します。提出前にチェックとテストを実行してください。

bun run devcheck
bun run test

ライセンス

Apache-2.0 — 詳細は LICENSE を参照してください。

コメント

MCP は初めてですか?

プロトコルの概要から最初のサーバーの導入まで、よくある疑問にまとめて答えます。

AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。