AT Protocol MCP Server
This server acts as a bridge between LLM clients (like Claude Desktop) and the AT Protocol/Bluesky social network, enabling natural language interaction via 60 specialized MCP tools.
Post & Content Management
Create, reply to, and delete posts with rich text (mentions, links, hashtags)
Create multi-post threads (up to 25 posts), generate link previews
Upload images (JPEG, PNG, GIF, WebP) and videos (MP4, MOV, WebM)
Social Interactions
Like/unlike, repost/unrepost, quote-repost, follow/unfollow users
View profiles, follower/following lists, timelines, custom feeds, threads, and notifications
Batch Operations
Follow, like, or repost up to 25 items in a single call
Search & Discovery
Search posts by text, author, language, date range, domain, and more
Discover trending topics, similar users, influential voices, and communities
Get personalized content recommendations
Analytics & Insights
Analyze engagement patterns, network connections, and post performance
Get content strategy recommendations and moderation status analysis
List Management
Create curated or moderation lists, add/remove users
Moderation
Mute/unmute, block/unblock users, report content or accounts
Media Tools
Generate alt text for images, analyze image metadata, extract all media from a post or thread
Composite Operations
Get a full user summary (profile + posts + engagement) or full post context in one call
Authentication
Unauthenticated mode: access public profiles and follower/following lists
Authenticated mode (app passwords): full access including write operations and private data
Experimental Features
OAuth authorization flow initiation (token exchange not yet implemented)
Real-time firehose streaming with keyword/user monitoring (frame decoding not yet functional)
Provides tools for interacting with the Bluesky social network via the AT Protocol, including unauthenticated access to public data (view profiles, manage OAuth flows) and authenticated access for full functionality (search posts, create/like/repost, manage timelines, feeds, and notifications).
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@AT Protocol MCP Serversearch for recent posts about AI"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
AT Protocol MCP Server
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-mcpruns 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-mcpto access public data without any setup - view profiles, search accounts, and fetch follower/following listsOptional Authentication: Enable full functionality with app passwords for write operations, feeds, and private data
Complete AT Protocol Integration: Full implementation using official
@atproto/apiMCP Server Compliance: Built with
@modelcontextprotocol/sdkfollowing MCP specificationType-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 → BlueskyUsers 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:
Use the official
@atproto/apipackage directly, ORBuild 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-mcpOr install globally:
npm install -g atproto-mcpClaude 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
Option 1: Unauthenticated Mode (Recommended for most use cases)
Perfect for LLM clients that need to access public AT Protocol data:
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"] } } }Start your LLM client - it will automatically launch the MCP server
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_connectionswithdirection: '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 authenticationBrowsing 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:
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" } } } }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 viadirection: '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 postscreate_thread- Create multi-post threads in one callreply_to_post- Reply to existing posts with threadinglike_post/unlike_post- Like and unlike postsrepost/unrepost- Repost content with optional quotesfollow_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 feedsget_timeline- Retrieve personalized timelinesget_notifications- Access notification feeds (usecountOnly: truefor a cheap unread badge count)mark_notifications_seen- Mark notifications as seen up to a timestamp
Direct Messages
list_conversations- List your Bluesky DM conversationsget_conversation_messages- Read a conversation's message historysend_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 postsget_bookmarks- List your private bookmarks
Content Management
upload_image/upload_video- Upload media contentdelete_post- Remove postsupdate_profile- Modify profile and settingsgenerate_link_preview- Generate link previews for posts
List Management
create_list- Create user listsadd_to_list/remove_from_list- Manage list membersget_list- Retrieve list information
Moderation
mute_user/unmute_user- Mute and unmute usersblock_user/unblock_user- Block and unblock usersreport_content/report_user- Report content and usersanalyze_moderation_status- Check moderation status of content
Batch Operations
batch_action- Apply one action across up to 25 targets in a single call viaaction: 'follow' | 'like' | 'repost'
Analytics & Insights
analyze_account- Analyze a single account along one dimension viadimension: 'engagement' | 'network' | 'strategy'find_influential_users- Find influential users in a topic area
Content Discovery
discover- Surface timeline content viamode: 'trending' | 'recommended'find_similar_users- Find users similar to a given userdiscover_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.
App Passwords (Recommended for Development)
export ATPROTO_IDENTIFIER="your-handle.bsky.social"
export ATPROTO_PASSWORD="your-app-password"
atproto-mcpApp 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 buildAvailable 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 issuesCross-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:uiIntegration 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:integrationWhat'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.
Fork the repository
Create a feature branch
Make your changes
Add tests
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-mcpEnvironment configuration
# Copy the example environment file and edit it (loaded automatically by the CLI)
cp .env.example .envATPROTO_IDENTIFIER=your.handle.bsky.social
ATPROTO_PASSWORD=your-app-password
NODE_ENV=production
LOG_LEVEL=infoSee .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:
Secure Your Credentials
Never commit
.envfiles to version controlUse app passwords instead of your main account password
Rotate credentials regularly
Use a secret management system where available (AWS Secrets Manager, HashiCorp Vault, etc.)
Run in production mode
Set
NODE_ENV=productionso returned error messages are sanitized
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 toolsadd_bookmarkAIdempotent
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.
| Name | Required | Description | Default |
|---|---|---|---|
| uri | Yes | AT-URI of the post to bookmark (at://did/app.bsky.feed.post/rkey). Only posts can be bookmarked. | |
| cid | No | CID of the post record. Optional: resolved automatically from the post view when omitted. |
Output Schema
| Name | Required | Description |
|---|---|---|
| success | Yes | Whether the bookmark operation succeeded. |
| message | Yes | Human-readable status message. |
| alreadyBookmarked | Yes | True 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). |
| bookmarkedPost | Yes | The post that was bookmarked. There is no bookmark-record URI: bookmarks are private server-side state keyed by this post URI. |
TDQS
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.
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.
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.
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.
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.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| listUri | Yes | AT-URI of the target list (at://did/app.bsky.graph.list/rkey). | |
| actor | Yes | Handle (e.g. alice.bsky.social) or DID of the user to add to the list. |
Output Schema
| Name | Required | Description |
|---|---|---|
| success | Yes | Whether the user was added successfully. |
| message | Yes | Human-readable result message. |
| listItem | Yes | Details of the newly created list-item record. |
TDQS
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.
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.
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.
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.
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.
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_accountARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| actor | No | Handle or DID to analyze. Defaults to the authenticated user when omitted. | |
| dimension | Yes | Which analysis to run: 'engagement' = recent-post performance; 'network' = follower/following graph health; 'strategy' = posting recommendations. | |
| limit | No | How many recent posts to sample for engagement analysis (1–100, default 50). | |
| maxSampleSize | No | How many connections to sample for network analysis (default per old behavior). | |
| includeReplies | No | Engagement: include replies in the sampled author feed (default true). | |
| includeFollowers | No | Network: sample the followers graph (default true). | |
| includeFollows | No | Network: sample the follows graph (default true). | |
| analyzePosts | No | Strategy: how many recent posts to analyze (10–100, default 50). | |
| includeTimingAnalysis | No | Strategy: include best-posting-time analysis (default true). | |
| includeTopicAnalysis | No | Strategy: include topic/keyword analysis (default true). |
Output Schema
| Name | Required | Description |
|---|---|---|
| success | Yes | Whether the analysis succeeded. |
| dimension | Yes | Which analysis was run. |
| actor | No | network/strategy dimensions: the DID or handle that was analyzed. |
| summary | No | engagement dimension: aggregate statistics over the sampled posts (reposts of other authors excluded). |
| topPosts | No | engagement dimension: up to 10 posts ranked by total engagement. |
| network | No | network dimension: headline follow-graph counts for the account. |
| analysis | No | Dimension-specific analysis: graph classification for 'network', post-performance statistics for 'strategy'. |
| insights | No | Dimension-specific insights: structured content patterns for 'engagement', human-readable strings for 'network'. |
| recommendations | No | Dimension-specific recommendations: actionable strings for 'engagement', a structured plan for 'strategy'. |
TDQS
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.
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.
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.
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.
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.
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_imageARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| blob | Yes | 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. | |
| includeOptimizationSuggestions | No | When true (default), the response includes a list of human-readable optimization and accessibility suggestions based on the blob size and MIME type. |
Output Schema
| Name | Required | Description |
|---|---|---|
| success | Yes | True when the analysis completed without errors. |
| analysis | Yes | Metadata derived from the blob. |
| suggestions | No | List of human-readable optimization and accessibility suggestions. Present only when includeOptimizationSuggestions is true. |
TDQS
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.
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.
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.
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.
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.
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_statusARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| subject | Yes | 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. | |
| includeLabels | No | Whether to fetch and include content labels in the response (default true). Set to false to skip label fetching for a faster call. |
Output Schema
| Name | Required | Description |
|---|---|---|
| success | Yes | Whether the analysis completed successfully. |
| subject | Yes | The DID or AT-URI that was analyzed. |
| subjectType | Yes | "user" when subject is a DID; "post" when subject is an AT-URI. |
| moderation | Yes | Raw moderation state for the subject. |
| analysis | Yes | Derived safety analysis based on labels and moderation state. |
TDQS
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.
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.
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.
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.
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.
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_actionAIdempotent
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.
| Name | Required | Description | Default |
|---|---|---|---|
| action | Yes | The action to apply to every target. | |
| targets | Yes | For action=follow: handles or DIDs. For action=like/repost: post AT-URIs. 1–25 items. | |
| continueOnError | No | Whether 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
| Name | Required | Description |
|---|---|---|
| success | Yes | |
| action | Yes | |
| results | Yes | Per-target outcome {target/uri, success, error?}. |
| summary | Yes | Counts: total, succeeded, failed, skipped. |
TDQS
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.
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.
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.
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.
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.
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_userADestructive
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.
| Name | Required | Description | Default |
|---|---|---|---|
| actor | Yes | Handle (e.g. alice.bsky.social) or DID of the account to block. |
Output Schema
| Name | Required | Description |
|---|---|---|
| success | Yes | Whether the block operation succeeded. |
| message | Yes | Human-readable result message. |
| blockedUser | Yes | Details of the blocked account. |
TDQS
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.
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.
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.
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.
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.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Display name for the list (1–64 characters). | |
| description | No | Optional plain-text description of the list (max 300 characters). | |
| purpose | No | List type: "curatelist" for a user-curated follow list, "modlist" for a moderation/block list. Defaults to "curatelist". | curatelist |
Output Schema
| Name | Required | Description |
|---|---|---|
| success | Yes | Whether the list was created successfully. |
| message | Yes | Human-readable result message. |
| list | Yes | Metadata of the newly created list. |
TDQS
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.
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.
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.
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.
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.
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).
| Name | Required | Description | Default |
|---|---|---|---|
| text | Yes | 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. | |
| reply | No | Set to make this post a reply in an existing thread. | |
| embed | No | Optional media embed: images OR an external link card OR a video (at most one). | |
| facets | No | 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. | |
| quote | No | Quote another post (record embed). Mutually exclusive with the `embed.images`, `embed.external`, and `embed.video` embeds. | |
| langs | No | Optional BCP-47 language tags (e.g. en, en-US, pt-BR) declaring the languages of the post text. | |
| replyControls | No | 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. | |
| quoteControls | No | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
| uri | Yes | AT-URI of the newly created post. |
| cid | Yes | CID (content hash) of the newly created post. |
| success | Yes | Whether the post was created successfully. |
| message | Yes | Human-readable status message. |
| gateApplied | No | 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). |
| warning | No | 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. |
TDQS
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.
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.
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.
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.
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.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| posts | Yes | Ordered array of posts to publish as a thread (2–25 items). Each post is automatically chained as a reply to the previous one. | |
| langs | No | Default language tags (BCP-47) applied to every post in the thread. Individual posts can override this with their own langs field. | |
| replyControls | No | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
| success | Yes | True when all posts were published; false when only some posts were created before a failure. |
| message | Yes | Human-readable summary of the outcome, including partial-failure details when applicable. |
| thread | Yes | Ordered list of every post that was successfully published. |
| rootPost | Yes | URI and CID of the root (first) post, which anchors the entire thread. |
| totalPosts | Yes | Number of posts actually published (may be less than requested if an error occurred mid-thread). |
| failedAtPosition | No | 1-based position of the post that failed; present only when success is false. |
| gateApplied | No | 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`. |
| warning | No | Present only when gateApplied is false: explains that the threadgate write failed and how to retry. The published posts themselves are unaffected. |
TDQS
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.
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.
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.
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.
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.
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_postADestructive
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.
| Name | Required | Description | Default |
|---|---|---|---|
| uri | Yes | AT-URI of the post to delete (at://did/app.bsky.feed.post/rkey). Must belong to the authenticated user. |
Output Schema
| Name | Required | Description |
|---|---|---|
| success | Yes | Whether the post was successfully deleted. |
| message | Yes | Human-readable result message. |
| deletedPost | Yes | Information about the deleted post. |
TDQS
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.
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.
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.
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.
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.
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.
discoverARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| mode | Yes | What to surface from your timeline: 'trending' = trending topics/hashtags; 'recommended' = posts you're likely to engage with. | |
| limit | No | How 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). | |
| timeWindow | No | Lookback window. Only used when mode=trending (default 24h). | |
| includeHashtags | No | Include trending hashtags. Only used when mode=trending (default true). | |
| includeTopics | No | Include trending topics/keywords. Only used when mode=trending (default true). | |
| includePosts | No | Include notable trending posts. Only used when mode=trending (default true). | |
| actor | No | Optional 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. | |
| topics | No | Restrict recommendations to posts matching these topic keywords. Only used when mode=recommended. | |
| minLikes | No | Minimum like count for a recommended post. Only used when mode=recommended (default 5). | |
| maxAge | No | Maximum post age in hours for recommendations. Only used when mode=recommended (default 24). | |
| excludeReposts | No | Exclude reposts from recommendations. Only used when mode=recommended. |
Output Schema
| Name | Required | Description |
|---|---|---|
| success | Yes | Whether the discovery run completed successfully. |
| mode | Yes | Which discovery mode was run; determines which other fields are present. |
| timeWindow | No | Lookback window that was analyzed. Present when mode=trending. |
| trendingHashtags | No | Trending hashtags ranked by count and recent growth (empty when includeHashtags is false). Present when mode=trending. |
| trendingTopics | No | Trending topic keywords ranked by engagement (empty when includeTopics is false). Present when mode=trending. |
| trendingPosts | No | Notable posts ranked by engagement with a recency boost (empty when includePosts is false). Present when mode=trending. |
| summary | No | Summary of the analyzed timeline sample. Present when mode=trending. |
| recommendations | No | Recommended posts sorted by descending recommendationScore. Present when mode=recommended. |
| insights | No | Human-readable observations about the recommendations (or advice when none matched). Present when mode=recommended. |
TDQS
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.
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.
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.
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.
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.
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_communitiesARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| topic | Yes | Keyword or phrase to search for (e.g. "climate", "web dev"). Used to retrieve relevant posts and identify active community clusters. | |
| maxResults | No | Maximum number of communities to return (1–50, default 20). | |
| minCommunitySize | No | Minimum number of distinct members required for a cluster to be reported as a community (minimum 2, default 5). | |
| includeMetrics | No | When true, includes a metrics object on each community with avgFollowerCount, totalPosts, and interconnectedness values (default true). |
Output Schema
| Name | Required | Description |
|---|---|---|
| success | Yes | Whether the operation completed successfully. |
| communities | Yes | List of discovered communities sorted by size and engagement (descending). |
| insights | Yes | Summary observations about the discovered communities (e.g. total members, largest community). |
TDQS
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.
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.
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.
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.
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.
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_usersARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| topic | No | Topic keyword(s) to search for (e.g. "climate change"). Used as the search query when searchQuery is not provided. | |
| searchQuery | No | Explicit search query string. When provided, takes precedence over topic. At least one of topic or searchQuery must be supplied. | |
| minFollowers | No | Minimum follower count a user must have to be included in results (default 100). | |
| maxResults | No | Maximum number of users to return (1–50, default 20). | |
| sortBy | No | 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". | followers |
Output Schema
| Name | Required | Description |
|---|---|---|
| success | Yes | Whether the operation completed successfully. |
| query | Yes | The search query that was used. |
| users | Yes | List of influential users found, sorted per the sortBy parameter. |
| insights | Yes | Human-readable insight strings summarising the results. |
TDQS
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.
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.
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.
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.
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.
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_usersARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| actor | Yes | Handle (e.g. alice.bsky.social) or DID of the target account to find similar users for. | |
| maxResults | No | Maximum number of similar users to return (1–50, default 20). | |
| minFollowerCount | No | Minimum follower count a candidate must have to be included in results (default 0, meaning no minimum). | |
| includeMetrics | No | When true, includes a metrics object on each result with followsBaseUser and followerRatioSimilarity values (default true). |
Output Schema
| Name | Required | Description |
|---|---|---|
| success | Yes | Whether the operation completed successfully. |
| similarUsers | Yes | List of similar users sorted by descending similarity score. |
| baseUser | Yes | Profile summary of the queried base user. |
| insights | Yes | Summary observations about the results (e.g. average similarity score, average follower count). |
TDQS
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.
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.
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.
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.
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.
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_userAIdempotent
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.
| Name | Required | Description | Default |
|---|---|---|---|
| actor | Yes | Handle (e.g. alice.bsky.social) or DID of the account to follow. |
Output Schema
| Name | Required | Description |
|---|---|---|
| uri | Yes | AT-URI of the follow record (at://did/app.bsky.graph.follow/rkey). |
| cid | Yes | CID of the follow record; empty string when the follow already existed. |
| success | Yes | Whether the follow is now in effect (true even if already followed). |
| message | Yes | Human-readable outcome message. |
| followedUser | Yes | Identifying information for the account that was followed. |
TDQS
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.
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.
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.
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.
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.
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.
generate_link_previewARead-only
Generate a link preview with title, description, and thumbnail for a given URL. Fetches the target page, extracts Open Graph / meta tags, optionally downloads and uploads the og:image thumbnail as an AT Protocol blob, and returns a preview object ready to attach as an external embed to a create_post or create_thread call. Requires authentication (app password). Use this tool before create_post when you want a rich URL card; use upload_image or upload_video for local media instead. Subject to per-tool rate limiting.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
| success | Yes | Whether the preview was generated successfully. |
| message | Yes | Human-readable status message. |
| preview | Yes | Link preview data suitable for use as an AT Protocol external embed. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations provide readOnlyHint and openWorldHint. Description adds concrete behavioral details: fetches up to 2 MB HTML and 1 MB image, SSRF-safe, optionally uploads blob. 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three well-structured sentences, no redundancy. Front-loaded with core purpose, each sentence adds unique value.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given one parameter, output schema exists, and annotations present, the description fully covers prerequisites, alternatives, security, and process. Highly complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema covers URL format and constraints (100% coverage). Description adds meaning by explaining the extraction process (OG/meta tags, blob upload) and return type. Baseline 3, plus extra context.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states it generates a link preview with title, description, and thumbnail, specifying the verb 'Generate' and resource 'link preview'. It details the process (fetching, extracting meta tags, uploading thumbnail) and distinguishes from sibling tools like upload_image and upload_video.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly says 'Use this tool before create_post when you want a rich URL card; use upload_image or upload_video for local media instead.' 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.
get_author_feedARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| actor | Yes | Handle or DID of the account whose posts to list. | |
| limit | No | Max posts per page (1–100, default 50). | |
| cursor | No | Pagination cursor from a previous response. | |
| filter | No | Which of the author's posts to include (default posts_with_replies). |
Output Schema
| Name | Required | Description |
|---|---|---|
| success | Yes | Whether the request succeeded. |
| posts | Yes | Posts by the specified author for this page. |
| cursor | No | Opaque cursor for the next page; absent when there are no more results. |
TDQS
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.
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.
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.
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.
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.
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_bookmarksARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max bookmarks per page (1–100, default 50). | |
| cursor | No | Opaque pagination cursor from the previous response cursor field; omit for the first page. |
Output Schema
| Name | Required | Description |
|---|---|---|
| success | Yes | Whether the request succeeded. |
| bookmarks | Yes | The bookmarks for this page. |
| cursor | No | Opaque cursor for the next page; absent when there are no more results. |
| hasMore | Yes | Whether additional pages of results are available. |
TDQS
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.
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.
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.
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.
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.
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_messagesAIdempotent
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.
| Name | Required | Description | Default |
|---|---|---|---|
| convoId | Yes | Id of the conversation to read, from list_conversations or a send_direct_message response. | |
| limit | No | Max messages per page (1-100, default 50). Messages are returned newest first. | |
| cursor | No | Opaque pagination cursor from the previous response cursor field; omit for the newest page. | |
| markRead | No | When 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
| Name | Required | Description |
|---|---|---|
| success | Yes | Whether the request succeeded. |
| convoId | Yes | Id of the conversation that was read. |
| messages | Yes | The messages for this page, newest first. Deleted messages keep their id/sender/timestamp but have no text. |
| cursor | No | Opaque cursor for the next (older) page; absent when there are no more messages. |
| hasMore | Yes | Whether another (older) page is available (a cursor was returned). |
| markedRead | Yes | Whether the conversation was marked read by this call (markRead=true). |
TDQS
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.
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.
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.
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.
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.
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_feedARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| feedUri | Yes | AT-URI of the custom algorithm feed generator (at://did/app.bsky.feed.generator/rkey). | |
| limit | No | Max posts to return per page (1–100, default 50). | |
| cursor | No | Opaque pagination cursor from the previous response cursor field; omit for the first page. |
Output Schema
| Name | Required | Description |
|---|---|---|
| success | Yes | Whether the feed was retrieved successfully. |
| feed | Yes | Metadata about the feed generator. |
| posts | Yes | Paginated posts from the feed. |
| cursor | No | Pagination cursor for the next page; absent when no more pages. |
TDQS
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.
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.
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.
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.
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.
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_listARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| listUri | Yes | AT-URI of the list to read (at://did/app.bsky.graph.list/rkey). | |
| limit | No | Max list members to return per page (1–100, default 50). | |
| cursor | No | Opaque pagination cursor from the previous response cursor field; omit for the first page. |
Output Schema
| Name | Required | Description |
|---|---|---|
| success | Yes | Whether the list was retrieved successfully. |
| list | Yes | Metadata about the list. |
| items | Yes | Paginated list of member entries. |
| cursor | No | Pagination cursor for the next page; absent when no more pages. |
TDQS
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.
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.
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.
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.
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.
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_notificationsARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max notifications per page (1–100, default 50). | |
| cursor | No | Opaque pagination cursor from the previous response cursor field; omit for the first page. | |
| seenAt | No | ISO 8601 timestamp; only return notifications that occurred after this time. Optional filter. | |
| countOnly | No | When true, return only the unread count and skip fetching the notification list (cheap badge-number path). |
Output Schema
| Name | Required | Description |
|---|---|---|
| success | Yes | Whether the request succeeded. |
| unreadCount | Yes | Number of unread notifications (always present). |
| notifications | No | Notification entries. Present only when countOnly is false/absent. |
| cursor | No | Opaque cursor for the next page. Present only when countOnly is false/absent. |
| hasMore | No | True when a next page is available. Present only when countOnly is false/absent. |
| seenAt | No | The timestamp up to which notifications have been seen. Present only when countOnly is false/absent. |
TDQS
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.
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.
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.
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.
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.
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_contextARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| uri | Yes | AT-URI of the post to read (at://...). | |
| includeThread | No | Include thread context (parent chain, root, and replies). Default true. | |
| includeAuthorProfile | No | Include the post author's full profile. Default true. | |
| includeEngagement | No | Include computed engagement metrics (likes, reposts, replies, rate, age). Default true. | |
| depth | No | How many levels of replies to fetch (0–10, default 6). | |
| parentHeight | No | How many parent posts up the chain to fetch (0–80, default 80). | |
| includeMedia | No | Extract media embeds (images, videos, external links, quote posts) from the post. Default false. |
Output Schema
| Name | Required | Description |
|---|---|---|
| success | Yes | Whether the post context was retrieved. |
| post | Yes | The requested post (normalized post view). |
| thread | No | Thread context (present when includeThread is true): the immediate parent, the true thread root, the direct replies, and the parent-chain depth. |
| authorProfile | No | The post author's full profile (present when includeAuthorProfile is true). |
| engagement | No | Computed engagement metrics (present when includeEngagement is true). |
| media | No | Media embeds extracted from the post (present when includeMedia is true). |
TDQS
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.
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.
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.
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.
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.
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_packARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| starterPack | Yes | Starter 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
| Name | Required | Description |
|---|---|---|
| success | Yes | Whether the request succeeded. |
| starterPack | Yes | Full view of the starter pack. |
TDQS
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.
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.
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.
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.
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.
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_timelineARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| algorithm | No | 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. | |
| limit | No | Maximum number of posts to return (1–100, default 50). | |
| cursor | No | Opaque pagination cursor from the previous response cursor field; omit for the first page. |
Output Schema
| Name | Required | Description |
|---|---|---|
| success | Yes | Whether the timeline was retrieved successfully. |
| posts | Yes | List of posts in the timeline. |
| cursor | No | Opaque pagination cursor to pass in a subsequent request to retrieve the next page; absent when there are no more results. |
| hasMore | Yes | Whether additional pages of results are available. |
| algorithm | No | The algorithm or feed generator AT-URI used for this request, if provided. |
TDQS
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.
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.
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.
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.
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.
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_connectionsARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| actor | Yes | Handle (e.g. alice.bsky.social) or DID of the account whose connections to list. | |
| direction | Yes | Which side of the follow graph to return: 'followers' = accounts that follow the actor; 'follows' = accounts the actor follows. | |
| limit | No | Max accounts per page (1–100, default 50). | |
| cursor | No | Opaque pagination cursor from the previous response cursor field; omit for the first page. |
Output Schema
| Name | Required | Description |
|---|---|---|
| success | Yes | Whether the request succeeded. |
| actor | Yes | The actor whose connections were fetched. |
| direction | Yes | The direction of the follow graph that was queried. |
| connections | Yes | The matched accounts for this page. |
| cursor | No | Opaque cursor for the next page; absent when there are no more results. |
TDQS
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.
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.
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.
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.
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.
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_profileARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| actor | Yes | Handle (e.g. alice.bsky.social) or DID (e.g. did:plc:...) of the account whose profile to retrieve. |
Output Schema
| Name | Required | Description |
|---|---|---|
| success | Yes | Whether the profile was retrieved successfully. |
| profile | Yes | Full profile record for the requested account. |
TDQS
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.
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.
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.
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.
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.
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_summaryARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| actor | Yes | Handle (e.g. alice.bsky.social) or DID of the target account. | |
| includeRecentPosts | No | Whether to include recent posts in the response. Default true. | |
| postLimit | No | Number of recent posts to fetch (1–50, default 10). Only used when includeRecentPosts or includeEngagementStats is true. | |
| includeEngagementStats | No | Whether to compute engagement statistics (avg likes, reposts, replies) over the recent posts. Default true. |
Output Schema
| Name | Required | Description |
|---|---|---|
| success | Yes | Whether the summary was retrieved successfully. |
| profile | Yes | The user's full profile, including optional viewer context when authenticated. |
| recentPosts | No | Recent posts (present when includeRecentPosts is true). |
| engagementStats | No | Engagement statistics computed over the fetched posts (present when includeEngagementStats is true). |
| summary | Yes | Condensed key metrics for quick consumption. |
TDQS
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.
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.
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.
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.
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.
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_postAIdempotent
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.
| Name | Required | Description | Default |
|---|---|---|---|
| uri | Yes | AT-URI of the post to like (at://did/app.bsky.feed.post/rkey). | |
| cid | Yes | CID of the post record; used to confirm the exact version being liked. |
Output Schema
| Name | Required | Description |
|---|---|---|
| uri | Yes | AT-URI of the newly created (or existing) like record. |
| cid | No | 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. |
| success | Yes | Whether the like operation succeeded. |
| message | Yes | Human-readable status message. |
| alreadyLiked | Yes | True when the post was already liked; the existing like record URI is returned and no duplicate is created. |
| likedPost | Yes | The post that was liked. |
TDQS
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.
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.
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.
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.
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.
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_conversationsARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max conversations per page (1-100, default 50). | |
| cursor | No | Opaque pagination cursor from the previous response cursor field; omit for the first page. | |
| status | No | Filter by conversation status: 'request' = incoming chat requests not yet accepted; 'accepted' = active conversations. Omit to list both. |
Output Schema
| Name | Required | Description |
|---|---|---|
| success | Yes | Whether the request succeeded. |
| conversations | Yes | The conversations for this page, most recently active first. |
| cursor | No | Opaque cursor for the next page; absent when there are no more results. |
| hasMore | Yes | Whether another page is available (a cursor was returned). |
TDQS
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.
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.
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.
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.
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.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| seenAt | No | ISO 8601 timestamp; notifications up to this time are marked seen. Defaults to now. |
Output Schema
| Name | Required | Description |
|---|---|---|
| success | Yes | Whether the operation succeeded. |
| message | Yes | Human-readable confirmation message. |
| seenAt | Yes | ISO 8601 timestamp that was submitted as the seen-up-to marker (the value that was persisted). |
TDQS
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.
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.
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.
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.
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.
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_userAIdempotent
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.
| Name | Required | Description | Default |
|---|---|---|---|
| actor | Yes | Handle (e.g. alice.bsky.social) or DID of the account to mute. |
Output Schema
| Name | Required | Description |
|---|---|---|
| success | Yes | Whether the mute operation succeeded. |
| message | Yes | Human-readable result message. |
| mutedUser | Yes | Details of the muted account. |
TDQS
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.
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.
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.
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.
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.
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_bookmarkADestructiveIdempotent
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.
| Name | Required | Description | Default |
|---|---|---|---|
| uri | Yes | AT-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
| Name | Required | Description |
|---|---|---|
| success | Yes | Whether the remove operation succeeded. |
| message | Yes | Human-readable status message. |
| wasBookmarked | Yes | True 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. |
| removedBookmark | Yes | The post whose bookmark was removed (or confirmed absent). |
TDQS
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.
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.
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.
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.
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.
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_listADestructiveIdempotent
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.
| Name | Required | Description | Default |
|---|---|---|---|
| listUri | Yes | AT-URI of the target list (at://did/app.bsky.graph.list/rkey). | |
| actor | Yes | Handle (e.g. alice.bsky.social) or DID of the user to remove from the list. |
Output Schema
| Name | Required | Description |
|---|---|---|
| success | Yes | 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). |
| message | Yes | Human-readable result message. |
| removedFrom | Yes | Identifies the list and actor involved in the operation. |
TDQS
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.
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.
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.
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.
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.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| text | Yes | Text content of the reply (1–300 graphemes / up to 3000 bytes). Mentions (@handle), links, and hashtags are automatically resolved into rich-text facets. | |
| root | Yes | 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. | |
| parent | Yes | 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. | |
| langs | No | List of BCP-47 language tags indicating the language(s) of the reply text. Omit if unknown. |
Output Schema
| Name | Required | Description |
|---|---|---|
| uri | Yes | AT-URI of the newly created reply post (at://did/app.bsky.feed.post/rkey). |
| cid | Yes | Content identifier (CID) of the newly created reply record. |
| success | Yes | True when the reply was created successfully. |
| message | Yes | Human-readable status message describing the outcome. |
| replyTo | Yes | The root and parent URIs that this reply is linked to. |
TDQS
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.
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.
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.
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.
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.
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_contentADestructive
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.
| Name | Required | Description | Default |
|---|---|---|---|
| subject | Yes | Strong reference identifying the specific content record to report. | |
| reasonType | Yes | 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. | |
| reason | No | Optional free-text explanation of the violation (max 2000 characters). Providing detail helps moderators act faster. |
Output Schema
| Name | Required | Description |
|---|---|---|
| success | Yes | Whether the report was submitted successfully. |
| message | Yes | Human-readable result message. |
| reportId | Yes | Numeric identifier of the newly created moderation report (as a string). |
| reportDetails | Yes | Echo of the submitted report parameters. |
TDQS
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.
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.
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.
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.
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.
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_userADestructive
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.
| Name | Required | Description | Default |
|---|---|---|---|
| actor | Yes | Handle (e.g. alice.bsky.social) or DID of the account to report. | |
| reasonType | Yes | 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. | |
| reason | No | Optional free-text explanation of the violation (max 2000 characters). Providing detail helps moderators act faster. |
Output Schema
| Name | Required | Description |
|---|---|---|
| success | Yes | Whether the report was submitted successfully. |
| message | Yes | Human-readable result message. |
| reportId | Yes | Numeric identifier of the newly created moderation report (as a string). |
| reportDetails | Yes | Echo of the submitted report parameters. |
TDQS
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.
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.
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.
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.
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.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| uri | Yes | AT-URI of the post to repost or quote (at://did/app.bsky.feed.post/rkey). | |
| cid | Yes | CID of the post to repost or quote; used for content integrity verification. | |
| text | No | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
| uri | Yes | AT-URI of the newly created repost or quote-post record. |
| cid | No | 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. |
| success | Yes | Whether the operation succeeded. |
| message | Yes | Human-readable status message. |
| repostedPost | Yes | The original post that was reposted or quoted. |
| isQuotePost | Yes | True when text was supplied and a quote post was created rather than a plain repost. |
| alreadyReposted | Yes | True when a plain repost already existed; the existing record URI/CID is returned. |
TDQS
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.
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.
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.
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.
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.
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_actorsARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | Search term matched against handle and display name (e.g. "alice" or "Alice Smith"). | |
| limit | No | Max accounts to return (1–100, default 25). | |
| cursor | No | Pagination cursor from a previous response; omit for the first page. |
Output Schema
| Name | Required | Description |
|---|---|---|
| success | Yes | Whether the request succeeded. |
| actors | Yes | Accounts matching the search term. |
| cursor | No | Opaque cursor for the next page; absent when there are no more results. |
TDQS
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.
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.
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.
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.
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.
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_postsARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| q | Yes | Full-text search query string (1–300 characters). Supports keywords and hashtags (e.g. "#bluesky launch"). | |
| limit | No | Maximum number of posts to return per page (1–100, default 25). | |
| cursor | No | Opaque pagination cursor from the previous response cursor field; omit for the first page. | |
| sort | No | Sort order for results: "latest" returns newest posts first (default), "top" returns most-engaged posts first. | latest |
| since | No | ISO 8601 datetime lower bound (inclusive) for post creation time (e.g. "2024-01-01T00:00:00Z"). Omit to search all time. | |
| until | No | ISO 8601 datetime upper bound (exclusive) for post creation time (e.g. "2024-12-31T23:59:59Z"). Omit for no upper bound. | |
| mentions | No | Filter to posts that mention this handle or DID (e.g. "alice.bsky.social" or "did:plc:..."). | |
| author | No | Filter to posts authored by this handle or DID (e.g. "alice.bsky.social" or "did:plc:..."). Requires a non-empty q term. | |
| lang | No | BCP-47 language tag to filter posts by language (e.g. "en", "en-US", "pt-BR"). | |
| domain | No | Filter to posts containing links from this domain (e.g. "bsky.app"). Do not include protocol or path. | |
| url | No | Filter to posts containing this exact URL (must be a fully-qualified URL, e.g. "https://example.com/article"). |
Output Schema
| Name | Required | Description |
|---|---|---|
| success | Yes | Whether the search request completed successfully. |
| posts | Yes | Array of posts matching the search query. |
| cursor | No | Opaque pagination cursor to pass as cursor in the next call; absent when no further pages exist. |
| hasMore | Yes | True when a subsequent page of results is available. |
| searchQuery | Yes | The search query string that was executed. |
| totalResults | No | Approximate total number of matching posts reported by the API, if available. |
TDQS
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.
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.
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.
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.
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.
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_packsARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| q | Yes | Search query string for finding starter packs (e.g. "typescript developers"). Plain keywords work; the API recommends Lucene query syntax for advanced queries. | |
| limit | No | Max starter packs per page (1–100, default 25). | |
| cursor | No | Opaque pagination cursor from the previous response cursor field; omit for the first page. |
Output Schema
| Name | Required | Description |
|---|---|---|
| success | Yes | Whether the search succeeded. |
| query | Yes | The search query that was executed. |
| starterPacks | Yes | The matched starter packs for this page. |
| cursor | No | Opaque cursor for the next page; absent when there are no more results. |
| hasMore | Yes | Whether additional pages of results are available. |
TDQS
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.
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.
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.
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.
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.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| convoId | No | Id of an existing conversation to send into (from list_conversations). Provide exactly one of convoId or member. | |
| member | No | Handle (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. | |
| text | Yes | Plain-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
| Name | Required | Description |
|---|---|---|
| success | Yes | Whether the message was sent. |
| message | Yes | Human-readable status message. |
| convoId | Yes | Id of the conversation the message was sent into; reuse it for follow-up messages to skip conversation resolution. |
| recipientDid | No | DID the member parameter resolved to. Present only when member was used to target the message. |
| sentMessage | Yes | The message as recorded by the chat service. |
TDQS
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.
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.
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.
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.
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.
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_userADestructiveIdempotent
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.
| Name | Required | Description | Default |
|---|---|---|---|
| actor | Yes | Handle (e.g. alice.bsky.social) or DID of the account to unblock. |
Output Schema
| Name | Required | Description |
|---|---|---|
| success | Yes | True if the block was removed; false if the account was not blocked. |
| message | Yes | Human-readable result message. |
| unblockedUser | Yes | Details of the targeted account. |
TDQS
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.
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.
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.
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.
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.
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_userADestructive
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.
| Name | Required | Description | Default |
|---|---|---|---|
| followUri | Yes | AT-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
| Name | Required | Description |
|---|---|---|
| success | Yes | True when the follow record was successfully deleted. |
| message | Yes | Human-readable outcome message. |
| deletedFollow | Yes | Information about the deleted follow record. |
TDQS
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.
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.
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.
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.
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.
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_postADestructive
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.
| Name | Required | Description | Default |
|---|---|---|---|
| likeUri | Yes | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
| success | Yes | Whether the unlike operation succeeded. |
| message | Yes | Human-readable status message. |
| deletedLike | Yes | The like record that was deleted. |
TDQS
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.
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.
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.
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.
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.
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_userADestructiveIdempotent
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.
| Name | Required | Description | Default |
|---|---|---|---|
| actor | Yes | Handle (e.g. alice.bsky.social) or DID of the account to unmute. |
Output Schema
| Name | Required | Description |
|---|---|---|
| success | Yes | Whether the unmute operation succeeded. |
| message | Yes | Human-readable result message. |
| unmutedUser | Yes | Details of the unmuted account. |
TDQS
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.
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.
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.
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.
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.
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.
unrepostADestructive
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.
| Name | Required | Description | Default |
|---|---|---|---|
| repostUri | Yes | AT-URI of the repost record to delete (at://did/app.bsky.feed.repost/rkey). Must reference a repost record, not a post. |
Output Schema
| Name | Required | Description |
|---|---|---|
| success | Yes | Whether the repost was successfully deleted. |
| message | Yes | Human-readable status message. |
| deletedRepost | Yes | The repost record that was deleted. |
TDQS
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.
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.
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.
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.
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.
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_profileADestructiveIdempotent
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.
| Name | Required | Description | Default |
|---|---|---|---|
| displayName | No | New display name for the profile (max 64 graphemes; emoji count as one). Omit to leave unchanged. | |
| description | No | New bio/description for the profile (max 256 graphemes; emoji count as one). Omit to leave unchanged. | |
| avatar | No | 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. | |
| banner | No | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
| success | Yes | Whether the profile was successfully updated. |
| message | Yes | Human-readable result message. |
| updatedFields | Yes | Names of the profile fields that were actually changed (e.g. ["displayName", "description"]). |
| profile | Yes | The new values of the updated profile fields. |
TDQS
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.
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.
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.
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.
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.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| filePath | Yes | 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. | |
| altText | No | Accessible alt-text description of the image (max 1000 characters). Omit if no description is available. |
Output Schema
| Name | Required | Description |
|---|---|---|
| success | Yes | Whether the upload succeeded. |
| message | Yes | Human-readable status message. |
| image | Yes | Uploaded image blob descriptor and metadata. Pass the `blob` object as create_post embed.images[].image or update_profile avatar/banner. |
TDQS
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.
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.
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.
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.
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.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| filePath | Yes | 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). | |
| altText | No | Accessible alt-text description of the video (max 1000 characters). Omit if no description is available. | |
| captions | No | Optional list of caption tracks to attach to the video. Each entry pairs a language code with a WebVTT file path. |
Output Schema
| Name | Required | Description |
|---|---|---|
| success | Yes | Whether the upload and processing succeeded. |
| message | Yes | Human-readable status message. |
| video | Yes | 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. |
TDQS
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.
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.
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.
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.
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.
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.
51 tool updates
v0.6.2- Changed
add_bookmark1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
add_to_list1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
analyze_account1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
analyze_image4 fields changed- removed
Input schema / additionalPropertiesRemoved value: -false - removed
Input schema / properties / blob / additionalPropertiesRemoved value: -false - changed
Input schema / properties / blob / properties / ref / anyOfPrevious 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" + } +] - added
Input schema / properties / blob / properties / size / maximumAdded value: +9007199254740991
- Changed
analyze_moderation_status1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
batch_action1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
block_user1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
create_list1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
create_post30 fields changed- removed
Input schema / additionalPropertiesRemoved value: -false - removed
Input schema / properties / embed / additionalPropertiesRemoved value: -false - removed
Input schema / properties / embed / properties / external / additionalPropertiesRemoved value: -false - removed
Input schema / properties / embed / properties / external / properties / thumb / additionalPropertiesRemoved value: -false - changed
Input schema / properties / embed / properties / external / properties / thumb / properties / ref / anyOfPrevious 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" + } +] - added
Input schema / properties / embed / properties / external / properties / thumb / properties / size / maximumAdded value: +9007199254740991 - removed
Input schema / properties / embed / properties / images / items / additionalPropertiesRemoved value: -false - removed
Input schema / properties / embed / properties / images / items / properties / image / additionalPropertiesRemoved value: -false - changed
Input schema / properties / embed / properties / images / items / properties / image / properties / ref / anyOfPrevious 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" + } +] - added
Input schema / properties / embed / properties / images / items / properties / image / properties / size / maximumAdded value: +9007199254740991 - removed
Input schema / properties / embed / properties / video / additionalPropertiesRemoved value: -false - removed
Input schema / properties / embed / properties / video / properties / aspectRatio / additionalPropertiesRemoved value: -false - added
Input schema / properties / embed / properties / video / properties / aspectRatio / properties / height / maximumAdded value: +9007199254740991 - added
Input schema / properties / embed / properties / video / properties / aspectRatio / properties / width / maximumAdded value: +9007199254740991 - removed
Input schema / properties / embed / properties / video / properties / captions / items / additionalPropertiesRemoved value: -false - removed
Input schema / properties / embed / properties / video / properties / captions / items / properties / file / additionalPropertiesRemoved value: -false - changed
Input schema / properties / embed / properties / video / properties / captions / items / properties / file / properties / ref / anyOfPrevious 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" + } +] - added
Input schema / properties / embed / properties / video / properties / captions / items / properties / file / properties / size / maximumAdded value: +9007199254740991 - removed
Input schema / properties / embed / properties / video / properties / video / additionalPropertiesRemoved value: -false - changed
Input schema / properties / embed / properties / video / properties / video / properties / ref / anyOfPrevious 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" + } +] - added
Input schema / properties / embed / properties / video / properties / video / properties / size / maximumAdded value: +9007199254740991 - removed
Input schema / properties / facets / items / additionalPropertiesRemoved value: -false - removed
Input schema / properties / facets / items / properties / features / items / additionalPropertiesRemoved value: -false - removed
Input schema / properties / facets / items / properties / index / additionalPropertiesRemoved value: -false - added
Input schema / properties / facets / items / properties / index / properties / byteEnd / maximumAdded value: +9007199254740991 - added
Input schema / properties / facets / items / properties / index / properties / byteStart / maximumAdded value: +9007199254740991 - removed
Input schema / properties / quote / additionalPropertiesRemoved value: -false - removed
Input schema / properties / quoteControls / additionalPropertiesRemoved value: -false - removed
Input schema / properties / reply / additionalPropertiesRemoved value: -false - removed
Input schema / properties / replyControls / additionalPropertiesRemoved value: -false
- Changed
create_thread3 fields changed- removed
Input schema / additionalPropertiesRemoved value: -false - removed
Input schema / properties / posts / items / additionalPropertiesRemoved value: -false - removed
Input schema / properties / replyControls / additionalPropertiesRemoved value: -false
- Changed
delete_post1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
discover3 fields changed- removed
Input schema / additionalPropertiesRemoved value: -false - added
Input schema / properties / maxAge / maximumAdded value: +9007199254740991 - added
Input schema / properties / minLikes / maximumAdded value: +9007199254740991
- Changed
discover_communities2 fields changed- removed
Input schema / additionalPropertiesRemoved value: -false - added
Input schema / properties / minCommunitySize / maximumAdded value: +9007199254740991
- Changed
find_influential_users1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
find_similar_users2 fields changed- removed
Input schema / additionalPropertiesRemoved value: -false - added
Input schema / properties / minFollowerCount / maximumAdded value: +9007199254740991
- Changed
follow_user1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
generate_link_preview1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
get_author_feed1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
get_bookmarks1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
get_conversation_messages1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
get_custom_feed1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
get_list1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
get_notifications1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
get_post_context1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
get_starter_pack1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
get_timeline1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
get_user_connections1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
get_user_profile1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
get_user_summary1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
like_post1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
list_conversations1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
mark_notifications_seen1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
mute_user1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
remove_bookmark1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
remove_from_list1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
reply_to_post1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
report_content2 fields changed- removed
Input schema / additionalPropertiesRemoved value: -false - removed
Input schema / properties / subject / additionalPropertiesRemoved value: -false
- Changed
report_user1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
repost1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
search_actors1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
search_posts1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
search_starter_packs1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
send_direct_message1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
unblock_user1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
unfollow_user1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
unlike_post1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
unmute_user1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
unrepost1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
update_profile7 fields changed- removed
Input schema / additionalPropertiesRemoved value: -false - removed
Input schema / properties / avatar / additionalPropertiesRemoved value: -false - changed
Input schema / properties / avatar / properties / ref / anyOfPrevious 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" + } +] - added
Input schema / properties / avatar / properties / size / maximumAdded value: +9007199254740991 - removed
Input schema / properties / banner / additionalPropertiesRemoved value: -false - changed
Input schema / properties / banner / properties / ref / anyOfPrevious 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" + } +] - added
Input schema / properties / banner / properties / size / maximumAdded value: +9007199254740991
- Changed
upload_image1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
upload_video2 fields changed- removed
Input schema / additionalPropertiesRemoved value: -false - removed
Input schema / properties / captions / items / additionalPropertiesRemoved value: -false
75 tool updates
v0.6.0- Added
add_bookmark - Changed
add_to_list3 fields changed- added
Input schema / properties / actor / descriptionAdded value: +"Handle (e.g. alice.bsky.social) or DID of the user to add to the list." - added
Input schema / properties / listUri / descriptionAdded value: +"AT-URI of the target list (at://did/app.bsky.graph.list/rkey)." - changed
Output 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" +}
- Added
analyze_account - Removed
analyze_engagement - Changed
analyze_image15 fields changed- added
Input schema / properties / blob / descriptionAdded 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." - added
Input schema / properties / blob / properties / mimeType / descriptionAdded value: +"MIME type of the uploaded blob (e.g. \"image/jpeg\")." - added
Input schema / properties / blob / properties / mimeType / minLengthAdded value: +1 - removed
Input schema / properties / blob / properties / ref / additionalPropertiesRemoved value: -false - added
Input schema / properties / blob / properties / ref / anyOfAdded 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" + } +] - added
Input schema / properties / blob / properties / ref / descriptionAdded value: +"CID reference of the uploaded blob: either the flat string returned by upload_image, or the lexicon { \"$link\": \"<cid>\" } object form." - removed
Input schema / properties / blob / properties / ref / propertiesRemoved value: -{ - "$link": { - "type": "string" - } -} - removed
Input schema / properties / blob / properties / ref / requiredRemoved value: -[ - "$link" -] - removed
Input schema / properties / blob / properties / ref / typeRemoved value: -"object" - added
Input schema / properties / blob / properties / size / descriptionAdded value: +"Size of the uploaded blob in bytes." - added
Input schema / properties / blob / properties / size / exclusiveMinimumAdded value: +0 - changed
Input schema / properties / blob / properties / size / typePrevious value: -"number"New value: +"integer" - added
Input schema / properties / blob / properties / typeAdded value: +{ + "const": "blob", + "description": "Discriminator emitted by upload_image; always \"blob\" when present. May be omitted.", + "type": "string" +} - added
Input schema / properties / includeOptimizationSuggestions / descriptionAdded value: +"When true (default), the response includes a list of human-readable optimization and accessibility suggestions based on the blob size and MIME type." - changed
Output 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" +}
- Changed
analyze_moderation_status3 fields changed- added
Input schema / properties / includeLabels / descriptionAdded value: +"Whether to fetch and include content labels in the response (default true). Set to false to skip label fetching for a faster call." - added
Input schema / properties / subject / descriptionAdded 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." - changed
Output 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" +}
- Removed
analyze_network - Added
batch_action - Removed
batch_follow - Removed
batch_like - Removed
batch_repost - Changed
block_user2 fields changed- added
Input schema / properties / actor / descriptionAdded value: +"Handle (e.g. alice.bsky.social) or DID of the account to block." - changed
Output 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" +}
- Changed
create_list4 fields changed- added
Input schema / properties / description / descriptionAdded value: +"Optional plain-text description of the list (max 300 characters)." - added
Input schema / properties / name / descriptionAdded value: +"Display name for the list (1–64 characters)." - added
Input schema / properties / purpose / descriptionAdded value: +"List type: \"curatelist\" for a user-curated follow list, \"modlist\" for a moderation/block list. Defaults to \"curatelist\"." - changed
Output 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" +}
- Changed
create_post26 fields changed- added
Input schema / properties / embed / descriptionAdded value: +"Optional media embed: images OR an external link card OR a video (at most one)." - added
Input schema / properties / embed / properties / external / descriptionAdded value: +"An external link card to attach. Mutually exclusive with `images`, `video`, and `quote`." - added
Input schema / properties / embed / properties / external / properties / description / descriptionAdded value: +"Description shown on the external link card (max 1000 characters)." - added
Input schema / properties / embed / properties / external / properties / thumbAdded 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" +} - added
Input schema / properties / embed / properties / external / properties / title / descriptionAdded value: +"Title shown on the external link card (max 300 characters)." - added
Input schema / properties / embed / properties / external / properties / uri / descriptionAdded value: +"The URL the external link card points to." - added
Input schema / properties / embed / properties / images / descriptionAdded value: +"Up to 4 images to attach. Mutually exclusive with `external` and `quote`." - added
Input schema / properties / embed / properties / images / items / properties / alt / descriptionAdded value: +"Accessibility alt text describing the image (max 1000 characters)." - added
Input schema / properties / embed / properties / images / items / properties / image / additionalPropertiesAdded value: +false - added
Input schema / properties / embed / properties / images / items / properties / image / descriptionAdded 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." - added
Input schema / properties / embed / properties / images / items / properties / image / propertiesAdded 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" + } +} - added
Input schema / properties / embed / properties / images / items / properties / image / requiredAdded value: +[ + "ref", + "mimeType", + "size" +] - added
Input schema / properties / embed / properties / images / items / properties / image / typeAdded value: +"object" - changed
Input schema / properties / embed / properties / images / items / requiredPrevious value: -[ - "alt" -]New value: +[ + "alt", + "image" +] - added
Input schema / properties / embed / properties / videoAdded 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" +} - added
Input schema / properties / facetsAdded 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" +} - added
Input schema / properties / langs / descriptionAdded value: +"Optional BCP-47 language tags (e.g. en, en-US, pt-BR) declaring the languages of the post text." - added
Input schema / properties / quoteAdded 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" +} - added
Input schema / properties / quoteControlsAdded 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" +} - added
Input schema / properties / reply / descriptionAdded value: +"Set to make this post a reply in an existing thread." - added
Input schema / properties / reply / properties / parent / descriptionAdded value: +"AT-URI of the immediate parent post being replied to." - added
Input schema / properties / reply / properties / root / descriptionAdded value: +"AT-URI of the root post of the thread being replied to." - added
Input schema / properties / replyControlsAdded 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" +} - added
Input schema / properties / text / descriptionAdded 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." - changed
Input schema / properties / text / maxLengthPrevious value: -300New value: +3000 - changed
Output 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" +}
- Removed
create_rich_text_post - Changed
create_thread9 fields changed- added
Input schema / properties / langs / descriptionAdded value: +"Default language tags (BCP-47) applied to every post in the thread. Individual posts can override this with their own langs field." - added
Input schema / properties / langs / items / descriptionAdded value: +"BCP-47 language tag (e.g. \"en\", \"en-US\", \"pt-BR\")." - added
Input schema / properties / posts / descriptionAdded 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." - added
Input schema / properties / posts / items / properties / langs / descriptionAdded value: +"Per-post language tags (BCP-47). When set, overrides the thread-level langs field for this individual post." - added
Input schema / properties / posts / items / properties / langs / items / descriptionAdded value: +"BCP-47 language tag for this post (e.g. \"en\", \"en-US\", \"pt-BR\"). Overrides the thread-level langs for this post only." - added
Input schema / properties / posts / items / properties / text / descriptionAdded value: +"Text content of this post (1–300 graphemes / 3000 bytes). Mentions (@handle), URLs, and hashtags are auto-linked via richtext facets." - changed
Input schema / properties / posts / items / properties / text / maxLengthPrevious value: -300New value: +3000 - added
Input schema / properties / replyControlsAdded 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" +} - changed
Output 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" +}
- Changed
delete_post2 fields changed- added
Input schema / properties / uri / descriptionAdded value: +"AT-URI of the post to delete (at://did/app.bsky.feed.post/rkey). Must belong to the authenticated user." - changed
Output 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" +}
- Added
discover - Changed
discover_communities5 fields changed- added
Input schema / properties / includeMetrics / descriptionAdded value: +"When true, includes a metrics object on each community with avgFollowerCount, totalPosts, and interconnectedness values (default true)." - added
Input schema / properties / maxResults / descriptionAdded value: +"Maximum number of communities to return (1–50, default 20)." - added
Input schema / properties / minCommunitySize / descriptionAdded value: +"Minimum number of distinct members required for a cluster to be reported as a community (minimum 2, default 5)." - added
Input schema / properties / topic / descriptionAdded value: +"Keyword or phrase to search for (e.g. \"climate\", \"web dev\"). Used to retrieve relevant posts and identify active community clusters." - changed
Output 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" +}
- Removed
discover_trending - Removed
extract_media_from_post - Changed
find_influential_users6 fields changed- added
Input schema / properties / maxResults / descriptionAdded value: +"Maximum number of users to return (1–50, default 20)." - added
Input schema / properties / minFollowers / descriptionAdded value: +"Minimum follower count a user must have to be included in results (default 100)." - added
Input schema / properties / searchQuery / descriptionAdded value: +"Explicit search query string. When provided, takes precedence over topic. At least one of topic or searchQuery must be supplied." - added
Input schema / properties / sortBy / descriptionAdded 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\"." - added
Input schema / properties / topic / descriptionAdded value: +"Topic keyword(s) to search for (e.g. \"climate change\"). Used as the search query when searchQuery is not provided." - changed
Output 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" +}
- Changed
find_similar_users5 fields changed- added
Input schema / properties / actor / descriptionAdded value: +"Handle (e.g. alice.bsky.social) or DID of the target account to find similar users for." - added
Input schema / properties / includeMetrics / descriptionAdded value: +"When true, includes a metrics object on each result with followsBaseUser and followerRatioSimilarity values (default true)." - added
Input schema / properties / maxResults / descriptionAdded value: +"Maximum number of similar users to return (1–50, default 20)." - added
Input schema / properties / minFollowerCount / descriptionAdded value: +"Minimum follower count a candidate must have to be included in results (default 0, meaning no minimum)." - changed
Output 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" +}
- Changed
follow_user2 fields changed- added
Input schema / properties / actor / descriptionAdded value: +"Handle (e.g. alice.bsky.social) or DID of the account to follow." - changed
Output 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" +}
- Removed
generate_alt_text - Changed
generate_link_preview2 fields changed- added
Input schema / properties / url / descriptionAdded 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." - changed
Output 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" +}
- Added
get_author_feed - Added
get_bookmarks - Added
get_conversation_messages - Changed
get_custom_feed4 fields changed- added
Input schema / properties / cursor / descriptionAdded value: +"Opaque pagination cursor from the previous response cursor field; omit for the first page." - added
Input schema / properties / feedUri / descriptionAdded value: +"AT-URI of the custom algorithm feed generator (at://did/app.bsky.feed.generator/rkey)." - added
Input schema / properties / limit / descriptionAdded value: +"Max posts to return per page (1–100, default 50)." - changed
Output 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" +}
- Removed
get_followers - Removed
get_follows - Changed
get_list4 fields changed- added
Input schema / properties / cursor / descriptionAdded value: +"Opaque pagination cursor from the previous response cursor field; omit for the first page." - added
Input schema / properties / limit / descriptionAdded value: +"Max list members to return per page (1–100, default 50)." - added
Input schema / properties / listUri / descriptionAdded value: +"AT-URI of the list to read (at://did/app.bsky.graph.list/rkey)." - changed
Output 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" +}
- Changed
get_notifications6 fields changed- added
Input schema / properties / countOnlyAdded value: +{ + "description": "When true, return only the unread count and skip fetching the notification list (cheap badge-number path).", + "type": "boolean" +} - added
Input schema / properties / cursor / descriptionAdded value: +"Opaque pagination cursor from the previous response cursor field; omit for the first page." - removed
Input schema / properties / limit / defaultRemoved value: -50 - added
Input schema / properties / limit / descriptionAdded value: +"Max notifications per page (1–100, default 50)." - added
Input schema / properties / seenAt / descriptionAdded value: +"ISO 8601 timestamp; only return notifications that occurred after this time. Optional filter." - changed
Output 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" +}
- Changed
get_post_context8 fields changed- added
Input schema / properties / depthAdded value: +{ + "description": "How many levels of replies to fetch (0–10, default 6).", + "maximum": 10, + "minimum": 0, + "type": "integer" +} - added
Input schema / properties / includeAuthorProfile / descriptionAdded value: +"Include the post author's full profile. Default true." - added
Input schema / properties / includeEngagement / descriptionAdded value: +"Include computed engagement metrics (likes, reposts, replies, rate, age). Default true." - added
Input schema / properties / includeMediaAdded value: +{ + "default": false, + "description": "Extract media embeds (images, videos, external links, quote posts) from the post. Default false.", + "type": "boolean" +} - added
Input schema / properties / includeThread / descriptionAdded value: +"Include thread context (parent chain, root, and replies). Default true." - added
Input schema / properties / parentHeightAdded value: +{ + "description": "How many parent posts up the chain to fetch (0–80, default 80).", + "maximum": 80, + "minimum": 0, + "type": "integer" +} - added
Input schema / properties / uri / descriptionAdded value: +"AT-URI of the post to read (at://...)." - changed
Output 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" +}
- Removed
get_recent_events - Added
get_starter_pack - Removed
get_streaming_status - Removed
get_thread - Changed
get_timeline4 fields changed- added
Input schema / properties / algorithm / descriptionAdded 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." - added
Input schema / properties / cursor / descriptionAdded value: +"Opaque pagination cursor from the previous response cursor field; omit for the first page." - added
Input schema / properties / limit / descriptionAdded value: +"Maximum number of posts to return (1–100, default 50)." - changed
Output 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" +}
- Added
get_user_connections - Changed
get_user_profile2 fields changed- added
Input schema / properties / actor / descriptionAdded value: +"Handle (e.g. alice.bsky.social) or DID (e.g. did:plc:...) of the account whose profile to retrieve." - changed
Output 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" +}
- Changed
get_user_summary5 fields changed- added
Input schema / properties / actor / descriptionAdded value: +"Handle (e.g. alice.bsky.social) or DID of the target account." - added
Input schema / properties / includeEngagementStats / descriptionAdded value: +"Whether to compute engagement statistics (avg likes, reposts, replies) over the recent posts. Default true." - added
Input schema / properties / includeRecentPosts / descriptionAdded value: +"Whether to include recent posts in the response. Default true." - added
Input schema / properties / postLimit / descriptionAdded value: +"Number of recent posts to fetch (1–50, default 10). Only used when includeRecentPosts or includeEngagementStats is true." - changed
Output 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" +}
- Removed
handle_oauth_callback - Changed
like_post3 fields changed- added
Input schema / properties / cid / descriptionAdded value: +"CID of the post record; used to confirm the exact version being liked." - added
Input schema / properties / uri / descriptionAdded value: +"AT-URI of the post to like (at://did/app.bsky.feed.post/rkey)." - changed
Output 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" +}
- Added
list_conversations - Added
mark_notifications_seen - Removed
monitor_keywords - Changed
mute_user2 fields changed- added
Input schema / properties / actor / descriptionAdded value: +"Handle (e.g. alice.bsky.social) or DID of the account to mute." - changed
Output 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" +}
- Removed
recommend_content - Removed
refresh_oauth_tokens - Added
remove_bookmark - Changed
remove_from_list3 fields changed- added
Input schema / properties / actor / descriptionAdded value: +"Handle (e.g. alice.bsky.social) or DID of the user to remove from the list." - added
Input schema / properties / listUri / descriptionAdded value: +"AT-URI of the target list (at://did/app.bsky.graph.list/rkey)." - changed
Output 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" +}
- Changed
reply_to_post7 fields changed- added
Input schema / properties / langs / descriptionAdded value: +"List of BCP-47 language tags indicating the language(s) of the reply text. Omit if unknown." - added
Input schema / properties / langs / items / descriptionAdded value: +"A single BCP-47 language tag for the reply text (e.g. \"en\", \"en-US\", \"pt-BR\")." - added
Input schema / properties / parent / descriptionAdded 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." - added
Input schema / properties / root / descriptionAdded 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." - added
Input schema / properties / text / descriptionAdded 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." - changed
Input schema / properties / text / maxLengthPrevious value: -300New value: +3000 - changed
Output 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" +}
- Changed
report_content6 fields changed- added
Input schema / properties / reason / descriptionAdded value: +"Optional free-text explanation of the violation (max 2000 characters). Providing detail helps moderators act faster." - added
Input schema / properties / reasonType / descriptionAdded 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." - added
Input schema / properties / subject / descriptionAdded value: +"Strong reference identifying the specific content record to report." - added
Input schema / properties / subject / properties / cid / descriptionAdded value: +"CID (Content Identifier) of the specific version of the record to report." - added
Input schema / properties / subject / properties / uri / descriptionAdded value: +"AT-URI of the content to report (at://did/collection/rkey)." - changed
Output 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" +}
- Changed
report_user4 fields changed- added
Input schema / properties / actor / descriptionAdded value: +"Handle (e.g. alice.bsky.social) or DID of the account to report." - added
Input schema / properties / reason / descriptionAdded value: +"Optional free-text explanation of the violation (max 2000 characters). Providing detail helps moderators act faster." - added
Input schema / properties / reasonType / descriptionAdded 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." - changed
Output 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" +}
- Changed
repost5 fields changed- added
Input schema / properties / cid / descriptionAdded value: +"CID of the post to repost or quote; used for content integrity verification." - added
Input schema / properties / text / descriptionAdded 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." - changed
Input schema / properties / text / maxLengthPrevious value: -300New value: +3000 - added
Input schema / properties / uri / descriptionAdded value: +"AT-URI of the post to repost or quote (at://did/app.bsky.feed.post/rkey)." - changed
Output 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" +}
- Removed
revoke_oauth_tokens - Added
search_actors - Changed
search_posts12 fields changed- added
Input schema / properties / author / descriptionAdded value: +"Filter to posts authored by this handle or DID (e.g. \"alice.bsky.social\" or \"did:plc:...\"). Requires a non-empty q term." - added
Input schema / properties / cursor / descriptionAdded value: +"Opaque pagination cursor from the previous response cursor field; omit for the first page." - added
Input schema / properties / domain / descriptionAdded value: +"Filter to posts containing links from this domain (e.g. \"bsky.app\"). Do not include protocol or path." - added
Input schema / properties / lang / descriptionAdded value: +"BCP-47 language tag to filter posts by language (e.g. \"en\", \"en-US\", \"pt-BR\")." - added
Input schema / properties / limit / descriptionAdded value: +"Maximum number of posts to return per page (1–100, default 25)." - added
Input schema / properties / mentions / descriptionAdded value: +"Filter to posts that mention this handle or DID (e.g. \"alice.bsky.social\" or \"did:plc:...\")." - added
Input schema / properties / q / descriptionAdded value: +"Full-text search query string (1–300 characters). Supports keywords and hashtags (e.g. \"#bluesky launch\")." - added
Input schema / properties / since / descriptionAdded value: +"ISO 8601 datetime lower bound (inclusive) for post creation time (e.g. \"2024-01-01T00:00:00Z\"). Omit to search all time." - added
Input schema / properties / sort / descriptionAdded value: +"Sort order for results: \"latest\" returns newest posts first (default), \"top\" returns most-engaged posts first." - added
Input schema / properties / until / descriptionAdded value: +"ISO 8601 datetime upper bound (exclusive) for post creation time (e.g. \"2024-12-31T23:59:59Z\"). Omit for no upper bound." - added
Input schema / properties / url / descriptionAdded value: +"Filter to posts containing this exact URL (must be a fully-qualified URL, e.g. \"https://example.com/article\")." - changed
Output 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" +}
- Added
search_starter_packs - Added
send_direct_message - Removed
start_oauth_flow - Removed
start_streaming - Removed
stop_streaming - Removed
suggest_content_strategy - Removed
track_users - Changed
unblock_user2 fields changed- added
Input schema / properties / actor / descriptionAdded value: +"Handle (e.g. alice.bsky.social) or DID of the account to unblock." - changed
Output 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" +}
- Changed
unfollow_user2 fields changed- added
Input schema / properties / followUri / descriptionAdded 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." - changed
Output 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" +}
- Changed
unlike_post2 fields changed- added
Input schema / properties / likeUri / descriptionAdded 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." - changed
Output 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" +}
- Changed
unmute_user2 fields changed- added
Input schema / properties / actor / descriptionAdded value: +"Handle (e.g. alice.bsky.social) or DID of the account to unmute." - changed
Output 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" +}
- Changed
unrepost2 fields changed- added
Input schema / properties / repostUri / descriptionAdded value: +"AT-URI of the repost record to delete (at://did/app.bsky.feed.repost/rkey). Must reference a repost record, not a post." - changed
Output 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" +}
- Changed
update_profile15 fields changed- added
Input schema / properties / avatar / additionalPropertiesAdded value: +false - added
Input schema / properties / avatar / descriptionAdded 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." - added
Input schema / properties / avatar / propertiesAdded 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" + } +} - added
Input schema / properties / avatar / requiredAdded value: +[ + "ref", + "mimeType", + "size" +] - added
Input schema / properties / avatar / typeAdded value: +"object" - added
Input schema / properties / banner / additionalPropertiesAdded value: +false - added
Input schema / properties / banner / descriptionAdded 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." - added
Input schema / properties / banner / propertiesAdded 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" + } +} - added
Input schema / properties / banner / requiredAdded value: +[ + "ref", + "mimeType", + "size" +] - added
Input schema / properties / banner / typeAdded value: +"object" - added
Input schema / properties / description / descriptionAdded value: +"New bio/description for the profile (max 256 graphemes; emoji count as one). Omit to leave unchanged." - removed
Input schema / properties / description / maxLengthRemoved value: -256 - added
Input schema / properties / displayName / descriptionAdded value: +"New display name for the profile (max 64 graphemes; emoji count as one). Omit to leave unchanged." - removed
Input schema / properties / displayName / maxLengthRemoved value: -64 - changed
Output 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" +}
- Changed
upload_image3 fields changed- added
Input schema / properties / altText / descriptionAdded value: +"Accessible alt-text description of the image (max 1000 characters). Omit if no description is available." - added
Input schema / properties / filePath / descriptionAdded 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." - changed
Output 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" +}
- Changed
upload_video6 fields changed- added
Input schema / properties / altText / descriptionAdded value: +"Accessible alt-text description of the video (max 1000 characters). Omit if no description is available." - added
Input schema / properties / captions / descriptionAdded value: +"Optional list of caption tracks to attach to the video. Each entry pairs a language code with a WebVTT file path." - added
Input schema / properties / captions / items / properties / file / descriptionAdded 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." - added
Input schema / properties / captions / items / properties / lang / descriptionAdded value: +"BCP-47 language code for the caption track (e.g. \"en\", \"fr\", \"pt-BR\")." - added
Input schema / properties / filePath / descriptionAdded 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)." - changed
Output 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" +}
60 tool updates
v0.3.0- Changed
add_to_list1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
analyze_engagement1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
analyze_image1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
analyze_moderation_status1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
analyze_network1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
batch_follow1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
batch_like1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
batch_repost1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
block_user1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
create_list1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
create_post4 fields changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#" - removed
Input schema / properties / langs / items / maxLengthRemoved value: -2 - removed
Input schema / properties / langs / items / minLengthRemoved value: -2 - added
Input schema / properties / langs / items / patternAdded value: +"^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,8})*$"
- Changed
create_rich_text_post8 fields changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#" - removed
Input schema / properties / embed / properties / external / properties / thumbRemoved value: -{ - "type": "string" -} - added
Input schema / properties / embed / properties / external / properties / thumbFilePathAdded value: +{ + "type": "string" +} - added
Input schema / properties / embed / properties / images / items / properties / alt / maxLengthAdded value: +1000 - added
Input schema / properties / embed / properties / images / items / properties / filePathAdded value: +{ + "minLength": 1, + "type": "string" +} - removed
Input schema / properties / embed / properties / images / items / properties / imageRemoved value: -{ - "type": "string" -} - changed
Input schema / properties / embed / properties / images / items / requiredPrevious value: -[ - "image", - "alt" -]New value: +[ + "filePath", + "alt" +] - added
Input schema / properties / embed / properties / images / maxItemsAdded value: +4
- Changed
create_thread7 fields changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#" - removed
Input schema / properties / langs / items / maxLengthRemoved value: -2 - removed
Input schema / properties / langs / items / minLengthRemoved value: -2 - added
Input schema / properties / langs / items / patternAdded value: +"^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,8})*$" - removed
Input schema / properties / posts / items / properties / langs / items / maxLengthRemoved value: -2 - removed
Input schema / properties / posts / items / properties / langs / items / minLengthRemoved value: -2 - added
Input schema / properties / posts / items / properties / langs / items / patternAdded value: +"^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,8})*$"
- Changed
delete_post1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
discover_communities1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
discover_trending1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
extract_media_from_post1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
find_influential_users1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
find_similar_users1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
follow_user1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
generate_alt_text1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
generate_link_preview1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
get_custom_feed1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
get_followers1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
get_follows1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
get_list1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
get_notifications1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
get_post_context1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
get_recent_events1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
get_streaming_status1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
get_thread1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
get_timeline1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
get_user_profile1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
get_user_summary1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
handle_oauth_callback1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
like_post1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
monitor_keywords1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
mute_user1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
recommend_content1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
refresh_oauth_tokens1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
remove_from_list1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
reply_to_post4 fields changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#" - removed
Input schema / properties / langs / items / maxLengthRemoved value: -2 - removed
Input schema / properties / langs / items / minLengthRemoved value: -2 - added
Input schema / properties / langs / items / patternAdded value: +"^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,8})*$"
- Changed
report_content1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
report_user1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
repost1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
revoke_oauth_tokens1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
search_posts4 fields changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#" - removed
Input schema / properties / lang / maxLengthRemoved value: -2 - removed
Input schema / properties / lang / minLengthRemoved value: -2 - added
Input schema / properties / lang / patternAdded value: +"^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,8})*$"
- Changed
start_oauth_flow1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
start_streaming1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
stop_streaming1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
suggest_content_strategy1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
track_users1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
unblock_user1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
unfollow_user1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
unlike_post1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
unmute_user1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
unrepost1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
update_profile1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
upload_image1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
upload_video1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
60 tool updates
v0.2.1- First observed
add_to_list - First observed
analyze_engagement - First observed
analyze_image - First observed
analyze_moderation_status - First observed
analyze_network - First observed
batch_follow - First observed
batch_like - First observed
batch_repost - First observed
block_user - First observed
create_list - First observed
create_post - First observed
create_rich_text_post - First observed
create_thread - First observed
delete_post - First observed
discover_communities - First observed
discover_trending - First observed
extract_media_from_post - First observed
find_influential_users - First observed
find_similar_users - First observed
follow_user - First observed
generate_alt_text - First observed
generate_link_preview - First observed
get_custom_feed - First observed
get_followers - First observed
get_follows - First observed
get_list - First observed
get_notifications - First observed
get_post_context - First observed
get_recent_events - First observed
get_streaming_status - First observed
get_thread - First observed
get_timeline - First observed
get_user_profile - First observed
get_user_summary - First observed
handle_oauth_callback - First observed
like_post - First observed
monitor_keywords - First observed
mute_user - First observed
recommend_content - First observed
refresh_oauth_tokens - First observed
remove_from_list - First observed
reply_to_post - First observed
report_content - First observed
report_user - First observed
repost - First observed
revoke_oauth_tokens - First observed
search_posts - First observed
start_oauth_flow - First observed
start_streaming - First observed
stop_streaming - First observed
suggest_content_strategy - First observed
track_users - First observed
unblock_user - First observed
unfollow_user - First observed
unlike_post - First observed
unmute_user - First observed
unrepost - First observed
update_profile - First observed
upload_image - First observed
upload_video
TDQS
Scored across 51 tools
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.
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.
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.
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
Related MCP Connectors
Read public AT Protocol profiles, records, threads, backlinks and lexicons. No API key required.
271Search ATProto writing, annotations, identity, agents, and forum posts. 12 read-only tools.
Bulk Bluesky profiles plus full follower/following exports via the open AT Protocol. Pay per record.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceMCP server for Bluesky/AT Protocol enabling LLM clients and agents to authenticate, search, post, like, follow, and manage chat on Bluesky.14 npm1MIT
- FlicenseBqualityDmaintenanceA 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-
- AlicenseAqualityCmaintenanceEnables 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.1111 npmMIT
- AlicenseNot gradedqualityCmaintenanceEnables interaction with Bluesky social network through the AT Protocol, including searching posts, fetching profiles, browsing feeds, and retrieving threads and follower data.6 npmMIT