├── .gitignore ├── requirements.txt ├── config.py ├── LICENSE ├── facebook_api.py ├── README.md ├── manager.py └── server.py /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | .venv 3 | __pycache__ -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | mcp 2 | python-dotenv 3 | requests 4 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | import os 2 | from dotenv import load_dotenv 3 | 4 | load_dotenv() 5 | 6 | # Facebook Graph API setup 7 | GRAPH_API_VERSION = "v22.0" 8 | PAGE_ACCESS_TOKEN = os.getenv("FACEBOOK_ACCESS_TOKEN") 9 | PAGE_ID = os.getenv("FACEBOOK_PAGE_ID") 10 | GRAPH_API_BASE_URL = f"https://graph.facebook.com/{GRAPH_API_VERSION}" 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Hagai Hen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /facebook_api.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from typing import Any 3 | from config import GRAPH_API_BASE_URL, PAGE_ID, PAGE_ACCESS_TOKEN 4 | 5 | 6 | class FacebookAPI: 7 | # Generic Graph API request method 8 | def _request(self, method: str, endpoint: str, params: dict[str, Any], json: dict[str, Any] = None) -> dict[str, Any]: 9 | url = f"{GRAPH_API_BASE_URL}/{endpoint}" 10 | params["access_token"] = PAGE_ACCESS_TOKEN 11 | response = requests.request(method, url, params=params, json=json) 12 | return response.json() 13 | 14 | def post_message(self, message: str) -> dict[str, Any]: 15 | return self._request("POST", f"{PAGE_ID}/feed", {"message": message}) 16 | 17 | def reply_to_comment(self, comment_id: str, message: str) -> dict[str, Any]: 18 | return self._request("POST", f"{comment_id}/comments", {"message": message}) 19 | 20 | def get_posts(self) -> dict[str, Any]: 21 | return self._request("GET", f"{PAGE_ID}/posts", {"fields": "id,message,created_time"}) 22 | 23 | def get_comments(self, post_id: str) -> dict[str, Any]: 24 | return self._request("GET", f"{post_id}/comments", {"fields": "id,message,from,created_time"}) 25 | 26 | def delete_post(self, post_id: str) -> dict[str, Any]: 27 | return self._request("DELETE", f"{post_id}", {}) 28 | 29 | def delete_comment(self, comment_id: str) -> dict[str, Any]: 30 | return self._request("DELETE", f"{comment_id}", {}) 31 | 32 | def hide_comment(self, comment_id: str) -> dict[str, Any]: 33 | """Hide a comment from the Page.""" 34 | return self._request("POST", f"{comment_id}", {"is_hidden": True}) 35 | 36 | def unhide_comment(self, comment_id: str) -> dict[str, Any]: 37 | """Unhide a previously hidden comment.""" 38 | return self._request("POST", f"{comment_id}", {"is_hidden": False}) 39 | 40 | def get_insights(self, post_id: str, metric: str, period: str = "lifetime") -> dict[str, Any]: 41 | return self._request("GET", f"{post_id}/insights", {"metric": metric, "period": period}) 42 | 43 | def get_bulk_insights(self, post_id: str, metrics: list[str], period: str = "lifetime") -> dict[str, Any]: 44 | metric_str = ",".join(metrics) 45 | return self.get_insights(post_id, metric_str, period) 46 | 47 | def post_image_to_facebook(self, image_url: str, caption: str) -> dict[str, Any]: 48 | params = { 49 | "url": image_url, 50 | "caption": caption 51 | } 52 | return self._request("POST", f"{PAGE_ID}/photos", params) 53 | 54 | def send_dm_to_user(self, user_id: str, message: str) -> dict[str, Any]: 55 | payload = { 56 | "recipient": {"id": user_id}, 57 | "message": {"text": message}, 58 | "messaging_type": "RESPONSE" 59 | } 60 | return self._request("POST", "me/messages", {}, json=payload) 61 | 62 | def update_post(self, post_id: str, new_message: str) -> dict[str, Any]: 63 | return self._request("POST", f"{post_id}", {"message": new_message}) 64 | 65 | def schedule_post(self, message: str, publish_time: int) -> dict[str, Any]: 66 | params = { 67 | "message": message, 68 | "published": False, 69 | "scheduled_publish_time": publish_time, 70 | } 71 | return self._request("POST", f"{PAGE_ID}/feed", params) 72 | 73 | def get_page_fan_count(self) -> int: 74 | data = self._request("GET", f"{PAGE_ID}", {"fields": "fan_count"}) 75 | return data.get("fan_count", 0) 76 | 77 | def get_post_share_count(self, post_id: str) -> int: 78 | data = self._request("GET", f"{post_id}", {"fields": "shares"}) 79 | return data.get("shares", {}).get("count", 0) 80 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Facebook MCP Server 2 | 3 | This project is a **MCP server** for automating and managing interactions on a Facebook Page using the Facebook Graph API. It exposes tools to create posts, moderate comments, fetch post insights, and filter negative feedback — ready to plug into Claude, or other LLM-based agents. 4 | 5 | [![Trust Score](https://archestra.ai/mcp-catalog/api/badge/quality/HagaiHen/facebook-mcp-server)](https://archestra.ai/mcp-catalog/hagaihen__facebook-mcp-server) 6 | 7 | 8 | 9 | 10 | --- 11 | 12 | ## 🤖 What Is This? 13 | 14 | This MCP provides a suite of AI-callable tools that connect directly to a Facebook Page, abstracting common API operations as LLM-friendly functions. 15 | 16 | ### ✅ Benefits 17 | 18 | - Empowers **social media managers** to automate moderation and analytics. 19 | - Seamlessly integrates with **Claude Desktop or any Agent client**. 20 | - Enables fine-grained control over Facebook content from natural language. 21 | 22 | --- 23 | 24 | ## 📦 Features 25 | 26 | | Tool | Description | 27 | |----------------------------------|---------------------------------------------------------------------| 28 | | `post_to_facebook` | Create a new Facebook post with a message. | 29 | | `reply_to_comment` | Reply to a specific comment on a post. | 30 | | `get_page_posts` | Retrieve recent posts from the Page. | 31 | | `get_post_comments` | Fetch comments on a given post. | 32 | | `delete_post` | Delete a specific post by ID. | 33 | | `delete_comment` | Delete a specific comment by ID. | 34 | | `hide_comment` | Hide a comment from public view. | 35 | | `unhide_comment` | Unhide a previously hidden comment. | 36 | | `delete_comment_from_post` | Alias for deleting a comment from a specific post. | 37 | | `filter_negative_comments` | Filter out comments with negative sentiment keywords. | 38 | | `get_number_of_comments` | Count the number of comments on a post. | 39 | | `get_number_of_likes` | Count the number of likes on a post. | 40 | | `get_post_impressions` | Get total impressions on a post. | 41 | | `get_post_impressions_unique` | Get number of unique users who saw the post. | 42 | | `get_post_impressions_paid` | Get number of paid impressions on the post. | 43 | | `get_post_impressions_organic` | Get number of organic impressions on the post. | 44 | | `get_post_engaged_users` | Get number of users who engaged with the post. | 45 | | `get_post_clicks` | Get number of clicks on the post. | 46 | | `get_post_reactions_like_total` | Get total number of 'Like' reactions. | 47 | | `get_post_top_commenters` | Get the top commenters on a post. | 48 | | `post_image_to_facebook` | Post an image with a caption to the Facebook page. | 49 | | `send_dm_to_user` | Send a direct message to a user. | 50 | | `update_post` | Updates an existing post's message. | 51 | | `schedule_post` | Schedule a post for future publication. | 52 | | `get_page_fan_count` | Retrieve the total number of Page fans. | 53 | | `get_post_share_count` | Get the number of shares on a post. | 54 | | `get_post_reactions_breakdown` | Get all reaction counts for a post in one call. | 55 | | `bulk_delete_comments` | Delete multiple comments by ID. | 56 | | `bulk_hide_comments` | Hide multiple comments by ID. | 57 | 58 | --- 59 | 60 | ## 🚀 Setup & Installation 61 | 62 | ### 1. Clone the Repository 63 | 64 | ```bash 65 | git clone https://github.com/your-org/facebook-mcp-server.git 66 | cd facebook-mcp-server 67 | ``` 68 | 69 | ### 2. 🛠️ Installation 70 | 71 | Install dependencies using uv, a fast Python package manager: 72 | If uv is not already installed, run: 73 | ```bash 74 | curl -Ls https://astral.sh/uv/install.sh | bash 75 | ``` 76 | 77 | Once uv is installed, install the project dependencies: 78 | ```bash 79 | uv pip install -r requirements.txt 80 | ``` 81 | 82 | ### 3. Set Up Environment 83 | 84 | Create a .env file in the root directory and add your Facebook Page credentials. 85 | You can obtain these from https://developers.facebook.com/tools/explorer 86 | 87 | ```bash 88 | FACEBOOK_ACCESS_TOKEN=your_facebook_page_access_token 89 | FACEBOOK_PAGE_ID=your_page_id 90 | ``` 91 | 92 | ## 🧩 Using with Claude Desktop 93 | To set up the FacebookMCP in Clade: 94 | 95 | 1. Open Clade. 96 | 2. Go to Settings → Developer → Edit Config. 97 | 3. In the config file that opens, add the following entry: 98 | 99 | ```bash 100 | "FacebookMCP": { 101 | "command": "uv", 102 | "args": [ 103 | "run", 104 | "--with", 105 | "mcp[cli]", 106 | "--with", 107 | "requests", 108 | "mcp", 109 | "run", 110 | "/path/to/facebook-mcp-server/server.py" 111 | ] 112 | } 113 | ``` 114 | 115 | --- 116 | 117 | ## ✅ You’re Ready to Go! 118 | 119 | That’s it — your Facebook MCP server is now fully configured and ready to power Claude Desktop. You can now post, moderate, and measure engagement all through natural language prompts! 120 | 121 | --- 122 | 123 | ## 🤝 Contributing 124 | 125 | Contributions, issues, and feature requests are welcome! 126 | Feel free to fork the repo and submit a pull request. 127 | 128 | - Create a branch: `git checkout -b feature/YourFeature` 129 | - Commit your changes: `git commit -m 'feat: add new feature'` 130 | - Push to the branch: `git push origin feature/YourFeature` 131 | - Open a pull request 🎉 132 | -------------------------------------------------------------------------------- /manager.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | from facebook_api import FacebookAPI 3 | 4 | 5 | class Manager: 6 | def __init__(self): 7 | self.api = FacebookAPI() 8 | 9 | def post_to_facebook(self, message: str) -> dict[str, Any]: 10 | return self.api.post_message(message) 11 | 12 | def reply_to_comment(self, post_id: str, comment_id: str, message: str) -> dict[str, Any]: 13 | return self.api.reply_to_comment(comment_id, message) 14 | 15 | def get_page_posts(self) -> dict[str, Any]: 16 | return self.api.get_posts() 17 | 18 | def get_post_comments(self, post_id: str) -> dict[str, Any]: 19 | return self.api.get_comments(post_id) 20 | 21 | def delete_post(self, post_id: str) -> dict[str, Any]: 22 | return self.api.delete_post(post_id) 23 | 24 | def delete_comment(self, comment_id: str) -> dict[str, Any]: 25 | return self.api.delete_comment(comment_id) 26 | 27 | def hide_comment(self, comment_id: str) -> dict[str, Any]: 28 | return self.api.hide_comment(comment_id) 29 | 30 | def unhide_comment(self, comment_id: str) -> dict[str, Any]: 31 | return self.api.unhide_comment(comment_id) 32 | 33 | def delete_comment_from_post(self, post_id: str, comment_id: str) -> dict[str, Any]: 34 | return self.api.delete_comment(comment_id) 35 | 36 | def filter_negative_comments(self, comments: dict[str, Any]) -> list[dict[str, Any]]: 37 | keywords = ["bad", "terrible", "awful", "hate", "dislike", "problem", "issue"] 38 | return [c for c in comments.get("data", []) if any(k in c.get("message", "").lower() for k in keywords)] 39 | 40 | def get_number_of_comments(self, post_id: str) -> int: 41 | return len(self.api.get_comments(post_id).get("data", [])) 42 | 43 | def get_number_of_likes(self, post_id: str) -> int: 44 | return self.api._request("GET", post_id, {"fields": "likes.summary(true)"}).get("likes", {}).get("summary", {}).get("total_count", 0) 45 | 46 | def get_post_insights(self, post_id: str) -> dict[str, Any]: 47 | metrics = [ 48 | "post_impressions", "post_impressions_unique", "post_impressions_paid", 49 | "post_impressions_organic", "post_engaged_users", "post_clicks", 50 | "post_reactions_like_total", "post_reactions_love_total", "post_reactions_wow_total", 51 | "post_reactions_haha_total", "post_reactions_sorry_total", "post_reactions_anger_total", 52 | ] 53 | return self.api.get_bulk_insights(post_id, metrics) 54 | 55 | def get_post_impressions(self, post_id: str) -> dict[str, Any]: 56 | return self.api.get_insights(post_id, "post_impressions") 57 | 58 | def get_post_impressions_unique(self, post_id: str) -> dict[str, Any]: 59 | return self.api.get_insights(post_id, "post_impressions_unique") 60 | 61 | def get_post_impressions_paid(self, post_id: str) -> dict[str, Any]: 62 | return self.api.get_insights(post_id, "post_impressions_paid") 63 | 64 | def get_post_impressions_organic(self, post_id: str) -> dict[str, Any]: 65 | return self.api.get_insights(post_id, "post_impressions_organic") 66 | 67 | def get_post_engaged_users(self, post_id: str) -> dict[str, Any]: 68 | return self.api.get_insights(post_id, "post_engaged_users") 69 | 70 | def get_post_clicks(self, post_id: str) -> dict[str, Any]: 71 | return self.api.get_insights(post_id, "post_clicks") 72 | 73 | def get_post_reactions_like_total(self, post_id: str) -> dict[str, Any]: 74 | return self.api.get_insights(post_id, "post_reactions_like_total") 75 | 76 | def get_post_reactions_love_total(self, post_id: str) -> dict[str, Any]: 77 | return self.api.get_insights(post_id, "post_reactions_love_total") 78 | 79 | def get_post_reactions_wow_total(self, post_id: str) -> dict[str, Any]: 80 | return self.api.get_insights(post_id, "post_reactions_wow_total") 81 | 82 | def get_post_reactions_haha_total(self, post_id: str) -> dict[str, Any]: 83 | return self.api.get_insights(post_id, "post_reactions_haha_total") 84 | 85 | def get_post_reactions_sorry_total(self, post_id: str) -> dict[str, Any]: 86 | return self.api.get_insights(post_id, "post_reactions_sorry_total") 87 | 88 | def get_post_reactions_anger_total(self, post_id: str) -> dict[str, Any]: 89 | return self.api.get_insights(post_id, "post_reactions_anger_total") 90 | 91 | def get_post_top_commenters(self, post_id: str) -> list[dict[str, Any]]: 92 | comments = self.get_post_comments(post_id).get("data", []) 93 | counter = {} 94 | for comment in comments: 95 | user_id = comment.get("from", {}).get("id") 96 | if user_id: 97 | counter[user_id] = counter.get(user_id, 0) + 1 98 | return sorted([{"user_id": k, "count": v} for k, v in counter.items()], key=lambda x: x["count"], reverse=True) 99 | 100 | def post_image_to_facebook(self, image_url: str, caption: str) -> dict[str, Any]: 101 | return self.api.post_image_to_facebook(image_url, caption) 102 | 103 | def send_dm_to_user(self, user_id: str, message: str) -> dict[str, Any]: 104 | return self.api.send_dm_to_user(user_id, message) 105 | 106 | def update_post(self, post_id: str, new_message: str) -> dict[str, Any]: 107 | return self.api.update_post(post_id, new_message) 108 | 109 | def schedule_post(self, message: str, publish_time: int) -> dict[str, Any]: 110 | return self.api.schedule_post(message, publish_time) 111 | 112 | def get_page_fan_count(self) -> int: 113 | return self.api.get_page_fan_count() 114 | 115 | def get_post_share_count(self, post_id: str) -> int: 116 | return self.api.get_post_share_count(post_id) 117 | 118 | def get_post_reactions_breakdown(self, post_id: str) -> dict[str, Any]: 119 | """Return counts for all reaction types on a post.""" 120 | metrics = [ 121 | "post_reactions_like_total", 122 | "post_reactions_love_total", 123 | "post_reactions_wow_total", 124 | "post_reactions_haha_total", 125 | "post_reactions_sorry_total", 126 | "post_reactions_anger_total", 127 | ] 128 | raw = self.api.get_bulk_insights(post_id, metrics) 129 | results: dict[str, Any] = {} 130 | for item in raw.get("data", []): 131 | name = item.get("name") 132 | value = item.get("values", [{}])[0].get("value") 133 | results[name] = value 134 | return results 135 | 136 | def bulk_delete_comments(self, comment_ids: list[str]) -> list[dict[str, Any]]: 137 | """Delete multiple comments and return their results.""" 138 | results = [] 139 | for cid in comment_ids: 140 | res = self.api.delete_comment(cid) 141 | results.append({"comment_id": cid, "result": res}) 142 | return results 143 | 144 | def bulk_hide_comments(self, comment_ids: list[str]) -> list[dict[str, Any]]: 145 | """Hide multiple comments and return their results.""" 146 | results = [] 147 | for cid in comment_ids: 148 | res = self.api.hide_comment(cid) 149 | results.append({"comment_id": cid, "result": res}) 150 | return results 151 | -------------------------------------------------------------------------------- /server.py: -------------------------------------------------------------------------------- 1 | from mcp.server.fastmcp import FastMCP 2 | from manager import Manager 3 | from typing import Any 4 | 5 | mcp = FastMCP("FacebookMCP") 6 | manager = Manager() 7 | 8 | @mcp.tool() 9 | def post_to_facebook(message: str) -> dict[str, Any]: 10 | """Create a new Facebook Page post with a text message. 11 | Input: message (str) 12 | Output: dict with post ID and creation status 13 | """ 14 | return manager.post_to_facebook(message) 15 | 16 | @mcp.tool() 17 | def reply_to_comment(post_id: str, comment_id: str, message: str) -> dict[str, Any]: 18 | """Reply to a specific comment on a Facebook post. 19 | Input: post_id (str), comment_id (str), message (str) 20 | Output: dict with reply creation status 21 | """ 22 | return manager.reply_to_comment(post_id, comment_id, message) 23 | 24 | @mcp.tool() 25 | def get_page_posts() -> dict[str, Any]: 26 | """Fetch the most recent posts on the Page. 27 | Input: None 28 | Output: dict with list of post objects and metadata 29 | """ 30 | return manager.get_page_posts() 31 | 32 | @mcp.tool() 33 | def get_post_comments(post_id: str) -> dict[str, Any]: 34 | """Retrieve all comments for a given post. 35 | Input: post_id (str) 36 | Output: dict with comment objects 37 | """ 38 | return manager.get_post_comments(post_id) 39 | 40 | @mcp.tool() 41 | def delete_post(post_id: str) -> dict[str, Any]: 42 | """Delete a specific post from the Facebook Page. 43 | Input: post_id (str) 44 | Output: dict with deletion result 45 | """ 46 | return manager.delete_post(post_id) 47 | 48 | @mcp.tool() 49 | def delete_comment(comment_id: str) -> dict[str, Any]: 50 | """Delete a specific comment from the Page. 51 | Input: comment_id (str) 52 | Output: dict with deletion result 53 | """ 54 | return manager.delete_comment(comment_id) 55 | 56 | 57 | @mcp.tool() 58 | def hide_comment(comment_id: str) -> dict[str, Any]: 59 | """Hide a comment from public view.""" 60 | return manager.hide_comment(comment_id) 61 | 62 | 63 | @mcp.tool() 64 | def unhide_comment(comment_id: str) -> dict[str, Any]: 65 | """Unhide a previously hidden comment.""" 66 | return manager.unhide_comment(comment_id) 67 | 68 | @mcp.tool() 69 | def delete_comment_from_post(post_id: str, comment_id: str) -> dict[str, Any]: 70 | """Alias to delete a comment on a post. 71 | Input: post_id (str), comment_id (str) 72 | Output: dict with deletion result 73 | """ 74 | return manager.delete_comment_from_post(post_id, comment_id) 75 | 76 | @mcp.tool() 77 | def filter_negative_comments(comments: dict[str, Any]) -> list[dict[str, Any]]: 78 | """Filter comments for basic negative sentiment. 79 | Input: comments (dict) 80 | Output: list of flagged negative comments 81 | """ 82 | return manager.filter_negative_comments(comments) 83 | 84 | @mcp.tool() 85 | def get_number_of_comments(post_id: str) -> int: 86 | """Count the number of comments on a given post. 87 | Input: post_id (str) 88 | Output: integer count of comments 89 | """ 90 | return manager.get_number_of_comments(post_id) 91 | 92 | @mcp.tool() 93 | def get_number_of_likes(post_id: str) -> int: 94 | """Return the number of likes on a post. 95 | Input: post_id (str) 96 | Output: integer count of likes 97 | """ 98 | return manager.get_number_of_likes(post_id) 99 | 100 | @mcp.tool() 101 | def get_post_insights(post_id: str) -> dict[str, Any]: 102 | """Fetch all insights metrics (impressions, reactions, clicks, etc). 103 | Input: post_id (str) 104 | Output: dict with multiple metrics and their values 105 | """ 106 | return manager.get_post_insights(post_id) 107 | 108 | @mcp.tool() 109 | def get_post_impressions(post_id: str) -> dict[str, Any]: 110 | """Fetch total impressions of a post. 111 | Input: post_id (str) 112 | Output: dict with total impression count 113 | """ 114 | return manager.get_post_impressions(post_id) 115 | 116 | @mcp.tool() 117 | def get_post_impressions_unique(post_id: str) -> dict[str, Any]: 118 | """Fetch unique impressions of a post. 119 | Input: post_id (str) 120 | Output: dict with unique impression count 121 | """ 122 | return manager.get_post_impressions_unique(post_id) 123 | 124 | @mcp.tool() 125 | def get_post_impressions_paid(post_id: str) -> dict[str, Any]: 126 | """Fetch paid impressions of a post. 127 | Input: post_id (str) 128 | Output: dict with paid impression count 129 | """ 130 | return manager.get_post_impressions_paid(post_id) 131 | 132 | @mcp.tool() 133 | def get_post_impressions_organic(post_id: str) -> dict[str, Any]: 134 | """Fetch organic impressions of a post. 135 | Input: post_id (str) 136 | Output: dict with organic impression count 137 | """ 138 | return manager.get_post_impressions_organic(post_id) 139 | 140 | @mcp.tool() 141 | def get_post_engaged_users(post_id: str) -> dict[str, Any]: 142 | """Fetch number of engaged users. 143 | Input: post_id (str) 144 | Output: dict with engagement count 145 | """ 146 | return manager.get_post_engaged_users(post_id) 147 | 148 | @mcp.tool() 149 | def get_post_clicks(post_id: str) -> dict[str, Any]: 150 | """Fetch number of post clicks. 151 | Input: post_id (str) 152 | Output: dict with click count 153 | """ 154 | return manager.get_post_clicks(post_id) 155 | 156 | @mcp.tool() 157 | def get_post_reactions_like_total(post_id: str) -> dict[str, Any]: 158 | """Fetch number of 'Like' reactions. 159 | Input: post_id (str) 160 | Output: dict with like count 161 | """ 162 | return manager.get_post_reactions_like_total(post_id) 163 | 164 | @mcp.tool() 165 | def get_post_reactions_love_total(post_id: str) -> dict[str, Any]: 166 | """Fetch number of 'Love' reactions. 167 | Input: post_id (str) 168 | Output: dict with love count 169 | """ 170 | return manager.get_post_reactions_love_total(post_id) 171 | 172 | @mcp.tool() 173 | def get_post_reactions_wow_total(post_id: str) -> dict[str, Any]: 174 | """Fetch number of 'Wow' reactions. 175 | Input: post_id (str) 176 | Output: dict with wow count 177 | """ 178 | return manager.get_post_reactions_wow_total(post_id) 179 | 180 | @mcp.tool() 181 | def get_post_reactions_haha_total(post_id: str) -> dict[str, Any]: 182 | """Fetch number of 'Haha' reactions. 183 | Input: post_id (str) 184 | Output: dict with haha count 185 | """ 186 | return manager.get_post_reactions_haha_total(post_id) 187 | 188 | @mcp.tool() 189 | def get_post_reactions_sorry_total(post_id: str) -> dict[str, Any]: 190 | """Fetch number of 'Sorry' reactions. 191 | Input: post_id (str) 192 | Output: dict with sorry count 193 | """ 194 | return manager.get_post_reactions_sorry_total(post_id) 195 | 196 | @mcp.tool() 197 | def get_post_reactions_anger_total(post_id: str) -> dict[str, Any]: 198 | """Fetch number of 'Anger' reactions. 199 | Input: post_id (str) 200 | Output: dict with anger count 201 | """ 202 | return manager.get_post_reactions_anger_total(post_id) 203 | 204 | @mcp.tool() 205 | def get_post_top_commenters(post_id: str) -> list[dict[str, Any]]: 206 | """Get the top commenters on a post. 207 | Input: post_id (str) 208 | Output: list of user IDs with comment counts 209 | """ 210 | return manager.get_post_top_commenters(post_id) 211 | 212 | @mcp.tool() 213 | def post_image_to_facebook(image_url: str, caption: str) -> dict[str, Any]: 214 | """Post an image with a caption to the Facebook page. 215 | Input: image_url (str), caption (str) 216 | Output: dict of post result 217 | """ 218 | return manager.post_image_to_facebook(image_url, caption) 219 | 220 | @mcp.tool() 221 | def send_dm_to_user(user_id: str, message: str) -> dict[str, Any]: 222 | """Send a direct message to a user. 223 | Input: user_id (str), message (str) 224 | Output: dict of result from Messenger API 225 | """ 226 | return manager.send_dm_to_user(user_id, message) 227 | 228 | @mcp.tool() 229 | def update_post(post_id: str, new_message: str) -> dict[str, Any]: 230 | """Updates an existing post's message. 231 | Input: post_id (str), new_message (str) 232 | Output: dict of update result 233 | """ 234 | return manager.update_post(post_id, new_message) 235 | @mcp.tool() 236 | def schedule_post(message: str, publish_time: int) -> dict[str, Any]: 237 | """Schedule a new post for future publishing. 238 | Input: message (str), publish_time (Unix timestamp) 239 | Output: dict with scheduled post info 240 | """ 241 | return manager.schedule_post(message, publish_time) 242 | 243 | @mcp.tool() 244 | def get_page_fan_count() -> int: 245 | """Get the Page's total fan/like count. 246 | Input: None 247 | Output: integer fan count 248 | """ 249 | return manager.get_page_fan_count() 250 | 251 | @mcp.tool() 252 | def get_post_share_count(post_id: str) -> int: 253 | """Get the number of shares for a post. 254 | Input: post_id (str) 255 | Output: integer share count 256 | """ 257 | return manager.get_post_share_count(post_id) 258 | 259 | 260 | @mcp.tool() 261 | def get_post_reactions_breakdown(post_id: str) -> dict[str, Any]: 262 | """Get counts for all reaction types on a post.""" 263 | return manager.get_post_reactions_breakdown(post_id) 264 | 265 | 266 | @mcp.tool() 267 | def bulk_delete_comments(comment_ids: list[str]) -> list[dict[str, Any]]: 268 | """Delete multiple comments by ID.""" 269 | return manager.bulk_delete_comments(comment_ids) 270 | 271 | 272 | @mcp.tool() 273 | def bulk_hide_comments(comment_ids: list[str]) -> list[dict[str, Any]]: 274 | """Hide multiple comments by ID.""" 275 | return manager.bulk_hide_comments(comment_ids) 276 | 277 | --------------------------------------------------------------------------------