关于 bluesky-mcp-server
bluesky-mcp-server 让 MCP 客户端读取 Bluesky 的公开个人资料、帖子、信息流、讨论串和趋势,并可选启用需身份验证的全文帖子搜索。它支持 stdio 和 Streamable HTTP,也提供公开托管端点。
主要功能
- 01搜索帖子和账号
- 02读取个人资料及作者信息流
- 03获取讨论串和引用帖
- 04浏览信息流与热门趋势
- 05访问公开个人资料资源
适用场景
- 01按作者、主题或日期研究帖子
- 02查看讨论及其引用帖
- 03探索热门趋势及相关信息流
- 04为 Agent 获取个人资料上下文
MCP 相关
该服务提供 10 个用于搜索和读取 Bluesky 数据的工具,以及可按账号标识访问的个人资料资源。它支持 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。
概述
无需账号即可浏览 Bluesky:查看个人资料、搜索信息流目录、了解热门话题及相关信息流,以及浏览自定义信息流、作者信息流、讨论串和引用帖子。在需要输入链接或账号的地方,均可粘贴 bsky.app 链接或 @handle;添加可选的应用密码即可解锁全文帖子搜索。
工具
| 工具 | 描述 |
|---|---|
bsky_search_posts | 在 Bluesky 公开帖子中进行全文搜索,并支持按作者、提及、语言、标签、域名、URL、日期和排序方式筛选 — 仅在配置了应用密码时提供 |
bsky_get_profile | 按句柄或 DID 获取 Bluesky 用户的公开个人资料 — 用于解析句柄↔DID |
bsky_get_feed | 读取 feed 生成器的帖子 — 趋势 feed、Discover 或任何自定义 feed — 可通过 AT-URI 或 bsky.app URL 获取 |
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 | 搜索公开 feed 目录中的名称和描述,然后使用 bsky_get_feed 读取返回的 feedUri |
bsky_get_follows | 分页读取社交关系边 — 某用户关注了谁,或谁关注了该用户 |
bsky_get_trending | 获取 Bluesky 上的实时热门话题,包括故事摘要、帖子数、类别、状态、推动各话题的账号,以及汇集相关帖子的 feed |
资源
| 资源 | 描述 |
|---|---|
bsky://profile/{actor} | Bluesky 用户的公开个人资料,可通过句柄或 DID 访问 |
所有资源数据也可通过工具获取。程序化访问请使用 bsky_get_profile,或使用 bsky://profile/{actor} 直接注入个人资料上下文。
能力参考
bsky_search_posts 工具
- Bluesky 拒绝在未登录账号的情况下搜索帖子,因此只有设置了
BLUESKY_IDENTIFIER和BLUESKY_APP_PASSWORD时才会注册此工具;否则它不会出现在tools/list中(参见帖子搜索) - 搜索会通过该账号的 PDS 执行;首次搜索时会登录,之后会重用并刷新会话,登录遭拒后不会重试。失败会显示为
search_auth_failed(登录或会话续期遭拒)、search_login_limited(账号的每日登录次数已用尽——错误信息会说明何时可以恢复搜索),或search_refused(Bluesky 拒绝了搜索) - 筛选条件:作者和被提及的账号(handle 或 DID,也可使用
@handle或 bsky.app 个人资料 URL——mentions仅匹配富文本提及)、两字母语言代码、话题标签(带或不带#)、链接中的domain(不带协议的主机名,会移除开头的www.)或精确url(文本链接或链接卡片中的 URL)、since/until,以及top/latest排序;每次调用最多返回 100 条结果,可通过不透明游标分页 since/until按每条帖子的排序时间进行比较,即createdAt和indexedAt中较早的时间,精确到秒且包含边界;单独的日期按 UTC 00:00:00 处理,因此until: "2026-01-01"会涵盖整个 2025-12-31- 标识符、语言、域名、URL 和日期输入会在调用上游之前进行模式验证。三字母语言代码(
"fil"、"eng")会被拒绝,因为 Bluesky 搜索会忽略它们并返回未筛选的结果;发送的代码会转换为小写,后续子标签虽可接受,但会被上游忽略(en-US会按en筛选) - 当 Bluesky 拒绝某个参数时,会通过
upstream_rejected_filter错误原因显示其自身的说明,而不是只显示状态码;无法解码的游标会在一次请求后以invalid_cursor失败 hitsTotal是 Bluesky 给出的估算值,绝非精确计数:低于 10,000 时,它是分页结果数量的上限(Bluesky 会先计数,再剔除不会返回的帖子);恰好为 10,000(上限)则表示“至少有这么多”;truncated/shown/cap的值取决于返回的游标;如果没有更多匹配项,Bluesky 会省略该游标- 如果某一页在任一响应通道上会超出 48,000 字节的响应预算,则会按能够容纳的
limit重新搜索并完整返回该页——budgetCapped: true,且hitsTotal和游标取自该次响应 - 嵌入内容会规范化为由
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 工具
- 接受动态源生成器 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 - 所有者为 handle 时会额外查询一次(
resolveHandle),所有者为 DID 时则无需查询;其他任何 collection(例如帖子 AT-URI)都会在上游调用前被拒绝 - 被动态源置顶的帖子带有
pinned: true;转帖带有repostedBy/repostedAt - 每次调用最多返回 100 条帖子,通过游标分页;如果动态源返回的帖子少于
limit,但后面还有更多内容,游标会注明结果已截断 - 如果某一页会超过 48,000 字节的响应预算,则会使用合适的
limit重新请求并完整返回该页,并标记budgetCapped: true;排名动态源会在每次请求时重新排名,因此帖子和游标仍来自同一响应 feed_not_found(动态源或 handle 不存在)、feed_unavailable(动态源生成器未响应——不会重试,因此不可用的动态源会快速失败)、feed_requires_login(个性化动态源)- 始终以未认证状态调用,即使已设置搜索凭据也是如此
bsky_get_profile 工具
- 接受 handle 或 DID,也接受
@handle或账号的 bsky.app 页面(https://bsky.app/profile/<handle-or-did>);返回 displayName、handle、DID、简介、代词、网站、关注者/关注/帖子数量、头像、审核标签和置顶帖 AT-URI - 如果账号未设置
website和pronouns,则不会返回这两个字段;可选的links会单独于website携带按顺序排列的个人资料按钮,每条记录包含 URI/CID、完整目标 URL、可选标题/图标和审核标签(val、src?、cts?)。未提供的数组保持省略,显式提供的空数组则保持为空;个人资料资源会返回相同字段 verification携带 Bluesky 的完整验证状态——verifiedStatus和trustedVerifierStatus(valid、invalid或none,原样传递),以及受信任验证者签发的每项验证信息,包括签发者、有效性、日期和记录 AT-URI;如果 Bluesky 未发送验证信息,则省略此字段- 简介会在
content[]中以 Markdown 引用块呈现,因为这是账号撰写的文本,可能包含自身的 Markdown 结构 - 如果 handle 无法解析,则返回
actor_not_found——请先使用bsky_search_actors解析 handle - 用于 handle↔DID 转换的主要解析工具——已知 handle、但需要 DID 或 AT-URI 的其他工具调用前,请先使用此工具
bsky_get_author_feed 工具
- 接受 handle 或 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,因此大量转帖账号返回的本人帖子可能远少于limit所示数量;只要存在转帖,originalPosts/reposts就会报告实际数量拆分 - 每次调用最多返回 100 条帖子,通过游标分页;如果某一页会超过 48,000 字节的响应预算,则会使用合适的
limit重新请求(置顶帖不计入),并标记budgetCapped: true,其游标会从第一条未返回的帖子继续 - 如果 handle 或 DID 无法解析,则返回
actor_not_found;如果 Bluesky 无法解码传入的游标,则在请求一次后返回invalid_cursor(Bluesky 会对此类游标返回 HTTP 500,而其他 HTTP 500 通常会重试)
bsky_get_post_thread 工具
- 接受帖子的 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 工具
- 接受帖子的 AT-URI 或其 bsky.app 页面;其他任何集合都会在调用上游之前被拒绝
- DID 权限的帖子需要一次请求;句柄需要额外进行一次查找(
resolveHandle),因为getQuotes对句柄权限返回空列表 - 每条结果都引用同一帖子,因此每条结果的嵌入内容仅保留该帖子的 AT-URI 和 CID、其不可读时的
recordKind,以及引用帖附带的任何媒体——不会在每个条目中重复查询帖子的文本和附件 - 每次调用最多返回 100 条引用;
sort: latest(默认值)按时间从新到旧排列,sort: top则按 Bluesky 排序后的点赞数降序排列。两个接口都会返回实际应用的sort;使用同一帖子和排序方式传回游标。截断情况会通过游标说明,因为即使后面还有内容,页面返回的条目数也经常少于limit - 若页面响应超过 48,000 字节的预算,则会以适合的
limit重新请求,并标记budgetCapped: true;其游标会从第一条被省略的引用开始继续 quoteCount是此处返回数量的上限——Bluesky 的计数器会保留已从索引中移除的引用- 如果帖子不存在或其句柄无法解析,则返回
post_not_found(会通过一次getPosts检查空的第一页);对于 Bluesky 无法解码的游标,会在发出一次请求后返回invalid_cursor
bsky_search_actors 工具
- 返回按排名排序的个人资料,包含 handle、DID、displayName、bio,以及已设置的代词;不包含关注者数、关注数或帖子数,也不包含
website,只有bsky_get_profile会返回该字段 - 如果 Bluesky 提供了验证状态,每个账号都会包含两种状态(
verification.verifiedStatus、verification.trustedVerifierStatus),可据此区分已验证账号与仿冒 handle;验证的颁发方见bsky_get_profile - 简介会以 Markdown 引用块的形式显示在
content[]中,因为这是账号作者撰写的文本 - 每次调用最多返回 100 条结果,通过游标分页;每页通常少于
limit条,但仍可继续翻页,最后一页不带游标 - Bluesky 无法解码传入的游标时,会在一次请求后返回
invalid_cursor(此类请求会收到 HTTP 400) - 已知名称但尚未确认 handle 时,可先用此工具,再调用
bsky_get_profile或bsky_get_author_feed
bsky_search_feeds 工具
- 搜索提供方公开的信息流目录元数据,而非帖子文本;按提供方的顺序返回
feedUri、displayName、可选的description,以及创建者的 DID/handle 和可选的显示名称 - 始终无需密钥,即使已配置帖子搜索凭据也是如此;将返回的
feedUri直接传给bsky_get_feed即可读取其中的帖子 query为必填项;limit默认为 25,并根据统一的限制策略上限设为 100。使用相同的查询传入不透明的cursor;只要存在游标就表示还有后续内容,即使当前页为空也是如此- 使用不稳定的
app.bsky.unspecced.getPopularFeedGenerators,该接口可能变更或消失;catalog_unavailable时可通过bsky_get_trending恢复。这是用于发现内容的工具,并非完整枚举 - 信息流名称、说明和创建者身份会作为带引文的第三方数据呈现;提供的空白值与省略的字段会保持区分
bsky_get_follows 工具
direction:followers(关注该账号的人)或following(该账号关注的人)sort:latest(Bluesky 的默认值,最近关注的账号排在前面)或top(Bluesky 自行排列的知名账号,不按关注者数量排序);传回游标时须使用相同的sort- 接受 handle 或 DID,也接受
@handle或账号的 bsky.app 页面 - 返回分页个人资料(handle、DID、displayName、bio、已设置的代词、验证状态),以及账号主体自己的个人资料摘要,其中也包含其验证状态
- 此视图中的列表和账号主体均不包含关注者数、关注数、帖子数或
website;需要这些信息时,请使用bsky_get_profile查询 - 每页最多 100 条,通过游标分页。如果剩余账号都无法解析,游标可能会指向空页;响应会将其报告为列表末尾
- handle 或 DID 无法解析时返回
actor_not_found
bsky_get_trending 工具
- 返回趋势话题,包含显示名称、Bluesky 的一句话故事摘要(
description)、帖子数、类别、状态(例如hot、cooling、stale)、开始时间,以及每个话题最多 5 个推动该趋势的代表性账号 - 每个趋势都由一个信息流生成器提供支持:
feedUri是该信息流的 AT-URI,从趋势的link中解析得出;bsky_get_feed可读取其中的帖子;topic是信息流的记录键,而非搜索词 - 无游标;返回当前快照,最多
limit条(默认为 10,最大为 25,这是 Bluesky 自身的上限);truncated表示趋势话题数量多于limit,因此当limit为 25 时不会出现 - 使用不稳定的
app.bsky.unspecced.getTrends接口,Bluesky 可能会在不另行通知的情况下更改该接口
bsky://profile/{actor} 资源
- 以可注入上下文的形式返回与
bsky_get_profile相同的字段:displayName、handle、DID、bio、pronouns、website、关注者/关注人数/帖子数量、头像、审核标签、置顶帖子的 AT-URI、完整的验证状态 - 可通过
{actor}中的 handle 或 DID 寻址,支持带或不带前导@(bsky://profile/@bsky.app) - 当 handle 无法解析时返回
actor_not_found
功能
基于 @cyanheads/mcp-ts-core 构建:支持 stdio 和 Streamable HTTP 传输、可插拔身份验证(none / jwt / oauth)、可切换存储(in-memory、filesystem、Supabase、Cloudflare KV/R2/D1),并提供结构化日志记录和可选的 OpenTelemetry 跟踪。
Bluesky 专属功能:
- 十个工具中有九个无需凭据即可读取
api.bsky.app;bsky_search_posts通过可选的应用密码会话运行,没有该会话时不提供此工具 - 支持将分享链接作为输入:在需要账号的地方接受
@handle和 bsky.app 个人资料 URL,在需要帖子或信息流的地方接受 bsky.app 帖子或信息流 URL;请求前会将链接重写为其指向的 handle、DID 或 AT-URI - 使用单一的
BlueskyService封装 AT Protocol AppView;每个请求的超时时间为 15 秒;上游出现暂时性故障时会重试(最多 3 次,基础间隔为 500ms;登录请求或已映射为工具错误的故障不会重试);使用带版本号的User-Agent - 所有错误中均不包含 HTML:Bluesky 边缘节点返回的拦截页面会从错误数据中移除,仅保留状态
- Embed 规范化:将原始的嵌套 AT Protocol embed 对象展平为简洁的、由
type区分的联合类型 - 原样呈现审核标签,不作过滤
- 在每个工具的描述中首次出现时,解释 AT Protocol 标识符类型(handle、DID、AT-URI)
便于 Agent 使用的输出:
- 每篇帖子都带有 AT-URI,可直接串联
bsky_search_feeds或bsky_get_trending→bsky_get_feed→bsky_get_post_thread,无需额外步骤 - 为 Agent 选择或引用的每个账号提供验证信息:所有五个帖子工具中的帖子作者、actor 搜索结果和关注关系图,都会在作者的 DID 行中包含 Bluesky 的
verifiedStatus/trustedVerifierStatus;bsky_get_profile还会说明每项验证由谁签发 - 由判别字段区分的 embed 联合类型:
type: "images" | "external" | "record" | "video" | "unknown"让调用方可以根据数据分支处理,而不必解析$type字符串;未映射的 lexicon 类型会以unknown形式返回,并附带其原始$type,而不会消失 - 将第三方文本渲染为 Markdown 引用块:帖子正文、个人简介、替代文本和链接卡片文本会在
content[]中渲染为以>开头的引用块,因此帖子的标题或代码围栏不会与服务端结构合并;以行内形式渲染的值(显示名称、代词、主题名称)也会将换行符折叠为空格,原因相同。两种形式中的 Markdown 和 CommonMark 渲染器会处理的原始 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 密钥;要启用帖子搜索,请将 BLUESKY_IDENTIFIER 和 BLUESKY_APP_PASSWORD 添加到 env。
{
"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 | 执行帖子搜索的账户的 Handle、DID 或电子邮件。请与 BLUESKY_APP_PASSWORD 一起设置,或两者都不设置。 | — |
BLUESKY_APP_PASSWORD | 该账户的应用密码。未同时设置这两个变量时,不会提供 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。只设置其中一个变量会导致启动失败,并显示缺少的变量名称。
- 使用专用账户和标准(非特权)应用密码——可在“设置”→“隐私与安全”→“应用密码”下创建。服务无需访问私信。
- 所有调用者的搜索都会使用该账户执行。 AppView 会排除与该账户存在任一方向屏蔽关系的账户所发布的帖子;但不会应用静音设置。在共享实例上,任何人都可以屏蔽该账户,从而使自己的帖子不出现在搜索结果中。
- 登录次数有限。 Bluesky 将每个账户的
createSession次数限制在每天约 10 次,因此服务会在首次搜索时登录——启动时不会登录——并复用会话;访问令牌过期时会刷新,仅在刷新遭拒时重新登录。登录遭拒后,服务不会重试,直到进程重启。如果 Bluesky 在登录或刷新时返回 429,所有搜索都会失败并返回search_login_limited,在其指定的ratelimit-reset时间之前不会发送请求(如果未指定,则等待 15 分钟),之后再重新登录。 - 凭据和令牌仅保留在内存中;它们不会出现在日志、错误信息或工具输出中,其他工具也不会发送它们。
运行服务
本地开发
-
构建并运行:
# 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 工具。 |
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 调用:校验原始数据 → 规范化为领域类型 → 返回输出架构;绝不编造缺失字段
贡献
欢迎提交问题。提交前请运行检查和测试:
bun run devcheck
bun run test
许可证
Apache-2.0——详情请参阅 LICENSE。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。