This directory contains MCP (Model Context Protocol) tools that enable Cursor agent to interact with Bluesky using the ssky command-line client.
The MCP server provides 10 comprehensive tools for Bluesky interaction:
- 📋 Content Retrieval: Get posts, search posts/users, view profiles
- ✍️ Content Creation: Post with images/quotes/replies
- 🤝 Social Actions: Follow, unfollow, repost, unrepost
- 🗑️ Content Management: Delete posts
Before starting, ensure you have the following installed:
Docker is required to run the MCP server. Install Docker for your platform:
- Linux: Follow the official Docker installation guide
- macOS: Download Docker Desktop for Mac
- Windows: Download Docker Desktop for Windows
After installation, verify Docker is running:
docker --version
docker infoEnsure the ssky package is installed and available in your PATH:
pip install sskySet up your Bluesky credentials using one of these methods:
ssky login your-handle.bsky.social:your-passwordexport SSKY_USER=your-handle.bsky.social:your-passwordCreate a .env file in your project root:
SSKY_USER=your-handle.bsky.social:your-password
To enable image posting functionality, the default configuration uses ~/Pictures directory.
- Create the Pictures directory if it doesn't exist:
mkdir -p ~/Pictures-
Place your images in the
~/Picturesdirectory -
When posting images, use the container path
/app/images/:
# Example: Post with image
ssky_post(
message="Check out this amazing photo!",
images="/app/images/photo.jpg" # Use container path
)If you want to use a different directory, modify the volume mount in your MCP configuration:
{
"mcpServers": {
"ssky": {
"command": "docker",
"args": [
"run", "-i", "--rm",
"-v", "/your/custom/path:/app/images",
"-e", "SSKY_USER",
"ghcr.io/simpleskyclient/ssky-mcp:latest"
]
}
}
}If you don't have an existing .cursor/mcp.json file, this is the simplest approach using the pre-built Docker image:
-
Copy the sample configuration:
# Create .cursor directory if it doesn't exist mkdir -p .cursor # Copy sample as your MCP configuration cp mcp/mcp.sample.json .cursor/mcp.json
-
Restart Cursor to load the MCP tools
✅ That's it! Docker will automatically pull the pre-built image (ghcr.io/simpleskyclient/ssky-mcp:latest) when first used.
💡 Note: The first use may take a moment for Docker to pull the image (~276MB).
Available Docker Images:
ghcr.io/simpleskyclient/ssky-mcp:latest- Latest stable version (auto-built from main branch)ghcr.io/simpleskyclient/ssky-mcp:v0.1.2- Specific version (auto-built from tags)- Source: GitHub Container Registry
- CI/CD: Automatically built via GitHub Actions
If you already have .cursor/mcp.json with other MCP servers (e.g., GitHub):
-
Back up your existing configuration:
cp .cursor/mcp.json .cursor/mcp.json.backup
-
Add the ssky server to your
.cursor/mcp.json:Open
.cursor/mcp.jsonin your editor and add the"ssky"section inside"mcpServers":{ "mcpServers": { "your-existing-server": { // ... your existing configuration }, "ssky": { "command": "docker", "args": [ "run", "-i", "--rm", "-e", "SSKY_USER", "ghcr.io/simpleskyclient/ssky-mcp:latest" ] } } }💡 Tip: You can copy the exact configuration from
mcp/mcp.sample.json -
Restart Cursor to reload the configuration
If you prefer to build the Docker image locally or need to modify the MCP server:
-
Build the Docker image:
cd mcp ./build.sh cd ..
The script will:
- Check Docker requirements
- Build the Docker image (
ssky-mcp:latest) - Run health checks
- Test MCP protocol compatibility
-
Update the sample configuration for local image:
# Create .cursor directory if it doesn't exist mkdir -p .cursor # Copy and modify for local image sed 's|ghcr.io/simpleskyclient/ssky-mcp:latest|ssky-mcp:latest|' mcp/mcp.sample.json > .cursor/mcp.json
-
Restart Cursor to load the MCP tools
Notes:
- Use this option if you want to customize the MCP server
- Local builds use the tag
ssky-mcp:latest - Make sure your
SSKY_USERenvironment variable is set before testing
Get posts from Bluesky timeline or specific user.
Parameters:
param(optional): Target to get posts from- Empty: Your timeline
- Handle:
user.bsky.social - DID:
did:plc:... - URI:
at://... - Special:
"myself"for your own posts
limit(optional): Maximum number of posts (default: 25)output_format(optional): Output format (default: "long" for AI readability)"long": Detailed format with metadata"text": Text content only"json": JSON format"id": IDs only
delimiter(optional): Custom delimiter stringoutput_dir(optional): Output to files in specified directory
Examples:
# Get your timeline (default)
ssky_get()
# Get specific user's posts in detail
ssky_get(param="user.bsky.social", limit=10)
# Get your own posts
ssky_get(param="myself", limit=5)
# Get specific post by URI
ssky_get(param="at://did:plc:.../app.bsky.feed.post/...")Search posts on Bluesky.
Parameters:
query(required): Search query stringlimit(optional): Maximum number of results (default: 25)author(optional): Filter by author handle or DIDsince(optional): Since timestamp (ex. "2001-01-01T00:00:00Z")until(optional): Until timestamp (ex. "2099-12-31T23:59:59Z")output_format(optional): Output format (default: "long")delimiter(optional): Custom delimiter stringoutput_dir(optional): Output to files in specified directory
Examples:
# Search for posts containing "bluesky"
ssky_search(query="bluesky")
# Search your own posts about specific topic
ssky_search(query="LLM", author="myself")
# Search with date range
ssky_search(query="AI", since="2024-01-01T00:00:00Z", limit=10)Search users on Bluesky.
Parameters:
query(required): Search query for userslimit(optional): Maximum number of results (default: 25)output_format(optional): Output format (default: "long")delimiter(optional): Custom delimiter stringoutput_dir(optional): Output to files in specified directory
Examples:
# Search for users
ssky_user(query="researcher")
# Get detailed user information
ssky_user(query="AI expert", output_format="long")Show user profile information.
Parameters:
handle(required): User handle or DIDoutput_format(optional): Output format (default: "long")delimiter(optional): Custom delimiter stringoutput_dir(optional): Output to files in specified directory
Examples:
# Get user profile
ssky_profile(handle="user.bsky.social")
# Get profile in JSON format
ssky_profile(handle="user.bsky.social", output_format="json")Post a message to Bluesky.
Parameters:
message(optional): The message to postdry_run(optional): If true, shows what would be posted without postingimages(optional): Comma-separated list of image file pathsquote_uri(optional): URI of post to quote (at://...)reply_to_uri(optional): URI of post to reply to (at://...)output_format(optional): Output format (default: "text")delimiter(optional): Custom delimiter stringoutput_dir(optional): Output to files in specified directory
Examples:
# Simple post
ssky_post(message="Hello, Bluesky!")
# Post with images
ssky_post(
message="New research findings on mobility solutions",
images="/app/images/chart.png"
)
# Reply to a post
ssky_post(message="Great post!", reply_to_uri="at://did:plc:.../app.bsky.feed.post/...")
# Quote a post
ssky_post(message="Interesting perspective!", quote_uri="at://did:plc:.../app.bsky.feed.post/...")
# Dry run (preview without posting)
ssky_post(message="Test message", dry_run=True)Follow a user on Bluesky.
Parameters:
handle(required): User handle or DID to followoutput_format(optional): Output format (default: "text")delimiter(optional): Custom delimiter stringoutput_dir(optional): Output to files in specified directory
Unfollow a user on Bluesky.
Parameters:
handle(required): User handle or DID to unfollowoutput_format(optional): Output format (default: "text")delimiter(optional): Custom delimiter stringoutput_dir(optional): Output to files in specified directory
Repost a post on Bluesky.
Parameters:
post_uri(required): URI of the post to repost (at://...)output_format(optional): Output format (default: "text")delimiter(optional): Custom delimiter stringoutput_dir(optional): Output to files in specified directory
Remove a repost on Bluesky.
Parameters:
post_uri(required): URI of the post to unrepost (at://...)output_format(optional): Output format (default: "text")delimiter(optional): Custom delimiter stringoutput_dir(optional): Output to files in specified directory
Delete a post on Bluesky.
Parameters:
post_uri(required): URI of the post to delete (at://...)
Example:
# Delete a specific post
ssky_delete(post_uri="at://did:plc:.../app.bsky.feed.post/...")- Long format default: Content retrieval tools default to "long" format for better AI understanding
- Comprehensive metadata: Detailed information including timestamps, DIDs, and URIs
- Structured output: Consistent formatting across all tools
When posting messages, ssky automatically detects and handles:
- Mentions:
@username.bsky.social - Hashtags:
#bluesky #socialmedia - Links:
https://example.com(automatically creates link cards)
All tools support multiple output formats:
"long": Detailed format with full metadata (default for retrieval)"text": Clean text content only"json": Structured JSON format"id": IDs only for efficient processing
# Search for recent posts about a topic
posts = ssky_search(query="government", author="myself")
# Get detailed information about a specific post
detail = ssky_get(param="at://did:plc:.../app.bsky.feed.post/...")
# Find users discussing the topic
users = ssky_user(query="transportation policy")# Follow interesting users found in search
ssky_follow(handle="researcher.bsky.social")
# Repost interesting content
ssky_repost(post_uri="at://did:plc:.../app.bsky.feed.post/...")
# Reply to discussions
ssky_post(
message="Thanks for sharing this research!",
reply_to_uri="at://did:plc:.../app.bsky.feed.post/..."
)# Draft a post (dry run first)
draft = ssky_post(
message="Excited about new developments in public transportation! #policy",
dry_run=True
)
# Post with image
ssky_post(
message="New research findings on mobility solutions",
images="/app/images/chart.png" # Use container path: /app/images/
)
# Quote an interesting post with commentary
ssky_post(
message="This aligns with our recent findings on urban mobility",
quote_uri="at://did:plc:.../app.bsky.feed.post/..."
)- Never commit your Bluesky credentials to version control
- Use environment variables or the
ssky logincommand for authentication - The
dry_runoption is useful for testing posts before publishing - Social actions (follow, repost) are immediately executed - use with care
-
Docker not found: Ensure Docker is installed and running
- Install Docker from official website
- Start Docker service:
sudo systemctl start docker(Linux) or start Docker Desktop - Verify:
docker --versionanddocker info
-
Command not found: Ensure
sskyis installed and in your PATH- Install:
pip install ssky - Verify:
ssky --help
- Install:
-
Authentication failed: Check your credentials and network connection
- Verify credentials:
ssky login your-handle.bsky.social:your-password - Check environment variable:
echo $SSKY_USER
- Verify credentials:
-
Docker build failed: Check Docker daemon and permissions
- Ensure Docker daemon is running
- Check user permissions for Docker (may need
sudoor add user to docker group)
-
Permission denied: Ensure proper file permissions
- Make build script executable:
chmod +x mcp/build.sh
- Make build script executable:
-
Timeout errors: Operations have a 30-second timeout; network issues may cause delays
-
MCP connection issues: Restart Cursor to reload the MCP server configuration
- Verify Docker image exists:
docker images | grep ssky-mcp - Check
.cursor/mcp.jsonconfiguration format
- Verify Docker image exists:
# Get multiple users' profiles
profiles = [
ssky_profile(handle="user1.bsky.social"),
ssky_profile(handle="user2.bsky.social"),
ssky_profile(handle="user3.bsky.social")
]
# Search multiple topics
topics = ["AI", "climate", "technology"]
results = [ssky_search(query=topic, limit=5) for topic in topics]# Save timeline to files
ssky_get(output_dir="./timeline_backup", limit=100)
# Custom delimiter for CSV processing
data = ssky_search(query="data science", delimiter=",", output_format="text")This comprehensive toolset enables rich interaction with Bluesky directly from Cursor, supporting both automated workflows and interactive social media management.
For development and testing of local changes:
-
Build from local source:
cd mcp ./build.sh --localThis creates
ssky-mcp:localusingDockerfile.devwith your local source code. -
Update MCP configuration: Update
.cursor/mcp.jsonto use the local image:{ "mcpServers": { "ssky": { "command": "docker", "args": ["run", "-i", "--rm", "-e", "SSKY_USER", "ssky-mcp:local"] } } } -
Test your changes:
# Quick functionality test ./test_mcp_quick.sh # Comprehensive test suite ./test_mcp_full.sh
The MCP server includes comprehensive testing tools:
test_mcp_full.sh: Complete MCP protocol test with proper initialization sequencetest_mcp_quick.sh: Quick functionality tests for immediate feedback- Colored output: Clear status indicators for test results
- Stateful testing: Proper MCP session handling for accurate protocol testing
These testing tools ensure a reliable and developer-friendly MCP server experience.