Skip to main content
Glama

bluesky-mcp-server

Get Bluesky Social Graph

bsky_get_follows
Read-onlyIdempotent

Fetch the social graph edges for a Bluesky account — who follows them, or who they follow. Returns paginated actor profiles (handle, DID, displayName, bio, pronouns when set, and Bluesky verification status) plus a summary of the subject account. Follower, following, and post counts and the website are not on this view, for the listed accounts or the subject — bsky_get_profile returns them for one account. Accounts with large social graphs return only the first page; use cursor pagination to walk through the full list, or sort "top" to put the accounts Bluesky ranks most prominent first.

Input Schema

NameRequiredDescriptionDefault
sortNoOrder of the list. "latest" (Bluesky's default, and what omitting this gives) puts the most recent follows first; "top" is Bluesky's own ranking, which surfaces prominent accounts but is not a follower-count order. Pass a cursor back with the same sort it came from.
actorYesHandle (e.g. "alice.bsky.social") or DID of the account to query. A leading "@" and the account's bsky.app page ("https://bsky.app/profile/alice.bsky.social") are accepted and read as the handle or DID they carry. A bare name without a dot is not a handle — use bsky_search_actors to resolve one.
limitNoMaximum number of actors to return per page. Default 25; values above 100 are clamped with a notice unless read-limit clamping is disabled.
cursorNoOpaque pagination cursor from a previous response. Omit for the first page.
directionYes"followers" returns accounts that follow this actor. "following" returns accounts this actor follows.

Output Schema

NameRequiredDescriptionDefault
capNoThe limit applied to this page.
errorNoPresent when the call failed. Absent on success.
shownNoNumber of actors returned on this page.
actorsNoActors in the requested direction of the social graph.
cursorNoOpaque cursor for the next page. Absent on the last page.
noticeNoGuidance when the result set is empty or constrained.
subjectNoProfile summary of the queried actor.
truncatedNoTrue when Bluesky returned a cursor, whatever this page held. A cursor is not proof more accounts exist: the next page can come back empty when the remaining accounts are unavailable.
totalReturnedNoNumber of actors in this response page.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed2 schema fields changed
    • changedInput schema / properties / limit / description
      Previous value: -"Maximum number of actors to return per page (1–100). Default 25."New value: +"Maximum number of actors to return per page. Default 25; values above 100 are clamped with a notice unless read-limit clamping is disabled."
    • changedInput schema / properties / limit / maximum
      Previous value: -100New value: +9007199254740991
  2. Changed2 schema fields changed
    • addedOutput schema / properties / actors / items / properties / verification
      Added value: +{
      +  "additionalProperties": false,
      +  "description": "Bluesky verification of this account — what tells it from a look-alike handle. Absent when Bluesky sent none. Who issued it is on bsky_get_profile.",
      +  "properties": {
      +    "trustedVerifierStatus": {
      +      "description": "Whether this account is itself a trusted verifier — same values as verifiedStatus.",
      +      "type": "string"
      +    },
      +    "verifiedStatus": {
      +      "description": "Whether a trusted verifier verified this account: \"valid\", \"invalid\" (verified once, no longer holds), or \"none\". Passed through as Bluesky sends it, so another value may appear.",
      +      "type": "string"
      +    }
      +  },
      +  "required": [
      +    "verifiedStatus",
      +    "trustedVerifierStatus"
      +  ],
      +  "type": "object"
      +}
    • addedOutput schema / properties / subject / properties / verification
      Added value: +{
      +  "additionalProperties": false,
      +  "description": "Bluesky verification of this account — what tells it from a look-alike handle. Absent when Bluesky sent none. Who issued it is on bsky_get_profile.",
      +  "properties": {
      +    "trustedVerifierStatus": {
      +      "description": "Whether this account is itself a trusted verifier — same values as verifiedStatus.",
      +      "type": "string"
      +    },
      +    "verifiedStatus": {
      +      "description": "Whether a trusted verifier verified this account: \"valid\", \"invalid\" (verified once, no longer holds), or \"none\". Passed through as Bluesky sends it, so another value may appear.",
      +      "type": "string"
      +    }
      +  },
      +  "required": [
      +    "verifiedStatus",
      +    "trustedVerifierStatus"
      +  ],
      +  "type": "object"
      +}
  3. Changed4 schema fields changed
    • changedInput schema / properties / actor / description
      Previous value: -"Handle (e.g. \"alice.bsky.social\") or DID of the account to query. A bare name without a dot is not a handle — use bsky_search_actors to resolve one."New value: +"Handle (e.g. \"alice.bsky.social\") or DID of the account to query. A leading \"@\" and the account's bsky.app page (\"https://bsky.app/profile/alice.bsky.social\") are accepted and read as the handle or DID they carry. A bare name without a dot is not a handle — use bsky_search_actors to resolve one."
    • changedInput schema / properties / actor / maxLength
      Previous value: -253New value: +2048
    • changedInput schema / properties / actor / pattern
      Previous value: -"^(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?|did:[a-z]+:[a-zA-Z0-9._:%-]*[a-zA-Z0-9._-])$"New value: +"^(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?|did:[a-z]+:[a-zA-Z0-9._:%-]*[a-zA-Z0-9._-]|@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?|https:\\/\\/bsky\\.app\\/profile\\/(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?|did:[a-z]+:[a-zA-Z0-9._:%-]*[a-zA-Z0-9._-])\\/?(?:[?#].*)?)$"
    • addedInput schema / properties / sort
      Added value: +{
      +  "description": "Order of the list. \"latest\" (Bluesky's default, and what omitting this gives) puts the most recent follows first; \"top\" is Bluesky's own ranking, which surfaces prominent accounts but is not a follower-count order. Pass a cursor back with the same sort it came from.",
      +  "enum": [
      +    "latest",
      +    "top"
      +  ],
      +  "type": "string"
      +}
  4. Changed4 schema fields changed
    • removedOutput schema / properties / actors / items / properties / followersCount
      Removed value: -{
      -  "description": "Number of followers.",
      -  "type": "number"
      -}
    • removedOutput schema / properties / subject / properties / followersCount
      Removed value: -{
      -  "description": "Subject's follower count.",
      -  "type": "number"
      -}
    • removedOutput schema / properties / subject / properties / followsCount
      Removed value: -{
      -  "description": "Subject's following count.",
      -  "type": "number"
      -}
    • changedOutput schema / properties / truncated / description
      Previous value: -"True when more actors exist beyond this page (a cursor was returned)."New value: +"True when Bluesky returned a cursor, whatever this page held. A cursor is not proof more accounts exist: the next page can come back empty when the remaining accounts are unavailable."
  5. Changed6 schema fields changed
    • changedInput schema / $schema
      Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
    • addedInput schema / additionalProperties
      Added value: +false
    • changedOutput schema / $schema
      Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
    • addedOutput schema / anyOf
      Added value: +[
      +  {
      +    "not": {
      +      "required": [
      +        "error"
      +      ]
      +    },
      +    "required": [
      +      "actors",
      +      "subject",
      +      "totalReturned"
      +    ]
      +  },
      +  {
      +    "required": [
      +      "error"
      +    ]
      +  }
      +]
    • addedOutput schema / properties / error
      Added value: +{
      +  "additionalProperties": {},
      +  "description": "Present when the call failed. Absent on success.",
      +  "properties": {
      +    "code": {
      +      "description": "JSON-RPC error code for this failure.",
      +      "maximum": 9007199254740991,
      +      "minimum": -9007199254740991,
      +      "type": "integer"
      +    },
      +    "data": {
      +      "additionalProperties": {},
      +      "properties": {
      +        "reason": {
      +          "description": "Machine-readable failure mode. Declared by this tool: `actor_not_found`: The actor handle or DID does not resolve to an existing account. Other values are possible when a failure originates below the handler.",
      +          "examples": [
      +            "actor_not_found"
      +          ],
      +          "type": "string"
      +        },
      +        "recovery": {
      +          "additionalProperties": {},
      +          "description": "Actionable next step for the caller.",
      +          "properties": {
      +            "hint": {
      +              "type": "string"
      +            }
      +          },
      +          "required": [
      +            "hint"
      +          ],
      +          "type": "object"
      +        },
      +        "retryable": {
      +          "description": "Whether retrying may succeed.",
      +          "type": "boolean"
      +        }
      +      },
      +      "type": "object"
      +    },
      +    "message": {
      +      "description": "Human-readable description of what went wrong.",
      +      "type": "string"
      +    }
      +  },
      +  "required": [
      +    "code",
      +    "message"
      +  ],
      +  "type": "object"
      +}
    • removedOutput schema / required
      Removed value: -[
      -  "actors",
      -  "subject",
      -  "totalReturned"
      -]
  6. Changed2 schema fields changed
    • addedOutput schema / properties / actors / items / properties / pronouns
      Added value: +{
      +  "description": "Free-form pronouns the account set, e.g. \"they/he\". Absent when it set none. Account-authored text bounded only by length, not a fixed vocabulary — read it as written rather than parsing it.",
      +  "type": "string"
      +}
    • addedOutput schema / properties / subject / properties / pronouns
      Added value: +{
      +  "description": "Free-form pronouns the subject account set, e.g. \"they/he\". Absent when it set none.",
      +  "type": "string"
      +}
  7. Changed3 schema fields changed
    • changedInput schema / properties / actor / description
      Previous value: -"Handle (e.g. \"alice.bsky.social\") or DID of the account to query."New value: +"Handle (e.g. \"alice.bsky.social\") or DID of the account to query. A bare name without a dot is not a handle — use bsky_search_actors to resolve one."
    • addedInput schema / properties / actor / minLength
      Added value: +1
    • addedInput schema / properties / actor / pattern
      Added value: +"^(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?|did:[a-z]+:[a-zA-Z0-9._:%-]*[a-zA-Z0-9._-])$"
  8. Changed3 schema fields changed
    • addedOutput schema / properties / cap
      Added value: +{
      +  "description": "The limit applied to this page.",
      +  "type": "number"
      +}
    • addedOutput schema / properties / shown
      Added value: +{
      +  "description": "Number of actors returned on this page.",
      +  "type": "number"
      +}
    • addedOutput schema / properties / truncated
      Added value: +{
      +  "description": "True when more actors exist beyond this page (a cursor was returned).",
      +  "type": "boolean"
      +}
  9. First observed

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly/openWorld/idempotent, so the bar is low, and the description still adds real value: it names the returned profile fields, states which fields are absent, and warns that large graphs return only the first page. It stops short of explaining cursor expiry or error behavior, so not a full 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the action and scope, then returns, then exclusions, then pagination. Mostly earns its space, though the cursor/sort advice duplicates the schema's own parameter descriptions.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 5-parameter read tool with an output schema and full annotation coverage, this is complete: direction semantics, pagination caveat, field coverage, and the sibling to use for missing data are all addressed.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, including detailed enum docs for sort and direction and a pattern note on actor, so the schema carries the parameter burden. The description only restates the pagination/sort guidance already present in the schema, adding little new meaning.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening clause names a specific verb and resource (fetch social graph edges) and immediately disambiguates the two directions (followers vs following). It also explicitly distinguishes itself from bsky_get_profile, which handles the counts this tool omits.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description states when to use the cursor (large graphs), when to use sort="top" (prominence ranking), and when to go elsewhere (bsky_get_profile for follower/following/post counts and website). Alternatives and exclusions are both explicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.