"""Demo script showing all ATProto MCP server capabilities.""" import argparse import asyncio import json from typing import cast from atproto_mcp.server import atproto_mcp from atproto_mcp.types import ( NotificationsResult, PostResult, ProfileInfo, SearchResult, TimelineResult, ) from fastmcp import Client async def main(enable_posting: bool = False): print("๐Ÿ”ต ATProto MCP Server Demo\n") async with Client(atproto_mcp) as client: # 1. Check connection status (resource) print("1. Checking connection status...") result = await client.read_resource("atproto://profile/status") status: ProfileInfo = ( json.loads(result[0].text) if result else cast(ProfileInfo, {}) ) if status.get("connected"): print(f"โœ… Connected as: @{status['handle']}") print(f" Followers: {status['followers']}") print(f" Following: {status['following']}") print(f" Posts: {status['posts']}") else: print(f"โŒ Connection failed: {status.get('error')}") return # 2. Get timeline print("\n2. Getting timeline...") result = await client.read_resource("atproto://timeline") timeline: TimelineResult = ( json.loads(result[0].text) if result else cast(TimelineResult, {}) ) if timeline.get("success") and timeline["posts"]: print(f"โœ… Found {timeline['count']} posts") post = timeline["posts"][0] print(f" Latest by @{post['author']}: {post['text'][:80]}...") save_uri = post["uri"] # Save for later interactions else: print("โŒ No posts in timeline") save_uri = None # 3. Search for posts print("\n3. Searching for posts about 'Bluesky'...") result = await client.call_tool("search", {"query": "Bluesky", "limit": 5}) search: SearchResult = ( json.loads(result[0].text) if result else cast(SearchResult, {}) ) if search.get("success") and search["posts"]: print(f"โœ… Found {search['count']} posts") print(f" Sample: {search['posts'][0]['text'][:80]}...") # 4. Get notifications print("\n4. Checking notifications...") result = await client.read_resource("atproto://notifications") notifs: NotificationsResult = ( json.loads(result[0].text) if result else cast(NotificationsResult, {}) ) if notifs.get("success"): print(f"โœ… You have {notifs['count']} notifications") unread = sum(1 for n in notifs["notifications"] if not n["is_read"]) if unread: print(f" ({unread} unread)") # 5. Demo posting capabilities if enable_posting: print("\n5. Demonstrating posting capabilities...") # a. Simple post print("\n a) Creating a simple post...") result = await client.call_tool( "post", {"text": "๐Ÿงช Testing the unified ATProto MCP post tool! #FastMCP"}, ) post_result: PostResult = json.loads(result[0].text) if result else {} if post_result.get("success"): print(" โœ… Posted successfully!") simple_uri = post_result["uri"] else: print(f" โŒ Failed: {post_result.get('error')}") simple_uri = None # b. Post with rich text (link and mention) print("\n b) Creating a post with rich text...") result = await client.call_tool( "post", { "text": "Check out FastMCP and follow @alternatebuild.dev for updates!", "links": [ { "text": "FastMCP", "url": "https://github.com/PrefectHQ/fastmcp", } ], "mentions": [ { "handle": "alternatebuild.dev", "display_text": "@alternatebuild.dev", } ], }, ) if json.loads(result[0].text).get("success"): print(" โœ… Rich text post created!") # c. Reply to a post if save_uri: print("\n c) Replying to a post...") result = await client.call_tool( "post", {"text": "Great post! ๐Ÿ‘", "reply_to": save_uri} ) if json.loads(result[0].text).get("success"): print(" โœ… Reply posted!") # d. Quote post if simple_uri: print("\n d) Creating a quote post...") result = await client.call_tool( "post", { "text": "Quoting my own test post for demo purposes ๐Ÿ”„", "quote": simple_uri, }, ) if json.loads(result[0].text).get("success"): print(" โœ… Quote post created!") # e. Post with image print("\n e) Creating a post with image...") result = await client.call_tool( "post", { "text": "Here's a test image post! ๐Ÿ“ธ", "images": ["https://picsum.photos/400/300"], "image_alts": ["Random test image"], }, ) if json.loads(result[0].text).get("success"): print(" โœ… Image post created!") # f. Quote with image (advanced) if simple_uri: print("\n f) Creating a quote post with image...") result = await client.call_tool( "post", { "text": "Quote + image combo! ๐ŸŽจ", "quote": simple_uri, "images": ["https://picsum.photos/300/200"], "image_alts": ["Another test image"], }, ) if json.loads(result[0].text).get("success"): print(" โœ… Quote with image created!") # g. Social actions if save_uri: print("\n g) Demonstrating social actions...") # Like result = await client.call_tool("like", {"uri": save_uri}) if json.loads(result[0].text).get("success"): print(" โœ… Liked a post!") # Repost result = await client.call_tool("repost", {"uri": save_uri}) if json.loads(result[0].text).get("success"): print(" โœ… Reposted!") # Follow result = await client.call_tool( "follow", {"handle": "alternatebuild.dev"} ) if json.loads(result[0].text).get("success"): print(" โœ… Followed @alternatebuild.dev!") # h. Thread creation (new!) print("\n h) Creating a thread...") result = await client.call_tool( "create_thread", { "posts": [ { "text": "Let me share some thoughts about the ATProto MCP server ๐Ÿงต" }, { "text": "First, it makes posting from the terminal incredibly smooth" }, { "text": "The unified post API means one tool handles everything", "links": [ { "text": "everything", "url": "https://github.com/PrefectHQ/fastmcp", } ], }, { "text": "And now with create_thread, multi-post threads are trivial!" }, ] }, ) if json.loads(result[0].text).get("success"): thread_result = json.loads(result[0].text) print(f" โœ… Thread created with {thread_result['post_count']} posts!") else: print("\n5. Posting capabilities (not enabled):") print(" To test posting, run with --post flag") print(" Example: python demo.py --post") # 6. Show available capabilities print("\n6. Available capabilities:") print("\n Resources (read-only):") print(" - atproto://profile/status") print(" - atproto://timeline") print(" - atproto://notifications") print("\n Tools (actions):") print(" - post: Unified posting with rich features") print(" โ€ข Simple text posts") print(" โ€ข Images (up to 4)") print(" โ€ข Rich text (links, mentions)") print(" โ€ข Replies and threads") print(" โ€ข Quote posts") print(" โ€ข Combinations (quote + image, reply + rich text, etc.)") print(" - search: Search for posts") print(" - create_thread: Post multi-part threads") print(" - follow: Follow users") print(" - like: Like posts") print(" - repost: Share posts") print("\nโœจ Demo complete!") if __name__ == "__main__": parser = argparse.ArgumentParser(description="ATProto MCP Server Demo") parser.add_argument( "--post", action="store_true", help="Enable posting test messages to Bluesky", ) args = parser.parse_args() asyncio.run(main(enable_posting=args.post))