bluesky-mcp-server について
bluesky-mcp-serverは、MCPクライアントにBlueskyの公開プロフィール、投稿、フィード、スレッド、トレンドへのアクセスを提供し、任意で認証付き全文投稿検索にも対応します。stdioとStreamable HTTPをサポートし、公開ホスト型エンドポイントも利用できます。
主な機能
- 01投稿とアカウントの検索
- 02プロフィールとユーザーフィードの取得
- 03スレッドと引用投稿の取得
- 04フィードとトレンドの閲覧
- 05公開プロフィールリソースへのアクセス
活用シーン
- 01投稿を作成者・話題・日付で調査
- 02会話と引用投稿の確認
- 03トレンドと関連フィードの探索
- 04エージェント向けプロフィール情報の取得
MCP 対応
Blueskyデータの検索・読み取り用に10個のツールと、アクターで指定できるプロフィールリソースを公開します。stdioとStreamable HTTPに対応し、全文投稿検索はアプリパスワードの認証情報を設定した場合にのみ利用できます。
設定
リモートサーバーはインストール不要です。クライアントを選んで設定をコピーするか、ワンクリックで導入できます。
https://bluesky.caseyjhand.com/mcpclaude mcp add --transport http cyanheads-bluesky https://bluesky.caseyjhand.com/mcp{"mcpServers":{"cyanheads-bluesky":{"type":"http","url":"https://bluesky.caseyjhand.com/mcp"}}}Cursor で開く{"servers":{"cyanheads-bluesky":{"type":"http","url":"https://bluesky.caseyjhand.com/mcp"}}}VS Code で開く{"mcpServers":{"cyanheads-bluesky":{"type":"http","url":"https://bluesky.caseyjhand.com/mcp"}}}{"mcpServers":{"cyanheads-bluesky":{"serverUrl":"https://bluesky.caseyjhand.com/mcp"}}}コマンドはターミナルで実行し、JSON は各クライアントの MCP 設定に貼り付けて再起動します。認可が必要なサーバーは初回接続時にログインを求めます。
ツール
8| パラメーター | 型 | 必須 | 説明 |
|---|---|---|---|
| actor | string | はい | 検索するアクターのハンドル(例:"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 に対応しています。
公開ホストサーバー: https://bluesky.caseyjhand.com/mcp
概要
アカウントなしで Bluesky の情報を読み取れます。プロフィール、フィードカタログの検索、トレンドトピックとそれに関連するフィード、カスタムフィードや投稿者フィード、スレッド、引用投稿に対応しています。bsky.app のリンクまたは @handle を、入力を求められた箇所に貼り付けてください。任意のアプリパスワードを追加すると、投稿の全文検索も利用できます。
ツール
| ツール | 説明 |
|---|---|
bsky_search_posts | 公開Bluesky投稿を全文検索します。投稿者、メンション、言語、タグ、ドメイン、URL、日付、並べ替えのフィルターを利用できます。アプリパスワードが設定されている場合のみ提供されます |
bsky_get_profile | ハンドルまたはDIDでBlueskyアクターの公開プロフィールを取得します。ハンドルとDIDの解決に使います |
bsky_get_feed | AT-URIまたはbsky.appのURLを指定して、フィードジェネレーターの投稿を読み取ります。トレンドのフィード、Discover、または任意のカスタムフィードを指定できます |
bsky_get_author_feed | ユーザーの最近の投稿を新しい順に取得します。投稿タイプで絞り込めます |
bsky_get_post_thread | AT-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_trending | Blueskyのリアルタイムのトレンドトピックを取得します。ストーリーの概要、投稿数、カテゴリ、ステータス、各トピックを牽引するアカウント、投稿を集約するフィードが含まれます |
リソース
| リソース | 説明 |
|---|---|
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キーもアカウントも不要です。有効にするには投稿検索をご覧ください。
インストール
- リポジトリをクローンします。
git clone https://github.com/cyanheads/bluesky-mcp-server.git
- ディレクトリに移動します。
cd bluesky-mcp-server
- 依存関係をインストールします。
bun install
- 環境を設定します(任意)。
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またはhttp | stdio |
MCP_SESSION_MODE | HTTPセッションモード:stateful、stateless、またはauto。未設定または空の場合、サーバーは独自のcreateApp({ sessionMode })宣言に基づいてstatelessに設定します。明示的な値を指定した場合は、その値が優先されます。 | stateless |
MCP_HTTP_PORT | HTTPサーバーのポート | 3010 |
MCP_AUTH_MODE | 認証モード:none、jwt、またはoauth | none |
MCP_LOG_LEVEL | ログレベル(RFC 5424) | info |
LOGS_DIR | ログファイルのディレクトリ(Node.jsのみ) | <project-root>/logs |
STORAGE_PROVIDER_TYPE | ストレージバックエンド | in-memory |
OTEL_ENABLED | OpenTelemetryのインストルメンテーションを有効にします | 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.ts | createApp() エントリーポイント — 設定を読み込み、ツールとリソースを登録し、サービスを初期化します。 |
src/config | 読み取り制限ポリシーと、投稿検索を有効にする任意のアプリパスワードのペアです。 |
src/services/bluesky | AT 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 を参照してください。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。