@cyanheads/bluesky-mcp-server

v0.1.3 pre-1.0

Search posts, profiles, feeds, threads, and trending topics on Bluesky via MCP. STDIO or Streamable HTTP.

@cyanheads/bluesky-mcp-server
claude mcp add --transport http bluesky-mcp-server https://bluesky.caseyjhand.com/mcp
codex mcp add bluesky-mcp-server --url https://bluesky.caseyjhand.com/mcp
{
  "mcpServers": {
    "bluesky-mcp-server": {
      "url": "https://bluesky.caseyjhand.com/mcp"
    }
  }
}
gemini mcp add --transport http bluesky-mcp-server https://bluesky.caseyjhand.com/mcp
{
  "mcpServers": {
    "bluesky-mcp-server": {
      "command": "bunx",
      "args": [
        "@cyanheads/bluesky-mcp-server@latest"
      ]
    }
  }
}
{
  "mcpServers": {
    "bluesky-mcp-server": {
      "type": "http",
      "url": "https://bluesky.caseyjhand.com/mcp"
    }
  }
}
curl -X POST https://bluesky.caseyjhand.com/mcp \
  -H "Content-Type: application/json" \
  -H "MCP-Protocol-Version: 2025-11-25" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"curl","version":"1.0.0"}}}'

Tools

7

bsky_get_profile

open-world

Fetch a Bluesky actor's public profile by handle (e.g. "bsky.app") or DID (e.g. "did:plc:z72i7hdynmk6r22z27h6tvur"). Returns displayName, handle, DID, bio, follower/following/post counts, avatar URL, moderation labels, and pinned post AT-URI. Use this as the first step to resolve a handle to a DID before calling tools that require a DID or AT-URI. Handles and DIDs are interchangeable as input.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "bsky_get_profile",
    "arguments": {
      "actor": "<actor>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "actor": {
      "type": "string",
      "maxLength": 253,
      "description": "Handle (e.g. \"bsky.app\", \"alice.bsky.social\") or DID (e.g. \"did:plc:z72i7hdynmk6r22z27h6tvur\") of the actor to look up."
    }
  },
  "required": [
    "actor"
  ],
  "additionalProperties": false
}
view source ↗

bsky_search_actors

open-world

Find Bluesky accounts by name or handle fragment. Returns ranked profiles with handle, DID, displayName, bio, and follower count. Use before bsky_get_profile or bsky_get_author_feed when you have a name but not a confirmed handle. Supports cursor-based pagination for browsing beyond the first page of results.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "bsky_search_actors",
    "arguments": {
      "query": "<query>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "query": {
      "type": "string",
      "maxLength": 500,
      "description": "Name or handle fragment to search for, e.g. \"alice\" or \"nytimes.com\"."
    },
    "limit": {
      "default": 25,
      "description": "Maximum number of actors to return (1–100). Default 25.",
      "type": "integer",
      "minimum": 1,
      "maximum": 100
    },
    "cursor": {
      "description": "Opaque pagination cursor from a previous response. Note: the public Bluesky AppView restricts cursor-based search pagination for unauthenticated requests — passing a cursor may return a 403 error. Cursor pagination is reliable only for bsky_get_author_feed and bsky_get_follows.",
      "type": "string",
      "maxLength": 2048
    }
  },
  "required": [
    "query",
    "limit"
  ],
  "additionalProperties": false
}
view source ↗

bsky_get_author_feed

open-world

Get a Bluesky user's recent posts ordered newest-first. Filter by post type: "posts_with_replies" (everything), "posts_no_replies" (original posts only), "posts_with_media" (posts with images or links), or "posts_and_author_threads" (posts the author started). Returns posts with full text, engagement counts, embeds, and AT-URIs for drilling into threads via bsky_get_post_thread. Supports cursor pagination.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "bsky_get_author_feed",
    "arguments": {
      "actor": "<actor>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "actor": {
      "type": "string",
      "maxLength": 253,
      "description": "Handle (e.g. \"alice.bsky.social\") or DID of the author whose feed to fetch."
    },
    "filter": {
      "default": "posts_no_replies",
      "description": "Filter for post types: \"posts_no_replies\" for original posts only, \"posts_with_replies\" for everything, \"posts_with_media\" for posts with images/links, \"posts_and_author_threads\" for threads the author started.",
      "type": "string",
      "enum": [
        "posts_with_replies",
        "posts_no_replies",
        "posts_with_media",
        "posts_and_author_threads"
      ]
    },
    "limit": {
      "default": 25,
      "description": "Maximum number of posts to return (1–100). Default 25.",
      "type": "integer",
      "minimum": 1,
      "maximum": 100
    },
    "cursor": {
      "description": "Opaque pagination cursor from a previous response. Omit for the first page.",
      "type": "string",
      "maxLength": 2048
    }
  },
  "required": [
    "actor",
    "filter",
    "limit"
  ],
  "additionalProperties": false
}
view source ↗

bsky_search_posts

open-world

Full-text search across public Bluesky posts. Filters by author (handle or DID), language (BCP-47 code, e.g. "en"), hashtag (without the # prefix), date range (ISO 8601), and sort order. Returns posts with text, author info, engagement counts (likes/reposts/replies), normalized embeds, AT-URIs for thread drilling, and hitsTotal when the API reports the total number of matching posts. This is the primary entry point for social listening — pass any AT-URI from results to bsky_get_post_thread to read the full conversation.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "bsky_search_posts",
    "arguments": {
      "query": "<query>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "query": {
      "type": "string",
      "maxLength": 500,
      "description": "Full-text search query, e.g. \"climate change\" or \"#ai announcement\"."
    },
    "author_handle": {
      "description": "Filter to posts by this author. Accepts handle (e.g. \"bsky.app\") or DID. Use bsky_get_profile to resolve a name to a handle first.",
      "type": "string",
      "maxLength": 253
    },
    "language": {
      "description": "BCP-47 language code to restrict results to, e.g. \"en\", \"ja\", \"es\".",
      "type": "string",
      "maxLength": 10
    },
    "tag": {
      "description": "Hashtag to filter by — provide without the # prefix, e.g. \"ai\" not \"#ai\".",
      "type": "string",
      "maxLength": 100
    },
    "since": {
      "description": "Return posts after this ISO 8601 datetime (inclusive), e.g. \"2025-01-01T00:00:00Z\".",
      "type": "string",
      "maxLength": 32
    },
    "until": {
      "description": "Return posts before this ISO 8601 datetime (inclusive), e.g. \"2025-12-31T23:59:59Z\".",
      "type": "string",
      "maxLength": 32
    },
    "sort": {
      "default": "latest",
      "description": "\"latest\" returns posts in reverse-chronological order (default). \"top\" returns by engagement score.",
      "type": "string",
      "enum": [
        "top",
        "latest"
      ]
    },
    "limit": {
      "default": 25,
      "description": "Maximum posts to return (1–100). Default 25.",
      "type": "integer",
      "minimum": 1,
      "maximum": 100
    },
    "cursor": {
      "description": "Opaque pagination cursor from a previous response. Note: the public Bluesky AppView restricts cursor-based search pagination for unauthenticated requests — passing a cursor may return a 403 error. Cursor pagination is reliable only for bsky_get_author_feed and bsky_get_follows.",
      "type": "string",
      "maxLength": 2048
    }
  },
  "required": [
    "query",
    "sort",
    "limit"
  ],
  "additionalProperties": false
}
view source ↗

bsky_get_post_thread

open-world

Fetch the full conversation for a post by AT-URI — the parent chain upward and the reply tree downward. Enter the thread at any point and traverse the full discussion. AT-URIs have the format "at://<did>/<collection>/<rkey>" and are returned by bsky_search_posts and bsky_get_author_feed in the "uri" field of each post. Returns the root post, parent chain, and nested replies with per-post author and engagement data. "truncated: true" on a reply node means there are more replies below — increase depth to load them.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "bsky_get_post_thread",
    "arguments": {
      "uri": "<uri>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "uri": {
      "type": "string",
      "maxLength": 2048,
      "description": "AT-URI of the post to fetch, e.g. \"at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.post/abc123\". Obtain from bsky_search_posts or bsky_get_author_feed."
    },
    "depth": {
      "default": 6,
      "description": "How many levels of replies to include in the reply tree. Default 6.",
      "type": "integer",
      "minimum": 0,
      "maximum": 1000
    },
    "parent_height": {
      "default": 80,
      "description": "How many parent posts to include in the parent chain above the target post. Default 80.",
      "type": "integer",
      "minimum": 0,
      "maximum": 1000
    }
  },
  "required": [
    "uri",
    "depth",
    "parent_height"
  ],
  "additionalProperties": false
}
view source ↗

bsky_get_follows

open-world

Fetch the social graph edges for a Bluesky account — who follows them, or who they follow. Returns paginated actor profiles (handle, DID, displayName, bio, follower count) plus a summary of the subject account. Accounts with large social graphs return only the first page; use cursor pagination to walk through the full list.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "bsky_get_follows",
    "arguments": {
      "actor": "<actor>",
      "direction": "<direction>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "actor": {
      "type": "string",
      "maxLength": 253,
      "description": "Handle (e.g. \"alice.bsky.social\") or DID of the account to query."
    },
    "direction": {
      "type": "string",
      "enum": [
        "followers",
        "following"
      ],
      "description": "\"followers\" returns accounts that follow this actor. \"following\" returns accounts this actor follows."
    },
    "limit": {
      "default": 25,
      "description": "Maximum number of actors to return per page (1–100). Default 25.",
      "type": "integer",
      "minimum": 1,
      "maximum": 100
    },
    "cursor": {
      "description": "Opaque pagination cursor from a previous response. Omit for the first page.",
      "type": "string",
      "maxLength": 2048
    }
  },
  "required": [
    "actor",
    "direction",
    "limit"
  ],
  "additionalProperties": false
}
view source ↗

Resources

1

A Bluesky actor's public profile, addressable by handle or DID. Returns the same data as bsky_get_profile in injectable-context form — displayName, handle, DID, bio, follower/following/post counts, avatar, moderation labels, and pinned post AT-URI.

uri bsky://profile/{actor} mime application/json