Skip to main content
Glama
cameronrye

AT Protocol MCP Server

AT Protocol MCP Server

npm version License: MIT TypeScript

CI Integration Tests Documentation Release Coverage

Node.js pnpm AT Protocol MCP SDK

GitHub stars npm downloads GitHub last commit GitHub contributors

Code Style Documentation Security npm bundle size

A comprehensive Model Context Protocol (MCP) server that provides LLMs with direct access to the AT Protocol ecosystem, enabling seamless interaction with Bluesky and other AT Protocol-based social networks.

Supports both authenticated and unauthenticated modes - Start immediately with public data access (view profiles, search accounts, fetch follower/following lists), or add authentication for full functionality (search, write operations, private data, feeds).

Zero-config launch: npx atproto-mcp runs the server in unauthenticated public-data mode — no credentials required.

Recent additions: Bluesky direct messages, private bookmarks, starter pack discovery, reply/quote controls on posts, parameterized MCP resource templates, and an optional Streamable HTTP transport (--transport http).

Architecture

This MCP server acts as a bridge between LLM clients and the AT Protocol ecosystem:

┌─────────────────┐
│      User       │  "Search for posts about AI"
└────────┬────────┘
         │ Natural Language
         ▼
┌─────────────────┐
│   LLM Client    │  (Claude Desktop, etc.)
│  (MCP Client)   │
└────────┬────────┘
         │ MCP Protocol (JSON-RPC 2.0)
         ▼
┌─────────────────┐
│   This Server   │  AT Protocol MCP Server
│  (MCP Server)   │  - Tools, Resources, Prompts
└────────┬────────┘
         │ AT Protocol API
         ▼
┌─────────────────┐
│  AT Protocol    │  Bluesky, Custom PDS, etc.
│   Ecosystem     │
└─────────────────┘

Key Point: Users don't interact with this server directly. Instead, they talk to their LLM client in natural language, and the LLM client uses this MCP server to access AT Protocol functionality.

Related MCP server: Bluesky MCP Server

Features

Highlights

  • Batch Operations: Perform multiple operations in a single call (follow/like/repost up to 25 items at once)

  • Analytics & Insights: Analyze engagement patterns, network connections, and get content strategy recommendations

  • Content Discovery: Find similar users, trending topics, starter packs, and influential voices in your areas of interest

  • Direct Messages: List conversations, read message history, and send Bluesky DMs (requires a DM-enabled app password)

  • Private Bookmarks: Save, list, and remove private bookmarks on posts

Core Features

  • Zero-config Unauthenticated Mode: Run npx atproto-mcp to access public data without any setup - view profiles, search accounts, and fetch follower/following lists

  • Optional Authentication: Enable full functionality with app passwords for write operations, feeds, and private data

  • Complete AT Protocol Integration: Full implementation using official @atproto/api

  • MCP Server Compliance: Built with @modelcontextprotocol/sdk following MCP specification

  • Type-Safe: Written in TypeScript with strict type checking

  • Comprehensive Tools: 51 MCP tools for social networking operations

  • Rate Limiting: Built-in respect for AT Protocol rate limits

  • Extensible: Modular architecture for easy customization

Planned: OAuth login is on the roadmap but not yet functional — app-password authentication is the supported auth path today. Real-time firehose streaming is not planned as MCP tools: a request/response tool cannot honestly expose a continuous stream, and the unused firehose client code has been removed. If streaming ever ships it will be built fresh on Jetstream.

Who Is This For?

Primary Audience: LLM Clients

This is an MCP (Model Context Protocol) server designed to be consumed by LLM clients such as:

  • Claude Desktop

  • Other MCP-compatible AI assistants

  • Custom LLM applications using the MCP SDK

How it works:

User → LLM Client (Claude Desktop) → MCP Protocol → This Server → AT Protocol → Bluesky

Users interact with their LLM client in natural language (e.g., "search for posts about AI"), and the LLM client uses this MCP server to fulfill those requests by calling the appropriate tools via the MCP protocol.

Secondary Audience: Developers

This project is also for developers who want to:

  • Deploy the MCP server for their LLM clients to connect to

  • Extend the server with custom MCP tools and resources

  • Contribute to the open-source project

This Is NOT:

  • A direct-use REST API or SDK for application developers

  • A JavaScript/TypeScript library to import into your app

  • An end-user application

If you're building an application that needs AT Protocol functionality, you should either:

  1. Use the official @atproto/api package directly, OR

  2. Build an LLM-powered application that uses this MCP server through an LLM client

Installation

Run it with no install and no configuration — npx atproto-mcp launches the server in unauthenticated public-data mode immediately:

npx atproto-mcp

Or install globally:

npm install -g atproto-mcp

Claude Desktop

Add this to your Claude Desktop MCP configuration to run the server with zero config:

{
  "mcpServers": {
    "atproto": { "command": "npx", "args": ["-y", "atproto-mcp"] }
  }
}

Quick Start

Perfect for LLM clients that need to access public AT Protocol data:

  1. Configure your LLM client (e.g., Claude Desktop) to launch the MCP server:

    Add to your LLM client's MCP configuration:

    {
      "mcpServers": {
        "atproto": {
          "command": "npx",
          "args": ["atproto-mcp"]
        }
      }
    }
  2. Start your LLM client - it will automatically launch the MCP server

  3. Interact in natural language - Ask your LLM to search posts, view profiles, etc.

What your LLM can do in unauthenticated mode:

  • View user profiles (get_user_profile - works without auth, provides additional viewer-specific data when authenticated)

  • Search for accounts by handle or name (search_actors)

  • List a user's posts (get_author_feed)

  • View follower/following lists (get_user_connections with direction: 'followers' | 'follows' - ENHANCED mode: works without auth, enriches the underlying API call when authenticated)

Note: The following features require authentication:

  • Searching posts and hashtags (search_posts) - API changed in 2025 to require authentication

  • Browsing feeds and threads (get_post_context, get_custom_feed, get_timeline)

  • All write operations (create, like, repost, follow, etc.)

  • Resources (timeline, profile, notifications) - these are listed but require authentication to return data

Prompts (content composition, reply templates) are pure text templates and work without authentication.

Important: All tools, resources, and prompts are listed by the MCP server regardless of authentication state. Most tools and resources that require authentication will return a clear error message when called without proper credentials.

Option 2: Authenticated Mode (For full functionality)

Enable write operations and private data access for your LLM:

  1. Configure your LLM client with AT Protocol credentials:

    {
      "mcpServers": {
        "atproto": {
          "command": "npx",
          "args": ["atproto-mcp"],
          "env": {
            "ATPROTO_IDENTIFIER": "your-handle.bsky.social",
            "ATPROTO_PASSWORD": "your-app-password"
          }
        }
      }
    }
  2. Start your LLM client - it will launch the authenticated MCP server

What your LLM can do in authenticated mode:

  • Create, edit, and delete posts

  • Follow/unfollow users

  • Like and repost content

  • Access personalized timelines and notifications

  • Manage lists and moderation settings

Available Tools

The server provides 51 MCP tools across multiple categories. See the complete API documentation for detailed information on each tool.

Public Tools (No Authentication Required)

Data Retrieval

  • get_user_profile - Retrieve basic user information (ENHANCED mode: works without auth, provides additional viewer-specific data when authenticated)

  • get_user_summary - Get a profile with recent posts and engagement stats in one call (ENHANCED mode)

  • search_actors - Find accounts by handle or display name (ENHANCED mode)

  • get_author_feed - List a specific user's posts (ENHANCED mode)

  • get_user_connections - Get follower or following lists via direction: 'followers' | 'follows' (ENHANCED mode: works without auth, enriches the underlying API call when authenticated)

  • get_post_context - Get a post with optional thread, author profile, engagement metrics, and media (ENHANCED mode)

  • search_starter_packs / get_starter_pack - Search Bluesky starter packs by keyword and fetch a pack's details (ENHANCED mode)

Rich Media

  • analyze_image - Report blob-declared size and MIME type for an image (PUBLIC mode: no auth required; does not decode pixels, so no dimensions/aspect ratio)

Note: As of 2025, the AT Protocol API has changed to require authentication for most endpoints that were previously public, including search_posts.

Private Tools (Authentication Required)

Social Operations

  • create_post - Create posts with text, auto-detected or explicit richtext facets, replies, image/external embeds, and quote posts

  • create_thread - Create multi-post threads in one call

  • reply_to_post - Reply to existing posts with threading

  • like_post / unlike_post - Like and unlike posts

  • repost / unrepost - Repost content with optional quotes

  • follow_user / unfollow_user - Follow and unfollow users

Data Retrieval

  • search_posts - Search for posts and content across the network (⚠️ API changed in 2025 to require auth)

  • get_custom_feed - Access custom feeds

  • get_timeline - Retrieve personalized timelines

  • get_notifications - Access notification feeds (use countOnly: true for a cheap unread badge count)

  • mark_notifications_seen - Mark notifications as seen up to a timestamp

Direct Messages

  • list_conversations - List your Bluesky DM conversations

  • get_conversation_messages - Read a conversation's message history

  • send_direct_message - Send a DM (requires an app password created with "Allow access to your direct messages" enabled)

Bookmarks

  • add_bookmark / remove_bookmark - Privately bookmark and un-bookmark posts

  • get_bookmarks - List your private bookmarks

Content Management

  • upload_image / upload_video - Upload media content

  • delete_post - Remove posts

  • update_profile - Modify profile and settings

  • generate_link_preview - Generate link previews for posts

List Management

  • create_list - Create user lists

  • add_to_list / remove_from_list - Manage list members

  • get_list - Retrieve list information

Moderation

  • mute_user / unmute_user - Mute and unmute users

  • block_user / unblock_user - Block and unblock users

  • report_content / report_user - Report content and users

  • analyze_moderation_status - Check moderation status of content

Batch Operations

  • batch_action - Apply one action across up to 25 targets in a single call via action: 'follow' | 'like' | 'repost'

Analytics & Insights

  • analyze_account - Analyze a single account along one dimension via dimension: 'engagement' | 'network' | 'strategy'

  • find_influential_users - Find influential users in a topic area

Content Discovery

  • discover - Surface timeline content via mode: 'trending' | 'recommended'

  • find_similar_users - Find users similar to a given user

  • discover_communities - Discover communities around topics

Documentation

Visit our documentation site for:

  • Getting Started Guide

  • API Reference

  • Configuration Options

  • Examples and Tutorials

  • Troubleshooting

Authentication (Optional)

The server works perfectly without authentication for accessing public data. Authentication is only needed for write operations and private data access.

export ATPROTO_IDENTIFIER="your-handle.bsky.social"
export ATPROTO_PASSWORD="your-app-password"
atproto-mcp

App passwords are the supported authentication path. OAuth login is planned but not yet functional.

Development

Quick Start

# Clone the repository
git clone https://github.com/cameronrye/atproto-mcp.git
cd atproto-mcp

# Install dependencies (use pnpm, npm, or yarn)
pnpm install  # or: npm install

# Start development server
pnpm dev      # or: npm run dev

# Run tests
pnpm test     # or: npm test

# Build for production
pnpm build    # or: npm run build

Available Commands

This project provides cross-platform npm scripts that work on Windows, macOS, and Linux:

# Show all available commands
npm run help

# Development
npm run dev              # Start development server with hot reload
npm run build            # Build for production
npm run start            # Start production server

# Testing & Quality
npm test                 # Run tests
npm run test:coverage    # Run tests with coverage
npm run test:ui          # Run tests with interactive UI

# Integration Tests (connects to real AT Protocol servers)
npm run test:integration

npm run lint             # Run ESLint
npm run lint:fix         # Fix linting issues
npm run format           # Format code with Prettier
npm run type-check       # Run TypeScript type checking
npm run check            # Run all quality checks

# Utilities
npm run clean            # Clean build artifacts
npm run clean:all        # Clean everything including node_modules
npm run status           # Show project status
npm run ci               # Run full CI pipeline locally

# Dependencies
npm run deps:update      # Update dependencies
npm run deps:audit       # Audit for security issues

Cross-Platform Compatibility

All build commands work on Windows, macOS, and Linux without requiring additional tools. Simply use npm scripts on any platform (e.g., npm run dev, npm test, npm run build).

Testing

The project includes comprehensive test coverage:

Unit Tests

# Run all unit tests
pnpm test

# Run with coverage
pnpm test:coverage

# Run with interactive UI
pnpm test:ui

Integration Tests

Comprehensive integration tests that connect to real AT Protocol servers to validate all public-facing functionality:

# Run integration tests (requires internet connection)
npm run test:integration

What's tested:

  • Public/enhanced tools (get_user_profile, get_user_connections, get_author_feed) and authenticated tools (search_posts, get_post_context, get_custom_feed)

  • DID and handle resolution

  • Pagination support

  • Error handling

  • AT Protocol specification compliance

  • Rate limiting behavior

Note: Integration tests are opt-in and disabled by default to avoid hitting real servers during normal development. See Integration Tests Documentation for details.

Contributing

We welcome contributions! Please see our Contributing Guide for details.

  1. Fork the repository

  2. Create a feature branch

  3. Make your changes

  4. Add tests

  5. Submit a pull request

License

This project is licensed under the MIT License.

Acknowledgments

  • AT Protocol Team for the excellent protocol and SDK

  • Anthropic for the Model Context Protocol

  • The open source community for inspiration and contributions

Support

Deployment

By default this is a stdio MCP server: it is normally launched by an MCP client (e.g. Claude Desktop) via npx atproto-mcp and communicates over stdin/stdout, binding no network port. Alternatively, --transport http serves the MCP Streamable HTTP transport at http://<host>:<port>/mcp (default binding 127.0.0.1:3000, loopback only); exposing it beyond loopback (e.g. --host 0.0.0.0) is the operator's responsibility to secure. stdio remains the default and the recommended setup for MCP clients.

Built-in safeguards

  • Error sanitization: in NODE_ENV=production, internal error details are redacted before being returned to the client.

  • Rate limiting: per-tool invocation rate limiting guards against runaway loops.

  • SSRF protection: outbound URL/media fetches reject private/internal network destinations and cap response size and time.

  • Path-traversal protection: local file reads are confined to an allowed base directory (ATPROTO_MEDIA_DIR, default: working directory).

Running in Docker

The container runs the same stdio server, so attach to it via your MCP client rather than mapping a port:

docker build -t atproto-mcp .
docker run -i --rm \
  -e ATPROTO_IDENTIFIER=your.handle.bsky.social \
  -e ATPROTO_PASSWORD=your-app-password \
  atproto-mcp

Environment configuration

# Copy the example environment file and edit it (loaded automatically by the CLI)
cp .env.example .env
ATPROTO_IDENTIFIER=your.handle.bsky.social
ATPROTO_PASSWORD=your-app-password
NODE_ENV=production
LOG_LEVEL=info

See .env.example for the full list of variables the server reads.

Security

Security is a top priority for this project. Please review our security practices and policies:

Security Best Practices

Before deploying to production:

  1. Secure Your Credentials

    • Never commit .env files to version control

    • Use app passwords instead of your main account password

    • Rotate credentials regularly

    • Use a secret management system where available (AWS Secrets Manager, HashiCorp Vault, etc.)

  2. Run in production mode

    • Set NODE_ENV=production so returned error messages are sanitized

  3. Keep Dependencies Updated

    pnpm audit
    pnpm update

Reporting Security Vulnerabilities

If you discover a security vulnerability, please review our Security Policy for responsible disclosure guidelines.

Do not open public issues for security vulnerabilities. Instead, send me a message privately.

Security Features

  • Input validation and sanitization

  • Rate limiting and abuse prevention

  • Credential redaction in logs

  • Non-root Docker containers

  • HTTPS support for AT Protocol

  • Error sanitization to prevent information leakage

For more details, see SECURITY.md.


Made with ❤️ by Cameron Rye

Available Tools

51 tools
add_bookmarkA
Idempotent

Privately bookmark a post on AT Protocol (app.bsky.bookmark.createBookmark). Bookmarks are private to your account and stored server-side — they are not public records, other users cannot see them, and no bookmark-record URI exists (bookmarks are keyed by the post URI). Only posts can be bookmarked; bookmarking an already-bookmarked post is a no-op. Requires authentication (app password). Use remove_bookmark to remove and get_bookmarks to list. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
uriYesAT-URI of the post to bookmark (at://did/app.bsky.feed.post/rkey). Only posts can be bookmarked.
cidNoCID of the post record. Optional: resolved automatically from the post view when omitted.

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYesWhether the bookmark operation succeeded.
messageYesHuman-readable status message.
alreadyBookmarkedYesTrue when the post was already bookmarked; no create call is issued (the endpoint is also idempotent server-side, so a duplicate create would be a no-op anyway).
bookmarkedPostYesThe post that was bookmarked. There is no bookmark-record URI: bookmarks are private server-side state keyed by this post URI.

TDQS

A4.9/5.0
Behavior5/5

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

Discloses beyond annotations: bookmarks are private, server-side, no public record, keyed by post URI, rate limiting. Annotation hints (idempotentHint, destructiveHint, openWorldHint) are fully explained in the description.

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

Conciseness5/5

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

Concise, front-loaded sentence, efficiently packs all key details (privacy, no-op, alternatives, auth, rate limit) without redundancy.

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?

Coverage of purpose, parameters, behavior, alternatives, constraints, authentication, and rate limiting is comprehensive for a simple bookmarking tool, especially with output schema present.

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

Parameters4/5

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

Schema coverage is 100%, but description adds meaningful context: specifies URI format 'at://did/app.bsky.feed.post/rkey' and explains CID is optional and auto-resolved. Adds value beyond schema.

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?

Clearly states the specific action: 'Privately bookmark a post'. Distinguishes from siblings (remove_bookmark, get_bookmarks) and specifies scope ('Only posts can be bookmarked').

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?

Explicitly states when to use: to bookmark a post. Provides alternatives: 'Use remove_bookmark to remove and get_bookmarks to list.' Mentions no-op for already-bookmarked posts and authentication requirement.

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

add_to_listA

Add a user to an existing list. Requires authentication (app password). Creates a listitem record in the authenticated user's repository; the actor's handle is resolved to a DID before insertion. Use remove_from_list to undo the addition and get_list to verify membership. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
listUriYesAT-URI of the target list (at://did/app.bsky.graph.list/rkey).
actorYesHandle (e.g. alice.bsky.social) or DID of the user to add to the list.

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYesWhether the user was added successfully.
messageYesHuman-readable result message.
listItemYesDetails of the newly created list-item record.

TDQS

A5/5.0
Behavior5/5

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

Describes internal creation of a listitem record, DID resolution, and rate limiting. Annotations (destructiveHint=false, idempotentHint=false, openWorldHint=true) are consistent and the description adds context beyond them.

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

Conciseness5/5

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

Three sentences, front-loaded with key action, no unnecessary words.

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?

Covers purpose, usage, auth, alternatives, internal behavior, and output schema context. Complete for a simple two-parameter tool.

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

Parameters5/5

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

Input schema has 100% coverage with descriptions; the description adds detail about DID resolution for the actor parameter and AT-URI format for listUri.

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 description states 'Add a user to an existing list' with a specific verb and resource. It distinguishes from siblings like remove_from_list, get_list, and create_list.

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?

Explicitly mentions authentication requirement, provides alternatives (remove_from_list, get_list), and notes rate limiting.

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

analyze_accountA
Read-only

Analyze a single account along one dimension. Requires authentication (app password). Read-only: performs no writes. One account-analysis tool: pick a dimension. For topic/search-based influencer discovery use find_influential_users instead. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
actorNoHandle or DID to analyze. Defaults to the authenticated user when omitted.
dimensionYesWhich analysis to run: 'engagement' = recent-post performance; 'network' = follower/following graph health; 'strategy' = posting recommendations.
limitNoHow many recent posts to sample for engagement analysis (1–100, default 50).
maxSampleSizeNoHow many connections to sample for network analysis (default per old behavior).
includeRepliesNoEngagement: include replies in the sampled author feed (default true).
includeFollowersNoNetwork: sample the followers graph (default true).
includeFollowsNoNetwork: sample the follows graph (default true).
analyzePostsNoStrategy: how many recent posts to analyze (10–100, default 50).
includeTimingAnalysisNoStrategy: include best-posting-time analysis (default true).
includeTopicAnalysisNoStrategy: include topic/keyword analysis (default true).

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYesWhether the analysis succeeded.
dimensionYesWhich analysis was run.
actorNonetwork/strategy dimensions: the DID or handle that was analyzed.
summaryNoengagement dimension: aggregate statistics over the sampled posts (reposts of other authors excluded).
topPostsNoengagement dimension: up to 10 posts ranked by total engagement.
networkNonetwork dimension: headline follow-graph counts for the account.
analysisNoDimension-specific analysis: graph classification for 'network', post-performance statistics for 'strategy'.
insightsNoDimension-specific insights: structured content patterns for 'engagement', human-readable strings for 'network'.
recommendationsNoDimension-specific recommendations: actionable strings for 'engagement', a structured plan for 'strategy'.

TDQS

A4.6/5.0
Behavior5/5

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

Annotations already declare readOnlyHint=true and openWorldHint=true. The description adds useful context: requires authentication, is read-only, and subject to per-tool rate limiting. No contradictions.

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

Conciseness5/5

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

The description is three sentences, each adding distinct value: core function, authentication/read-only, and usage alternatives. No unnecessary words.

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

Completeness4/5

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

Given the tool has 10 parameters and an output schema, the description covers key aspects (purpose, auth, mutation, rate limiting, sibling differentiation) without being exhaustive. It's sufficient for most agents.

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 coverage is 100%, so the description does not need to explain parameters. The description adds minimal extra value beyond the schema (e.g., default behavior for 'actor'). Baseline of 3 is appropriate.

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 description clearly states the verb 'analyze', the resource 'a single account', and the constraint 'along one dimension'. It also distinguishes from the sibling tool 'find_influential_users' by noting the alternative use case.

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 explicitly says 'One account-analysis tool: pick a dimension' and directs users to 'find_influential_users' for topic/search-based discovery. It also mentions authentication requirement, providing clear when-to-use and when-not-to-use guidance.

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

analyze_imageA
Read-only

Analyze an image blob's metadata (MIME type, file size in bytes/KB/MB, derived format, and whether it is within optimized-size thresholds) and optionally return human-readable optimization and accessibility suggestions. Does not decode the image, so it cannot report pixel dimensions or aspect ratio. No authentication required. Use upload_image to obtain the blob reference first, then pass it here; prefer this tool over upload_image for pre-flight size checks before actually posting. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
blobYesBlob descriptor as returned by upload_image (the `image.blob` object in its output): ref (flat CID string or { "$link": "<cid>" } object), mimeType, and size.
includeOptimizationSuggestionsNoWhen true (default), the response includes a list of human-readable optimization and accessibility suggestions based on the blob size and MIME type.

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYesTrue when the analysis completed without errors.
analysisYesMetadata derived from the blob.
suggestionsNoList of human-readable optimization and accessibility suggestions. Present only when includeOptimizationSuggestions is true.

TDQS

A4.9/5.0
Behavior5/5

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

Discloses limitations (does not decode image, no pixel dimensions) and rate limiting, adding value beyond readOnlyHint and openWorldHint annotations. No contradictions with annotations.

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

Conciseness5/5

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

Five sentences covering all essentials: what it does, prerequisites, limitations, usage preference, and rate limiting. No unnecessary words.

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?

Given output_schema exists (so return values need no explanation), annotations provide safety context, and schema covers all parameters, the description is complete: it tells what to do, prerequisites, limitations, and alternatives.

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

Parameters4/5

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

Schema coverage is 100%, baseline 3. The description adds context by explaining the blob parameter as a result from upload_image and the includeOptimizationSuggestions parameter's default behavior, but parameter details are already well-covered by schema.

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 description clearly states the tool analyzes image blob metadata (MIME type, size, derived format, optimization thresholds) and optionally returns suggestions. It explicitly distinguishes from siblings by noting it does not decode the image and cannot report pixel dimensions, and differentiates from upload_image for pre-flight checks.

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?

Provides explicit guidance: use upload_image first, then pass the blob reference; prefer this tool over upload_image for pre-flight size checks; notes no authentication required and rate limiting.

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

analyze_moderation_statusA
Read-only

Analyze moderation status of a post or user. Returns content labels, moderation decisions, and personal moderation state (blocks, mutes). Subject can be a DID (for users) or AT-URI (for posts). Works without authentication; richer with auth. Use this tool to evaluate safety before rendering content; use block_user or mute_user to act on the results. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
subjectYesDID of a user account (e.g. did:plc:abc123) or AT-URI of a post (at://did/app.bsky.feed.post/rkey) to analyze.
includeLabelsNoWhether to fetch and include content labels in the response (default true). Set to false to skip label fetching for a faster call.

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYesWhether the analysis completed successfully.
subjectYesThe DID or AT-URI that was analyzed.
subjectTypeYes"user" when subject is a DID; "post" when subject is an AT-URI.
moderationYesRaw moderation state for the subject.
analysisYesDerived safety analysis based on labels and moderation state.

TDQS

A4.9/5.0
Behavior5/5

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

Annotations declare readOnlyHint=true and openWorldHint=true; description adds that it works without auth but richer with auth, and mentions rate limiting. No contradictions.

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

Conciseness5/5

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

Four sentences, each adding value: purpose, returns, subject types, auth, rate limiting, usage guidance. No wasted words.

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?

With output schema, return values are covered. Description covers subject types, auth dependency, rate limiting, and relationship to sibling tools. Complete.

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

Parameters4/5

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

Schema coverage is 100%, so baseline 3. Description adds meaning: explains subject format (DID or AT-URI) and includeLabels effect (faster if false). Above baseline.

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?

Description clearly states 'Analyze moderation status of a post or user' and lists return types. It distinguishes from sibling tools like block_user and mute_user.

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?

Explicitly says 'Use this tool to evaluate safety before rendering content; use block_user or mute_user to act on the results.' Provides clear usage context and alternatives.

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

batch_actionA
Idempotent

Batch the same action across up to 25 targets in one call. Supported actions: follow (handles/DIDs), like (AT-URIs), repost (AT-URIs). Requires authentication (app password). Performs real writes (follows/likes/reposts) to the network on the caller's behalf. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
actionYesThe action to apply to every target.
targetsYesFor action=follow: handles or DIDs. For action=like/repost: post AT-URIs. 1–25 items.
continueOnErrorNoWhether to keep processing remaining targets after an individual target fails. Defaults to true; set to false to stop at the first failure (unprocessed targets are reported as skipped in the summary).

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYes
actionYes
resultsYesPer-target outcome {target/uri, success, error?}.
summaryYesCounts: total, succeeded, failed, skipped.

TDQS

A4.4/5.0
Behavior4/5

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

Discloses real writes to network, authentication need, and rate limiting. Aligns with annotations (idempotentHint=true, destructiveHint=false, openWorldHint=true). Adds context beyond annotations.

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

Conciseness5/5

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

Three sentences, each essential: purpose, supported actions and target formats, auth/write/rate limiting. Front-loaded with main capability. No wasted words.

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

Completeness4/5

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

Covers main aspects: scope, actions, error handling, requirements. Output schema exists (not shown), so return values need not be explained. Minor gap: no mention of response summary structure.

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

Parameters4/5

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

Schema covers all parameters (100%). Description adds value by clarifying target formats per action and continueOnError default behavior. Baseline 3, increased for added clarity.

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?

Clearly states verb 'batch', resource 'same action across targets', and scope 'up to 25 targets'. Distinguishes from sibling single-action tools (follow_user, like_post, repost) by grouping multiple identical actions.

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

Usage Guidelines4/5

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

Explains when to use (batching multiple same actions) and requirements (authentication, rate limiting). Implicitly excludes single-action tools. Could be explicit about not using for mixed action types.

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

block_userA
Destructive

Block a user to prevent them from seeing your content and interacting with you. Creates a block record in your repo; the action cannot be undone without calling unblock_user. Requires authentication (app password). Use block_user for mutual visibility restriction; use mute_user for a private, one-sided feed suppression that does not affect the target. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
actorYesHandle (e.g. alice.bsky.social) or DID of the account to block.

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYesWhether the block operation succeeded.
messageYesHuman-readable result message.
blockedUserYesDetails of the blocked account.

TDQS

A4.7/5.0
Behavior5/5

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

Annotations already indicate destructiveHint=true and idempotentHint=false. The description adds key behavioral details: creates a block record, cannot be undone without unblock_user, requires authentication, and is subject to rate limiting. No contradictions.

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

Conciseness5/5

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

Four sentences, front-loaded with the primary purpose. Every sentence adds unique value: purpose, effect, irreversibility, authentication, sibling guidance, rate limiting. No wasted words.

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?

Given the tool's simplicity (one parameter, simple schema), the description covers all essential aspects: purpose, effect, usage guidelines, behavioral properties, and prerequisites. Output schema exists so return values need not be described.

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?

With 100% schema description coverage, the input schema already documents the 'actor' parameter well (handle or DID). The description adds no further parameter details, so baseline 3 is appropriate.

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 description uses a specific verb 'Block' with resource 'user' and clearly states the effect: preventing the user from seeing content and interacting. It distinguishes from sibling tool 'mute_user' by explicitly contrasting the behavior.

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 explicitly states when to use this tool ('for mutual visibility restriction'), provides an alternative ('use mute_user instead...'), notes the irreversibility without calling unblock_user, and requires authentication. All guidelines are explicit.

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

create_listA

Create a new list for organizing users (curate list) or moderation purposes (mod list). Requires authentication (app password). Creates a permanent list record in the authenticated user's repository; use add_to_list / remove_from_list to manage its members and get_list to inspect them. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYesDisplay name for the list (1–64 characters).
descriptionNoOptional plain-text description of the list (max 300 characters).
purposeNoList type: "curatelist" for a user-curated follow list, "modlist" for a moderation/block list. Defaults to "curatelist".curatelist

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYesWhether the list was created successfully.
messageYesHuman-readable result message.
listYesMetadata of the newly created list.

TDQS

A4.5/5.0
Behavior4/5

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

Annotations include destructiveHint=false, idempotentHint=false, openWorldHint=true. The description adds that authentication is required, lists are permanent, and there is per-tool rate limiting. This supplements the annotations well.

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

Conciseness5/5

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

Two sentences that efficiently convey the core action, prerequisites, lifecycle, and related tools. No unnecessary words.

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?

Given the three parameters and presence of an output schema, the description covers creation, authentication, persistence, and rate limiting comprehensively. It is complete for the tool's complexity.

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

Parameters4/5

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

Schema coverage is 100%, but the description adds context by explaining the purpose parameter's enum values and the characters of name and description. It provides meaning beyond the schema.

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 description clearly states the tool creates a list for organizing users (curatelist) or moderation (modlist). It distinguishes from siblings like add_to_list, remove_from_list, and get_list by explaining their roles.

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

Usage Guidelines4/5

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

The description provides when to use (create lists), mentions authentication and rate limiting, and hints at alternatives for managing and inspecting lists. However, it does not explicitly exclude cases where other tools should be preferred.

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

create_postA

Create a new post on AT Protocol (Bluesky). The single rich post-creation tool: supports plain text with auto-detected mentions/links/#hashtags, explicit richtext facets, replies, image embeds, a video embed (from upload_video), an external link card, a quote (record) embed, language tags, reply controls (who can reply, via a threadgate record), and quote controls (quote policy, via a postgate record). Requires authentication (app password). SIDE EFFECT: publishes a public post visible to everyone. Subject to per-tool rate limiting. Use create_thread to publish a multi-post chain in one call, and reply_to_post to reply to an existing post; use this tool for a single standalone post (it can also reply via the reply field).

ParametersJSON Schema
NameRequiredDescriptionDefault
textYesThe post body. Max 300 graphemes / 3000 bytes (emoji count as one grapheme). Mentions, links and #hashtags are auto-detected into richtext facets unless you supply `facets` explicitly.
replyNoSet to make this post a reply in an existing thread.
embedNoOptional media embed: images OR an external link card OR a video (at most one).
facetsNoOptional explicit richtext facets (byte-range annotations). For mentions, `value` is a handle or DID; for links, a URL; for hashtags, the tag without #. Omit to let the server auto-detect facets from the text.
quoteNoQuote another post (record embed). Mutually exclusive with the `embed.images`, `embed.external`, and `embed.video` embeds.
langsNoOptional BCP-47 language tags (e.g. en, en-US, pt-BR) declaring the languages of the post text.
replyControlsNoWho can reply to this post. Writes an app.bsky.feed.threadgate record (same rkey as the post) AFTER the post is created. Enabled options combine, up to 5 rules. Provide the object with NO rules enabled to let nobody reply; omit it entirely to leave replies open to everyone. If the gate write fails after the post succeeded, the call still succeeds with gateApplied:false and a warning instead of failing.
quoteControlsNoQuote (embed) policy for this post. Only allowQuotes:false writes an app.bsky.feed.postgate record, AFTER the post is created. If that write fails after the post succeeded, the call still succeeds with gateApplied:false and a warning instead of failing.

Output Schema

ParametersJSON Schema
NameRequiredDescription
uriYesAT-URI of the newly created post.
cidYesCID (content hash) of the newly created post.
successYesWhether the post was created successfully.
messageYesHuman-readable status message.
gateAppliedNoPresent only when replyControls and/or quoteControls were requested. True when every requested gate record (threadgate/postgate) is in effect. False when the post was created but a gate write failed — the post is LIVE without the requested controls (success stays true; see `warning` for which gate failed and how to retry).
warningNoPresent only when gateApplied is false: explains which gate record (threadgate and/or postgate) could not be written and how to retry. The post itself was created successfully.

TDQS

A4.8/5.0
Behavior5/5

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

The description discloses important behavioral traits beyond annotations: the post is public, subject to rate limiting, requires auth, and creates additional records (threadgate, postgate) for reply/quote controls. It also explains side effects and error handling for gate write failures.

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?

The description is well-structured with a clear opening, a list of supported features, side effects, and alternatives. It is somewhat lengthy due to the tool's complexity, but every sentence contributes meaningful information. Slightly more conciseness could be achieved by reducing redundancy.

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?

Given the tool's complexity (8 parameters, nested objects, output schema present), the description covers all necessary aspects: main functionality, usage modes, side effects, preconditions, and error handling. It leaves no significant gaps for an agent to misinterpret.

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

Parameters4/5

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

Schema coverage is 100%, so the baseline is 3. The description adds value by explaining mutual exclusivity among embed types, auto-detection of facets, and the relationship between reply controls and threadgate records. This provides context that enriches the schema descriptions.

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 description clearly states the tool's purpose: 'Create a new post on AT Protocol (Bluesky).' It explicitly distinguishes itself from sibling tools like create_thread and reply_to_post by stating when to use each.

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 provides explicit guidance on when to use this tool versus alternatives: 'Use create_thread to publish a multi-post chain... and reply_to_post to reply...; use this tool for a single standalone post.' It also mentions prerequisites (authentication, app password) and side effects.

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

create_threadA

Create a thread of 2–25 posts in a single operation, automatically chaining each post as a reply to the previous one so readers see them as a continuous conversation. Use this instead of repeated create_post calls when content spans multiple posts; use create_post for a single standalone post or reply_to_post to append to an existing thread. Optional replyControls gate who can reply (a threadgate record on the ROOT post, written after the whole thread is published). Requires authentication (app password). If publishing fails mid-thread, already-published posts are returned with a failedAtPosition indicator so you can delete or resume them. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
postsYesOrdered array of posts to publish as a thread (2–25 items). Each post is automatically chained as a reply to the previous one.
langsNoDefault language tags (BCP-47) applied to every post in the thread. Individual posts can override this with their own langs field.
replyControlsNoWho can reply to the thread. Applies to the ROOT post only: a single app.bsky.feed.threadgate record is written with the root post’s rkey, AFTER every post in the thread is published (the in-thread replies are your own posts, created before the gate exists, so they are unaffected). Enabled options combine, up to 5 rules; provide the object with NO rules enabled to let nobody reply; omit it to leave replies open. If the gate write fails after the posts succeeded, the call still succeeds with gateApplied:false and a warning instead of failing.

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYesTrue when all posts were published; false when only some posts were created before a failure.
messageYesHuman-readable summary of the outcome, including partial-failure details when applicable.
threadYesOrdered list of every post that was successfully published.
rootPostYesURI and CID of the root (first) post, which anchors the entire thread.
totalPostsYesNumber of posts actually published (may be less than requested if an error occurred mid-thread).
failedAtPositionNo1-based position of the post that failed; present only when success is false.
gateAppliedNoPresent only when replyControls were provided. True when the threadgate record was written on the root post (it is written after the whole thread is published, and is still attempted on the live root if the thread partially fails). False when the posts were published but the gate write failed — replies are then OPEN; see `warning`.
warningNoPresent only when gateApplied is false: explains that the threadgate write failed and how to retry. The published posts themselves are unaffected.

TDQS

A4.9/5.0
Behavior5/5

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

Adds significant behavioral context beyond annotations: auto-chaining, partial failure handling with failedAtPosition, threadgate writing after publication, and rate limiting. No contradiction with annotations.

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

Conciseness5/5

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

Concise paragraph with front-loaded purpose. Every sentence adds value – no redundancy.

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?

Comprehensive for a tool with 3 params and nested objects. Covers partial failure, authentication, rate limiting, and sibling differentiation. Output schema exists, so return values are covered.

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

Parameters4/5

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

Schema has 100% description coverage, so baseline is 3. The description adds extra semantics like chaining behavior, replyControls timing, and failure handling, earning a 4.

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 description clearly states it creates a thread of 2-25 posts, auto-chaining them, and distinguishes from create_post and reply_to_post.

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?

Explicitly states when to use this tool versus alternatives (create_post for single posts, reply_to_post for appending to existing threads). Also mentions authentication and optional replyControls.

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

delete_postA
Destructive

Delete a post on AT Protocol. Permanently removes the post from the authenticated user's repository; this action cannot be undone. Use this instead of create_post/reply_to_post when you need to remove existing content. Requires authentication (app password). Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
uriYesAT-URI of the post to delete (at://did/app.bsky.feed.post/rkey). Must belong to the authenticated user.

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYesWhether the post was successfully deleted.
messageYesHuman-readable result message.
deletedPostYesInformation about the deleted post.

TDQS

A4.7/5.0
Behavior4/5

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

Reinforces destructiveHint by noting 'Permanently removes... cannot be undone'. Adds context about authentication and rate limiting not covered by annotations. No contradiction.

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

Conciseness5/5

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

Two short, front-loaded sentences with no waste. First sentence states purpose, second provides usage and behavioral context.

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?

Complete given the simple input (1 required param with full schema coverage) and presence of output schema. Covers purpose, usage, behavior, and constraints.

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

Parameters4/5

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

Schema coverage is 100%, baseline 3. Description adds the constraint that the URI must belong to the authenticated user, providing extra clarity beyond the schema's description.

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?

Clearly states 'Delete a post on AT Protocol', specifying the verb and resource. Distinguishes from siblings like create_post and reply_to_post by noting its use for removal.

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?

Explicitly states 'Use this instead of create_post/reply_to_post when you need to remove existing content', providing clear when-to-use guidance. Also mentions authentication and rate limiting.

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

discoverA
Read-only

Surface content from your own home timeline. Requires authentication (app password). Read-only: performs no writes. Two timeline-driven discovery modes. For finding accounts similar to a given user use find_similar_users; for topic-based communities use discover_communities. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
modeYesWhat to surface from your timeline: 'trending' = trending topics/hashtags; 'recommended' = posts you're likely to engage with.
limitNoHow many items to return. mode=trending: items returned PER category (hashtags/topics/posts), default 10, values above 25 are capped at 25; a fixed sample of 100 timeline posts is analyzed regardless. mode=recommended: number of recommended posts returned (1–100, default 20).
timeWindowNoLookback window. Only used when mode=trending (default 24h).
includeHashtagsNoInclude trending hashtags. Only used when mode=trending (default true).
includeTopicsNoInclude trending topics/keywords. Only used when mode=trending (default true).
includePostsNoInclude notable trending posts. Only used when mode=trending (default true).
actorNoOptional account (handle or DID) to tailor recommendations to: its recent author feed seeds the interest profile (topics and authors) used for scoring. Only used when mode=recommended; defaults to inferring interests from the authenticated user’s own timeline engagement.
topicsNoRestrict recommendations to posts matching these topic keywords. Only used when mode=recommended.
minLikesNoMinimum like count for a recommended post. Only used when mode=recommended (default 5).
maxAgeNoMaximum post age in hours for recommendations. Only used when mode=recommended (default 24).
excludeRepostsNoExclude reposts from recommendations. Only used when mode=recommended.

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYesWhether the discovery run completed successfully.
modeYesWhich discovery mode was run; determines which other fields are present.
timeWindowNoLookback window that was analyzed. Present when mode=trending.
trendingHashtagsNoTrending hashtags ranked by count and recent growth (empty when includeHashtags is false). Present when mode=trending.
trendingTopicsNoTrending topic keywords ranked by engagement (empty when includeTopics is false). Present when mode=trending.
trendingPostsNoNotable posts ranked by engagement with a recency boost (empty when includePosts is false). Present when mode=trending.
summaryNoSummary of the analyzed timeline sample. Present when mode=trending.
recommendationsNoRecommended posts sorted by descending recommendationScore. Present when mode=recommended.
insightsNoHuman-readable observations about the recommendations (or advice when none matched). Present when mode=recommended.

TDQS

A4.9/5.0
Behavior5/5

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

The description adds value beyond the annotations: it states authentication requirement (app password), mentions per-tool rate limiting, and describes the two discovery modes. This is consistent with the readOnlyHint and openWorldHint annotations and provides additional behavioral context.

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

Conciseness5/5

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

The description is concise and front-loaded: it starts with the core purpose, then adds key details (auth, read-only, modes, alternatives, rate limiting). Every sentence is necessary and well-structured.

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?

Given the tool's complexity (11 parameters, 1 required, output schema exists), the description is complete. It covers purpose, authentication, read-only, mode differentiation, sibling alternatives, and rate limiting. The input schema provides detailed parameter info, and the output schema is present, so no further return value explanation is needed.

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

Parameters4/5

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

Schema documentation coverage is 100%, so baseline is 3. The description adds value by explaining the two modes and how parameters relate to each mode (e.g., 'limit' behavior differs by mode). This extra context helps the agent understand parameter usage beyond individual schema descriptions.

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 description clearly states the tool's purpose: 'Surface content from your own home timeline.' It specifies it is read-only, requires authentication, and offers two modes. It distinguishes from siblings by explicitly naming 'find_similar_users' and 'discover_communities' as alternatives.

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 provides explicit when-to-use and when-not-to-use guidance: it tells the agent to use find_similar_users for similar accounts and discover_communities for topic-based communities. It also notes authentication requirements and read-only nature.

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

discover_communitiesA
Read-only

Discover communities and groups of users around specific topics or interests by searching recent posts and clustering authors who interact with each other. Works without authentication; richer with auth. Use this instead of find_similar_users when you want topic-based community clusters rather than accounts structurally similar to a specific user. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
topicYesKeyword or phrase to search for (e.g. "climate", "web dev"). Used to retrieve relevant posts and identify active community clusters.
maxResultsNoMaximum number of communities to return (1–50, default 20).
minCommunitySizeNoMinimum number of distinct members required for a cluster to be reported as a community (minimum 2, default 5).
includeMetricsNoWhen true, includes a metrics object on each community with avgFollowerCount, totalPosts, and interconnectedness values (default true).

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYesWhether the operation completed successfully.
communitiesYesList of discovered communities sorted by size and engagement (descending).
insightsYesSummary observations about the discovered communities (e.g. total members, largest community).

TDQS

A4.7/5.0
Behavior5/5

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

Discloses the reading behavior (clustering posts/authors) which aligns with annotations (readOnlyHint). Adds context about authentication and rate limits, going beyond the annotations to inform the agent of constraints and richer functionality.

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

Conciseness5/5

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

The description is four concise sentences with no redundant information. Each sentence serves a distinct purpose: stating the primary function, authentication, differentiation from a sibling, and rate limiting. It is front-loaded with the core action.

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?

Given the presence of an output schema (no need to describe returns), the description covers the tool's behavior, authentication, rate limiting, and relationship to a sibling tool. It is complete for a tool of this complexity with good annotations.

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%, so the baseline is 3. The tool description does not add new parameter details beyond what the schema provides, but it provides overall context for how parameters ('topic') are used (searching posts and clustering authors), which is adequate.

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 description clearly states the tool discovers topic-based communities by searching posts and clustering interacting authors. It distinguishes itself from the sibling 'find_similar_users' by specifying when to use each, making the purpose unambiguous.

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?

Explicitly mentions authentication requirements ('Works without authentication; richer with auth'), provides a condition for use ('Use this instead of find_similar_users when you want topic-based community clusters'), and notes rate limiting ('Subject to per-tool rate limiting').

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

find_influential_usersA
Read-only

Find influential users in a topic or network by searching recent posts and ranking their authors. Works without authentication; richer with auth. Read-only — produces no side effects. Use this instead of find_similar_users when you want topic-driven discovery of high-reach accounts rather than a specific user's social graph. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
topicNoTopic keyword(s) to search for (e.g. "climate change"). Used as the search query when searchQuery is not provided.
searchQueryNoExplicit search query string. When provided, takes precedence over topic. At least one of topic or searchQuery must be supplied.
minFollowersNoMinimum follower count a user must have to be included in results (default 100).
maxResultsNoMaximum number of users to return (1–50, default 20).
sortByNoSort order for results: "followers" (by follower count), "engagement" (by computed influence score), or "relevance" (by how many matched posts are from that user). Default "followers".followers

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYesWhether the operation completed successfully.
queryYesThe search query that was used.
usersYesList of influential users found, sorted per the sortBy parameter.
insightsYesHuman-readable insight strings summarising the results.

TDQS

A4.2/5.0
Behavior4/5

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

Annotations already declare readOnlyHint=true; description reinforces with 'Read-only — produces no side effects' and adds rate limiting note. No contradictions, and adds context about auth modes.

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

Conciseness5/5

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

Three sentences, each adding value: purpose, auth/read-only note, usage comparison, rate limit. Front-loaded and no fluff.

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

Completeness4/5

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

Given output schema exists, description covers usage, safety, and rate limiting. Does not detail output structure or data freshness, but sufficient for a read-only tool with good annotations.

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 coverage is 100%, so baseline is 3. Description does not add extra meaning beyond schema; it lists no parameter details but provides general context.

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?

Clearly uses verb 'Find' with specific resource 'influential users' and method 'searching recent posts and ranking their authors'. Distinguishes from sibling tool find_similar_users by highlighting topic-driven discovery vs social graph.

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

Usage Guidelines4/5

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

Explicitly states when to use this tool over find_similar_users and notes authentication requirements (works without, richer with). Does not explicitly state when not to use, but provides clear context.

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

find_similar_usersA
Read-only

Find users similar to a given user based on shared follow-graph connections (second-degree follows and accounts that follow the base user) and follower/following-ratio similarity. Content-topic similarity is NOT analyzed. Works without authentication; richer with auth. Use this instead of search_actors when you want accounts structurally similar to a known user rather than keyword matches. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
actorYesHandle (e.g. alice.bsky.social) or DID of the target account to find similar users for.
maxResultsNoMaximum number of similar users to return (1–50, default 20).
minFollowerCountNoMinimum follower count a candidate must have to be included in results (default 0, meaning no minimum).
includeMetricsNoWhen true, includes a metrics object on each result with followsBaseUser and followerRatioSimilarity values (default true).

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYesWhether the operation completed successfully.
similarUsersYesList of similar users sorted by descending similarity score.
baseUserYesProfile summary of the queried base user.
insightsYesSummary observations about the results (e.g. average similarity score, average follower count).

TDQS

A4.9/5.0
Behavior5/5

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

Beyond annotations (readOnlyHint, openWorldHint), the description discloses that content-topic similarity is NOT analyzed, adding important behavioral context. No contradictions with annotations.

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

Conciseness5/5

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

Three concise sentences, each serving a distinct purpose: algorithm definition, exclusion note, and usage guidance. No unnecessary words.

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?

Given an output schema exists, the description covers purpose, algorithm, limitations, authentication, and alternatives comprehensively. No missing context for effective use.

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

Parameters4/5

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

Schema coverage is 100% with good parameter descriptions. The description adds algorithmic context (second-degree follows, follower ratio) that enhances interpretation of parameters, though it does not describe each parameter individually beyond schema.

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 description clearly states the tool finds users similar to a given user based on share follow-graph connections, specifying the mechanism (second-degree follows, follower/following ratio) and explicitly distinguishing from search_actors.

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?

Provides explicit when-to-use guidance: 'Use this instead of search_actors when you want accounts structurally similar to a known user rather than keyword matches.' Also notes authentication effects and rate limiting.

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

follow_userA
Idempotent

Follow a user on AT Protocol. Creates a follow record for the specified account; if the account is already followed the existing follow URI is returned without creating a duplicate. Requires authentication (app password). Use unfollow_user to reverse this action; for bulk relationship changes consider batch_action instead. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
actorYesHandle (e.g. alice.bsky.social) or DID of the account to follow.

Output Schema

ParametersJSON Schema
NameRequiredDescription
uriYesAT-URI of the follow record (at://did/app.bsky.graph.follow/rkey).
cidYesCID of the follow record; empty string when the follow already existed.
successYesWhether the follow is now in effect (true even if already followed).
messageYesHuman-readable outcome message.
followedUserYesIdentifying information for the account that was followed.

TDQS

A4.7/5.0
Behavior5/5

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

Adds value beyond annotations: duplicates return existing URI without creating new record, requires authentication app password, subject to rate limiting. Consistent with idempotentHint=true.

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

Conciseness5/5

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

Three focused sentences: action description, duplicate handling, usage guidance. No fluff.

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?

Completely covers the behavior, idempotency, auth, rate limits, and alternative tools for a simple follow action with one parameter.

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 coverage 100% and parameter description already explains 'actor' as Handle or DID. Description adds no new parameter information beyond schema.

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?

Clear verb ('Follow') and resource ('a user on AT Protocol') with precise description of creating a follow record. Distinguishes from sibling tools 'unfollow_user' and 'batch_action'.

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?

Explicitly states when to use (follow a user), when not (if already followed, no duplicate), and alternatives (unfollow_user, batch_action). Includes authentication and rate limit context.

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

get_author_feedA
Read-only

Retrieve posts by a specific AT Protocol user. Works without authentication; richer with auth. Lists a specific user's posts. Differs from get_timeline (your home feed) and search_posts (query-based search). Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
actorYesHandle or DID of the account whose posts to list.
limitNoMax posts per page (1–100, default 50).
cursorNoPagination cursor from a previous response.
filterNoWhich of the author's posts to include (default posts_with_replies).

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYesWhether the request succeeded.
postsYesPosts by the specified author for this page.
cursorNoOpaque cursor for the next page; absent when there are no more results.

TDQS

A4.2/5.0
Behavior4/5

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

Annotations already declare readOnlyHint and openWorldHint. The description adds value by stating the tool works without authentication and is subject to rate limiting, which are behavioral traits beyond what annotations provide.

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

Conciseness5/5

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

The description is four sentences, each serving a distinct purpose: purpose, auth behavior, scope, and differentiation with rate limit. It is front-loaded and contains no unnecessary words.

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

Completeness4/5

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

Given the 4 parameters (100% schema coverage) and presence of an output schema, the description covers necessary context: auth behavior, differentiation, and rate limiting. It is largely complete, though could mention pagination briefly, but cursor is in schema.

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 coverage is 100%, so baseline is 3. The description does not add additional meaning to any parameter beyond what is already in the schema. It mentions 'Lists a specific user's posts' but that is about tool scope, not specific parameter semantics.

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 description clearly states 'Retrieve posts by a specific AT Protocol user,' using a specific verb and resource. It also distinguishes from siblings 'get_timeline' and 'search_posts' by explaining their different purposes.

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

Usage Guidelines4/5

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

The description provides context on authentication behavior ('Works without authentication; richer with auth') and explicitly contrasts with two sibling tools. It does not give explicit when-not-to-use guidance but sufficiently implies usage context.

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

get_bookmarksA
Read-only

List the authenticated account's private bookmarks (app.bsky.bookmark.getBookmarks) with cursor pagination. Each entry carries the full bookmarked post view when the post is still viewable, or an unavailableReason (blocked / not_found) when it is not. Requires authentication (app password). Use add_bookmark / remove_bookmark to manage bookmarks. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMax bookmarks per page (1–100, default 50).
cursorNoOpaque pagination cursor from the previous response cursor field; omit for the first page.

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYesWhether the request succeeded.
bookmarksYesThe bookmarks for this page.
cursorNoOpaque cursor for the next page; absent when there are no more results.
hasMoreYesWhether additional pages of results are available.

TDQS

A4.5/5.0
Behavior5/5

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

The description adds value beyond annotations: explains read-only operation, authentication requirements, rate limiting, cursor pagination, and unavailable reasons. No contradiction with annotations (readOnlyHint, openWorldHint).

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

Conciseness5/5

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

Three sentences, no wasted words. First sentence states purpose, second details return behavior, third covers authentication and sibling tools. Well-structured and efficient.

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?

With output schema present, return values need no explanation. Description covers pagination, authentication, rate limiting, and error states (unavailable reasons). Fully complete for a read-only listing tool.

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 coverage is 100% with parameter descriptions already in the schema. The description mentions cursor pagination but adds no new meaning beyond what the schema provides. Baseline 3 is appropriate.

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 description clearly states 'List the authenticated account's private bookmarks' with a specific verb and resource. It also distinguishes from sibling tools add_bookmark and remove_bookmark.

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

Usage Guidelines4/5

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

Provides context for when to use (list bookmarks) and mentions management via siblings. While it doesn't explicitly state when not to use, the pagination, authentication, and rate-limit discussion offer sufficient guidance.

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

get_conversation_messagesA
Idempotent

Read the messages of a Bluesky direct-message conversation (chat.bsky.convo.getMessages, proxied to the bsky.chat service), newest first with cursor pagination. Read-only by default: the conversation is only marked read (unread count cleared) when markRead is true. Requires authentication with an app password created with "Allow access to your direct messages" enabled. Use list_conversations to find conversation ids and send_direct_message to reply. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
convoIdYesId of the conversation to read, from list_conversations or a send_direct_message response.
limitNoMax messages per page (1-100, default 50). Messages are returned newest first.
cursorNoOpaque pagination cursor from the previous response cursor field; omit for the newest page.
markReadNoWhen true, also mark the conversation read (chat.bsky.convo.updateRead) after fetching, clearing its unread count. Default false: reading is side-effect free.

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYesWhether the request succeeded.
convoIdYesId of the conversation that was read.
messagesYesThe messages for this page, newest first. Deleted messages keep their id/sender/timestamp but have no text.
cursorNoOpaque cursor for the next (older) page; absent when there are no more messages.
hasMoreYesWhether another (older) page is available (a cursor was returned).
markedReadYesWhether the conversation was marked read by this call (markRead=true).

TDQS

A4.9/5.0
Behavior5/5

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

Annotations (destructiveHint=false, idempotentHint=true) are consistent. Description adds: read-only default, markRead effect (clears unread count), auth requirement, rate limiting. No contradiction.

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

Conciseness5/5

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

Single paragraph, every sentence provides essential information. Front-loaded with main action, no superfluous text.

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?

Covers all aspects: purpose, pagination, side effect, auth, sibling tools, rate limiting. Output schema exists, so return values need not be detailed. Fully complete.

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

Parameters4/5

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

Schema coverage is 100%, baseline 3. Description adds meaningful context: convoId source, markRead side effect, cursor default behavior, limit ordering. Adds value beyond schema.

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?

Clear verb (read) and resource (messages of a Bluesky direct-message conversation). Differentiates from siblings list_conversations (to find IDs) and send_direct_message (to reply).

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?

Explicitly states when to mark read (only when markRead=true), prerequisites (app password with DM access), and when to use siblings (list_conversations, send_direct_message). Also mentions rate limiting.

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

get_custom_feedA
Read-only

Get posts from a custom algorithm feed. Works without authentication; richer with auth. Returns feed generator metadata and a paginated list of posts; use the returned cursor to fetch subsequent pages. Use get_timeline for the authenticated user's home feed instead. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
feedUriYesAT-URI of the custom algorithm feed generator (at://did/app.bsky.feed.generator/rkey).
limitNoMax posts to return per page (1–100, default 50).
cursorNoOpaque pagination cursor from the previous response cursor field; omit for the first page.

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYesWhether the feed was retrieved successfully.
feedYesMetadata about the feed generator.
postsYesPaginated posts from the feed.
cursorNoPagination cursor for the next page; absent when no more pages.

TDQS

A4.7/5.0
Behavior5/5

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

Beyond annotations (readOnlyHint=true, openWorldHint=true), the description adds meaningful behavioral traits: authentication variability, pagination cursor mechanism, return structure (feed generator metadata + posts), and rate limits. No contradictions with annotations.

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

Conciseness5/5

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

Four concise sentences, front-loaded with the core purpose, no redundant or irrelevant information. Every sentence contributes distinct value (purpose, auth, pagination, sibling distinction, rate limit).

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?

Covers all essential aspects for selecting and invoking this tool: purpose, authentication, pagination, sibling differentiation, and rate limiting. With an output schema present, the description does not need to detail return values further.

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 coverage is 100%, so the schema already documents 'feedUri', 'limit', and 'cursor' thoroughly. The description adds the cursor pagination hint but otherwise doesn't exceed the schema's parameter descriptions, justifying the baseline score of 3.

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 description clearly states the tool retrieves posts from a custom algorithm feed, specifying the resource ('custom algorithm feed') and action ('get posts'). It explicitly distinguishes from the sibling 'get_timeline' by naming the alternative use case.

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?

Provides explicit guidance on when to use this tool ('use get_timeline for the authenticated user's home feed instead') and notes authentication levels ('works without authentication; richer with auth'). Also mentions rate limiting, helping the agent decide contextually.

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

get_listA
Read-only

Get the contents of a list, including all users in the list. Works without authentication; richer with auth. Returns list metadata plus a paginated array of member profiles; use the returned cursor to fetch subsequent pages. Use add_to_list / remove_from_list to modify membership. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
listUriYesAT-URI of the list to read (at://did/app.bsky.graph.list/rkey).
limitNoMax list members to return per page (1–100, default 50).
cursorNoOpaque pagination cursor from the previous response cursor field; omit for the first page.

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYesWhether the list was retrieved successfully.
listYesMetadata about the list.
itemsYesPaginated list of member entries.
cursorNoPagination cursor for the next page; absent when no more pages.

TDQS

A4.7/5.0
Behavior4/5

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

Annotations indicate read-only and open world. Description adds pagination behavior with cursor, auth expectations, and mentions per-tool rate limiting. Slightly vague on rate limiting details but sufficient.

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

Conciseness5/5

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

Three sentences, each delivering unique information: purpose, auth/pagination, and modification alternative. No fluff. Front-loaded with key purpose.

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?

Covers all key aspects: purpose, auth, pagination, modification alternatives, rate limiting. Output schema exists so return values not needed. Highly complete for a retrieval tool.

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

Parameters4/5

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

Schema has 100% coverage, so the description adds value by explaining the pagination flow ('use the returned cursor to fetch subsequent pages') and the default/range for limit is in schema. No contradictions.

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?

Clearly states 'Get the contents of a list, including all users in the list.' Uses specific verb 'get' and identifies the resource 'list' with scope 'contents and users'. Distinguishes from sibling tools that modify membership.

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?

Explicitly tells when to use ('Get the contents...') and when not ('Use add_to_list / remove_from_list to modify'). Also notes authentication behavior ('Works without authentication; richer with auth').

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

get_notificationsA
Read-only

Retrieve notifications from AT Protocol (likes, reposts, follows, mentions, replies). Requires authentication (app password). Use countOnly: true to fetch only the unread badge count cheaply without loading the full list; use mark_notifications_seen to clear the unread state after processing. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMax notifications per page (1–100, default 50).
cursorNoOpaque pagination cursor from the previous response cursor field; omit for the first page.
seenAtNoISO 8601 timestamp; only return notifications that occurred after this time. Optional filter.
countOnlyNoWhen true, return only the unread count and skip fetching the notification list (cheap badge-number path).

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYesWhether the request succeeded.
unreadCountYesNumber of unread notifications (always present).
notificationsNoNotification entries. Present only when countOnly is false/absent.
cursorNoOpaque cursor for the next page. Present only when countOnly is false/absent.
hasMoreNoTrue when a next page is available. Present only when countOnly is false/absent.
seenAtNoThe timestamp up to which notifications have been seen. Present only when countOnly is false/absent.

TDQS

A4.7/5.0
Behavior5/5

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

Beyond annotations (readOnlyHint, openWorldHint), the description adds value: requires auth, subject to rate limiting, countOnly skips full list. No contradiction with annotations.

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

Conciseness5/5

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

Three sentences, each purposeful, front-loaded with main action. No wasted words.

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 read-only list tool with 4 parameters (all documented), annotations, and output schema, the description covers auth, rate limits, special logic (countOnly), and sibling reference. No gaps.

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 coverage is 100% with detailed parameter descriptions. The description reinforces countOnly but adds no new parameter semantics. Baseline 3 is appropriate.

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 description starts with a clear verb ('Retrieve') and specific resource ('notifications from AT Protocol'), listing types (likes, reposts, etc.). It distinguishes itself from siblings like mark_notifications_seen by explicitly mentioning it.

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?

Provides explicit guidance: when to use countOnly for cheap badge count, when to use mark_notifications_seen to clear state. Also notes authentication and rate limiting, helping the agent decide context.

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

get_post_contextA
Read-only

Get comprehensive post information in a single call. Single post reader: use include* flags for thread, author, engagement, and media. Replaces the former get_thread and extract_media_from_post tools. Works without authentication; richer with auth. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
uriYesAT-URI of the post to read (at://...).
includeThreadNoInclude thread context (parent chain, root, and replies). Default true.
includeAuthorProfileNoInclude the post author's full profile. Default true.
includeEngagementNoInclude computed engagement metrics (likes, reposts, replies, rate, age). Default true.
depthNoHow many levels of replies to fetch (0–10, default 6).
parentHeightNoHow many parent posts up the chain to fetch (0–80, default 80).
includeMediaNoExtract media embeds (images, videos, external links, quote posts) from the post. Default false.

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYesWhether the post context was retrieved.
postYesThe requested post (normalized post view).
threadNoThread context (present when includeThread is true): the immediate parent, the true thread root, the direct replies, and the parent-chain depth.
authorProfileNoThe post author's full profile (present when includeAuthorProfile is true).
engagementNoComputed engagement metrics (present when includeEngagement is true).
mediaNoMedia embeds extracted from the post (present when includeMedia is true).

TDQS

A4.5/5.0
Behavior4/5

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

Annotations already declare readOnlyHint and openWorldHint. Description adds behavioral details: works without authentication but richer with auth, subject to per-tool rate limiting, and that it replaces older tools. This provides useful context beyond the annotations, though no contradictions.

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

Conciseness5/5

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

Description is front-loaded with the core purpose. Five short sentences each add unique value: purpose, usage pattern, replacement info, auth behavior, and rate limiting. No wasted words.

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?

Given the tool's complexity (7 parameters, 1 required), presence of output schema, and annotations, the description covers key aspects: what it does, how to use flags, auth implications, rate limits, and relationship to previous tools. It is fully sufficient for an agent to invoke correctly.

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

Parameters4/5

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

Schema description coverage is 100%, so the schema fully documents parameters. The description adds value by summarizing the include* flags and noting the default behaviors (e.g., 'Default true' for include flags). It does not repeat all schema details, but the additive context is helpful.

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?

Description clearly states 'Get comprehensive post information in a single call' and 'Single post reader', with specific mention of include* flags for thread, author, engagement, and media. It explicitly replaces former tools get_thread and extract_media_from_post, distinguishing itself effectively from siblings.

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

Usage Guidelines4/5

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

Description provides clear context for when to use: 'Get comprehensive post information in a single call' and mentions it replaces previous tools. It also notes authentication options and rate limiting. However, it does not explicitly list when not to use this tool versus other sibling tools like get_author_feed or search_posts, leaving some ambiguity.

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

get_starter_packA
Read-only

Fetch a Bluesky starter pack (app.bsky.graph.getStarterPack) by AT-URI or bsky.app link, returning its name, description, creator, reference-list info, a sample of member profiles, included feeds, and joined counts. Works without authentication. Use search_starter_packs to discover packs by keyword, and get_list to page through the full member list. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
starterPackYesStarter pack reference: an AT-URI (at://did/app.bsky.graph.starterpack/rkey) or a bsky.app link (https://bsky.app/starter-pack/{handle-or-did}/{rkey}). go.bsky.app short links are not supported — open one in a browser and use the resulting bsky.app URL.

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYesWhether the request succeeded.
starterPackYesFull view of the starter pack.

TDQS

A4.7/5.0
Behavior5/5

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

Annotations already declare readOnlyHint and openWorldHint. Description adds that no authentication is required, is subject to rate limiting, and specifies exact return fields. No contradiction with annotations.

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

Conciseness5/5

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

Single paragraph, three sentences. First sentence defines core action and return values, second provides usage context, third adds constraints. No redundant information.

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?

Given the simple single-parameter tool with an output schema, the description covers purpose, parameters, usage boundaries, and practical limitations (rate limiting, short link issue). Complete for selection and invocation.

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%, so baseline is 3. The description repeats the same parameter info from the schema (AT-URI or bsky.app link, unsupported short links) without adding new meaning beyond what the schema provides.

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?

Clearly states the tool fetches a Bluesky starter pack by AT-URI or bsky.app link, and lists the returned data (name, description, creator, etc.). Differentiates from siblings like search_starter_packs and get_list.

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?

Explicitly tells when to use this tool (to fetch a specific pack) and when to use alternatives (search_starter_packs for discovery, get_list for full member list). Also notes it works without authentication and is rate-limited.

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

get_timelineA
Read-only

Retrieve the user's home timeline from AT Protocol, returning posts from followed accounts and algorithmically recommended content. Requires authentication (app password). Use get_author_feed to retrieve posts from a specific user instead of the authenticated user's own feed. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
algorithmNoFeed algorithm or generator AT-URI to use (e.g. "reverse-chronological" or at://did/.../app.bsky.feed.generator/rkey). Omit for the default home timeline algorithm.
limitNoMaximum number of posts to return (1–100, default 50).
cursorNoOpaque pagination cursor from the previous response cursor field; omit for the first page.

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYesWhether the timeline was retrieved successfully.
postsYesList of posts in the timeline.
cursorNoOpaque pagination cursor to pass in a subsequent request to retrieve the next page; absent when there are no more results.
hasMoreYesWhether additional pages of results are available.
algorithmNoThe algorithm or feed generator AT-URI used for this request, if provided.

TDQS

A4.5/5.0
Behavior4/5

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

Annotations already declare readOnlyHint and openWorldHint; description adds authentication requirement (app password) and rate limiting, providing extra context beyond annotations.

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

Conciseness5/5

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

Three concise sentences, front-loaded with key purpose, no wasted words.

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?

Covers all necessary context: resource, authentication, pagination, rate limiting, and differentiation from sibling; output schema exists to handle return values.

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 coverage is 100% with clear descriptions for all three parameters; description does not add significant meaning beyond the schema, so baseline score of 3 applies.

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?

Description uses specific verb 'Retrieve', clearly states resource 'user's home timeline', and distinguishes from sibling tool 'get_author_feed' for specific user feeds.

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?

Explicitly states when to use (home timeline), when not to use (use get_author_feed for specific user), and notes authentication and rate limiting requirements.

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

get_user_connectionsA
Read-only

Retrieve an account's followers or follows from AT Protocol. Works without authentication; richer with auth. Use direction='followers' to get who follows the actor or direction='follows' to get who the actor follows; use get_user_profile or get_user_summary for aggregate counts instead. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
actorYesHandle (e.g. alice.bsky.social) or DID of the account whose connections to list.
directionYesWhich side of the follow graph to return: 'followers' = accounts that follow the actor; 'follows' = accounts the actor follows.
limitNoMax accounts per page (1–100, default 50).
cursorNoOpaque pagination cursor from the previous response cursor field; omit for the first page.

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYesWhether the request succeeded.
actorYesThe actor whose connections were fetched.
directionYesThe direction of the follow graph that was queried.
connectionsYesThe matched accounts for this page.
cursorNoOpaque cursor for the next page; absent when there are no more results.

TDQS

A4.7/5.0
Behavior5/5

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

Beyond annotations (readOnly, openWorld), description adds that auth is optional but enriches results, notes rate limiting, and implies pagination via cursor, offering full behavioral context.

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

Conciseness5/5

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

Two well-structured sentences with front-loaded purpose, minimal waste, and clear separation of guidance.

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?

Given output schema exists, description covers authentication, pagination, rate limiting, and use cases; no gaps for a moderately complex tool.

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 coverage is 100%, so baseline 3 applies. Description reinforces direction parameter and mentions rate limiting but adds no new parameter-level detail beyond schema.

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?

Clearly specifies the tool retrieves followers or follows from AT Protocol, and distinguishes from siblings like get_user_profile and get_user_summary by noting they are for aggregate counts.

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?

Explicitly states authentication behavior, gives direction parameter usage, and directs users to alternative tools for aggregate counts, providing clear when-to and when-not-to guidance.

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

get_user_profileA
Read-only

Retrieve a user profile from AT Protocol, returning handle, display name, bio, avatar, banner, follower/following/post counts, and applied labels. Works without authentication; richer with auth — authenticated calls also return viewer-specific fields (following, followedBy, muted, blocking, blockedBy). Use get_user_summary for a condensed overview or get_user_connections for follower/following lists. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
actorYesHandle (e.g. alice.bsky.social) or DID (e.g. did:plc:...) of the account whose profile to retrieve.

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYesWhether the profile was retrieved successfully.
profileYesFull profile record for the requested account.

TDQS

A4.7/5.0
Behavior5/5

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

Annotations already declare readOnlyHint and openWorldHint. Description adds behavioral details beyond annotations: lists exact fields returned, explains that authenticated calls include viewer-specific fields (following, followedBy, muted, blocking, blockedBy), and mentions rate limiting. No contradiction; adds value.

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

Conciseness5/5

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

Three sentences, each earning its place: first sentence states core function and output, second adds authentication nuance, third provides alternatives and rate limit info. Front-loaded with essential info, no redundancy.

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 simple read tool with one required param and an output schema, the description covers all key aspects: purpose, return fields, auth behavior, alternatives, and rate limiting. No gaps. Sibling tools are numerous but differentiated well.

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 coverage is 100% with a clear description of the 'actor' parameter (handle or DID). Description does not add additional semantic meaning beyond what the schema already provides. Baseline 3 is appropriate since the schema does the heavy lifting.

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?

Description clearly states the tool retrieves a user profile from AT Protocol and lists key fields (handle, display name, bio, etc.). It distinguishes itself from siblings by noting auth-dependent richness and explicitly names alternatives (get_user_summary, get_user_connections). The verb 'retrieve' plus resource 'user profile' is specific.

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?

Provides explicit guidance: works without auth but is richer with auth; recommends get_user_summary for condensed overview and get_user_connections for follower/following lists. Also mentions rate limiting. Tells when and when not to use this tool effectively.

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

get_user_summaryA
Read-only

Get comprehensive user information in a single call, combining profile, recent posts, and engagement statistics. Works without authentication; richer with auth. Use get_user_profile for just the profile or get_author_feed for posts alone; use this tool when you need both in one round-trip. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
actorYesHandle (e.g. alice.bsky.social) or DID of the target account.
includeRecentPostsNoWhether to include recent posts in the response. Default true.
postLimitNoNumber of recent posts to fetch (1–50, default 10). Only used when includeRecentPosts or includeEngagementStats is true.
includeEngagementStatsNoWhether to compute engagement statistics (avg likes, reposts, replies) over the recent posts. Default true.

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYesWhether the summary was retrieved successfully.
profileYesThe user's full profile, including optional viewer context when authenticated.
recentPostsNoRecent posts (present when includeRecentPosts is true).
engagementStatsNoEngagement statistics computed over the fetched posts (present when includeEngagementStats is true).
summaryYesCondensed key metrics for quick consumption.

TDQS

A4.5/5.0
Behavior4/5

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

Annotations already declare readOnlyHint=true (non-destructive) and openWorldHint=true (variable results). Description adds behavior beyond annotations: mentions rate limiting and authentication sensitivity, which are useful for agent decision-making.

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

Conciseness5/5

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

Two concise sentences that front-load the purpose, then provide usage guidelines and behavioral notes. Every sentence adds value with no wasted words.

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?

Given 4 parameters with 100% schema coverage, presence of output schema, and comprehensive annotations, the description is fully adequate. It covers purpose, alternatives, authentication behavior, and rate limiting, leaving no gaps for agent comprehension.

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%, so baseline is 3. The description does not add significant meaning beyond what the schema provides; it mentions authentication context but does not elaborate on parameter usage or dependencies beyond schema.

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 description uses a specific verb ('Get'), identifies the resource ('comprehensive user information'), and distinguishes from sibling tools like get_user_profile and get_author_feed by stating it combines profile, recent posts, and engagement statistics in one call.

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?

Explicitly states when to use ('when you need both in one round-trip'), when not to use (alternatives get_user_profile and get_author_feed), and authentication context ('Works without authentication; richer with auth').

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

like_postA
Idempotent

Like a post on AT Protocol by creating a like record that references the target post. If the post is already liked the existing like is returned without creating a duplicate. Requires authentication (app password). Use unlike_post to remove a like. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
uriYesAT-URI of the post to like (at://did/app.bsky.feed.post/rkey).
cidYesCID of the post record; used to confirm the exact version being liked.

Output Schema

ParametersJSON Schema
NameRequiredDescription
uriYesAT-URI of the newly created (or existing) like record.
cidNoCID of the newly created like record. Absent when the post was already liked (alreadyLiked=true): the existing record is reused and its CID is not re-fetched.
successYesWhether the like operation succeeded.
messageYesHuman-readable status message.
alreadyLikedYesTrue when the post was already liked; the existing like record URI is returned and no duplicate is created.
likedPostYesThe post that was liked.

TDQS

A4.7/5.0
Behavior5/5

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

Discloses idempotency, authentication, and rate limiting beyond annotations. No contradiction with annotations (destructiveHint=false, idempotentHint=true). Adds valuable behavioral context.

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

Conciseness5/5

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

Concise, front-loaded with main action, and every sentence adds value. No wasted words.

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?

Covers authentication, idempotency, rate limiting, and references output schema (even if not shown). Complete for a simple mutation tool with good annotations and schema.

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 coverage is 100%, so baseline is 3. The description does not add extra meaning beyond the schema; it only indirectly references parameters. No new semantic information provided.

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 description clearly states the tool's purpose: 'Like a post on AT Protocol by creating a like record that references the target post.' It uses a specific verb (like) and resource (post), and distinguishes from sibling tools like unlike_post.

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?

Provides explicit when-to-use (liking a post), idempotency behavior (if already liked, returns existing like), alternative (unlike_post), authentication requirement, and rate limiting. Clearly guides the agent.

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

list_conversationsA
Read-only

List the authenticated user's Bluesky direct-message conversations (chat.bsky.convo.listConvos, proxied to the bsky.chat service). Returns each conversation's id, members, unread count, mute state, status (request/accepted) and a last-message preview, plus a pagination cursor. Requires authentication with an app password created with "Allow access to your direct messages" enabled. Use get_conversation_messages to read a conversation and send_direct_message to reply. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMax conversations per page (1-100, default 50).
cursorNoOpaque pagination cursor from the previous response cursor field; omit for the first page.
statusNoFilter by conversation status: 'request' = incoming chat requests not yet accepted; 'accepted' = active conversations. Omit to list both.

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYesWhether the request succeeded.
conversationsYesThe conversations for this page, most recently active first.
cursorNoOpaque cursor for the next page; absent when there are no more results.
hasMoreYesWhether another page is available (a cursor was returned).

TDQS

A4.6/5.0
Behavior5/5

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

Annotations already declare readOnlyHint=true and openWorldHint=true. The description adds authentication details (app password with DM access), rate limiting, and a comprehensive list of return fields (id, members, unread count, mute state, status, last-message preview, cursor). No contradictions.

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?

The description is concise, comprising two sentences with front-loaded purpose. The parenthetical endpoint detail is slightly verbose but not excessive.

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?

Given the tool's complexity (3 optional parameters, output schema present, annotations), the description comprehensively covers purpose, usage context, return format, authentication, rate limiting, and sibling tool references.

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 coverage is 100% and the description does not add additional meaning beyond the schema for the three parameters (limit, cursor, status). The return field mentions cursor and status, but that is output, not parameter semantics. Baseline 3 applies.

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 description clearly states the tool lists the authenticated user's Bluesky direct-message conversations. It specifies the endpoint and service, and distinguishes it from sibling tools like get_conversation_messages and send_direct_message.

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?

Explicit guidance on when to use alternatives: 'Use get_conversation_messages to read a conversation and send_direct_message to reply.' Also mentions authentication requirements.

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

mark_notifications_seenA

Mark notifications as seen up to a timestamp (defaults to now) so they are not reprocessed on subsequent get_notifications calls. Requires authentication (app password). Persists the seen cursor server-side; use get_notifications to retrieve new notifications after calling this. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
seenAtNoISO 8601 timestamp; notifications up to this time are marked seen. Defaults to now.

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYesWhether the operation succeeded.
messageYesHuman-readable confirmation message.
seenAtYesISO 8601 timestamp that was submitted as the seen-up-to marker (the value that was persisted).

TDQS

A4.5/5.0
Behavior4/5

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

Description adds that the action persists the seen cursor server-side and is subject to rate limiting, which supplements annotations (destructiveHint: false, idempotentHint: false, openWorldHint: true). No contradictions.

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

Conciseness5/5

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

Three sentences, each carrying distinct information: purpose, authentication, persistence and usage. No fluff; front-loaded with the core action.

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 simple tool with one optional parameter and an output schema (existence known), the description covers purpose, prerequisites, side effects, rate limiting, and relationship to sibling tool. Complete.

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

Parameters4/5

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

The single parameter seenAt is described in schema as ISO 8601 timestamp defaulting to now. Description reinforces this and clarifies effect. With 100% schema coverage, description adds value by explaining the default behavior.

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?

Description clearly states the tool marks notifications as seen up to a timestamp, distinguishing it from the sibling tool get_notifications (which retrieves notifications). The verb 'mark' and resource 'notifications' are specific.

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

Usage Guidelines4/5

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

Description mentions authentication requirement and per-tool rate limiting, and advises using get_notifications to retrieve new notifications. Provides clear context but does not explicitly state when not to use.

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

mute_userA
Idempotent

Mute a user to hide their content from your feeds and notifications without them knowing. The muted account is not notified. Requires authentication (app password). Use mute_user for a soft, private suppression; use block_user when you need to prevent the target from seeing your content or interacting with you. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
actorYesHandle (e.g. alice.bsky.social) or DID of the account to mute.

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYesWhether the mute operation succeeded.
messageYesHuman-readable result message.
mutedUserYesDetails of the muted account.

TDQS

A4.7/5.0
Behavior5/5

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

Discloses key behaviors: muted user is not notified, requires authentication, subject to rate limiting, and idempotent (consistent with idempotentHint). No contradiction with annotations (destructiveHint=false, idempotentHint=true, openWorldHint=true).

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

Conciseness5/5

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

Two sentences that are front-loaded and efficient, covering behavior, comparison to sibling, and required context (auth, rate limits). No unnecessary words.

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 simple tool with one parameter, an output schema, and annotations, the description fully covers purpose, usage guidelines, and behavioral transparency. No gaps.

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 coverage is 100% with a clear description for the 'actor' parameter. The description adds no additional meaning beyond the schema, meeting the baseline of 3.

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 description clearly states the verb 'mute' and the resource 'user', explaining that it hides content from feeds and notifications without notification. It also distinguishes from block_user, making its purpose unique among siblings.

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?

Explicitly describes when to use mute_user vs block_user with clear criteria: 'mute_user for a soft, private suppression; block_user when you need to prevent target interaction'. Also mentions authentication requirement and rate limiting.

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

remove_bookmarkA
DestructiveIdempotent

Remove a private bookmark from a post on AT Protocol (app.bsky.bookmark.deleteBookmark). Takes the bookmarked post AT-URI — bookmarks are keyed by post URI, there is no separate bookmark-record URI. Removing a bookmark that does not exist succeeds as a no-op. Requires authentication (app password). Use add_bookmark to add and get_bookmarks to list. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
uriYesAT-URI of the bookmarked post (at://did/app.bsky.feed.post/rkey). Bookmarks are keyed by the post URI — pass the post URI itself, not a bookmark-record URI (none exists).

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYesWhether the remove operation succeeded.
messageYesHuman-readable status message.
wasBookmarkedYesTrue when the post was confirmed bookmarked before removal. False when it was not bookmarked, or when the prior state could not be confirmed — in that case the delete is still issued and the server treats deleting a missing bookmark as a no-op.
removedBookmarkYesThe post whose bookmark was removed (or confirmed absent).

TDQS

A4.9/5.0
Behavior5/5

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

Describes idempotent behavior ('removing a bookmark that does not exist succeeds as a no-op'), authentication requirements ('requires authentication (app password)'), and rate limiting. Adds value beyond annotations like destructiveHint and idempotentHint.

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

Conciseness5/5

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

Four sentences, front-loaded with purpose, no redundant information. Efficiently covers usage, behavior, and auth.

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?

Given output schema exists (inferred), description fully explains behavior (no-op), auth needs, and rate limiting. Complete for a simple removal tool with one parameter.

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

Parameters4/5

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

Provides context on the 'uri' parameter beyond schema: clarifies that bookmarks are keyed by post URI, not a bookmark-record URI, and instructs to pass the post URI directly. With 100% schema coverage, this is useful additional guidance.

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?

Clearly states the action (remove a private bookmark) and resource (post bookmark on AT Protocol). Distinguishes from siblings by mentioning add_bookmark and get_bookmarks, providing contrast.

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?

Explicitly says when to use this tool (to remove a bookmark), what happens if bookmark doesn't exist (no-op), and references alternatives (add_bookmark, get_bookmarks).

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

remove_from_listA
DestructiveIdempotent

Remove a user from an existing list. Requires authentication (app password). Deletes the listitem record from the authenticated user's repository; the actor's handle is resolved to a DID and the full list is paged through to locate the record. Use add_to_list to re-add a member or get_list to inspect current members. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
listUriYesAT-URI of the target list (at://did/app.bsky.graph.list/rkey).
actorYesHandle (e.g. alice.bsky.social) or DID of the user to remove from the list.

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYesWhether the user was removed. False when the user was not in the list, or when the list was too large to scan fully (see message).
messageYesHuman-readable result message.
removedFromYesIdentifies the list and actor involved in the operation.

TDQS

A4.2/5.0
Behavior4/5

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

Beyond annotations (destructiveHint, idempotentHint, openWorldHint), the description explains the internal process: resolving the handle to a DID, paging through the list, and deleting the record. It also mentions authentication and rate limiting, adding valuable behavioral context.

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

Conciseness5/5

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

The description is three sentences, front-loaded with the purpose, then the process, then guidance. No unnecessary words; every sentence adds value.

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

Completeness4/5

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

Given the existence of an output schema, the description covers the key aspects: purpose, authentication, process, alternatives, and rate limiting. It might lack error scenarios, but overall it is sufficiently complete for a moderately complex tool.

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%, so parameters are well-documented. The description does not add extra meaning to the parameters beyond what is in the schema; it focuses on the process. Thus, the baseline of 3 is appropriate.

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 description begins with 'Remove a user from an existing list,' which clearly states the verb and resource. It further distinguishes from siblings by mentioning add_to_list and get_list as alternatives, providing differentiation.

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

Usage Guidelines4/5

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

The description explicitly advises using add_to_list to re-add a member and get_list to inspect current members, offering clear context for when to use alternatives. It implies authentication and rate limiting but does not explicitly state when not to use the tool.

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

reply_to_postA

Reply to an existing post on AT Protocol, creating a threaded reply with proper parent and root CID references. Requires authentication (app password). Creates a new post record linked into the thread; the action cannot be undone (use delete_post to remove it afterwards). Use this instead of create_post whenever the new content belongs inside an existing thread; use create_thread to start a multi-post thread from scratch. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
textYesText content of the reply (1–300 graphemes / up to 3000 bytes). Mentions (@handle), links, and hashtags are automatically resolved into rich-text facets.
rootYesAT-URI of the top-level post that started the thread (at://did/app.bsky.feed.post/rkey). Must be the original root even when replying to a nested reply.
parentYesAT-URI of the immediate post being replied to (at://did/app.bsky.feed.post/rkey). May equal root when replying directly to the thread starter.
langsNoList of BCP-47 language tags indicating the language(s) of the reply text. Omit if unknown.

Output Schema

ParametersJSON Schema
NameRequiredDescription
uriYesAT-URI of the newly created reply post (at://did/app.bsky.feed.post/rkey).
cidYesContent identifier (CID) of the newly created reply record.
successYesTrue when the reply was created successfully.
messageYesHuman-readable status message describing the outcome.
replyToYesThe root and parent URIs that this reply is linked to.

TDQS

A4.9/5.0
Behavior5/5

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

Description adds value beyond annotations by noting it creates a new post record, cannot be undone (delete_post as fallback), requires authentication, and is subject to rate limiting. No contradiction with annotations (destructiveHint false, idempotentHint false, openWorldHint true).

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

Conciseness5/5

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

Four sentences that are front-loaded with the core action and then provide alternatives, behavioral notes, and constraints. No wasted words; every sentence adds value.

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?

Given the tool's complexity (4 params, output schema), the description covers purpose, usage guidelines, behavioral traits, and parameter context adequately. Mentions rate limiting and undoability, leaving no critical gaps.

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

Parameters4/5

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

While input schema covers all parameters with descriptions, the tool description adds semantic context for root and parent parameters (e.g., root must be original top-level post). Schema coverage is 100%, so baseline is 3; additional guidance justifies 4.

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 description clearly states the tool's purpose: replying to an existing post on AT Protocol with proper thread references. It uses specific verbs ('reply', 'creating') and distinguishes itself from siblings like create_post and create_thread.

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?

Explicitly states when to use this tool ('when the new content belongs inside an existing thread') and when to use alternatives ('use create_thread to start a multi-post thread from scratch'). Also mentions authentication requirements and that the action cannot be undone.

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

report_contentA
Destructive

Report content that violates community guidelines or terms of service. Submits a moderation report to the network; moderators review it asynchronously and the report cannot be withdrawn once submitted. Requires authentication (app password). Use report_content when you have the AT-URI and CID of the specific post or record; use report_user when reporting an entire account rather than a single piece of content. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
subjectYesStrong reference identifying the specific content record to report.
reasonTypeYesCategory of the violation: "spam" for unsolicited bulk content, "violation" for ToS breach, "misleading" for misinformation, "sexual" for adult content, "rude" for harassment, "other" for anything else.
reasonNoOptional free-text explanation of the violation (max 2000 characters). Providing detail helps moderators act faster.

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYesWhether the report was submitted successfully.
messageYesHuman-readable result message.
reportIdYesNumeric identifier of the newly created moderation report (as a string).
reportDetailsYesEcho of the submitted report parameters.

TDQS

A4.7/5.0
Behavior5/5

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

The description adds significant behavioral context beyond annotations: the report is asynchronous, cannot be withdrawn, requires authentication (app password), and is subject to per-tool rate limiting. This aligns with the 'destructiveHint: true' annotation.

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

Conciseness5/5

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

The description is three sentences with no fluff. It front-loads the purpose, then provides key behavioral details, and finishes with sibling differentiation. Every sentence earns its place.

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?

Given the tool's complexity, presence of an output schema, and thorough annotations, the description covers purpose, usage guidelines, behavioral traits, and parameter hints adequately. It is complete for an agent to select and invoke correctly.

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?

The input schema covers all parameters with descriptions (100% coverage). The description only adds minimal extra value, noting that providing a reason helps moderators act faster. The baseline of 3 is appropriate.

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 description clearly states the tool reports content that violates community guidelines or terms of service, using the verb 'report' and specifying the resource as content. It distinguishes itself from the sibling 'report_user' by stating when to use each.

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 explicitly provides guidance on when to use this tool (when you have AT-URI and CID) versus the alternative 'report_user' (for accounts). It also mentions required authentication, async review, and non-withdrawal.

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

report_userA
Destructive

Report a user account that violates community guidelines or terms of service. Submits a moderation report targeting the entire account; moderators review it asynchronously and the report cannot be withdrawn once submitted. Requires authentication (app password). Use report_user to flag an account; use report_content when the violation is limited to a specific post or record identified by AT-URI and CID. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
actorYesHandle (e.g. alice.bsky.social) or DID of the account to report.
reasonTypeYesCategory of the violation: "spam" for unsolicited bulk content, "violation" for ToS breach, "misleading" for misinformation, "sexual" for adult content, "rude" for harassment, "other" for anything else.
reasonNoOptional free-text explanation of the violation (max 2000 characters). Providing detail helps moderators act faster.

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYesWhether the report was submitted successfully.
messageYesHuman-readable result message.
reportIdYesNumeric identifier of the newly created moderation report (as a string).
reportDetailsYesEcho of the submitted report parameters.

TDQS

A4.7/5.0
Behavior5/5

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

Discloses that reports are irreversible ('cannot be withdrawn once submitted'), requires authentication ('app password'), and is subject to rate limiting. Annotations indicate destructive hint, which aligns with the description. No contradictions.

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

Conciseness5/5

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

Three sentences, each serving a distinct purpose: stating the action, providing usage context, and differentiating from sibling. No unnecessary words.

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?

Given the full schema coverage, annotations, and output schema, the description covers all necessary aspects: purpose, behavioral details, usage guidance, and differentiation. No gaps.

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%, so the schema already fully explains each parameter. The description does not add new information beyond what is in the schema, meeting the baseline for high coverage.

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?

Clearly states the action ('report a user account'), the resource ('account'), and the scope ('entire account'). Differentiates from sibling 'report_content' by specifying that this tool targets accounts, not specific posts.

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?

Explicitly provides when to use this tool ('report a user account') and when to use the alternative ('use report_content when the violation is limited to a specific post'). Also notes that reports are reviewed asynchronously and cannot be withdrawn.

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

repostA

Repost content on AT Protocol. Reposts the identified post to the authenticated user's feed; if optional quote text is supplied, creates a quote post (new post embedding the original) instead. Requires authentication (app password). Deduplicates plain reposts so retrying on timeout is safe; use unrepost to remove a plain repost. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
uriYesAT-URI of the post to repost or quote (at://did/app.bsky.feed.post/rkey).
cidYesCID of the post to repost or quote; used for content integrity verification.
textNoOptional quote text (up to 300 graphemes / 3000 bytes). When provided the result is a quote post embedding the original; omit for a plain repost.

Output Schema

ParametersJSON Schema
NameRequiredDescription
uriYesAT-URI of the newly created repost or quote-post record.
cidNoCID of the newly created repost or quote-post record. Absent when alreadyReposted=true: the existing record is reused and its CID is not re-fetched.
successYesWhether the operation succeeded.
messageYesHuman-readable status message.
repostedPostYesThe original post that was reposted or quoted.
isQuotePostYesTrue when text was supplied and a quote post was created rather than a plain repost.
alreadyRepostedYesTrue when a plain repost already existed; the existing record URI/CID is returned.

TDQS

A4.9/5.0
Behavior5/5

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

Annotations provide minimal hints (non-destructive, not idempotent, open world). The description adds valuable behavioral context: requires authentication, deduplicates plain reposts for safe retries, differentiates two modes, and notes rate limiting. No contradiction with annotations.

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

Conciseness5/5

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

Four concise sentences, front-loaded with main purpose. Every sentence adds information: main action, two modes with optional text, authentication and safety note, rate limiting. No redundancy.

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?

Given tool complexity (two modes), presence of output schema (handled elsewhere), and annotations, the description is complete. It covers authentication, retry safety, rate limits, and guidance for removal via sibling tool.

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

Parameters4/5

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

Schema already covers all parameters with descriptions (100% coverage). The description adds value by explaining the effect of the optional 'text' parameter (triggers quote post) and providing practical guidance on deduplication for plain reposts.

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 description clearly states the tool's purpose ('Repost content on AT Protocol') and distinguishes between two distinct behaviors: plain repost and quote post with optional text. It also differentiates from sibling tool 'unrepost' for removal.

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?

Explicitly states when to use (repost/quote post) and when not to (use unrepost for removal). Provides guidance on safety of retrying plain reposts due to deduplication, and mentions authentication and rate limiting constraints.

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

search_actorsA
Read-only

Search for AT Protocol accounts by handle or display name. Works without authentication; richer with auth. Use this when you know a name but not the exact handle/DID; use get_user_profile when you already have the handle/DID. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryYesSearch term matched against handle and display name (e.g. "alice" or "Alice Smith").
limitNoMax accounts to return (1–100, default 25).
cursorNoPagination cursor from a previous response; omit for the first page.

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYesWhether the request succeeded.
actorsYesAccounts matching the search term.
cursorNoOpaque cursor for the next page; absent when there are no more results.

TDQS

A4.7/5.0
Behavior5/5

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

Annotations already indicate readOnlyHint and openWorldHint; description adds context about unauthenticated vs authenticated usage and rate limiting, enhancing transparency beyond annotations.

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

Conciseness5/5

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

Three sentences, front-loaded with purpose, no extraneous words; efficient and well-structured.

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?

Given output schema exists, description covers purpose, usage guidelines, authentication context, and rate limiting, making it complete for a search tool.

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%, so baseline is 3; description does not add much beyond schema, only providing a search example, which is marginal added value.

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?

Clearly states it searches AT Protocol accounts by handle or display name, and distinguishes from get_user_profile by specifying when to use each.

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?

Explicitly states when to use this tool vs get_user_profile, mentions authentication options and rate limiting, providing clear guidance on when and how to use it.

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

search_postsA
Read-only

Search for posts on Bluesky using full-text queries with optional filters for author, language, date range, mentions, domain, and URL. Returns a paginated list of matching posts sorted by recency or engagement. Requires authentication (app password). Use search_actors to find users instead of posts. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
qYesFull-text search query string (1–300 characters). Supports keywords and hashtags (e.g. "#bluesky launch").
limitNoMaximum number of posts to return per page (1–100, default 25).
cursorNoOpaque pagination cursor from the previous response cursor field; omit for the first page.
sortNoSort order for results: "latest" returns newest posts first (default), "top" returns most-engaged posts first.latest
sinceNoISO 8601 datetime lower bound (inclusive) for post creation time (e.g. "2024-01-01T00:00:00Z"). Omit to search all time.
untilNoISO 8601 datetime upper bound (exclusive) for post creation time (e.g. "2024-12-31T23:59:59Z"). Omit for no upper bound.
mentionsNoFilter to posts that mention this handle or DID (e.g. "alice.bsky.social" or "did:plc:...").
authorNoFilter to posts authored by this handle or DID (e.g. "alice.bsky.social" or "did:plc:..."). Requires a non-empty q term.
langNoBCP-47 language tag to filter posts by language (e.g. "en", "en-US", "pt-BR").
domainNoFilter to posts containing links from this domain (e.g. "bsky.app"). Do not include protocol or path.
urlNoFilter to posts containing this exact URL (must be a fully-qualified URL, e.g. "https://example.com/article").

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYesWhether the search request completed successfully.
postsYesArray of posts matching the search query.
cursorNoOpaque pagination cursor to pass as cursor in the next call; absent when no further pages exist.
hasMoreYesTrue when a subsequent page of results is available.
searchQueryYesThe search query string that was executed.
totalResultsNoApproximate total number of matching posts reported by the API, if available.

TDQS

A4.9/5.0
Behavior5/5

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

Annotations already declare readOnlyHint=true and openWorldHint=true. The description adds further behavioral context: authentication requirement, per-tool rate limiting, and paginated results sorted by recency or engagement. No contradictions.

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

Conciseness5/5

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

Two sentences efficiently convey the core action, filters, pagination, authentication, sibling tool, and rate limiting. No redundant information; every sentence earns its place.

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?

Given the presence of an output schema (not shown but indicated), the description adequately covers the tool's purpose, filter capabilities, pagination, authentication, and rate limits. It is complete for a search tool of this complexity.

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

Parameters4/5

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

Schema coverage is 100%, so baseline is 3. The description adds value by summarizing the types of filters (author, language, date range, mentions, domain, URL) and mentioning pagination (cursor), going beyond what the schema provides individually.

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 description clearly states it searches for posts on Bluesky using full-text queries with optional filters. It distinguishes from sibling tools like search_actors by specifying the tool's scope (posts vs. users).

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 provides explicit guidance: requires authentication (app password), subject to per-tool rate limiting, and suggests using search_actors to find users instead of posts. This helps the agent choose the right tool.

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

search_starter_packsA
Read-only

Search for Bluesky starter packs by keyword (app.bsky.graph.searchStarterPacks). Returns a paginated list of matching packs with name, description, creator, member count, and joined counts. Works without authentication. Use get_starter_pack to fetch full details (member sample, feeds, list info) for a specific pack. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
qYesSearch query string for finding starter packs (e.g. "typescript developers"). Plain keywords work; the API recommends Lucene query syntax for advanced queries.
limitNoMax starter packs per page (1–100, default 25).
cursorNoOpaque pagination cursor from the previous response cursor field; omit for the first page.

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYesWhether the search succeeded.
queryYesThe search query that was executed.
starterPacksYesThe matched starter packs for this page.
cursorNoOpaque cursor for the next page; absent when there are no more results.
hasMoreYesWhether additional pages of results are available.

TDQS

A4.4/5.0
Behavior4/5

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

Annotations already provide readOnlyHint and openWorldHint. The description adds useful context: pagination, return fields, and rate limiting. No contradictions.

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

Conciseness5/5

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

Three succinct sentences: purpose, return summary, and usage guidance. No filler, front-loaded with the primary action.

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

Completeness4/5

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

Covers purpose, return fields, pagination, authentication, rate limiting, and alternative tool. The presence of an output schema reduces burden on description. Lacks error handling notes but is adequate.

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

Parameters4/5

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

Schema coverage is 100%. The description adds valuable detail for the 'q' parameter, noting that plain keywords work but Lucene syntax is recommended. This goes beyond the schema description.

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?

Clearly states the verb 'search', the resource 'Bluesky starter packs', and the method 'by keyword'. References the underlying API function and distinguishes from sibling tool get_starter_pack, which fetches full details for a specific pack.

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

Usage Guidelines4/5

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

Explicitly states 'Works without authentication' for context and recommends get_starter_pack for detailed information. Lacks an explicit 'when not to use', but the alternative is clearly specified.

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

send_direct_messageA

Send a plain-text Bluesky direct message (chat.bsky.convo.sendMessage, proxied to the bsky.chat service). Target either an existing conversation by convoId, or a recipient by handle/DID via member — the conversation is then resolved (or created) automatically with chat.bsky.convo.getConvoForMembers. Message text is limited to 1000 graphemes / 10000 UTF-8 bytes and is sent without facets (mentions/links appear as plain text). Requires authentication with an app password created with "Allow access to your direct messages" enabled; the recipient's settings must also allow DMs from you. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
convoIdNoId of an existing conversation to send into (from list_conversations). Provide exactly one of convoId or member.
memberNoHandle (e.g. bob.bsky.social) or DID of the account to message. The conversation is resolved (or created) via chat.bsky.convo.getConvoForMembers, so no prior conversation is needed. Provide exactly one of convoId or member.
textYesPlain-text message body (max 1000 graphemes / 10000 UTF-8 bytes). Mentions, links and hashtags are sent as plain text — no facets are attached.

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYesWhether the message was sent.
messageYesHuman-readable status message.
convoIdYesId of the conversation the message was sent into; reuse it for follow-up messages to skip conversation resolution.
recipientDidNoDID the member parameter resolved to. Present only when member was used to target the message.
sentMessageYesThe message as recorded by the chat service.

TDQS

A4.7/5.0
Behavior5/5

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

The description adds significant behavioral context beyond annotations: authentication requirements, rate limiting, text length limits (graphemes/bytes), no facets behavior, and automatic conversation creation. The annotations only provide destructiveHint, idempotentHint, and openWorldHint, so the description carries the full burden and exceeds it.

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?

The description is relatively long but each sentence provides necessary detail. The main action is front-loaded. Minor redundancy in repeating 'plain text' but overall well-structured.

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?

Given the tool's complexity (two target modes, constraints, auth), the description covers all essential aspects for correct invocation. An output schema exists, so return values are not needed. The description is complete and actionable.

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

Parameters5/5

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

Schema coverage is 100% with parameter descriptions. The description adds meaning by specifying max lengths, the no-facets behavior, the API calls involved (chat.bsky.convo.sendMessage, chat.bsky.convo.getConvoForMembers), and the mutual exclusivity of convoId and member. This adds substantial value beyond the schema.

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 description clearly states it sends a plain-text Bluesky direct message, specifies the two target options (convoId or member), and references the underlying API. It distinguishes from sibling tools like list_conversations and get_conversation_messages, which are for reading rather than sending.

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

Usage Guidelines4/5

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

The description provides clear guidance on when to use convoId vs member, outlines prerequisites (app password with DM access, recipient settings), and mentions rate limiting. It does not explicitly state when not to use, but the context is sufficient for an agent to decide correctly.

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

unblock_userA
DestructiveIdempotent

Unblock a previously blocked user to restore normal interactions. Deletes the block record from your repo; if the user was not blocked, the operation returns success=false without error. Requires authentication (app password). Use this instead of unmute_user when the account was restricted via block rather than mute. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
actorYesHandle (e.g. alice.bsky.social) or DID of the account to unblock.

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYesTrue if the block was removed; false if the account was not blocked.
messageYesHuman-readable result message.
unblockedUserYesDetails of the targeted account.

TDQS

A4.9/5.0
Behavior5/5

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

Annotations already indicate destructive modification and idempotency. The description adds valuable context: deleting the block record, returning success=false if not blocked, requiring app password authentication, and being subject to per-tool rate limiting. No contradictions.

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

Conciseness5/5

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

Three sentences, no fluff. Each sentence adds distinct value: purpose, edge-case behavior, usage guidance, and requirements. Highly efficient.

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?

Given the low complexity (1 required param, no nested objects), the description covers all necessary aspects: core functionality, alternative tool differentiation, authentication, rate limiting, and edge-case behavior. The presence of an output schema means return values need not be detailed.

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

Parameters4/5

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

The input schema already describes the 'actor' parameter fully (handle or DID). The description reinforces the purpose but does not add new parameter details beyond what the schema provides. With 100% schema coverage, the baseline is 3; the description elevates it to 4 by contextualizing the parameter within the unblock action.

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 description starts with a clear verb+resource: 'Unblock a previously blocked user to restore normal interactions.' It also distinguishes from sibling tool unmute_user by specifying the type of restriction (block vs. mute).

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 provides explicit guidance: when to use (instead of unmute_user), behavior on non-blocked users (returns success=false without error), and mentions authentication and rate limiting requirements.

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

unfollow_userA
Destructive

Unfollow a user on AT Protocol. Deletes the follow record identified by its AT-URI, permanently removing the follow relationship; this action cannot be undone (a new follow_user call is required to re-follow). Requires authentication (app password). Use follow_user to obtain the follow URI before calling this tool; for bulk unfollows consider batch_action. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
followUriYesAT-URI of the follow record to delete (at://did/app.bsky.graph.follow/rkey). Must reference an app.bsky.graph.follow collection record.

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYesTrue when the follow record was successfully deleted.
messageYesHuman-readable outcome message.
deletedFollowYesInformation about the deleted follow record.

TDQS

A4.9/5.0
Behavior5/5

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

Explains irreversibility, need for re-following, and auth requirements beyond annotations. No contradictions.

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

Conciseness5/5

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

Three sentences, front-loaded with purpose, no wasted words. Every sentence adds value.

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?

Covers all needed aspects: what it does, how to use, side effects, prerequisites, rate limits. Output schema exists so return not needed.

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

Parameters4/5

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

Schema already describes parameter fully (100% coverage). Description adds minor extra constraint about collection type, but baseline is 3.

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?

Clear verb 'unfollow' and resource 'user' with specific action 'deletes the follow record'. Distinguishes from siblings like follow_user and batch_action.

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?

Explicitly states prerequisite (follow_user to get URI) and alternative for bulk (batch_action). Also mentions auth and rate limiting.

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

unlike_postA
Destructive

Remove a like from a post on AT Protocol by deleting the like record identified by its AT-URI. This action permanently removes the like and cannot be undone. Requires authentication (app password). Use like_post to add a like. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
likeUriYesAT-URI of the like record to delete (at://did/app.bsky.feed.like/rkey); obtained from a previous like_post response or post viewer state.

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYesWhether the unlike operation succeeded.
messageYesHuman-readable status message.
deletedLikeYesThe like record that was deleted.

TDQS

A4.5/5.0
Behavior4/5

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

Annotations already mark the tool as destructive (destructiveHint: true) and non-idempotent (idempotentHint: false). The description adds useful context: 'permanently removes the like and cannot be undone', the authentication requirement, and rate limiting. While valuable, the description largely restates the destructive nature, making the incremental value moderate.

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

Conciseness5/5

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

The description is extremely concise, consisting of three short sentences. The first sentence immediately states the core action, followed by a warning about permanence, and then complementary usage info. Every sentence adds value without redundancy.

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?

Given the tool's simplicity (single parameter, destructive), the description covers all essential aspects: the action, the method (deleting by AT-URI), irreversibility, authentication requirements, alternative tool, and rate limiting. An output schema exists, so the description does not need to detail return values. This is fully complete for its context.

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?

The input schema already provides a detailed description of the 'likeUri' parameter, including its format and source. With 100% schema coverage, the description adds no new parameter-specific information beyond what the schema provides. Therefore, the baseline of 3 is appropriate.

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 description clearly states the action: 'Remove a like from a post' by deleting a record. It explicitly distinguishes from the sibling tool 'like_post', which is the inverse operation. This provides a specific verb and resource with clear differentiation.

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 provides explicit guidance: it states the tool is for removing a like, mentions the alternative 'like_post' for adding, and specifies prerequisites ('Requires authentication (app password)') and constraints ('Subject to per-tool rate limiting'). This covers when and how to use it effectively.

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

unmute_userA
DestructiveIdempotent

Unmute a previously muted user to restore their content in your feeds. Reverses an earlier mute_user action without notifying the target. Requires authentication (app password). Use this instead of unblock_user when the account was suppressed via mute rather than block. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
actorYesHandle (e.g. alice.bsky.social) or DID of the account to unmute.

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYesWhether the unmute operation succeeded.
messageYesHuman-readable result message.
unmutedUserYesDetails of the unmuted account.

TDQS

A4.7/5.0
Behavior5/5

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

Adds context beyond annotations: 'without notifying the target', 'Requires authentication (app password)', 'Subject to per-tool rate limiting.' No contradiction with annotations.

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

Conciseness5/5

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

Three sentences, each serving a purpose: main action, usage guidance, and behavioral notes. No unnecessary words.

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?

Covers all relevant aspects for a simple unmute operation: purpose, alternative usage, authentication, notification behavior, rate limiting. Output schema exists, so return values are not needed.

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 coverage is 100% and the schema already describes the 'actor' parameter adequately. The description does not add additional parameter-level details, so baseline 3 is appropriate.

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 description clearly states the action 'Unmute' and the resource 'a previously muted user' with the outcome 'restore their content in your feeds.' It also distinguishes from the sibling 'unblock_user' by specifying the condition for use.

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?

Explicitly states when to use this tool versus alternatives ('Use this instead of unblock_user when the account was suppressed via mute rather than block.') and notes authentication and rate limiting requirements.

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

unrepostA
Destructive

Remove a repost on AT Protocol. Deletes the repost record owned by the authenticated user, undoing a previous plain repost; this action cannot be undone without re-calling repost. Requires authentication (app password). Use repost to create a repost and this tool only to remove one. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
repostUriYesAT-URI of the repost record to delete (at://did/app.bsky.feed.repost/rkey). Must reference a repost record, not a post.

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYesWhether the repost was successfully deleted.
messageYesHuman-readable status message.
deletedRepostYesThe repost record that was deleted.

TDQS

A4.7/5.0
Behavior5/5

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

Goes beyond annotations by stating the action deletes the record owned by the authenticated user, undoes a previous repost, is irreversible, requires authentication, and is rate-limited. No contradiction with annotations.

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

Conciseness5/5

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

Multiple sentences but all are necessary and informative. Front-loaded with purpose, succinctly covers usage, behavior, and constraints without redundancy.

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?

Given output schema exists, no need to explain return values. Tool has single parameter, annotations provide safety hints, and description covers purpose, usage, behavioral effects, and alternatives. Complete for decision-making.

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?

Single parameter 'repostUri' with schema description already providing full meaning. Description does not add extra parameter-level details beyond schema, so baseline score of 3 is appropriate.

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?

Clearly states it 'Remove a repost on AT Protocol' and explains it deletes the repost record. Distinguishes from sibling tool 'repost' by explicitly contrasting creation vs removal.

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?

Explicitly says use 'repost' to create a repost and this tool only to remove one. Also mentions it undoes a plain repost and cannot be undone without re-calling repost, plus authentication requirement.

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

update_profileA
DestructiveIdempotent

Update user profile on AT Protocol. Modifies the display name, description, avatar, and/or banner of the authenticated account; unspecified fields are preserved from the existing profile. Use upload_image to prepare blob values for avatar or banner before calling this tool. Requires authentication (app password). Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
displayNameNoNew display name for the profile (max 64 graphemes; emoji count as one). Omit to leave unchanged.
descriptionNoNew bio/description for the profile (max 256 graphemes; emoji count as one). Omit to leave unchanged.
avatarNoNew avatar image as a pre-uploaded blob descriptor: pass the `image.blob` object returned by upload_image verbatim. Omit to keep the existing avatar.
bannerNoNew banner/header image as a pre-uploaded blob descriptor: pass the `image.blob` object returned by upload_image verbatim. Omit to keep the existing banner.

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYesWhether the profile was successfully updated.
messageYesHuman-readable result message.
updatedFieldsYesNames of the profile fields that were actually changed (e.g. ["displayName", "description"]).
profileYesThe new values of the updated profile fields.

TDQS

A4.5/5.0
Behavior4/5

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

The description adds important context beyond the annotations: 'unspecified fields are preserved from the existing profile' clarifies partial update semantics. Annotations already provide destructiveHint, idempotentHint, and openWorldHint, but the description adds the preservation behavior and authentication note.

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

Conciseness5/5

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

The description is concise, with four sentences that each add necessary information (purpose, behavior, prerequisite, auth/rate limits). It is front-loaded with the core update action, and every sentence earns its place.

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?

Given the presence of an output schema (not shown), the description does not need to cover return values. It comprehensively covers purpose, parameters, behavior, prerequisites, authentication, and rate limiting. The sibling tools list includes get_user_profile and upload_image, which are appropriately referenced.

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

Parameters4/5

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

The input schema has 100% description coverage with detailed parameter descriptions. The description adds value by explaining that omitting a parameter leaves it unchanged, and by stating the prerequisite of using upload_image for avatar/banner blobs. This exceeds the baseline of 3.

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 description clearly states 'Update user profile on AT Protocol' and enumerates the specific fields that can be modified (display name, description, avatar, banner). This provides a specific verb and resource, and implicitly distinguishes from the sibling get_user_profile tool which is read-only.

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

Usage Guidelines4/5

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

The description gives explicit prerequisites: 'Use upload_image to prepare blob values for avatar or banner before calling this tool.' It also notes authentication requirements and rate limiting. While it does not explicitly state when not to use, the context implies it is for authenticated profile updates, and alternatives like get_user_profile exist for reading.

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

upload_imageA

Upload an image file to AT Protocol for use in posts and profiles. Reads a local image file (JPEG, PNG, GIF, WebP, or AVIF; max 1 MB) and uploads it as an AT Protocol blob, returning a blob descriptor and alt text. Pass the returned image.blob object verbatim as embed.images[].image in create_post, as avatar/banner in update_profile, or to analyze_image. Requires authentication (app password). Use upload_video instead for video files. Subject to per-tool rate limiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
filePathYesAbsolute or relative path to the image file on disk. Must resolve within the allowed media directory (ATPROTO_MEDIA_DIR env var, defaults to cwd). Accepted extensions: .jpg, .jpeg, .png, .gif, .webp, .avif. Maximum file size 1 MB.
altTextNoAccessible alt-text description of the image (max 1000 characters). Omit if no description is available.

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYesWhether the upload succeeded.
messageYesHuman-readable status message.
imageYesUploaded image blob descriptor and metadata. Pass the `blob` object as create_post embed.images[].image or update_profile avatar/banner.

TDQS

A4.7/5.0
Behavior5/5

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

Beyond annotations, the description details that the tool reads a local file, uploads it as a blob, returns a blob descriptor and alt text, requires authentication, and is rate-limited. No contradictions with annotations.

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

Conciseness5/5

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

The description is concise (4 sentences, ~80 words) and front-loaded with the purpose. Every sentence contributes meaningful information without redundancy.

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?

Given the presence of an output schema and complete input schema, the description provides full context: what to do with the output, authentication requirements, rate limiting, and sibling tool differentiation.

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 coverage is 100%, so the input schema fully describes both parameters. The description adds minimal semantic value beyond the schema, only slightly elaborating on filePath in the general tool description.

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 description clearly states the tool uploads an image file to AT Protocol for use in posts and profiles. It specifies supported formats, size limit, and distinguishes from sibling tools like upload_video and analyze_image.

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 provides explicit usage guidance: use this for images in posts/profiles, use upload_video for videos. It also explains how to use the output (pass image.blob to create_post, update_profile, or analyze_image).

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

upload_videoA

Upload a video to Bluesky through the app.bsky.video service (video.bsky.app), which transcodes it for playback and stores the processed blob on your PDS. Reads a local video file (MP4, MOV, or WebM; max 100 MB), checks your account video-upload quota, uploads with a service-auth token, polls processing until it completes, and returns the PROCESSED video blob descriptor — pass the returned video.blob object verbatim as embed.video.video in create_post, and any video.captions[].file descriptors as embed.video.captions[].file. WebVTT caption tracks are uploaded as ordinary PDS blobs; caption files the embed lexicon does not support (over 20 kB) are skipped. Requires authentication (app password). Use upload_image instead for still images. Subject to per-tool rate limiting and the video service daily quota.

ParametersJSON Schema
NameRequiredDescriptionDefault
filePathYesAbsolute or relative path to the video file on disk. Must resolve within the allowed media directory (ATPROTO_MEDIA_DIR env var, defaults to cwd). Accepted extensions: .mp4, .mov, .webm. Maximum file size 100 MB (the app.bsky.video service limit).
altTextNoAccessible alt-text description of the video (max 1000 characters). Omit if no description is available.
captionsNoOptional list of caption tracks to attach to the video. Each entry pairs a language code with a WebVTT file path.

Output Schema

ParametersJSON Schema
NameRequiredDescription
successYesWhether the upload and processing succeeded.
messageYesHuman-readable status message.
videoYesProcessed video blob descriptor and metadata. Pass the `blob` object as create_post embed.video.video and each `captions[].file` as embed.video.captions[].file.

TDQS

A4.6/5.0
Behavior5/5

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

The description discloses key behavioral traits beyond annotations: reads local file, checks quota, uploads with service-auth token, polls processing, returns blob. It also notes caption files over 20 kB are skipped. No contradictions with annotations.

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?

The description is reasonably concise given the complexity. It is front-loaded with the main purpose and each sentence adds necessary detail. Could be slightly tighter, but still effective.

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?

Given the complexity (3 parameters, output schema, annotations), the description is very complete. It covers authentication, rate limits, quota, processing steps, caption limitations, and usage of the returned blob. With output schema present, no need to explain return values.

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

Parameters4/5

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

Schema description coverage is 100%, so baseline is 3. The description adds extra context: explains that filePath must be within allowed directory, altText is accessible description, and captions pairs language with WebVTT file, with note that large captions are skipped. This adds value beyond schema.

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 description clearly states the action (upload video to Bluesky) and the resource (app.bsky.video service). It also explicitly distinguishes from the sibling tool upload_image, saying 'Use upload_image instead for still images.'

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

Usage Guidelines4/5

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

The description provides explicit when-to-use guidance (for video files), alternatives (upload_image for stills), and prerequisites (authentication, rate limits). It could be slightly more explicit about when not to use, but the alternative is clearly named.

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

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 51 tool updatesv0.6.2
    • Changedadd_bookmark1 field changed
      • removedInput schema / additionalProperties
        Removed value: -false
    • Changedadd_to_list1 field changed
      • removedInput schema / additionalProperties
        Removed value: -false
    • Changedanalyze_account1 field changed
      • removedInput schema / additionalProperties
        Removed value: -false
    • Changedanalyze_image4 fields changed
      • removedInput schema / additionalProperties
        Removed value: -false
      • removedInput schema / properties / blob / additionalProperties
        Removed value: -false
      • changedInput schema / properties / blob / properties / ref / anyOf
        Previous value: -[
        -  {
        -    "description": "CID of the uploaded blob as a flat string (e.g. \"bafkrei…\").",
        -    "minLength": 1,
        -    "type": "string"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "description": "Lexicon blob-ref object wrapping the CID as { \"$link\": \"<cid>\" }.",
        -    "properties": {
        -      "$link": {
        -        "description": "CID of the uploaded blob (e.g. \"bafkrei…\").",
        -        "minLength": 1,
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "$link"
        -    ],
        -    "type": "object"
        -  }
        -]New value: +[
        +  {
        +    "description": "CID of the uploaded blob as a flat string (e.g. \"bafkrei…\").",
        +    "minLength": 1,
        +    "type": "string"
        +  },
        +  {
        +    "description": "Lexicon blob-ref object wrapping the CID as { \"$link\": \"<cid>\" }.",
        +    "properties": {
        +      "$link": {
        +        "description": "CID of the uploaded blob (e.g. \"bafkrei…\").",
        +        "minLength": 1,
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "$link"
        +    ],
        +    "type": "object"
        +  }
        +]
      • addedInput schema / properties / blob / properties / size / maximum
        Added value: +9007199254740991
    • Changedanalyze_moderation_status1 field changed
      • removedInput schema / additionalProperties
        Removed value: -false
    • Changedbatch_action1 field changed
      • removedInput schema / additionalProperties
        Removed value: -false
    • Changedblock_user1 field changed
      • removedInput schema / additionalProperties
        Removed value: -false
    • Changedcreate_list1 field changed
      • removedInput schema / additionalProperties
        Removed value: -false
    • Changedcreate_post30 fields changed
      • removedInput schema / additionalProperties
        Removed value: -false
      • removedInput schema / properties / embed / additionalProperties
        Removed value: -false
      • removedInput schema / properties / embed / properties / external / additionalProperties
        Removed value: -false
      • removedInput schema / properties / embed / properties / external / properties / thumb / additionalProperties
        Removed value: -false
      • changedInput schema / properties / embed / properties / external / properties / thumb / properties / ref / anyOf
        Previous value: -[
        -  {
        -    "description": "CID of the uploaded blob as a flat string (e.g. \"bafkrei…\").",
        -    "minLength": 1,
        -    "type": "string"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "description": "Lexicon blob-ref object wrapping the CID as { \"$link\": \"<cid>\" }.",
        -    "properties": {
        -      "$link": {
        -        "description": "CID of the uploaded blob (e.g. \"bafkrei…\").",
        -        "minLength": 1,
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "$link"
        -    ],
        -    "type": "object"
        -  }
        -]New value: +[
        +  {
        +    "description": "CID of the uploaded blob as a flat string (e.g. \"bafkrei…\").",
        +    "minLength": 1,
        +    "type": "string"
        +  },
        +  {
        +    "description": "Lexicon blob-ref object wrapping the CID as { \"$link\": \"<cid>\" }.",
        +    "properties": {
        +      "$link": {
        +        "description": "CID of the uploaded blob (e.g. \"bafkrei…\").",
        +        "minLength": 1,
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "$link"
        +    ],
        +    "type": "object"
        +  }
        +]
      • addedInput schema / properties / embed / properties / external / properties / thumb / properties / size / maximum
        Added value: +9007199254740991
      • removedInput schema / properties / embed / properties / images / items / additionalProperties
        Removed value: -false
      • removedInput schema / properties / embed / properties / images / items / properties / image / additionalProperties
        Removed value: -false
      • changedInput schema / properties / embed / properties / images / items / properties / image / properties / ref / anyOf
        Previous value: -[
        -  {
        -    "description": "CID of the uploaded blob as a flat string (e.g. \"bafkrei…\").",
        -    "minLength": 1,
        -    "type": "string"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "description": "Lexicon blob-ref object wrapping the CID as { \"$link\": \"<cid>\" }.",
        -    "properties": {
        -      "$link": {
        -        "description": "CID of the uploaded blob (e.g. \"bafkrei…\").",
        -        "minLength": 1,
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "$link"
        -    ],
        -    "type": "object"
        -  }
        -]New value: +[
        +  {
        +    "description": "CID of the uploaded blob as a flat string (e.g. \"bafkrei…\").",
        +    "minLength": 1,
        +    "type": "string"
        +  },
        +  {
        +    "description": "Lexicon blob-ref object wrapping the CID as { \"$link\": \"<cid>\" }.",
        +    "properties": {
        +      "$link": {
        +        "description": "CID of the uploaded blob (e.g. \"bafkrei…\").",
        +        "minLength": 1,
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "$link"
        +    ],
        +    "type": "object"
        +  }
        +]
      • addedInput schema / properties / embed / properties / images / items / properties / image / properties / size / maximum
        Added value: +9007199254740991
      • removedInput schema / properties / embed / properties / video / additionalProperties
        Removed value: -false
      • removedInput schema / properties / embed / properties / video / properties / aspectRatio / additionalProperties
        Removed value: -false
      • addedInput schema / properties / embed / properties / video / properties / aspectRatio / properties / height / maximum
        Added value: +9007199254740991
      • addedInput schema / properties / embed / properties / video / properties / aspectRatio / properties / width / maximum
        Added value: +9007199254740991
      • removedInput schema / properties / embed / properties / video / properties / captions / items / additionalProperties
        Removed value: -false
      • removedInput schema / properties / embed / properties / video / properties / captions / items / properties / file / additionalProperties
        Removed value: -false
      • changedInput schema / properties / embed / properties / video / properties / captions / items / properties / file / properties / ref / anyOf
        Previous value: -[
        -  {
        -    "description": "CID of the uploaded blob as a flat string (e.g. \"bafkrei…\").",
        -    "minLength": 1,
        -    "type": "string"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "description": "Lexicon blob-ref object wrapping the CID as { \"$link\": \"<cid>\" }.",
        -    "properties": {
        -      "$link": {
        -        "description": "CID of the uploaded blob (e.g. \"bafkrei…\").",
        -        "minLength": 1,
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "$link"
        -    ],
        -    "type": "object"
        -  }
        -]New value: +[
        +  {
        +    "description": "CID of the uploaded blob as a flat string (e.g. \"bafkrei…\").",
        +    "minLength": 1,
        +    "type": "string"
        +  },
        +  {
        +    "description": "Lexicon blob-ref object wrapping the CID as { \"$link\": \"<cid>\" }.",
        +    "properties": {
        +      "$link": {
        +        "description": "CID of the uploaded blob (e.g. \"bafkrei…\").",
        +        "minLength": 1,
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "$link"
        +    ],
        +    "type": "object"
        +  }
        +]
      • addedInput schema / properties / embed / properties / video / properties / captions / items / properties / file / properties / size / maximum
        Added value: +9007199254740991
      • removedInput schema / properties / embed / properties / video / properties / video / additionalProperties
        Removed value: -false
      • changedInput schema / properties / embed / properties / video / properties / video / properties / ref / anyOf
        Previous value: -[
        -  {
        -    "description": "CID of the uploaded blob as a flat string (e.g. \"bafkrei…\").",
        -    "minLength": 1,
        -    "type": "string"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "description": "Lexicon blob-ref object wrapping the CID as { \"$link\": \"<cid>\" }.",
        -    "properties": {
        -      "$link": {
        -        "description": "CID of the uploaded blob (e.g. \"bafkrei…\").",
        -        "minLength": 1,
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "$link"
        -    ],
        -    "type": "object"
        -  }
        -]New value: +[
        +  {
        +    "description": "CID of the uploaded blob as a flat string (e.g. \"bafkrei…\").",
        +    "minLength": 1,
        +    "type": "string"
        +  },
        +  {
        +    "description": "Lexicon blob-ref object wrapping the CID as { \"$link\": \"<cid>\" }.",
        +    "properties": {
        +      "$link": {
        +        "description": "CID of the uploaded blob (e.g. \"bafkrei…\").",
        +        "minLength": 1,
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "$link"
        +    ],
        +    "type": "object"
        +  }
        +]
      • addedInput schema / properties / embed / properties / video / properties / video / properties / size / maximum
        Added value: +9007199254740991
      • removedInput schema / properties / facets / items / additionalProperties
        Removed value: -false
      • removedInput schema / properties / facets / items / properties / features / items / additionalProperties
        Removed value: -false
      • removedInput schema / properties / facets / items / properties / index / additionalProperties
        Removed value: -false
      • addedInput schema / properties / facets / items / properties / index / properties / byteEnd / maximum
        Added value: +9007199254740991
      • addedInput schema / properties / facets / items / properties / index / properties / byteStart / maximum
        Added value: +9007199254740991
      • removedInput schema / properties / quote / additionalProperties
        Removed value: -false
      • removedInput schema / properties / quoteControls / additionalProperties
        Removed value: -false
      • removedInput schema / properties / reply / additionalProperties
        Removed value: -false
      • removedInput schema / properties / replyControls / additionalProperties
        Removed value: -false
    • Changedcreate_thread3 fields changed
      • removedInput schema / additionalProperties
        Removed value: -false
      • removedInput schema / properties / posts / items / additionalProperties
        Removed value: -false
      • removedInput schema / properties / replyControls / additionalProperties
        Removed value: -false
    • Changeddelete_post1 field changed
      • removedInput schema / additionalProperties
        Removed value: -false
    • Changeddiscover3 fields changed
      • removedInput schema / additionalProperties
        Removed value: -false
      • addedInput schema / properties / maxAge / maximum
        Added value: +9007199254740991
      • addedInput schema / properties / minLikes / maximum
        Added value: +9007199254740991
    • Changeddiscover_communities2 fields changed
      • removedInput schema / additionalProperties
        Removed value: -false
      • addedInput schema / properties / minCommunitySize / maximum
        Added value: +9007199254740991
    • Changedfind_influential_users1 field changed
      • removedInput schema / additionalProperties
        Removed value: -false
    • Changedfind_similar_users2 fields changed
      • removedInput schema / additionalProperties
        Removed value: -false
      • addedInput schema / properties / minFollowerCount / maximum
        Added value: +9007199254740991
    • Changedfollow_user1 field changed
      • removedInput schema / additionalProperties
        Removed value: -false
    • Changedgenerate_link_preview1 field changed
      • removedInput schema / additionalProperties
        Removed value: -false
    • Changedget_author_feed1 field changed
      • removedInput schema / additionalProperties
        Removed value: -false
    • Changedget_bookmarks1 field changed
      • removedInput schema / additionalProperties
        Removed value: -false
    • Changedget_conversation_messages1 field changed
      • removedInput schema / additionalProperties
        Removed value: -false
    • Changedget_custom_feed1 field changed
      • removedInput schema / additionalProperties
        Removed value: -false
    • Changedget_list1 field changed
      • removedInput schema / additionalProperties
        Removed value: -false
    • Changedget_notifications1 field changed
      • removedInput schema / additionalProperties
        Removed value: -false
    • Changedget_post_context1 field changed
      • removedInput schema / additionalProperties
        Removed value: -false
    • Changedget_starter_pack1 field changed
      • removedInput schema / additionalProperties
        Removed value: -false
    • Changedget_timeline1 field changed
      • removedInput schema / additionalProperties
        Removed value: -false
    • Changedget_user_connections1 field changed
      • removedInput schema / additionalProperties
        Removed value: -false
    • Changedget_user_profile1 field changed
      • removedInput schema / additionalProperties
        Removed value: -false
    • Changedget_user_summary1 field changed
      • removedInput schema / additionalProperties
        Removed value: -false
    • Changedlike_post1 field changed
      • removedInput schema / additionalProperties
        Removed value: -false
    • Changedlist_conversations1 field changed
      • removedInput schema / additionalProperties
        Removed value: -false
    • Changedmark_notifications_seen1 field changed
      • removedInput schema / additionalProperties
        Removed value: -false
    • Changedmute_user1 field changed
      • removedInput schema / additionalProperties
        Removed value: -false
    • Changedremove_bookmark1 field changed
      • removedInput schema / additionalProperties
        Removed value: -false
    • Changedremove_from_list1 field changed
      • removedInput schema / additionalProperties
        Removed value: -false
    • Changedreply_to_post1 field changed
      • removedInput schema / additionalProperties
        Removed value: -false
    • Changedreport_content2 fields changed
      • removedInput schema / additionalProperties
        Removed value: -false
      • removedInput schema / properties / subject / additionalProperties
        Removed value: -false
    • Changedreport_user1 field changed
      • removedInput schema / additionalProperties
        Removed value: -false
    • Changedrepost1 field changed
      • removedInput schema / additionalProperties
        Removed value: -false
    • Changedsearch_actors1 field changed
      • removedInput schema / additionalProperties
        Removed value: -false
    • Changedsearch_posts1 field changed
      • removedInput schema / additionalProperties
        Removed value: -false
    • Changedsearch_starter_packs1 field changed
      • removedInput schema / additionalProperties
        Removed value: -false
    • Changedsend_direct_message1 field changed
      • removedInput schema / additionalProperties
        Removed value: -false
    • Changedunblock_user1 field changed
      • removedInput schema / additionalProperties
        Removed value: -false
    • Changedunfollow_user1 field changed
      • removedInput schema / additionalProperties
        Removed value: -false
    • Changedunlike_post1 field changed
      • removedInput schema / additionalProperties
        Removed value: -false
    • Changedunmute_user1 field changed
      • removedInput schema / additionalProperties
        Removed value: -false
    • Changedunrepost1 field changed
      • removedInput schema / additionalProperties
        Removed value: -false
    • Changedupdate_profile7 fields changed
      • removedInput schema / additionalProperties
        Removed value: -false
      • removedInput schema / properties / avatar / additionalProperties
        Removed value: -false
      • changedInput schema / properties / avatar / properties / ref / anyOf
        Previous value: -[
        -  {
        -    "description": "CID of the uploaded blob as a flat string (e.g. \"bafkrei…\").",
        -    "minLength": 1,
        -    "type": "string"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "description": "Lexicon blob-ref object wrapping the CID as { \"$link\": \"<cid>\" }.",
        -    "properties": {
        -      "$link": {
        -        "description": "CID of the uploaded blob (e.g. \"bafkrei…\").",
        -        "minLength": 1,
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "$link"
        -    ],
        -    "type": "object"
        -  }
        -]New value: +[
        +  {
        +    "description": "CID of the uploaded blob as a flat string (e.g. \"bafkrei…\").",
        +    "minLength": 1,
        +    "type": "string"
        +  },
        +  {
        +    "description": "Lexicon blob-ref object wrapping the CID as { \"$link\": \"<cid>\" }.",
        +    "properties": {
        +      "$link": {
        +        "description": "CID of the uploaded blob (e.g. \"bafkrei…\").",
        +        "minLength": 1,
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "$link"
        +    ],
        +    "type": "object"
        +  }
        +]
      • addedInput schema / properties / avatar / properties / size / maximum
        Added value: +9007199254740991
      • removedInput schema / properties / banner / additionalProperties
        Removed value: -false
      • changedInput schema / properties / banner / properties / ref / anyOf
        Previous value: -[
        -  {
        -    "description": "CID of the uploaded blob as a flat string (e.g. \"bafkrei…\").",
        -    "minLength": 1,
        -    "type": "string"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "description": "Lexicon blob-ref object wrapping the CID as { \"$link\": \"<cid>\" }.",
        -    "properties": {
        -      "$link": {
        -        "description": "CID of the uploaded blob (e.g. \"bafkrei…\").",
        -        "minLength": 1,
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "$link"
        -    ],
        -    "type": "object"
        -  }
        -]New value: +[
        +  {
        +    "description": "CID of the uploaded blob as a flat string (e.g. \"bafkrei…\").",
        +    "minLength": 1,
        +    "type": "string"
        +  },
        +  {
        +    "description": "Lexicon blob-ref object wrapping the CID as { \"$link\": \"<cid>\" }.",
        +    "properties": {
        +      "$link": {
        +        "description": "CID of the uploaded blob (e.g. \"bafkrei…\").",
        +        "minLength": 1,
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "$link"
        +    ],
        +    "type": "object"
        +  }
        +]
      • addedInput schema / properties / banner / properties / size / maximum
        Added value: +9007199254740991
    • Changedupload_image1 field changed
      • removedInput schema / additionalProperties
        Removed value: -false
    • Changedupload_video2 fields changed
      • removedInput schema / additionalProperties
        Removed value: -false
      • removedInput schema / properties / captions / items / additionalProperties
        Removed value: -false
  2. 75 tool updatesv0.6.0
    • Addedadd_bookmark
    • Changedadd_to_list3 fields changed
      • addedInput schema / properties / actor / description
        Added value: +"Handle (e.g. alice.bsky.social) or DID of the user to add to the list."
      • addedInput schema / properties / listUri / description
        Added value: +"AT-URI of the target list (at://did/app.bsky.graph.list/rkey)."
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "properties": {
        +    "listItem": {
        +      "description": "Details of the newly created list-item record.",
        +      "properties": {
        +        "actor": {
        +          "description": "Handle or DID of the user that was added.",
        +          "type": "string"
        +        },
        +        "listUri": {
        +          "description": "AT-URI of the parent list.",
        +          "type": "string"
        +        },
        +        "uri": {
        +          "description": "AT-URI of the new listitem record.",
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "uri",
        +        "listUri",
        +        "actor"
        +      ],
        +      "type": "object"
        +    },
        +    "message": {
        +      "description": "Human-readable result message.",
        +      "type": "string"
        +    },
        +    "success": {
        +      "description": "Whether the user was added successfully.",
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "message",
        +    "listItem"
        +  ],
        +  "type": "object"
        +}
    • Addedanalyze_account
    • Removedanalyze_engagement
    • Changedanalyze_image15 fields changed
      • addedInput schema / properties / blob / description
        Added value: +"Blob descriptor as returned by upload_image (the `image.blob` object in its output): ref (flat CID string or { \"$link\": \"<cid>\" } object), mimeType, and size."
      • addedInput schema / properties / blob / properties / mimeType / description
        Added value: +"MIME type of the uploaded blob (e.g. \"image/jpeg\")."
      • addedInput schema / properties / blob / properties / mimeType / minLength
        Added value: +1
      • removedInput schema / properties / blob / properties / ref / additionalProperties
        Removed value: -false
      • addedInput schema / properties / blob / properties / ref / anyOf
        Added value: +[
        +  {
        +    "description": "CID of the uploaded blob as a flat string (e.g. \"bafkrei…\").",
        +    "minLength": 1,
        +    "type": "string"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "description": "Lexicon blob-ref object wrapping the CID as { \"$link\": \"<cid>\" }.",
        +    "properties": {
        +      "$link": {
        +        "description": "CID of the uploaded blob (e.g. \"bafkrei…\").",
        +        "minLength": 1,
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "$link"
        +    ],
        +    "type": "object"
        +  }
        +]
      • addedInput schema / properties / blob / properties / ref / description
        Added value: +"CID reference of the uploaded blob: either the flat string returned by upload_image, or the lexicon { \"$link\": \"<cid>\" } object form."
      • removedInput schema / properties / blob / properties / ref / properties
        Removed value: -{
        -  "$link": {
        -    "type": "string"
        -  }
        -}
      • removedInput schema / properties / blob / properties / ref / required
        Removed value: -[
        -  "$link"
        -]
      • removedInput schema / properties / blob / properties / ref / type
        Removed value: -"object"
      • addedInput schema / properties / blob / properties / size / description
        Added value: +"Size of the uploaded blob in bytes."
      • addedInput schema / properties / blob / properties / size / exclusiveMinimum
        Added value: +0
      • changedInput schema / properties / blob / properties / size / type
        Previous value: -"number"New value: +"integer"
      • addedInput schema / properties / blob / properties / type
        Added value: +{
        +  "const": "blob",
        +  "description": "Discriminator emitted by upload_image; always \"blob\" when present. May be omitted.",
        +  "type": "string"
        +}
      • addedInput schema / properties / includeOptimizationSuggestions / description
        Added value: +"When true (default), the response includes a list of human-readable optimization and accessibility suggestions based on the blob size and MIME type."
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "properties": {
        +    "analysis": {
        +      "description": "Metadata derived from the blob.",
        +      "properties": {
        +        "format": {
        +          "description": "Format string derived from the MIME type subtype (e.g. \"jpeg\", \"png\", \"webp\").",
        +          "type": "string"
        +        },
        +        "isOptimized": {
        +          "description": "True when the blob is within the format-specific size threshold considered optimized for web use.",
        +          "type": "boolean"
        +        },
        +        "mimeType": {
        +          "description": "MIME type of the image as declared in the blob (e.g. \"image/jpeg\").",
        +          "type": "string"
        +        },
        +        "size": {
        +          "description": "Raw blob size in bytes.",
        +          "type": "number"
        +        },
        +        "sizeKB": {
        +          "description": "Blob size converted to kilobytes, rounded to two decimal places.",
        +          "type": "number"
        +        },
        +        "sizeMB": {
        +          "description": "Blob size converted to megabytes, rounded to two decimal places.",
        +          "type": "number"
        +        }
        +      },
        +      "required": [
        +        "mimeType",
        +        "size",
        +        "sizeKB",
        +        "sizeMB",
        +        "format",
        +        "isOptimized"
        +      ],
        +      "type": "object"
        +    },
        +    "success": {
        +      "description": "True when the analysis completed without errors.",
        +      "type": "boolean"
        +    },
        +    "suggestions": {
        +      "description": "List of human-readable optimization and accessibility suggestions. Present only when includeOptimizationSuggestions is true.",
        +      "items": {
        +        "description": "A single optimization or accessibility recommendation.",
        +        "type": "string"
        +      },
        +      "type": "array"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "analysis"
        +  ],
        +  "type": "object"
        +}
    • Changedanalyze_moderation_status3 fields changed
      • addedInput schema / properties / includeLabels / description
        Added value: +"Whether to fetch and include content labels in the response (default true). Set to false to skip label fetching for a faster call."
      • addedInput schema / properties / subject / description
        Added value: +"DID of a user account (e.g. did:plc:abc123) or AT-URI of a post (at://did/app.bsky.feed.post/rkey) to analyze."
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "properties": {
        +    "analysis": {
        +      "description": "Derived safety analysis based on labels and moderation state.",
        +      "properties": {
        +        "hasContentWarnings": {
        +          "description": "True if any content warnings were detected.",
        +          "type": "boolean"
        +        },
        +        "isNSFW": {
        +          "description": "True if NSFW-related labels were found.",
        +          "type": "boolean"
        +        },
        +        "isSpam": {
        +          "description": "True if spam labels were found.",
        +          "type": "boolean"
        +        },
        +        "requiresWarning": {
        +          "description": "True if a warning should be shown before displaying content.",
        +          "type": "boolean"
        +        },
        +        "safetyLevel": {
        +          "description": "\"safe\" = no issues; \"warning\" = minor labels or muted; \"restricted\" = spam/hate; \"blocked\" = mutual or list block.",
        +          "enum": [
        +            "safe",
        +            "warning",
        +            "restricted",
        +            "blocked"
        +          ],
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "hasContentWarnings",
        +        "isNSFW",
        +        "isSpam",
        +        "requiresWarning",
        +        "safetyLevel"
        +      ],
        +      "type": "object"
        +    },
        +    "moderation": {
        +      "description": "Raw moderation state for the subject.",
        +      "properties": {
        +        "blocked": {
        +          "description": "True if you are blocking this account (user subjects only).",
        +          "type": "boolean"
        +        },
        +        "blockedBy": {
        +          "description": "True if this account is blocking you (user subjects only).",
        +          "type": "boolean"
        +        },
        +        "blocking": {
        +          "description": "AT-URI of your block record for this account, if any.",
        +          "type": "string"
        +        },
        +        "blockingByList": {
        +          "description": "Moderation list that caused the block, if applicable.",
        +          "properties": {
        +            "name": {
        +              "description": "Display name of the list.",
        +              "type": "string"
        +            },
        +            "uri": {
        +              "description": "AT-URI of the list.",
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "uri"
        +          ],
        +          "type": "object"
        +        },
        +        "labels": {
        +          "description": "Content labels attached to the subject (present when includeLabels=true and labels exist).",
        +          "items": {
        +            "properties": {
        +              "cts": {
        +                "description": "ISO-8601 timestamp when the label was created.",
        +                "type": "string"
        +              },
        +              "src": {
        +                "description": "DID of the labeler that issued this label.",
        +                "type": "string"
        +              },
        +              "uri": {
        +                "description": "AT-URI of the labeled record.",
        +                "type": "string"
        +              },
        +              "val": {
        +                "description": "Label value (e.g. \"nsfw\", \"spam\").",
        +                "type": "string"
        +              }
        +            },
        +            "required": [
        +              "src",
        +              "uri",
        +              "val",
        +              "cts"
        +            ],
        +            "type": "object"
        +          },
        +          "type": "array"
        +        },
        +        "muted": {
        +          "description": "True if you have muted this account (user subjects only).",
        +          "type": "boolean"
        +        },
        +        "mutedByList": {
        +          "description": "Moderation list that caused the mute, if applicable.",
        +          "properties": {
        +            "name": {
        +              "description": "Display name of the list.",
        +              "type": "string"
        +            },
        +            "uri": {
        +              "description": "AT-URI of the list.",
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "uri"
        +          ],
        +          "type": "object"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    "subject": {
        +      "description": "The DID or AT-URI that was analyzed.",
        +      "type": "string"
        +    },
        +    "subjectType": {
        +      "description": "\"user\" when subject is a DID; \"post\" when subject is an AT-URI.",
        +      "enum": [
        +        "user",
        +        "post"
        +      ],
        +      "type": "string"
        +    },
        +    "success": {
        +      "description": "Whether the analysis completed successfully.",
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "subject",
        +    "subjectType",
        +    "moderation",
        +    "analysis"
        +  ],
        +  "type": "object"
        +}
    • Removedanalyze_network
    • Addedbatch_action
    • Removedbatch_follow
    • Removedbatch_like
    • Removedbatch_repost
    • Changedblock_user2 fields changed
      • addedInput schema / properties / actor / description
        Added value: +"Handle (e.g. alice.bsky.social) or DID of the account to block."
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "properties": {
        +    "blockedUser": {
        +      "description": "Details of the blocked account.",
        +      "properties": {
        +        "actor": {
        +          "description": "Handle or DID supplied in the request.",
        +          "type": "string"
        +        },
        +        "did": {
        +          "description": "Resolved DID of the blocked account.",
        +          "type": "string"
        +        },
        +        "uri": {
        +          "description": "AT-URI of the newly created block record in your repo.",
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "actor"
        +      ],
        +      "type": "object"
        +    },
        +    "message": {
        +      "description": "Human-readable result message.",
        +      "type": "string"
        +    },
        +    "success": {
        +      "description": "Whether the block operation succeeded.",
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "message",
        +    "blockedUser"
        +  ],
        +  "type": "object"
        +}
    • Changedcreate_list4 fields changed
      • addedInput schema / properties / description / description
        Added value: +"Optional plain-text description of the list (max 300 characters)."
      • addedInput schema / properties / name / description
        Added value: +"Display name for the list (1–64 characters)."
      • addedInput schema / properties / purpose / description
        Added value: +"List type: \"curatelist\" for a user-curated follow list, \"modlist\" for a moderation/block list. Defaults to \"curatelist\"."
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "properties": {
        +    "list": {
        +      "description": "Metadata of the newly created list.",
        +      "properties": {
        +        "cid": {
        +          "description": "CID of the new list record.",
        +          "type": "string"
        +        },
        +        "createdAt": {
        +          "description": "ISO 8601 creation timestamp.",
        +          "type": "string"
        +        },
        +        "description": {
        +          "description": "Optional description of the list.",
        +          "type": "string"
        +        },
        +        "name": {
        +          "description": "Display name of the list.",
        +          "type": "string"
        +        },
        +        "purpose": {
        +          "description": "List purpose: \"curatelist\" or \"modlist\".",
        +          "type": "string"
        +        },
        +        "uri": {
        +          "description": "AT-URI of the new list record.",
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "uri",
        +        "cid",
        +        "name",
        +        "purpose",
        +        "createdAt"
        +      ],
        +      "type": "object"
        +    },
        +    "message": {
        +      "description": "Human-readable result message.",
        +      "type": "string"
        +    },
        +    "success": {
        +      "description": "Whether the list was created successfully.",
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "message",
        +    "list"
        +  ],
        +  "type": "object"
        +}
    • Changedcreate_post26 fields changed
      • addedInput schema / properties / embed / description
        Added value: +"Optional media embed: images OR an external link card OR a video (at most one)."
      • addedInput schema / properties / embed / properties / external / description
        Added value: +"An external link card to attach. Mutually exclusive with `images`, `video`, and `quote`."
      • addedInput schema / properties / embed / properties / external / properties / description / description
        Added value: +"Description shown on the external link card (max 1000 characters)."
      • addedInput schema / properties / embed / properties / external / properties / thumb
        Added value: +{
        +  "additionalProperties": false,
        +  "description": "Optional thumbnail for the link card as a pre-uploaded blob descriptor: pass the `preview.thumb.blob` object from generate_link_preview (or the `image.blob` from upload_image) verbatim. Omit for a card without a thumbnail.",
        +  "properties": {
        +    "mimeType": {
        +      "description": "MIME type of the uploaded blob (e.g. \"image/jpeg\").",
        +      "minLength": 1,
        +      "type": "string"
        +    },
        +    "ref": {
        +      "anyOf": [
        +        {
        +          "description": "CID of the uploaded blob as a flat string (e.g. \"bafkrei…\").",
        +          "minLength": 1,
        +          "type": "string"
        +        },
        +        {
        +          "additionalProperties": false,
        +          "description": "Lexicon blob-ref object wrapping the CID as { \"$link\": \"<cid>\" }.",
        +          "properties": {
        +            "$link": {
        +              "description": "CID of the uploaded blob (e.g. \"bafkrei…\").",
        +              "minLength": 1,
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "$link"
        +          ],
        +          "type": "object"
        +        }
        +      ],
        +      "description": "CID reference of the uploaded blob: either the flat string returned by upload_image, or the lexicon { \"$link\": \"<cid>\" } object form."
        +    },
        +    "size": {
        +      "description": "Size of the uploaded blob in bytes.",
        +      "exclusiveMinimum": 0,
        +      "type": "integer"
        +    },
        +    "type": {
        +      "const": "blob",
        +      "description": "Discriminator emitted by upload_image; always \"blob\" when present. May be omitted.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "ref",
        +    "mimeType",
        +    "size"
        +  ],
        +  "type": "object"
        +}
      • addedInput schema / properties / embed / properties / external / properties / title / description
        Added value: +"Title shown on the external link card (max 300 characters)."
      • addedInput schema / properties / embed / properties / external / properties / uri / description
        Added value: +"The URL the external link card points to."
      • addedInput schema / properties / embed / properties / images / description
        Added value: +"Up to 4 images to attach. Mutually exclusive with `external` and `quote`."
      • addedInput schema / properties / embed / properties / images / items / properties / alt / description
        Added value: +"Accessibility alt text describing the image (max 1000 characters)."
      • addedInput schema / properties / embed / properties / images / items / properties / image / additionalProperties
        Added value: +false
      • addedInput schema / properties / embed / properties / images / items / properties / image / description
        Added value: +"Pre-uploaded image blob descriptor: pass the `image.blob` object from a prior upload_image call verbatim. The image must already be uploaded — this tool does not accept raw image data."
      • addedInput schema / properties / embed / properties / images / items / properties / image / properties
        Added value: +{
        +  "mimeType": {
        +    "description": "MIME type of the uploaded blob (e.g. \"image/jpeg\").",
        +    "minLength": 1,
        +    "type": "string"
        +  },
        +  "ref": {
        +    "anyOf": [
        +      {
        +        "description": "CID of the uploaded blob as a flat string (e.g. \"bafkrei…\").",
        +        "minLength": 1,
        +        "type": "string"
        +      },
        +      {
        +        "additionalProperties": false,
        +        "description": "Lexicon blob-ref object wrapping the CID as { \"$link\": \"<cid>\" }.",
        +        "properties": {
        +          "$link": {
        +            "description": "CID of the uploaded blob (e.g. \"bafkrei…\").",
        +            "minLength": 1,
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "$link"
        +        ],
        +        "type": "object"
        +      }
        +    ],
        +    "description": "CID reference of the uploaded blob: either the flat string returned by upload_image, or the lexicon { \"$link\": \"<cid>\" } object form."
        +  },
        +  "size": {
        +    "description": "Size of the uploaded blob in bytes.",
        +    "exclusiveMinimum": 0,
        +    "type": "integer"
        +  },
        +  "type": {
        +    "const": "blob",
        +    "description": "Discriminator emitted by upload_image; always \"blob\" when present. May be omitted.",
        +    "type": "string"
        +  }
        +}
      • addedInput schema / properties / embed / properties / images / items / properties / image / required
        Added value: +[
        +  "ref",
        +  "mimeType",
        +  "size"
        +]
      • addedInput schema / properties / embed / properties / images / items / properties / image / type
        Added value: +"object"
      • changedInput schema / properties / embed / properties / images / items / required
        Previous value: -[
        -  "alt"
        -]New value: +[
        +  "alt",
        +  "image"
        +]
      • addedInput schema / properties / embed / properties / video
        Added value: +{
        +  "additionalProperties": false,
        +  "description": "A video to attach (app.bsky.embed.video). Mutually exclusive with `images`, `external`, and `quote`.",
        +  "properties": {
        +    "alt": {
        +      "description": "Accessibility alt text describing the video (max 1000 characters).",
        +      "maxLength": 1000,
        +      "type": "string"
        +    },
        +    "aspectRatio": {
        +      "additionalProperties": false,
        +      "description": "Optional aspect ratio hint (e.g. {\"width\": 16, \"height\": 9}) clients use to reserve layout space before the video loads.",
        +      "properties": {
        +        "height": {
        +          "description": "Height component of the aspect ratio.",
        +          "minimum": 1,
        +          "type": "integer"
        +        },
        +        "width": {
        +          "description": "Width component of the aspect ratio.",
        +          "minimum": 1,
        +          "type": "integer"
        +        }
        +      },
        +      "required": [
        +        "width",
        +        "height"
        +      ],
        +      "type": "object"
        +    },
        +    "captions": {
        +      "description": "Up to 20 caption tracks, each pairing a language code with a pre-uploaded .vtt caption blob descriptor.",
        +      "items": {
        +        "additionalProperties": false,
        +        "properties": {
        +          "file": {
        +            "additionalProperties": false,
        +            "description": "Pre-uploaded WebVTT caption blob descriptor: pass a `video.captions[].file` object from upload_video verbatim.",
        +            "properties": {
        +              "mimeType": {
        +                "description": "MIME type of the uploaded blob (e.g. \"image/jpeg\").",
        +                "minLength": 1,
        +                "type": "string"
        +              },
        +              "ref": {
        +                "anyOf": [
        +                  {
        +                    "description": "CID of the uploaded blob as a flat string (e.g. \"bafkrei…\").",
        +                    "minLength": 1,
        +                    "type": "string"
        +                  },
        +                  {
        +                    "additionalProperties": false,
        +                    "description": "Lexicon blob-ref object wrapping the CID as { \"$link\": \"<cid>\" }.",
        +                    "properties": {
        +                      "$link": {
        +                        "description": "CID of the uploaded blob (e.g. \"bafkrei…\").",
        +                        "minLength": 1,
        +                        "type": "string"
        +                      }
        +                    },
        +                    "required": [
        +                      "$link"
        +                    ],
        +                    "type": "object"
        +                  }
        +                ],
        +                "description": "CID reference of the uploaded blob: either the flat string returned by upload_image, or the lexicon { \"$link\": \"<cid>\" } object form."
        +              },
        +              "size": {
        +                "description": "Size of the uploaded blob in bytes.",
        +                "exclusiveMinimum": 0,
        +                "type": "integer"
        +              },
        +              "type": {
        +                "const": "blob",
        +                "description": "Discriminator emitted by upload_image; always \"blob\" when present. May be omitted.",
        +                "type": "string"
        +              }
        +            },
        +            "required": [
        +              "ref",
        +              "mimeType",
        +              "size"
        +            ],
        +            "type": "object"
        +          },
        +          "lang": {
        +            "description": "BCP-47 language code for the caption track (e.g. \"en\", \"fr\", \"pt-BR\").",
        +            "minLength": 2,
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "lang",
        +          "file"
        +        ],
        +        "type": "object"
        +      },
        +      "maxItems": 20,
        +      "type": "array"
        +    },
        +    "video": {
        +      "additionalProperties": false,
        +      "description": "Pre-uploaded PROCESSED video blob descriptor: pass the `video.blob` object from a prior upload_video call verbatim. The video must already have been uploaded and processed by the video service — this tool does not accept raw video data.",
        +      "properties": {
        +        "mimeType": {
        +          "description": "MIME type of the uploaded blob (e.g. \"image/jpeg\").",
        +          "minLength": 1,
        +          "type": "string"
        +        },
        +        "ref": {
        +          "anyOf": [
        +            {
        +              "description": "CID of the uploaded blob as a flat string (e.g. \"bafkrei…\").",
        +              "minLength": 1,
        +              "type": "string"
        +            },
        +            {
        +              "additionalProperties": false,
        +              "description": "Lexicon blob-ref object wrapping the CID as { \"$link\": \"<cid>\" }.",
        +              "properties": {
        +                "$link": {
        +                  "description": "CID of the uploaded blob (e.g. \"bafkrei…\").",
        +                  "minLength": 1,
        +                  "type": "string"
        +                }
        +              },
        +              "required": [
        +                "$link"
        +              ],
        +              "type": "object"
        +            }
        +          ],
        +          "description": "CID reference of the uploaded blob: either the flat string returned by upload_image, or the lexicon { \"$link\": \"<cid>\" } object form."
        +        },
        +        "size": {
        +          "description": "Size of the uploaded blob in bytes.",
        +          "exclusiveMinimum": 0,
        +          "type": "integer"
        +        },
        +        "type": {
        +          "const": "blob",
        +          "description": "Discriminator emitted by upload_image; always \"blob\" when present. May be omitted.",
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "ref",
        +        "mimeType",
        +        "size"
        +      ],
        +      "type": "object"
        +    }
        +  },
        +  "required": [
        +    "video"
        +  ],
        +  "type": "object"
        +}
      • addedInput schema / properties / facets
        Added value: +{
        +  "description": "Optional explicit richtext facets (byte-range annotations). For mentions, `value` is a handle or DID; for links, a URL; for hashtags, the tag without #. Omit to let the server auto-detect facets from the text.",
        +  "items": {
        +    "additionalProperties": false,
        +    "properties": {
        +      "features": {
        +        "description": "One or more features applied to the annotated span.",
        +        "items": {
        +          "additionalProperties": false,
        +          "properties": {
        +            "type": {
        +              "description": "The kind of richtext feature this span represents.",
        +              "enum": [
        +                "mention",
        +                "link",
        +                "hashtag"
        +              ],
        +              "type": "string"
        +            },
        +            "value": {
        +              "description": "For a mention: a handle or DID (a leading @ is stripped); for a link: the URL; for a hashtag: the tag (a leading # is stripped).",
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "type",
        +            "value"
        +          ],
        +          "type": "object"
        +        },
        +        "type": "array"
        +      },
        +      "index": {
        +        "additionalProperties": false,
        +        "description": "UTF-8 byte range of the text span this facet annotates.",
        +        "properties": {
        +          "byteEnd": {
        +            "description": "End byte offset (UTF-8) of the annotated span, exclusive.",
        +            "minimum": 0,
        +            "type": "integer"
        +          },
        +          "byteStart": {
        +            "description": "Start byte offset (UTF-8) of the annotated span, inclusive.",
        +            "minimum": 0,
        +            "type": "integer"
        +          }
        +        },
        +        "required": [
        +          "byteStart",
        +          "byteEnd"
        +        ],
        +        "type": "object"
        +      }
        +    },
        +    "required": [
        +      "index",
        +      "features"
        +    ],
        +    "type": "object"
        +  },
        +  "type": "array"
        +}
      • addedInput schema / properties / langs / description
        Added value: +"Optional BCP-47 language tags (e.g. en, en-US, pt-BR) declaring the languages of the post text."
      • addedInput schema / properties / quote
        Added value: +{
        +  "additionalProperties": false,
        +  "description": "Quote another post (record embed). Mutually exclusive with the `embed.images`, `embed.external`, and `embed.video` embeds.",
        +  "properties": {
        +    "cid": {
        +      "description": "CID (content hash) of the quoted post.",
        +      "minLength": 1,
        +      "type": "string"
        +    },
        +    "uri": {
        +      "description": "AT-URI of the post to quote.",
        +      "minLength": 1,
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "uri",
        +    "cid"
        +  ],
        +  "type": "object"
        +}
      • addedInput schema / properties / quoteControls
        Added value: +{
        +  "additionalProperties": false,
        +  "description": "Quote (embed) policy for this post. Only allowQuotes:false writes an app.bsky.feed.postgate record, AFTER the post is created. If that write fails after the post succeeded, the call still succeeds with gateApplied:false and a warning instead of failing.",
        +  "properties": {
        +    "allowQuotes": {
        +      "description": "Set false to disable quoting/embedding of this post: writes an app.bsky.feed.postgate record (same rkey as the post) with a disableRule. true is the network default — quoting stays enabled and no postgate record is written.",
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "allowQuotes"
        +  ],
        +  "type": "object"
        +}
      • addedInput schema / properties / reply / description
        Added value: +"Set to make this post a reply in an existing thread."
      • addedInput schema / properties / reply / properties / parent / description
        Added value: +"AT-URI of the immediate parent post being replied to."
      • addedInput schema / properties / reply / properties / root / description
        Added value: +"AT-URI of the root post of the thread being replied to."
      • addedInput schema / properties / replyControls
        Added value: +{
        +  "additionalProperties": false,
        +  "description": "Who can reply to this post. Writes an app.bsky.feed.threadgate record (same rkey as the post) AFTER the post is created. Enabled options combine, up to 5 rules. Provide the object with NO rules enabled to let nobody reply; omit it entirely to leave replies open to everyone. If the gate write fails after the post succeeded, the call still succeeds with gateApplied:false and a warning instead of failing.",
        +  "properties": {
        +    "allowFollowers": {
        +      "description": "Allow replies from accounts that follow the author (threadgate followerRule).",
        +      "type": "boolean"
        +    },
        +    "allowFollowing": {
        +      "description": "Allow replies from accounts the author follows (threadgate followingRule).",
        +      "type": "boolean"
        +    },
        +    "allowListUris": {
        +      "description": "Allow replies from members of these moderation/curation lists (threadgate listRule). Each entry must be the AT-URI of an app.bsky.graph.list record (at://did/app.bsky.graph.list/rkey); anything else is rejected before the post is created.",
        +      "items": {
        +        "description": "AT-URI of an app.bsky.graph.list record whose members may reply.",
        +        "minLength": 1,
        +        "type": "string"
        +      },
        +      "maxItems": 5,
        +      "type": "array"
        +    },
        +    "allowMentioned": {
        +      "description": "Allow replies from accounts @-mentioned in the post text (threadgate mentionRule).",
        +      "type": "boolean"
        +    }
        +  },
        +  "type": "object"
        +}
      • addedInput schema / properties / text / description
        Added value: +"The post body. Max 300 graphemes / 3000 bytes (emoji count as one grapheme). Mentions, links and #hashtags are auto-detected into richtext facets unless you supply `facets` explicitly."
      • changedInput schema / properties / text / maxLength
        Previous value: -300New value: +3000
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "properties": {
        +    "cid": {
        +      "description": "CID (content hash) of the newly created post.",
        +      "type": "string"
        +    },
        +    "gateApplied": {
        +      "description": "Present only when replyControls and/or quoteControls were requested. True when every requested gate record (threadgate/postgate) is in effect. False when the post was created but a gate write failed — the post is LIVE without the requested controls (success stays true; see `warning` for which gate failed and how to retry).",
        +      "type": "boolean"
        +    },
        +    "message": {
        +      "description": "Human-readable status message.",
        +      "type": "string"
        +    },
        +    "success": {
        +      "description": "Whether the post was created successfully.",
        +      "type": "boolean"
        +    },
        +    "uri": {
        +      "description": "AT-URI of the newly created post.",
        +      "type": "string"
        +    },
        +    "warning": {
        +      "description": "Present only when gateApplied is false: explains which gate record (threadgate and/or postgate) could not be written and how to retry. The post itself was created successfully.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "uri",
        +    "cid",
        +    "success",
        +    "message"
        +  ],
        +  "type": "object"
        +}
    • Removedcreate_rich_text_post
    • Changedcreate_thread9 fields changed
      • addedInput schema / properties / langs / description
        Added value: +"Default language tags (BCP-47) applied to every post in the thread. Individual posts can override this with their own langs field."
      • addedInput schema / properties / langs / items / description
        Added value: +"BCP-47 language tag (e.g. \"en\", \"en-US\", \"pt-BR\")."
      • addedInput schema / properties / posts / description
        Added value: +"Ordered array of posts to publish as a thread (2–25 items). Each post is automatically chained as a reply to the previous one."
      • addedInput schema / properties / posts / items / properties / langs / description
        Added value: +"Per-post language tags (BCP-47). When set, overrides the thread-level langs field for this individual post."
      • addedInput schema / properties / posts / items / properties / langs / items / description
        Added value: +"BCP-47 language tag for this post (e.g. \"en\", \"en-US\", \"pt-BR\"). Overrides the thread-level langs for this post only."
      • addedInput schema / properties / posts / items / properties / text / description
        Added value: +"Text content of this post (1–300 graphemes / 3000 bytes). Mentions (@handle), URLs, and hashtags are auto-linked via richtext facets."
      • changedInput schema / properties / posts / items / properties / text / maxLength
        Previous value: -300New value: +3000
      • addedInput schema / properties / replyControls
        Added value: +{
        +  "additionalProperties": false,
        +  "description": "Who can reply to the thread. Applies to the ROOT post only: a single app.bsky.feed.threadgate record is written with the root post’s rkey, AFTER every post in the thread is published (the in-thread replies are your own posts, created before the gate exists, so they are unaffected). Enabled options combine, up to 5 rules; provide the object with NO rules enabled to let nobody reply; omit it to leave replies open. If the gate write fails after the posts succeeded, the call still succeeds with gateApplied:false and a warning instead of failing.",
        +  "properties": {
        +    "allowFollowers": {
        +      "description": "Allow replies from accounts that follow the author (threadgate followerRule).",
        +      "type": "boolean"
        +    },
        +    "allowFollowing": {
        +      "description": "Allow replies from accounts the author follows (threadgate followingRule).",
        +      "type": "boolean"
        +    },
        +    "allowListUris": {
        +      "description": "Allow replies from members of these moderation/curation lists (threadgate listRule). Each entry must be the AT-URI of an app.bsky.graph.list record (at://did/app.bsky.graph.list/rkey); anything else is rejected before the post is created.",
        +      "items": {
        +        "description": "AT-URI of an app.bsky.graph.list record whose members may reply.",
        +        "minLength": 1,
        +        "type": "string"
        +      },
        +      "maxItems": 5,
        +      "type": "array"
        +    },
        +    "allowMentioned": {
        +      "description": "Allow replies from accounts @-mentioned in the post text (threadgate mentionRule).",
        +      "type": "boolean"
        +    }
        +  },
        +  "type": "object"
        +}
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "properties": {
        +    "failedAtPosition": {
        +      "description": "1-based position of the post that failed; present only when success is false.",
        +      "type": "number"
        +    },
        +    "gateApplied": {
        +      "description": "Present only when replyControls were provided. True when the threadgate record was written on the root post (it is written after the whole thread is published, and is still attempted on the live root if the thread partially fails). False when the posts were published but the gate write failed — replies are then OPEN; see `warning`.",
        +      "type": "boolean"
        +    },
        +    "message": {
        +      "description": "Human-readable summary of the outcome, including partial-failure details when applicable.",
        +      "type": "string"
        +    },
        +    "rootPost": {
        +      "description": "URI and CID of the root (first) post, which anchors the entire thread.",
        +      "properties": {
        +        "cid": {
        +          "description": "CID of the root post record.",
        +          "type": "string"
        +        },
        +        "uri": {
        +          "description": "AT-URI of the root post.",
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "uri",
        +        "cid"
        +      ],
        +      "type": "object"
        +    },
        +    "success": {
        +      "description": "True when all posts were published; false when only some posts were created before a failure.",
        +      "type": "boolean"
        +    },
        +    "thread": {
        +      "description": "Ordered list of every post that was successfully published.",
        +      "items": {
        +        "properties": {
        +          "cid": {
        +            "description": "Content identifier (CID) of the published post record.",
        +            "type": "string"
        +          },
        +          "isRoot": {
        +            "description": "True only for the first post (the thread root).",
        +            "type": "boolean"
        +          },
        +          "position": {
        +            "description": "1-based index of this post within the thread.",
        +            "type": "number"
        +          },
        +          "text": {
        +            "description": "Original text content of this post.",
        +            "type": "string"
        +          },
        +          "uri": {
        +            "description": "AT-URI of the published post (at://did/app.bsky.feed.post/rkey).",
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "uri",
        +          "cid",
        +          "text",
        +          "position",
        +          "isRoot"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "totalPosts": {
        +      "description": "Number of posts actually published (may be less than requested if an error occurred mid-thread).",
        +      "type": "number"
        +    },
        +    "warning": {
        +      "description": "Present only when gateApplied is false: explains that the threadgate write failed and how to retry. The published posts themselves are unaffected.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "message",
        +    "thread",
        +    "rootPost",
        +    "totalPosts"
        +  ],
        +  "type": "object"
        +}
    • Changeddelete_post2 fields changed
      • addedInput schema / properties / uri / description
        Added value: +"AT-URI of the post to delete (at://did/app.bsky.feed.post/rkey). Must belong to the authenticated user."
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "properties": {
        +    "deletedPost": {
        +      "description": "Information about the deleted post.",
        +      "properties": {
        +        "uri": {
        +          "description": "AT-URI of the deleted post.",
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "uri"
        +      ],
        +      "type": "object"
        +    },
        +    "message": {
        +      "description": "Human-readable result message.",
        +      "type": "string"
        +    },
        +    "success": {
        +      "description": "Whether the post was successfully deleted.",
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "message",
        +    "deletedPost"
        +  ],
        +  "type": "object"
        +}
    • Addeddiscover
    • Changeddiscover_communities5 fields changed
      • addedInput schema / properties / includeMetrics / description
        Added value: +"When true, includes a metrics object on each community with avgFollowerCount, totalPosts, and interconnectedness values (default true)."
      • addedInput schema / properties / maxResults / description
        Added value: +"Maximum number of communities to return (1–50, default 20)."
      • addedInput schema / properties / minCommunitySize / description
        Added value: +"Minimum number of distinct members required for a cluster to be reported as a community (minimum 2, default 5)."
      • addedInput schema / properties / topic / description
        Added value: +"Keyword or phrase to search for (e.g. \"climate\", \"web dev\"). Used to retrieve relevant posts and identify active community clusters."
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "properties": {
        +    "communities": {
        +      "description": "List of discovered communities sorted by size and engagement (descending).",
        +      "items": {
        +        "properties": {
        +          "activityLevel": {
        +            "description": "Activity level based on average posts per member: high (>=3), medium (1.5–2.9), low (<1.5).",
        +            "enum": [
        +              "high",
        +              "medium",
        +              "low"
        +            ],
        +            "type": "string"
        +          },
        +          "coreMembers": {
        +            "description": "Up to 10 most relevant members, sorted by relevance score descending.",
        +            "items": {
        +              "properties": {
        +                "avatar": {
        +                  "description": "Avatar URL, if set.",
        +                  "type": "string"
        +                },
        +                "did": {
        +                  "description": "DID of the member.",
        +                  "type": "string"
        +                },
        +                "displayName": {
        +                  "description": "Display name, if set.",
        +                  "type": "string"
        +                },
        +                "followersCount": {
        +                  "description": "Follower count.",
        +                  "type": "number"
        +                },
        +                "handle": {
        +                  "description": "Bluesky handle of the member.",
        +                  "type": "string"
        +                },
        +                "postsCount": {
        +                  "description": "Posts contributed to the topic in this search.",
        +                  "type": "number"
        +                },
        +                "relevanceScore": {
        +                  "description": "Relevance score based on engagement and post count.",
        +                  "type": "number"
        +                }
        +              },
        +              "required": [
        +                "did",
        +                "handle",
        +                "followersCount",
        +                "postsCount",
        +                "relevanceScore"
        +              ],
        +              "type": "object"
        +            },
        +            "type": "array"
        +          },
        +          "description": {
        +            "description": "Auto-generated human-readable summary of the community.",
        +            "type": "string"
        +          },
        +          "metrics": {
        +            "description": "Optional metrics object; present when includeMetrics is true.",
        +            "properties": {
        +              "avgFollowerCount": {
        +                "description": "Average follower count across all members.",
        +                "type": "number"
        +              },
        +              "interconnectedness": {
        +                "description": "Ratio of cross-member interactions to community size.",
        +                "type": "number"
        +              },
        +              "totalPosts": {
        +                "description": "Total posts by all community members on this topic.",
        +                "type": "number"
        +              }
        +            },
        +            "type": "object"
        +          },
        +          "name": {
        +            "description": "Auto-generated name for the community (e.g. \"climate Community 1\").",
        +            "type": "string"
        +          },
        +          "size": {
        +            "description": "Number of distinct members in the community.",
        +            "type": "number"
        +          },
        +          "topic": {
        +            "description": "The topic keyword used to discover this community.",
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "name",
        +          "topic",
        +          "size",
        +          "coreMembers",
        +          "activityLevel",
        +          "description"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "insights": {
        +      "description": "Summary observations about the discovered communities (e.g. total members, largest community).",
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "success": {
        +      "description": "Whether the operation completed successfully.",
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "communities",
        +    "insights"
        +  ],
        +  "type": "object"
        +}
    • Removeddiscover_trending
    • Removedextract_media_from_post
    • Changedfind_influential_users6 fields changed
      • addedInput schema / properties / maxResults / description
        Added value: +"Maximum number of users to return (1–50, default 20)."
      • addedInput schema / properties / minFollowers / description
        Added value: +"Minimum follower count a user must have to be included in results (default 100)."
      • addedInput schema / properties / searchQuery / description
        Added value: +"Explicit search query string. When provided, takes precedence over topic. At least one of topic or searchQuery must be supplied."
      • addedInput schema / properties / sortBy / description
        Added value: +"Sort order for results: \"followers\" (by follower count), \"engagement\" (by computed influence score), or \"relevance\" (by how many matched posts are from that user). Default \"followers\"."
      • addedInput schema / properties / topic / description
        Added value: +"Topic keyword(s) to search for (e.g. \"climate change\"). Used as the search query when searchQuery is not provided."
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "properties": {
        +    "insights": {
        +      "description": "Human-readable insight strings summarising the results.",
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "query": {
        +      "description": "The search query that was used.",
        +      "type": "string"
        +    },
        +    "success": {
        +      "description": "Whether the operation completed successfully.",
        +      "type": "boolean"
        +    },
        +    "users": {
        +      "description": "List of influential users found, sorted per the sortBy parameter.",
        +      "items": {
        +        "properties": {
        +          "description": {
        +            "description": "User's profile bio/description, if set.",
        +            "type": "string"
        +          },
        +          "did": {
        +            "description": "Decentralized identifier of the user.",
        +            "type": "string"
        +          },
        +          "displayName": {
        +            "description": "User's display name, if set.",
        +            "type": "string"
        +          },
        +          "followersCount": {
        +            "description": "Number of followers the user has.",
        +            "type": "number"
        +          },
        +          "followsCount": {
        +            "description": "Number of accounts the user follows.",
        +            "type": "number"
        +          },
        +          "handle": {
        +            "description": "Bluesky handle of the user (e.g. alice.bsky.social).",
        +            "type": "string"
        +          },
        +          "influenceScore": {
        +            "description": "Computed influence score based on follower count, follower/following ratio, and post activity.",
        +            "type": "number"
        +          },
        +          "postsCount": {
        +            "description": "Total posts published by the user.",
        +            "type": "number"
        +          },
        +          "relevanceScore": {
        +            "description": "Number of matched search-result posts authored by this user.",
        +            "type": "number"
        +          }
        +        },
        +        "required": [
        +          "did",
        +          "handle",
        +          "followersCount",
        +          "followsCount",
        +          "postsCount",
        +          "influenceScore"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "query",
        +    "users",
        +    "insights"
        +  ],
        +  "type": "object"
        +}
    • Changedfind_similar_users5 fields changed
      • addedInput schema / properties / actor / description
        Added value: +"Handle (e.g. alice.bsky.social) or DID of the target account to find similar users for."
      • addedInput schema / properties / includeMetrics / description
        Added value: +"When true, includes a metrics object on each result with followsBaseUser and followerRatioSimilarity values (default true)."
      • addedInput schema / properties / maxResults / description
        Added value: +"Maximum number of similar users to return (1–50, default 20)."
      • addedInput schema / properties / minFollowerCount / description
        Added value: +"Minimum follower count a candidate must have to be included in results (default 0, meaning no minimum)."
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "properties": {
        +    "baseUser": {
        +      "description": "Profile summary of the queried base user.",
        +      "properties": {
        +        "did": {
        +          "description": "DID of the base user.",
        +          "type": "string"
        +        },
        +        "displayName": {
        +          "description": "Display name of the base user, if set.",
        +          "type": "string"
        +        },
        +        "handle": {
        +          "description": "Handle of the base user.",
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "did",
        +        "handle"
        +      ],
        +      "type": "object"
        +    },
        +    "insights": {
        +      "description": "Summary observations about the results (e.g. average similarity score, average follower count).",
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "similarUsers": {
        +      "description": "List of similar users sorted by descending similarity score.",
        +      "items": {
        +        "properties": {
        +          "avatar": {
        +            "description": "URL of the user's avatar image, if set.",
        +            "type": "string"
        +          },
        +          "description": {
        +            "description": "Profile bio of the user, if set.",
        +            "type": "string"
        +          },
        +          "did": {
        +            "description": "Decentralized identifier of the user.",
        +            "type": "string"
        +          },
        +          "displayName": {
        +            "description": "Display name of the user, if set.",
        +            "type": "string"
        +          },
        +          "followersCount": {
        +            "description": "Number of followers.",
        +            "type": "number"
        +          },
        +          "followsCount": {
        +            "description": "Number of accounts the user follows.",
        +            "type": "number"
        +          },
        +          "handle": {
        +            "description": "Bluesky handle of the user.",
        +            "type": "string"
        +          },
        +          "metrics": {
        +            "description": "Optional metrics object; present when includeMetrics is true.",
        +            "properties": {
        +              "followerRatioSimilarity": {
        +                "description": "Similarity of follower/following ratios (0–1).",
        +                "type": "number"
        +              },
        +              "followsBaseUser": {
        +                "description": "True when this candidate appears in a sample of the base user's followers, i.e. the candidate follows the base user. Sample-based: false does not prove absence.",
        +                "type": "boolean"
        +              }
        +            },
        +            "type": "object"
        +          },
        +          "postsCount": {
        +            "description": "Total posts by this user.",
        +            "type": "number"
        +          },
        +          "similarityReasons": {
        +            "description": "Human-readable reasons contributing to the similarity score.",
        +            "items": {
        +              "type": "string"
        +            },
        +            "type": "array"
        +          },
        +          "similarityScore": {
        +            "description": "Computed similarity score (higher is more similar).",
        +            "type": "number"
        +          }
        +        },
        +        "required": [
        +          "did",
        +          "handle",
        +          "followersCount",
        +          "followsCount",
        +          "postsCount",
        +          "similarityScore",
        +          "similarityReasons"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "success": {
        +      "description": "Whether the operation completed successfully.",
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "similarUsers",
        +    "baseUser",
        +    "insights"
        +  ],
        +  "type": "object"
        +}
    • Changedfollow_user2 fields changed
      • addedInput schema / properties / actor / description
        Added value: +"Handle (e.g. alice.bsky.social) or DID of the account to follow."
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "properties": {
        +    "cid": {
        +      "description": "CID of the follow record; empty string when the follow already existed.",
        +      "type": "string"
        +    },
        +    "followedUser": {
        +      "description": "Identifying information for the account that was followed.",
        +      "properties": {
        +        "did": {
        +          "description": "Decentralised Identifier of the followed account.",
        +          "type": "string"
        +        },
        +        "handle": {
        +          "description": "Human-readable handle of the followed account, if available.",
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "did"
        +      ],
        +      "type": "object"
        +    },
        +    "message": {
        +      "description": "Human-readable outcome message.",
        +      "type": "string"
        +    },
        +    "success": {
        +      "description": "Whether the follow is now in effect (true even if already followed).",
        +      "type": "boolean"
        +    },
        +    "uri": {
        +      "description": "AT-URI of the follow record (at://did/app.bsky.graph.follow/rkey).",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "uri",
        +    "cid",
        +    "success",
        +    "message",
        +    "followedUser"
        +  ],
        +  "type": "object"
        +}
    • Removedgenerate_alt_text
    • Changedgenerate_link_preview2 fields changed
      • addedInput schema / properties / url / description
        Added value: +"Fully-qualified HTTP or HTTPS URL of the webpage to preview. SSRF-safe: private/internal IP ranges and non-HTTP schemes are rejected. The server fetches up to 2 MB of the page HTML and up to 1 MB for the og:image thumbnail."
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "properties": {
        +    "message": {
        +      "description": "Human-readable status message.",
        +      "type": "string"
        +    },
        +    "preview": {
        +      "description": "Link preview data suitable for use as an AT Protocol external embed.",
        +      "properties": {
        +        "description": {
        +          "description": "Page description from meta description or og:description (truncated to 1000 characters).",
        +          "type": "string"
        +        },
        +        "thumb": {
        +          "description": "Uploaded thumbnail blob reference, present only when an og:image was found and successfully downloaded.",
        +          "properties": {
        +            "blob": {
        +              "description": "AT Protocol blob descriptor for the thumbnail image.",
        +              "properties": {
        +                "mimeType": {
        +                  "description": "MIME type of the thumbnail image (e.g. \"image/jpeg\").",
        +                  "type": "string"
        +                },
        +                "ref": {
        +                  "description": "CID reference string (bafkrei…) of the uploaded thumbnail blob.",
        +                  "type": "string"
        +                },
        +                "size": {
        +                  "description": "Size of the thumbnail blob in bytes.",
        +                  "type": "number"
        +                },
        +                "type": {
        +                  "description": "Always \"blob\".",
        +                  "type": "string"
        +                }
        +              },
        +              "required": [
        +                "type",
        +                "ref",
        +                "mimeType",
        +                "size"
        +              ],
        +              "type": "object"
        +            }
        +          },
        +          "required": [
        +            "blob"
        +          ],
        +          "type": "object"
        +        },
        +        "title": {
        +          "description": "Page title extracted from <title> or og:title (truncated to 300 characters).",
        +          "type": "string"
        +        },
        +        "uri": {
        +          "description": "The original URL that was previewed.",
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "uri",
        +        "title",
        +        "description"
        +      ],
        +      "type": "object"
        +    },
        +    "success": {
        +      "description": "Whether the preview was generated successfully.",
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "message",
        +    "preview"
        +  ],
        +  "type": "object"
        +}
    • Addedget_author_feed
    • Addedget_bookmarks
    • Addedget_conversation_messages
    • Changedget_custom_feed4 fields changed
      • addedInput schema / properties / cursor / description
        Added value: +"Opaque pagination cursor from the previous response cursor field; omit for the first page."
      • addedInput schema / properties / feedUri / description
        Added value: +"AT-URI of the custom algorithm feed generator (at://did/app.bsky.feed.generator/rkey)."
      • addedInput schema / properties / limit / description
        Added value: +"Max posts to return per page (1–100, default 50)."
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "properties": {
        +    "cursor": {
        +      "description": "Pagination cursor for the next page; absent when no more pages.",
        +      "type": "string"
        +    },
        +    "feed": {
        +      "description": "Metadata about the feed generator.",
        +      "properties": {
        +        "creator": {
        +          "description": "Optional profile of the feed creator (present when generator metadata is available).",
        +          "properties": {
        +            "did": {
        +              "description": "DID of the creator.",
        +              "type": "string"
        +            },
        +            "displayName": {
        +              "description": "Optional display name of the creator.",
        +              "type": "string"
        +            },
        +            "handle": {
        +              "description": "Handle of the creator.",
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "did",
        +            "handle"
        +          ],
        +          "type": "object"
        +        },
        +        "description": {
        +          "description": "Optional description of the feed.",
        +          "type": "string"
        +        },
        +        "displayName": {
        +          "description": "Optional display name of the feed.",
        +          "type": "string"
        +        },
        +        "uri": {
        +          "description": "AT-URI of the feed generator.",
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "uri"
        +      ],
        +      "type": "object"
        +    },
        +    "posts": {
        +      "description": "Paginated posts from the feed.",
        +      "items": {
        +        "properties": {
        +          "author": {
        +            "description": "Author of the post.",
        +            "properties": {
        +              "avatar": {
        +                "description": "Optional avatar image URL.",
        +                "type": "string"
        +              },
        +              "did": {
        +                "description": "DID of the author.",
        +                "type": "string"
        +              },
        +              "displayName": {
        +                "description": "Optional display name.",
        +                "type": "string"
        +              },
        +              "handle": {
        +                "description": "Handle of the author.",
        +                "type": "string"
        +              }
        +            },
        +            "required": [
        +              "did",
        +              "handle"
        +            ],
        +            "type": "object"
        +          },
        +          "cid": {
        +            "description": "CID of the post.",
        +            "type": "string"
        +          },
        +          "createdAt": {
        +            "description": "ISO 8601 timestamp when the post was created.",
        +            "type": "string"
        +          },
        +          "isLiked": {
        +            "description": "Whether the authenticated user has liked this post.",
        +            "type": "boolean"
        +          },
        +          "isReposted": {
        +            "description": "Whether the authenticated user has reposted this post.",
        +            "type": "boolean"
        +          },
        +          "likeCount": {
        +            "description": "Number of likes.",
        +            "type": "number"
        +          },
        +          "replyCount": {
        +            "description": "Number of replies.",
        +            "type": "number"
        +          },
        +          "repostCount": {
        +            "description": "Number of reposts.",
        +            "type": "number"
        +          },
        +          "text": {
        +            "description": "Plain text content of the post.",
        +            "type": "string"
        +          },
        +          "uri": {
        +            "description": "AT-URI of the post.",
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "uri",
        +          "cid",
        +          "author",
        +          "text",
        +          "createdAt",
        +          "replyCount",
        +          "repostCount",
        +          "likeCount",
        +          "isLiked",
        +          "isReposted"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "success": {
        +      "description": "Whether the feed was retrieved successfully.",
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "feed",
        +    "posts"
        +  ],
        +  "type": "object"
        +}
    • Removedget_followers
    • Removedget_follows
    • Changedget_list4 fields changed
      • addedInput schema / properties / cursor / description
        Added value: +"Opaque pagination cursor from the previous response cursor field; omit for the first page."
      • addedInput schema / properties / limit / description
        Added value: +"Max list members to return per page (1–100, default 50)."
      • addedInput schema / properties / listUri / description
        Added value: +"AT-URI of the list to read (at://did/app.bsky.graph.list/rkey)."
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "properties": {
        +    "cursor": {
        +      "description": "Pagination cursor for the next page; absent when no more pages.",
        +      "type": "string"
        +    },
        +    "items": {
        +      "description": "Paginated list of member entries.",
        +      "items": {
        +        "properties": {
        +          "subject": {
        +            "description": "Profile of the list member.",
        +            "properties": {
        +              "avatar": {
        +                "description": "Optional avatar image URL.",
        +                "type": "string"
        +              },
        +              "did": {
        +                "description": "DID of the member.",
        +                "type": "string"
        +              },
        +              "displayName": {
        +                "description": "Optional display name.",
        +                "type": "string"
        +              },
        +              "handle": {
        +                "description": "Handle of the member.",
        +                "type": "string"
        +              }
        +            },
        +            "required": [
        +              "did",
        +              "handle"
        +            ],
        +            "type": "object"
        +          },
        +          "uri": {
        +            "description": "AT-URI of the listitem record.",
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "uri",
        +          "subject"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "list": {
        +      "description": "Metadata about the list.",
        +      "properties": {
        +        "creator": {
        +          "description": "Profile of the list creator.",
        +          "properties": {
        +            "did": {
        +              "description": "DID of the creator.",
        +              "type": "string"
        +            },
        +            "displayName": {
        +              "description": "Optional display name of the creator.",
        +              "type": "string"
        +            },
        +            "handle": {
        +              "description": "Handle of the creator.",
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "did",
        +            "handle"
        +          ],
        +          "type": "object"
        +        },
        +        "description": {
        +          "description": "Optional plain-text description.",
        +          "type": "string"
        +        },
        +        "itemCount": {
        +          "description": "Total number of members in the list.",
        +          "type": "number"
        +        },
        +        "name": {
        +          "description": "Display name of the list.",
        +          "type": "string"
        +        },
        +        "purpose": {
        +          "description": "List purpose: \"curatelist\" or \"modlist\".",
        +          "type": "string"
        +        },
        +        "uri": {
        +          "description": "AT-URI of the list.",
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "uri",
        +        "name",
        +        "purpose",
        +        "creator",
        +        "itemCount"
        +      ],
        +      "type": "object"
        +    },
        +    "success": {
        +      "description": "Whether the list was retrieved successfully.",
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "list",
        +    "items"
        +  ],
        +  "type": "object"
        +}
    • Changedget_notifications6 fields changed
      • addedInput schema / properties / countOnly
        Added value: +{
        +  "description": "When true, return only the unread count and skip fetching the notification list (cheap badge-number path).",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / cursor / description
        Added value: +"Opaque pagination cursor from the previous response cursor field; omit for the first page."
      • removedInput schema / properties / limit / default
        Removed value: -50
      • addedInput schema / properties / limit / description
        Added value: +"Max notifications per page (1–100, default 50)."
      • addedInput schema / properties / seenAt / description
        Added value: +"ISO 8601 timestamp; only return notifications that occurred after this time. Optional filter."
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "description": "Notification result. When countOnly is true only unreadCount is present; otherwise the full list is returned.",
        +  "properties": {
        +    "cursor": {
        +      "description": "Opaque cursor for the next page. Present only when countOnly is false/absent.",
        +      "type": "string"
        +    },
        +    "hasMore": {
        +      "description": "True when a next page is available. Present only when countOnly is false/absent.",
        +      "type": "boolean"
        +    },
        +    "notifications": {
        +      "description": "Notification entries. Present only when countOnly is false/absent.",
        +      "items": {
        +        "properties": {
        +          "author": {
        +            "description": "Author of the action that triggered the notification.",
        +            "properties": {
        +              "avatar": {
        +                "type": "string"
        +              },
        +              "did": {
        +                "type": "string"
        +              },
        +              "displayName": {
        +                "type": "string"
        +              },
        +              "followersCount": {
        +                "type": "number"
        +              },
        +              "followsCount": {
        +                "type": "number"
        +              },
        +              "handle": {
        +                "type": "string"
        +              },
        +              "postsCount": {
        +                "type": "number"
        +              }
        +            },
        +            "required": [
        +              "did",
        +              "handle"
        +            ],
        +            "type": "object"
        +          },
        +          "cid": {
        +            "description": "CID of the notification record.",
        +            "type": "string"
        +          },
        +          "indexedAt": {
        +            "description": "ISO 8601 timestamp when indexed.",
        +            "type": "string"
        +          },
        +          "isRead": {
        +            "description": "Whether the notification has been read.",
        +            "type": "boolean"
        +          },
        +          "labels": {
        +            "description": "Moderation labels, if any.",
        +            "type": "array"
        +          },
        +          "reason": {
        +            "description": "Why this notification was generated. Known values: like, repost, follow, mention, reply, quote, starterpack-joined, verified, unverified, like-via-repost, repost-via-repost, subscribed-post. The AT Protocol lexicon defines this as an open union, so treat unrecognized values as new reason kinds rather than errors.",
        +            "type": "string"
        +          },
        +          "record": {
        +            "description": "Raw lexicon record payload.",
        +            "type": "object"
        +          },
        +          "uri": {
        +            "description": "AT URI of the notification record.",
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "uri",
        +          "cid",
        +          "author",
        +          "reason",
        +          "isRead",
        +          "indexedAt"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "seenAt": {
        +      "description": "The timestamp up to which notifications have been seen. Present only when countOnly is false/absent.",
        +      "type": "string"
        +    },
        +    "success": {
        +      "description": "Whether the request succeeded.",
        +      "type": "boolean"
        +    },
        +    "unreadCount": {
        +      "description": "Number of unread notifications (always present).",
        +      "type": "number"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "unreadCount"
        +  ],
        +  "type": "object"
        +}
    • Changedget_post_context8 fields changed
      • addedInput schema / properties / depth
        Added value: +{
        +  "description": "How many levels of replies to fetch (0–10, default 6).",
        +  "maximum": 10,
        +  "minimum": 0,
        +  "type": "integer"
        +}
      • addedInput schema / properties / includeAuthorProfile / description
        Added value: +"Include the post author's full profile. Default true."
      • addedInput schema / properties / includeEngagement / description
        Added value: +"Include computed engagement metrics (likes, reposts, replies, rate, age). Default true."
      • addedInput schema / properties / includeMedia
        Added value: +{
        +  "default": false,
        +  "description": "Extract media embeds (images, videos, external links, quote posts) from the post. Default false.",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / includeThread / description
        Added value: +"Include thread context (parent chain, root, and replies). Default true."
      • addedInput schema / properties / parentHeight
        Added value: +{
        +  "description": "How many parent posts up the chain to fetch (0–80, default 80).",
        +  "maximum": 80,
        +  "minimum": 0,
        +  "type": "integer"
        +}
      • addedInput schema / properties / uri / description
        Added value: +"AT-URI of the post to read (at://...)."
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "properties": {
        +    "authorProfile": {
        +      "description": "The post author's full profile (present when includeAuthorProfile is true).",
        +      "type": "object"
        +    },
        +    "engagement": {
        +      "description": "Computed engagement metrics (present when includeEngagement is true).",
        +      "properties": {
        +        "ageHours": {
        +          "description": "Age of the post in hours.",
        +          "type": "number"
        +        },
        +        "engagementRate": {
        +          "description": "Total engagement per hour since the post was created.",
        +          "type": "number"
        +        },
        +        "likeCount": {
        +          "description": "Number of likes.",
        +          "type": "number"
        +        },
        +        "replyCount": {
        +          "description": "Number of replies.",
        +          "type": "number"
        +        },
        +        "repostCount": {
        +          "description": "Number of reposts.",
        +          "type": "number"
        +        },
        +        "totalEngagement": {
        +          "description": "Sum of likes, reposts, and replies.",
        +          "type": "number"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    "media": {
        +      "description": "Media embeds extracted from the post (present when includeMedia is true).",
        +      "properties": {
        +        "externalLinks": {
        +          "description": "External link cards (uri, title, description, thumb).",
        +          "items": {
        +            "type": "object"
        +          },
        +          "type": "array"
        +        },
        +        "images": {
        +          "description": "Image embeds with alt text and aspect ratio.",
        +          "items": {
        +            "type": "object"
        +          },
        +          "type": "array"
        +        },
        +        "quotePosts": {
        +          "description": "Quoted posts referenced by record embeds (uri, cid).",
        +          "items": {
        +            "type": "object"
        +          },
        +          "type": "array"
        +        },
        +        "videos": {
        +          "description": "Video embeds with alt text and aspect ratio.",
        +          "items": {
        +            "type": "object"
        +          },
        +          "type": "array"
        +        }
        +      },
        +      "required": [
        +        "images",
        +        "videos",
        +        "externalLinks",
        +        "quotePosts"
        +      ],
        +      "type": "object"
        +    },
        +    "post": {
        +      "description": "The requested post (normalized post view).",
        +      "type": "object"
        +    },
        +    "success": {
        +      "description": "Whether the post context was retrieved.",
        +      "type": "boolean"
        +    },
        +    "thread": {
        +      "description": "Thread context (present when includeThread is true): the immediate parent, the true thread root, the direct replies, and the parent-chain depth.",
        +      "properties": {
        +        "depth": {
        +          "description": "Number of ancestors between the post and the thread root.",
        +          "type": "number"
        +        },
        +        "parent": {
        +          "description": "The immediate parent post, if any.",
        +          "type": "object"
        +        },
        +        "replies": {
        +          "description": "Direct replies to the post.",
        +          "items": {
        +            "type": "object"
        +          },
        +          "type": "array"
        +        },
        +        "root": {
        +          "description": "The topmost ancestor (true thread root).",
        +          "type": "object"
        +        }
        +      },
        +      "type": "object"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "post"
        +  ],
        +  "type": "object"
        +}
    • Removedget_recent_events
    • Addedget_starter_pack
    • Removedget_streaming_status
    • Removedget_thread
    • Changedget_timeline4 fields changed
      • addedInput schema / properties / algorithm / description
        Added value: +"Feed algorithm or generator AT-URI to use (e.g. \"reverse-chronological\" or at://did/.../app.bsky.feed.generator/rkey). Omit for the default home timeline algorithm."
      • addedInput schema / properties / cursor / description
        Added value: +"Opaque pagination cursor from the previous response cursor field; omit for the first page."
      • addedInput schema / properties / limit / description
        Added value: +"Maximum number of posts to return (1–100, default 50)."
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "properties": {
        +    "algorithm": {
        +      "description": "The algorithm or feed generator AT-URI used for this request, if provided.",
        +      "type": "string"
        +    },
        +    "cursor": {
        +      "description": "Opaque pagination cursor to pass in a subsequent request to retrieve the next page; absent when there are no more results.",
        +      "type": "string"
        +    },
        +    "hasMore": {
        +      "description": "Whether additional pages of results are available.",
        +      "type": "boolean"
        +    },
        +    "posts": {
        +      "description": "List of posts in the timeline.",
        +      "items": {
        +        "properties": {
        +          "author": {
        +            "description": "Author of the post.",
        +            "properties": {
        +              "avatar": {
        +                "description": "URL of the author's avatar image.",
        +                "type": "string"
        +              },
        +              "did": {
        +                "description": "Author's DID.",
        +                "type": "string"
        +              },
        +              "displayName": {
        +                "description": "Author's display name.",
        +                "type": "string"
        +              },
        +              "handle": {
        +                "description": "Author's handle.",
        +                "type": "string"
        +              }
        +            },
        +            "type": "object"
        +          },
        +          "cid": {
        +            "description": "Content identifier (CID) of the post.",
        +            "type": "string"
        +          },
        +          "indexedAt": {
        +            "description": "ISO 8601 timestamp when the post was indexed.",
        +            "type": "string"
        +          },
        +          "likeCount": {
        +            "description": "Number of likes.",
        +            "type": "number"
        +          },
        +          "record": {
        +            "description": "Post record content.",
        +            "properties": {
        +              "createdAt": {
        +                "description": "ISO 8601 timestamp when the post was created.",
        +                "type": "string"
        +              },
        +              "text": {
        +                "description": "Text content of the post.",
        +                "type": "string"
        +              }
        +            },
        +            "type": "object"
        +          },
        +          "replyCount": {
        +            "description": "Number of replies to the post.",
        +            "type": "number"
        +          },
        +          "repostCount": {
        +            "description": "Number of reposts.",
        +            "type": "number"
        +          },
        +          "uri": {
        +            "description": "AT-URI of the post.",
        +            "type": "string"
        +          }
        +        },
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "success": {
        +      "description": "Whether the timeline was retrieved successfully.",
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "posts",
        +    "hasMore"
        +  ],
        +  "type": "object"
        +}
    • Addedget_user_connections
    • Changedget_user_profile2 fields changed
      • addedInput schema / properties / actor / description
        Added value: +"Handle (e.g. alice.bsky.social) or DID (e.g. did:plc:...) of the account whose profile to retrieve."
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "properties": {
        +    "profile": {
        +      "description": "Full profile record for the requested account.",
        +      "properties": {
        +        "avatar": {
        +          "description": "URL of the account avatar image, if set.",
        +          "type": "string"
        +        },
        +        "banner": {
        +          "description": "URL of the profile banner image, if set.",
        +          "type": "string"
        +        },
        +        "description": {
        +          "description": "Bio/description text set by the user, if any.",
        +          "type": "string"
        +        },
        +        "did": {
        +          "description": "Decentralized identifier (DID) of the account.",
        +          "type": "string"
        +        },
        +        "displayName": {
        +          "description": "Display name set by the user, if any.",
        +          "type": "string"
        +        },
        +        "followersCount": {
        +          "description": "Number of accounts following this user.",
        +          "type": "number"
        +        },
        +        "followsCount": {
        +          "description": "Number of accounts this user follows.",
        +          "type": "number"
        +        },
        +        "handle": {
        +          "description": "Human-readable handle of the account (e.g. alice.bsky.social).",
        +          "type": "string"
        +        },
        +        "indexedAt": {
        +          "description": "ISO 8601 timestamp of when this profile was last indexed.",
        +          "type": "string"
        +        },
        +        "labels": {
        +          "description": "Moderation labels applied to this account.",
        +          "items": {
        +            "properties": {
        +              "cid": {
        +                "description": "CID of the labelled record version.",
        +                "type": "string"
        +              },
        +              "cts": {
        +                "description": "ISO 8601 timestamp when the label was created.",
        +                "type": "string"
        +              },
        +              "src": {
        +                "description": "DID of the labeler that issued this label.",
        +                "type": "string"
        +              },
        +              "uri": {
        +                "description": "AT-URI of the subject that was labelled.",
        +                "type": "string"
        +              },
        +              "val": {
        +                "description": "Label value string (e.g. \"spam\", \"nudity\").",
        +                "type": "string"
        +              }
        +            },
        +            "required": [
        +              "src",
        +              "uri",
        +              "cid",
        +              "val",
        +              "cts"
        +            ],
        +            "type": "object"
        +          },
        +          "type": "array"
        +        },
        +        "postsCount": {
        +          "description": "Total number of posts authored by this user.",
        +          "type": "number"
        +        },
        +        "viewer": {
        +          "description": "Viewer-specific relationship data; only populated when authenticated.",
        +          "properties": {
        +            "blockedBy": {
        +              "description": "Whether this account has blocked the authenticated user.",
        +              "type": "boolean"
        +            },
        +            "blocking": {
        +              "description": "AT-URI of the block record if the authenticated user is blocking this account.",
        +              "type": "string"
        +            },
        +            "followedBy": {
        +              "description": "AT-URI of the follow record if this account follows the authenticated user.",
        +              "type": "string"
        +            },
        +            "following": {
        +              "description": "AT-URI of the follow record if the authenticated user follows this account.",
        +              "type": "string"
        +            },
        +            "muted": {
        +              "description": "Whether the authenticated user has muted this account.",
        +              "type": "boolean"
        +            }
        +          },
        +          "type": "object"
        +        }
        +      },
        +      "required": [
        +        "did",
        +        "handle"
        +      ],
        +      "type": "object"
        +    },
        +    "success": {
        +      "description": "Whether the profile was retrieved successfully.",
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "profile"
        +  ],
        +  "type": "object"
        +}
    • Changedget_user_summary5 fields changed
      • addedInput schema / properties / actor / description
        Added value: +"Handle (e.g. alice.bsky.social) or DID of the target account."
      • addedInput schema / properties / includeEngagementStats / description
        Added value: +"Whether to compute engagement statistics (avg likes, reposts, replies) over the recent posts. Default true."
      • addedInput schema / properties / includeRecentPosts / description
        Added value: +"Whether to include recent posts in the response. Default true."
      • addedInput schema / properties / postLimit / description
        Added value: +"Number of recent posts to fetch (1–50, default 10). Only used when includeRecentPosts or includeEngagementStats is true."
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "properties": {
        +    "engagementStats": {
        +      "description": "Engagement statistics computed over the fetched posts (present when includeEngagementStats is true).",
        +      "properties": {
        +        "averageLikesPerPost": {
        +          "description": "Mean likes per post.",
        +          "type": "number"
        +        },
        +        "averageRepliesPerPost": {
        +          "description": "Mean replies per post.",
        +          "type": "number"
        +        },
        +        "averageRepostsPerPost": {
        +          "description": "Mean reposts per post.",
        +          "type": "number"
        +        },
        +        "mostLikedPost": {
        +          "description": "The post with the highest like count.",
        +          "type": "object"
        +        },
        +        "mostRepostedPost": {
        +          "description": "The post with the highest repost count.",
        +          "type": "object"
        +        },
        +        "totalLikes": {
        +          "description": "Sum of likes across analysed posts.",
        +          "type": "number"
        +        },
        +        "totalPosts": {
        +          "description": "Number of posts analysed.",
        +          "type": "number"
        +        },
        +        "totalReplies": {
        +          "description": "Sum of replies across analysed posts.",
        +          "type": "number"
        +        },
        +        "totalReposts": {
        +          "description": "Sum of reposts across analysed posts.",
        +          "type": "number"
        +        }
        +      },
        +      "required": [
        +        "totalPosts",
        +        "totalLikes",
        +        "totalReposts",
        +        "totalReplies",
        +        "averageLikesPerPost",
        +        "averageRepostsPerPost",
        +        "averageRepliesPerPost"
        +      ],
        +      "type": "object"
        +    },
        +    "profile": {
        +      "description": "The user's full profile, including optional viewer context when authenticated.",
        +      "properties": {
        +        "avatar": {
        +          "description": "URL to the avatar image.",
        +          "type": "string"
        +        },
        +        "description": {
        +          "description": "Profile bio.",
        +          "type": "string"
        +        },
        +        "did": {
        +          "description": "The user's DID.",
        +          "type": "string"
        +        },
        +        "displayName": {
        +          "description": "The user's display name.",
        +          "type": "string"
        +        },
        +        "followersCount": {
        +          "description": "Number of followers.",
        +          "type": "number"
        +        },
        +        "followsCount": {
        +          "description": "Number of accounts followed.",
        +          "type": "number"
        +        },
        +        "handle": {
        +          "description": "The user's handle.",
        +          "type": "string"
        +        },
        +        "indexedAt": {
        +          "description": "ISO timestamp when the profile was first indexed.",
        +          "type": "string"
        +        },
        +        "postsCount": {
        +          "description": "Total number of posts.",
        +          "type": "number"
        +        },
        +        "viewer": {
        +          "description": "Viewer relationship context (muted, blocking, following, etc.) — only present when authenticated.",
        +          "properties": {
        +            "blockedBy": {
        +              "description": "Whether this account has blocked the authenticated user.",
        +              "type": "boolean"
        +            },
        +            "blocking": {
        +              "description": "AT-URI of the block record if the authenticated user is blocking this account.",
        +              "type": "string"
        +            },
        +            "followedBy": {
        +              "description": "AT-URI of the follow record if this account follows the authenticated user.",
        +              "type": "string"
        +            },
        +            "following": {
        +              "description": "AT-URI of the follow record if the authenticated user follows this account.",
        +              "type": "string"
        +            },
        +            "muted": {
        +              "description": "Whether the authenticated user has muted this account.",
        +              "type": "boolean"
        +            }
        +          },
        +          "type": "object"
        +        }
        +      },
        +      "required": [
        +        "did",
        +        "handle"
        +      ],
        +      "type": "object"
        +    },
        +    "recentPosts": {
        +      "description": "Recent posts (present when includeRecentPosts is true).",
        +      "items": {
        +        "description": "Normalized post view.",
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "success": {
        +      "description": "Whether the summary was retrieved successfully.",
        +      "type": "boolean"
        +    },
        +    "summary": {
        +      "description": "Condensed key metrics for quick consumption.",
        +      "properties": {
        +        "displayName": {
        +          "description": "The user's display name.",
        +          "type": "string"
        +        },
        +        "followersCount": {
        +          "description": "Follower count.",
        +          "type": "number"
        +        },
        +        "followsCount": {
        +          "description": "Following count.",
        +          "type": "number"
        +        },
        +        "handle": {
        +          "description": "The user's handle.",
        +          "type": "string"
        +        },
        +        "isAuthenticated": {
        +          "description": "Whether the current session is authenticated.",
        +          "type": "boolean"
        +        },
        +        "postsCount": {
        +          "description": "Total post count.",
        +          "type": "number"
        +        }
        +      },
        +      "required": [
        +        "handle",
        +        "followersCount",
        +        "followsCount",
        +        "postsCount",
        +        "isAuthenticated"
        +      ],
        +      "type": "object"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "profile",
        +    "summary"
        +  ],
        +  "type": "object"
        +}
    • Removedhandle_oauth_callback
    • Changedlike_post3 fields changed
      • addedInput schema / properties / cid / description
        Added value: +"CID of the post record; used to confirm the exact version being liked."
      • addedInput schema / properties / uri / description
        Added value: +"AT-URI of the post to like (at://did/app.bsky.feed.post/rkey)."
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "properties": {
        +    "alreadyLiked": {
        +      "description": "True when the post was already liked; the existing like record URI is returned and no duplicate is created.",
        +      "type": "boolean"
        +    },
        +    "cid": {
        +      "description": "CID of the newly created like record. Absent when the post was already liked (alreadyLiked=true): the existing record is reused and its CID is not re-fetched.",
        +      "type": "string"
        +    },
        +    "likedPost": {
        +      "description": "The post that was liked.",
        +      "properties": {
        +        "cid": {
        +          "description": "CID of the liked post.",
        +          "type": "string"
        +        },
        +        "uri": {
        +          "description": "AT-URI of the liked post.",
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "uri",
        +        "cid"
        +      ],
        +      "type": "object"
        +    },
        +    "message": {
        +      "description": "Human-readable status message.",
        +      "type": "string"
        +    },
        +    "success": {
        +      "description": "Whether the like operation succeeded.",
        +      "type": "boolean"
        +    },
        +    "uri": {
        +      "description": "AT-URI of the newly created (or existing) like record.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "uri",
        +    "success",
        +    "message",
        +    "alreadyLiked",
        +    "likedPost"
        +  ],
        +  "type": "object"
        +}
    • Addedlist_conversations
    • Addedmark_notifications_seen
    • Removedmonitor_keywords
    • Changedmute_user2 fields changed
      • addedInput schema / properties / actor / description
        Added value: +"Handle (e.g. alice.bsky.social) or DID of the account to mute."
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "properties": {
        +    "message": {
        +      "description": "Human-readable result message.",
        +      "type": "string"
        +    },
        +    "mutedUser": {
        +      "description": "Details of the muted account.",
        +      "properties": {
        +        "actor": {
        +          "description": "Handle or DID supplied in the request.",
        +          "type": "string"
        +        },
        +        "did": {
        +          "description": "Resolved DID of the muted account, if returned by the API.",
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "actor"
        +      ],
        +      "type": "object"
        +    },
        +    "success": {
        +      "description": "Whether the mute operation succeeded.",
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "message",
        +    "mutedUser"
        +  ],
        +  "type": "object"
        +}
    • Removedrecommend_content
    • Removedrefresh_oauth_tokens
    • Addedremove_bookmark
    • Changedremove_from_list3 fields changed
      • addedInput schema / properties / actor / description
        Added value: +"Handle (e.g. alice.bsky.social) or DID of the user to remove from the list."
      • addedInput schema / properties / listUri / description
        Added value: +"AT-URI of the target list (at://did/app.bsky.graph.list/rkey)."
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "properties": {
        +    "message": {
        +      "description": "Human-readable result message.",
        +      "type": "string"
        +    },
        +    "removedFrom": {
        +      "description": "Identifies the list and actor involved in the operation.",
        +      "properties": {
        +        "actor": {
        +          "description": "Handle or DID of the user that was removed.",
        +          "type": "string"
        +        },
        +        "listUri": {
        +          "description": "AT-URI of the list.",
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "listUri",
        +        "actor"
        +      ],
        +      "type": "object"
        +    },
        +    "success": {
        +      "description": "Whether the user was removed. False when the user was not in the list, or when the list was too large to scan fully (see message).",
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "message",
        +    "removedFrom"
        +  ],
        +  "type": "object"
        +}
    • Changedreply_to_post7 fields changed
      • addedInput schema / properties / langs / description
        Added value: +"List of BCP-47 language tags indicating the language(s) of the reply text. Omit if unknown."
      • addedInput schema / properties / langs / items / description
        Added value: +"A single BCP-47 language tag for the reply text (e.g. \"en\", \"en-US\", \"pt-BR\")."
      • addedInput schema / properties / parent / description
        Added value: +"AT-URI of the immediate post being replied to (at://did/app.bsky.feed.post/rkey). May equal root when replying directly to the thread starter."
      • addedInput schema / properties / root / description
        Added value: +"AT-URI of the top-level post that started the thread (at://did/app.bsky.feed.post/rkey). Must be the original root even when replying to a nested reply."
      • addedInput schema / properties / text / description
        Added value: +"Text content of the reply (1–300 graphemes / up to 3000 bytes). Mentions (@handle), links, and hashtags are automatically resolved into rich-text facets."
      • changedInput schema / properties / text / maxLength
        Previous value: -300New value: +3000
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "properties": {
        +    "cid": {
        +      "description": "Content identifier (CID) of the newly created reply record.",
        +      "type": "string"
        +    },
        +    "message": {
        +      "description": "Human-readable status message describing the outcome.",
        +      "type": "string"
        +    },
        +    "replyTo": {
        +      "description": "The root and parent URIs that this reply is linked to.",
        +      "properties": {
        +        "parent": {
        +          "description": "AT-URI of the immediate post that was replied to.",
        +          "type": "string"
        +        },
        +        "root": {
        +          "description": "AT-URI of the top-level post that started the thread.",
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "root",
        +        "parent"
        +      ],
        +      "type": "object"
        +    },
        +    "success": {
        +      "description": "True when the reply was created successfully.",
        +      "type": "boolean"
        +    },
        +    "uri": {
        +      "description": "AT-URI of the newly created reply post (at://did/app.bsky.feed.post/rkey).",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "uri",
        +    "cid",
        +    "success",
        +    "message",
        +    "replyTo"
        +  ],
        +  "type": "object"
        +}
    • Changedreport_content6 fields changed
      • addedInput schema / properties / reason / description
        Added value: +"Optional free-text explanation of the violation (max 2000 characters). Providing detail helps moderators act faster."
      • addedInput schema / properties / reasonType / description
        Added value: +"Category of the violation: \"spam\" for unsolicited bulk content, \"violation\" for ToS breach, \"misleading\" for misinformation, \"sexual\" for adult content, \"rude\" for harassment, \"other\" for anything else."
      • addedInput schema / properties / subject / description
        Added value: +"Strong reference identifying the specific content record to report."
      • addedInput schema / properties / subject / properties / cid / description
        Added value: +"CID (Content Identifier) of the specific version of the record to report."
      • addedInput schema / properties / subject / properties / uri / description
        Added value: +"AT-URI of the content to report (at://did/collection/rkey)."
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "properties": {
        +    "message": {
        +      "description": "Human-readable result message.",
        +      "type": "string"
        +    },
        +    "reportDetails": {
        +      "description": "Echo of the submitted report parameters.",
        +      "properties": {
        +        "reason": {
        +          "description": "Optional free-text explanation supplied in the request.",
        +          "type": "string"
        +        },
        +        "reasonType": {
        +          "description": "The short reason category supplied in the request.",
        +          "type": "string"
        +        },
        +        "subject": {
        +          "description": "AT-URI of the reported content.",
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "subject",
        +        "reasonType"
        +      ],
        +      "type": "object"
        +    },
        +    "reportId": {
        +      "description": "Numeric identifier of the newly created moderation report (as a string).",
        +      "type": "string"
        +    },
        +    "success": {
        +      "description": "Whether the report was submitted successfully.",
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "message",
        +    "reportId",
        +    "reportDetails"
        +  ],
        +  "type": "object"
        +}
    • Changedreport_user4 fields changed
      • addedInput schema / properties / actor / description
        Added value: +"Handle (e.g. alice.bsky.social) or DID of the account to report."
      • addedInput schema / properties / reason / description
        Added value: +"Optional free-text explanation of the violation (max 2000 characters). Providing detail helps moderators act faster."
      • addedInput schema / properties / reasonType / description
        Added value: +"Category of the violation: \"spam\" for unsolicited bulk content, \"violation\" for ToS breach, \"misleading\" for misinformation, \"sexual\" for adult content, \"rude\" for harassment, \"other\" for anything else."
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "properties": {
        +    "message": {
        +      "description": "Human-readable result message.",
        +      "type": "string"
        +    },
        +    "reportDetails": {
        +      "description": "Echo of the submitted report parameters.",
        +      "properties": {
        +        "actor": {
        +          "description": "Handle or DID of the reported account.",
        +          "type": "string"
        +        },
        +        "reason": {
        +          "description": "Optional free-text explanation supplied in the request.",
        +          "type": "string"
        +        },
        +        "reasonType": {
        +          "description": "The short reason category supplied in the request.",
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "actor",
        +        "reasonType"
        +      ],
        +      "type": "object"
        +    },
        +    "reportId": {
        +      "description": "Numeric identifier of the newly created moderation report (as a string).",
        +      "type": "string"
        +    },
        +    "success": {
        +      "description": "Whether the report was submitted successfully.",
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "message",
        +    "reportId",
        +    "reportDetails"
        +  ],
        +  "type": "object"
        +}
    • Changedrepost5 fields changed
      • addedInput schema / properties / cid / description
        Added value: +"CID of the post to repost or quote; used for content integrity verification."
      • addedInput schema / properties / text / description
        Added value: +"Optional quote text (up to 300 graphemes / 3000 bytes). When provided the result is a quote post embedding the original; omit for a plain repost."
      • changedInput schema / properties / text / maxLength
        Previous value: -300New value: +3000
      • addedInput schema / properties / uri / description
        Added value: +"AT-URI of the post to repost or quote (at://did/app.bsky.feed.post/rkey)."
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "properties": {
        +    "alreadyReposted": {
        +      "description": "True when a plain repost already existed; the existing record URI/CID is returned.",
        +      "type": "boolean"
        +    },
        +    "cid": {
        +      "description": "CID of the newly created repost or quote-post record. Absent when alreadyReposted=true: the existing record is reused and its CID is not re-fetched.",
        +      "type": "string"
        +    },
        +    "isQuotePost": {
        +      "description": "True when text was supplied and a quote post was created rather than a plain repost.",
        +      "type": "boolean"
        +    },
        +    "message": {
        +      "description": "Human-readable status message.",
        +      "type": "string"
        +    },
        +    "repostedPost": {
        +      "description": "The original post that was reposted or quoted.",
        +      "properties": {
        +        "cid": {
        +          "description": "CID of the original post.",
        +          "type": "string"
        +        },
        +        "uri": {
        +          "description": "AT-URI of the original post.",
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "uri",
        +        "cid"
        +      ],
        +      "type": "object"
        +    },
        +    "success": {
        +      "description": "Whether the operation succeeded.",
        +      "type": "boolean"
        +    },
        +    "uri": {
        +      "description": "AT-URI of the newly created repost or quote-post record.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "uri",
        +    "success",
        +    "message",
        +    "repostedPost",
        +    "isQuotePost",
        +    "alreadyReposted"
        +  ],
        +  "type": "object"
        +}
    • Removedrevoke_oauth_tokens
    • Addedsearch_actors
    • Changedsearch_posts12 fields changed
      • addedInput schema / properties / author / description
        Added value: +"Filter to posts authored by this handle or DID (e.g. \"alice.bsky.social\" or \"did:plc:...\"). Requires a non-empty q term."
      • addedInput schema / properties / cursor / description
        Added value: +"Opaque pagination cursor from the previous response cursor field; omit for the first page."
      • addedInput schema / properties / domain / description
        Added value: +"Filter to posts containing links from this domain (e.g. \"bsky.app\"). Do not include protocol or path."
      • addedInput schema / properties / lang / description
        Added value: +"BCP-47 language tag to filter posts by language (e.g. \"en\", \"en-US\", \"pt-BR\")."
      • addedInput schema / properties / limit / description
        Added value: +"Maximum number of posts to return per page (1–100, default 25)."
      • addedInput schema / properties / mentions / description
        Added value: +"Filter to posts that mention this handle or DID (e.g. \"alice.bsky.social\" or \"did:plc:...\")."
      • addedInput schema / properties / q / description
        Added value: +"Full-text search query string (1–300 characters). Supports keywords and hashtags (e.g. \"#bluesky launch\")."
      • addedInput schema / properties / since / description
        Added value: +"ISO 8601 datetime lower bound (inclusive) for post creation time (e.g. \"2024-01-01T00:00:00Z\"). Omit to search all time."
      • addedInput schema / properties / sort / description
        Added value: +"Sort order for results: \"latest\" returns newest posts first (default), \"top\" returns most-engaged posts first."
      • addedInput schema / properties / until / description
        Added value: +"ISO 8601 datetime upper bound (exclusive) for post creation time (e.g. \"2024-12-31T23:59:59Z\"). Omit for no upper bound."
      • addedInput schema / properties / url / description
        Added value: +"Filter to posts containing this exact URL (must be a fully-qualified URL, e.g. \"https://example.com/article\")."
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "properties": {
        +    "cursor": {
        +      "description": "Opaque pagination cursor to pass as cursor in the next call; absent when no further pages exist.",
        +      "type": "string"
        +    },
        +    "hasMore": {
        +      "description": "True when a subsequent page of results is available.",
        +      "type": "boolean"
        +    },
        +    "posts": {
        +      "description": "Array of posts matching the search query.",
        +      "items": {
        +        "properties": {
        +          "author": {
        +            "description": "Profile of the post author.",
        +            "properties": {
        +              "avatar": {
        +                "description": "URL of the author avatar image.",
        +                "type": "string"
        +              },
        +              "did": {
        +                "description": "Decentralized identifier of the author.",
        +                "type": "string"
        +              },
        +              "displayName": {
        +                "description": "Display name of the author.",
        +                "type": "string"
        +              },
        +              "handle": {
        +                "description": "Handle of the author (e.g. alice.bsky.social).",
        +                "type": "string"
        +              }
        +            },
        +            "required": [
        +              "did",
        +              "handle"
        +            ],
        +            "type": "object"
        +          },
        +          "cid": {
        +            "description": "Content identifier (CID) of the post.",
        +            "type": "string"
        +          },
        +          "indexedAt": {
        +            "description": "ISO 8601 timestamp when the post was indexed.",
        +            "type": "string"
        +          },
        +          "likeCount": {
        +            "description": "Number of likes.",
        +            "type": "number"
        +          },
        +          "record": {
        +            "description": "The raw post record.",
        +            "properties": {
        +              "createdAt": {
        +                "description": "ISO 8601 creation timestamp.",
        +                "type": "string"
        +              },
        +              "langs": {
        +                "description": "BCP-47 language tags declared by the author.",
        +                "items": {
        +                  "type": "string"
        +                },
        +                "type": "array"
        +              },
        +              "tags": {
        +                "description": "Hashtags attached to the post.",
        +                "items": {
        +                  "type": "string"
        +                },
        +                "type": "array"
        +              },
        +              "text": {
        +                "description": "Plain text content of the post.",
        +                "type": "string"
        +              }
        +            },
        +            "required": [
        +              "text",
        +              "createdAt"
        +            ],
        +            "type": "object"
        +          },
        +          "replyCount": {
        +            "description": "Number of replies to the post.",
        +            "type": "number"
        +          },
        +          "repostCount": {
        +            "description": "Number of reposts.",
        +            "type": "number"
        +          },
        +          "uri": {
        +            "description": "AT-URI of the post (at://did/.../rkey).",
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "uri",
        +          "cid",
        +          "author",
        +          "record",
        +          "indexedAt"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "searchQuery": {
        +      "description": "The search query string that was executed.",
        +      "type": "string"
        +    },
        +    "success": {
        +      "description": "Whether the search request completed successfully.",
        +      "type": "boolean"
        +    },
        +    "totalResults": {
        +      "description": "Approximate total number of matching posts reported by the API, if available.",
        +      "type": "number"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "posts",
        +    "hasMore",
        +    "searchQuery"
        +  ],
        +  "type": "object"
        +}
    • Addedsearch_starter_packs
    • Addedsend_direct_message
    • Removedstart_oauth_flow
    • Removedstart_streaming
    • Removedstop_streaming
    • Removedsuggest_content_strategy
    • Removedtrack_users
    • Changedunblock_user2 fields changed
      • addedInput schema / properties / actor / description
        Added value: +"Handle (e.g. alice.bsky.social) or DID of the account to unblock."
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "properties": {
        +    "message": {
        +      "description": "Human-readable result message.",
        +      "type": "string"
        +    },
        +    "success": {
        +      "description": "True if the block was removed; false if the account was not blocked.",
        +      "type": "boolean"
        +    },
        +    "unblockedUser": {
        +      "description": "Details of the targeted account.",
        +      "properties": {
        +        "actor": {
        +          "description": "Handle or DID supplied in the request.",
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "actor"
        +      ],
        +      "type": "object"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "message",
        +    "unblockedUser"
        +  ],
        +  "type": "object"
        +}
    • Changedunfollow_user2 fields changed
      • addedInput schema / properties / followUri / description
        Added value: +"AT-URI of the follow record to delete (at://did/app.bsky.graph.follow/rkey). Must reference an app.bsky.graph.follow collection record."
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "properties": {
        +    "deletedFollow": {
        +      "description": "Information about the deleted follow record.",
        +      "properties": {
        +        "uri": {
        +          "description": "AT-URI of the deleted follow record.",
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "uri"
        +      ],
        +      "type": "object"
        +    },
        +    "message": {
        +      "description": "Human-readable outcome message.",
        +      "type": "string"
        +    },
        +    "success": {
        +      "description": "True when the follow record was successfully deleted.",
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "message",
        +    "deletedFollow"
        +  ],
        +  "type": "object"
        +}
    • Changedunlike_post2 fields changed
      • addedInput schema / properties / likeUri / description
        Added value: +"AT-URI of the like record to delete (at://did/app.bsky.feed.like/rkey); obtained from a previous like_post response or post viewer state."
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "properties": {
        +    "deletedLike": {
        +      "description": "The like record that was deleted.",
        +      "properties": {
        +        "uri": {
        +          "description": "AT-URI of the deleted like record.",
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "uri"
        +      ],
        +      "type": "object"
        +    },
        +    "message": {
        +      "description": "Human-readable status message.",
        +      "type": "string"
        +    },
        +    "success": {
        +      "description": "Whether the unlike operation succeeded.",
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "message",
        +    "deletedLike"
        +  ],
        +  "type": "object"
        +}
    • Changedunmute_user2 fields changed
      • addedInput schema / properties / actor / description
        Added value: +"Handle (e.g. alice.bsky.social) or DID of the account to unmute."
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "properties": {
        +    "message": {
        +      "description": "Human-readable result message.",
        +      "type": "string"
        +    },
        +    "success": {
        +      "description": "Whether the unmute operation succeeded.",
        +      "type": "boolean"
        +    },
        +    "unmutedUser": {
        +      "description": "Details of the unmuted account.",
        +      "properties": {
        +        "actor": {
        +          "description": "Handle or DID supplied in the request.",
        +          "type": "string"
        +        },
        +        "did": {
        +          "description": "Resolved DID of the unmuted account, if returned by the API.",
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "actor"
        +      ],
        +      "type": "object"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "message",
        +    "unmutedUser"
        +  ],
        +  "type": "object"
        +}
    • Changedunrepost2 fields changed
      • addedInput schema / properties / repostUri / description
        Added value: +"AT-URI of the repost record to delete (at://did/app.bsky.feed.repost/rkey). Must reference a repost record, not a post."
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "properties": {
        +    "deletedRepost": {
        +      "description": "The repost record that was deleted.",
        +      "properties": {
        +        "uri": {
        +          "description": "AT-URI of the deleted repost record.",
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "uri"
        +      ],
        +      "type": "object"
        +    },
        +    "message": {
        +      "description": "Human-readable status message.",
        +      "type": "string"
        +    },
        +    "success": {
        +      "description": "Whether the repost was successfully deleted.",
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "message",
        +    "deletedRepost"
        +  ],
        +  "type": "object"
        +}
    • Changedupdate_profile15 fields changed
      • addedInput schema / properties / avatar / additionalProperties
        Added value: +false
      • addedInput schema / properties / avatar / description
        Added value: +"New avatar image as a pre-uploaded blob descriptor: pass the `image.blob` object returned by upload_image verbatim. Omit to keep the existing avatar."
      • addedInput schema / properties / avatar / properties
        Added value: +{
        +  "mimeType": {
        +    "description": "MIME type of the uploaded blob (e.g. \"image/jpeg\").",
        +    "minLength": 1,
        +    "type": "string"
        +  },
        +  "ref": {
        +    "anyOf": [
        +      {
        +        "description": "CID of the uploaded blob as a flat string (e.g. \"bafkrei…\").",
        +        "minLength": 1,
        +        "type": "string"
        +      },
        +      {
        +        "additionalProperties": false,
        +        "description": "Lexicon blob-ref object wrapping the CID as { \"$link\": \"<cid>\" }.",
        +        "properties": {
        +          "$link": {
        +            "description": "CID of the uploaded blob (e.g. \"bafkrei…\").",
        +            "minLength": 1,
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "$link"
        +        ],
        +        "type": "object"
        +      }
        +    ],
        +    "description": "CID reference of the uploaded blob: either the flat string returned by upload_image, or the lexicon { \"$link\": \"<cid>\" } object form."
        +  },
        +  "size": {
        +    "description": "Size of the uploaded blob in bytes.",
        +    "exclusiveMinimum": 0,
        +    "type": "integer"
        +  },
        +  "type": {
        +    "const": "blob",
        +    "description": "Discriminator emitted by upload_image; always \"blob\" when present. May be omitted.",
        +    "type": "string"
        +  }
        +}
      • addedInput schema / properties / avatar / required
        Added value: +[
        +  "ref",
        +  "mimeType",
        +  "size"
        +]
      • addedInput schema / properties / avatar / type
        Added value: +"object"
      • addedInput schema / properties / banner / additionalProperties
        Added value: +false
      • addedInput schema / properties / banner / description
        Added value: +"New banner/header image as a pre-uploaded blob descriptor: pass the `image.blob` object returned by upload_image verbatim. Omit to keep the existing banner."
      • addedInput schema / properties / banner / properties
        Added value: +{
        +  "mimeType": {
        +    "description": "MIME type of the uploaded blob (e.g. \"image/jpeg\").",
        +    "minLength": 1,
        +    "type": "string"
        +  },
        +  "ref": {
        +    "anyOf": [
        +      {
        +        "description": "CID of the uploaded blob as a flat string (e.g. \"bafkrei…\").",
        +        "minLength": 1,
        +        "type": "string"
        +      },
        +      {
        +        "additionalProperties": false,
        +        "description": "Lexicon blob-ref object wrapping the CID as { \"$link\": \"<cid>\" }.",
        +        "properties": {
        +          "$link": {
        +            "description": "CID of the uploaded blob (e.g. \"bafkrei…\").",
        +            "minLength": 1,
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "$link"
        +        ],
        +        "type": "object"
        +      }
        +    ],
        +    "description": "CID reference of the uploaded blob: either the flat string returned by upload_image, or the lexicon { \"$link\": \"<cid>\" } object form."
        +  },
        +  "size": {
        +    "description": "Size of the uploaded blob in bytes.",
        +    "exclusiveMinimum": 0,
        +    "type": "integer"
        +  },
        +  "type": {
        +    "const": "blob",
        +    "description": "Discriminator emitted by upload_image; always \"blob\" when present. May be omitted.",
        +    "type": "string"
        +  }
        +}
      • addedInput schema / properties / banner / required
        Added value: +[
        +  "ref",
        +  "mimeType",
        +  "size"
        +]
      • addedInput schema / properties / banner / type
        Added value: +"object"
      • addedInput schema / properties / description / description
        Added value: +"New bio/description for the profile (max 256 graphemes; emoji count as one). Omit to leave unchanged."
      • removedInput schema / properties / description / maxLength
        Removed value: -256
      • addedInput schema / properties / displayName / description
        Added value: +"New display name for the profile (max 64 graphemes; emoji count as one). Omit to leave unchanged."
      • removedInput schema / properties / displayName / maxLength
        Removed value: -64
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "properties": {
        +    "message": {
        +      "description": "Human-readable result message.",
        +      "type": "string"
        +    },
        +    "profile": {
        +      "description": "The new values of the updated profile fields.",
        +      "properties": {
        +        "avatar": {
        +          "description": "Set to \"updated\" when a new avatar blob was set.",
        +          "type": "string"
        +        },
        +        "banner": {
        +          "description": "Set to \"updated\" when a new banner blob was set.",
        +          "type": "string"
        +        },
        +        "description": {
        +          "description": "Updated bio/description, if it was changed.",
        +          "type": "string"
        +        },
        +        "displayName": {
        +          "description": "Updated display name, if it was changed.",
        +          "type": "string"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    "success": {
        +      "description": "Whether the profile was successfully updated.",
        +      "type": "boolean"
        +    },
        +    "updatedFields": {
        +      "description": "Names of the profile fields that were actually changed (e.g. [\"displayName\", \"description\"]).",
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "message",
        +    "updatedFields",
        +    "profile"
        +  ],
        +  "type": "object"
        +}
    • Changedupload_image3 fields changed
      • addedInput schema / properties / altText / description
        Added value: +"Accessible alt-text description of the image (max 1000 characters). Omit if no description is available."
      • addedInput schema / properties / filePath / description
        Added value: +"Absolute or relative path to the image file on disk. Must resolve within the allowed media directory (ATPROTO_MEDIA_DIR env var, defaults to cwd). Accepted extensions: .jpg, .jpeg, .png, .gif, .webp, .avif. Maximum file size 1 MB."
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "properties": {
        +    "image": {
        +      "description": "Uploaded image blob descriptor and metadata. Pass the `blob` object as create_post embed.images[].image or update_profile avatar/banner.",
        +      "properties": {
        +        "alt": {
        +          "description": "Alt text for the image (empty string if none was provided).",
        +          "type": "string"
        +        },
        +        "blob": {
        +          "description": "AT Protocol blob descriptor.",
        +          "properties": {
        +            "mimeType": {
        +              "description": "MIME type of the uploaded image (e.g. \"image/jpeg\").",
        +              "type": "string"
        +            },
        +            "ref": {
        +              "description": "CID reference string (bafkrei…) of the uploaded blob.",
        +              "type": "string"
        +            },
        +            "size": {
        +              "description": "Size of the uploaded blob in bytes.",
        +              "type": "number"
        +            },
        +            "type": {
        +              "description": "Always \"blob\".",
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "type",
        +            "ref",
        +            "mimeType",
        +            "size"
        +          ],
        +          "type": "object"
        +        }
        +      },
        +      "required": [
        +        "blob",
        +        "alt"
        +      ],
        +      "type": "object"
        +    },
        +    "message": {
        +      "description": "Human-readable status message.",
        +      "type": "string"
        +    },
        +    "success": {
        +      "description": "Whether the upload succeeded.",
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "message",
        +    "image"
        +  ],
        +  "type": "object"
        +}
    • Changedupload_video6 fields changed
      • addedInput schema / properties / altText / description
        Added value: +"Accessible alt-text description of the video (max 1000 characters). Omit if no description is available."
      • addedInput schema / properties / captions / description
        Added value: +"Optional list of caption tracks to attach to the video. Each entry pairs a language code with a WebVTT file path."
      • addedInput schema / properties / captions / items / properties / file / description
        Added value: +"Absolute or relative path to the WebVTT (.vtt) caption file for this language. Must resolve within the allowed media directory. Caption files over 20 kB are not supported by the embed lexicon and are skipped."
      • addedInput schema / properties / captions / items / properties / lang / description
        Added value: +"BCP-47 language code for the caption track (e.g. \"en\", \"fr\", \"pt-BR\")."
      • addedInput schema / properties / filePath / description
        Added value: +"Absolute or relative path to the video file on disk. Must resolve within the allowed media directory (ATPROTO_MEDIA_DIR env var, defaults to cwd). Accepted extensions: .mp4, .mov, .webm. Maximum file size 100 MB (the app.bsky.video service limit)."
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "properties": {
        +    "message": {
        +      "description": "Human-readable status message.",
        +      "type": "string"
        +    },
        +    "success": {
        +      "description": "Whether the upload and processing succeeded.",
        +      "type": "boolean"
        +    },
        +    "video": {
        +      "description": "Processed video blob descriptor and metadata. Pass the `blob` object as create_post embed.video.video and each `captions[].file` as embed.video.captions[].file.",
        +      "properties": {
        +        "alt": {
        +          "description": "Alt text for the video (empty string if none was provided).",
        +          "type": "string"
        +        },
        +        "blob": {
        +          "description": "AT Protocol blob descriptor for the PROCESSED video (transcoded by the video service and stored on the PDS).",
        +          "properties": {
        +            "mimeType": {
        +              "description": "MIME type of the processed video (typically \"video/mp4\").",
        +              "type": "string"
        +            },
        +            "ref": {
        +              "description": "CID reference string (bafkrei…) of the processed video blob.",
        +              "type": "string"
        +            },
        +            "size": {
        +              "description": "Size of the processed video blob in bytes.",
        +              "type": "number"
        +            },
        +            "type": {
        +              "description": "Always \"blob\".",
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "type",
        +            "ref",
        +            "mimeType",
        +            "size"
        +          ],
        +          "type": "object"
        +        },
        +        "captions": {
        +          "description": "Uploaded caption tracks. Each entry pairs a BCP-47 language code with the caption blob descriptor for create_post embed.video.captions.",
        +          "items": {
        +            "properties": {
        +              "file": {
        +                "description": "AT Protocol blob descriptor for the uploaded .vtt caption blob.",
        +                "properties": {
        +                  "mimeType": {
        +                    "description": "MIME type of the caption blob (always \"text/vtt\").",
        +                    "type": "string"
        +                  },
        +                  "ref": {
        +                    "description": "CID reference string of the caption blob.",
        +                    "type": "string"
        +                  },
        +                  "size": {
        +                    "description": "Size of the caption blob in bytes.",
        +                    "type": "number"
        +                  },
        +                  "type": {
        +                    "description": "Always \"blob\".",
        +                    "type": "string"
        +                  }
        +                },
        +                "required": [
        +                  "type",
        +                  "ref",
        +                  "mimeType",
        +                  "size"
        +                ],
        +                "type": "object"
        +              },
        +              "lang": {
        +                "description": "BCP-47 language code for the caption track.",
        +                "type": "string"
        +              }
        +            },
        +            "required": [
        +              "lang",
        +              "file"
        +            ],
        +            "type": "object"
        +          },
        +          "type": "array"
        +        },
        +        "jobId": {
        +          "description": "Video-service processing job id (useful for support/debugging).",
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "blob",
        +        "alt",
        +        "jobId"
        +      ],
        +      "type": "object"
        +    }
        +  },
        +  "required": [
        +    "success",
        +    "message",
        +    "video"
        +  ],
        +  "type": "object"
        +}
  3. 60 tool updatesv0.3.0
    • Changedadd_to_list1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedanalyze_engagement1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedanalyze_image1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedanalyze_moderation_status1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedanalyze_network1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedbatch_follow1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedbatch_like1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedbatch_repost1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedblock_user1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedcreate_list1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedcreate_post4 fields changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
      • removedInput schema / properties / langs / items / maxLength
        Removed value: -2
      • removedInput schema / properties / langs / items / minLength
        Removed value: -2
      • addedInput schema / properties / langs / items / pattern
        Added value: +"^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,8})*$"
    • Changedcreate_rich_text_post8 fields changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
      • removedInput schema / properties / embed / properties / external / properties / thumb
        Removed value: -{
        -  "type": "string"
        -}
      • addedInput schema / properties / embed / properties / external / properties / thumbFilePath
        Added value: +{
        +  "type": "string"
        +}
      • addedInput schema / properties / embed / properties / images / items / properties / alt / maxLength
        Added value: +1000
      • addedInput schema / properties / embed / properties / images / items / properties / filePath
        Added value: +{
        +  "minLength": 1,
        +  "type": "string"
        +}
      • removedInput schema / properties / embed / properties / images / items / properties / image
        Removed value: -{
        -  "type": "string"
        -}
      • changedInput schema / properties / embed / properties / images / items / required
        Previous value: -[
        -  "image",
        -  "alt"
        -]New value: +[
        +  "filePath",
        +  "alt"
        +]
      • addedInput schema / properties / embed / properties / images / maxItems
        Added value: +4
    • Changedcreate_thread7 fields changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
      • removedInput schema / properties / langs / items / maxLength
        Removed value: -2
      • removedInput schema / properties / langs / items / minLength
        Removed value: -2
      • addedInput schema / properties / langs / items / pattern
        Added value: +"^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,8})*$"
      • removedInput schema / properties / posts / items / properties / langs / items / maxLength
        Removed value: -2
      • removedInput schema / properties / posts / items / properties / langs / items / minLength
        Removed value: -2
      • addedInput schema / properties / posts / items / properties / langs / items / pattern
        Added value: +"^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,8})*$"
    • Changeddelete_post1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changeddiscover_communities1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changeddiscover_trending1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedextract_media_from_post1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedfind_influential_users1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedfind_similar_users1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedfollow_user1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedgenerate_alt_text1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedgenerate_link_preview1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedget_custom_feed1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedget_followers1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedget_follows1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedget_list1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedget_notifications1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedget_post_context1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedget_recent_events1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedget_streaming_status1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedget_thread1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedget_timeline1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedget_user_profile1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedget_user_summary1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedhandle_oauth_callback1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedlike_post1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedmonitor_keywords1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedmute_user1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedrecommend_content1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedrefresh_oauth_tokens1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedremove_from_list1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedreply_to_post4 fields changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
      • removedInput schema / properties / langs / items / maxLength
        Removed value: -2
      • removedInput schema / properties / langs / items / minLength
        Removed value: -2
      • addedInput schema / properties / langs / items / pattern
        Added value: +"^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,8})*$"
    • Changedreport_content1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedreport_user1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedrepost1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedrevoke_oauth_tokens1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedsearch_posts4 fields changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
      • removedInput schema / properties / lang / maxLength
        Removed value: -2
      • removedInput schema / properties / lang / minLength
        Removed value: -2
      • addedInput schema / properties / lang / pattern
        Added value: +"^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,8})*$"
    • Changedstart_oauth_flow1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedstart_streaming1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedstop_streaming1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedsuggest_content_strategy1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedtrack_users1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedunblock_user1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedunfollow_user1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedunlike_post1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedunmute_user1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedunrepost1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedupdate_profile1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedupload_image1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedupload_video1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
  4. 60 tool updatesv0.2.1
    • First observedadd_to_list
    • First observedanalyze_engagement
    • First observedanalyze_image
    • First observedanalyze_moderation_status
    • First observedanalyze_network
    • First observedbatch_follow
    • First observedbatch_like
    • First observedbatch_repost
    • First observedblock_user
    • First observedcreate_list
    • First observedcreate_post
    • First observedcreate_rich_text_post
    • First observedcreate_thread
    • First observeddelete_post
    • First observeddiscover_communities
    • First observeddiscover_trending
    • First observedextract_media_from_post
    • First observedfind_influential_users
    • First observedfind_similar_users
    • First observedfollow_user
    • First observedgenerate_alt_text
    • First observedgenerate_link_preview
    • First observedget_custom_feed
    • First observedget_followers
    • First observedget_follows
    • First observedget_list
    • First observedget_notifications
    • First observedget_post_context
    • First observedget_recent_events
    • First observedget_streaming_status
    • First observedget_thread
    • First observedget_timeline
    • First observedget_user_profile
    • First observedget_user_summary
    • First observedhandle_oauth_callback
    • First observedlike_post
    • First observedmonitor_keywords
    • First observedmute_user
    • First observedrecommend_content
    • First observedrefresh_oauth_tokens
    • First observedremove_from_list
    • First observedreply_to_post
    • First observedreport_content
    • First observedreport_user
    • First observedrepost
    • First observedrevoke_oauth_tokens
    • First observedsearch_posts
    • First observedstart_oauth_flow
    • First observedstart_streaming
    • First observedstop_streaming
    • First observedsuggest_content_strategy
    • First observedtrack_users
    • First observedunblock_user
    • First observedunfollow_user
    • First observedunlike_post
    • First observedunmute_user
    • First observedunrepost
    • First observedupdate_profile
    • First observedupload_image
    • First observedupload_video

TDQS

A4.6/5.0

Scored across 51 tools

Disambiguation5/5

Every tool has a clearly distinct purpose, targeting specific actions on specific resources (posts, users, lists, bookmarks, etc.). Overlap is minimal and explicitly addressed in tool descriptions, with cross-references to alternative tools.

Naming Consistency5/5

Tool names follow a consistent verb_noun pattern (e.g., create_post, delete_post, follow_user, search_actors). The naming convention is uniform across all 51 tools, with no mixing of styles.

Tool Count4/5

51 tools is a high count, but it covers the extensive AT Protocol surface area (posts, threads, likes, reposts, follows, lists, moderation, search, DMs, bookmarks, media uploads). While slightly heavy, each tool serves a necessary function.

Completeness5/5

The tool surface is remarkably comprehensive, covering virtually all core social media operations including CRUD for posts, follows, likes, reposts, lists, moderation, direct messages, bookmarks, and media. No significant gaps are apparent for the domain.

Maintenance

ActivityInactive
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • F
    license
    B
    quality
    D
    maintenance
    A Model Context Protocol server that connects to Bluesky and provides natural language tools to interact with the ATProtocol, enabling feed fetching, post management, search, and profile analysis.
    32
    -
  • A
    license
    A
    quality
    C
    maintenance
    Enables search, reading, and posting to Bluesky from any MCP client; features 11 tools (5 read, 6 write) with gated writes requiring explicit confirmation to prevent accidental publishing.
    11
    11 npm
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables interaction with Bluesky social network through the AT Protocol, including searching posts, fetching profiles, browsing feeds, and retrieving threads and follower data.
    6 npm
    MIT