diff --git a/README.md b/README.md index bf51af4..f163c96 100644 --- a/README.md +++ b/README.md @@ -157,6 +157,7 @@ Once configured, you can use these tools in Cursor: - `get_todos` - Get todos from a todo list (returns all pages; handles Basecamp pagination transparently) - `search_basecamp` - Search across projects, todos, and messages - `get_comments` - Get comments for a Basecamp item +- `create_comment` - Create a comment on a Basecamp item - `get_campfire_lines` - Get recent messages from a Basecamp campfire - `get_daily_check_ins` - Get project's daily check-in questions - `get_question_answers` - Get answers to daily check-in questions diff --git a/basecamp_fastmcp.py b/basecamp_fastmcp.py index 9db0948..e2ab688 100644 --- a/basecamp_fastmcp.py +++ b/basecamp_fastmcp.py @@ -526,6 +526,38 @@ async def get_comments(recording_id: str, project_id: str) -> Dict[str, Any]: "message": str(e) } +@mcp.tool() +async def create_comment(recording_id: str, project_id: str, content: str) -> Dict[str, Any]: + """Create a comment on a Basecamp item. + + Args: + recording_id: The item ID + project_id: The project ID + content: The comment content in HTML format + """ + client = _get_basecamp_client() + if not client: + return _get_auth_error_response() + + try: + comment = await _run_sync(client.create_comment, recording_id, project_id, content) + return { + "status": "success", + "comment": comment, + "message": "Comment created successfully" + } + except Exception as e: + logger.error(f"Error creating comment: {e}") + if "401" in str(e) and "expired" in str(e).lower(): + return { + "error": "OAuth token expired", + "message": "Your Basecamp OAuth token expired during the API call. Please re-authenticate by visiting http://localhost:8000 and completing the OAuth flow again.", + } + return { + "error": "Execution error", + "message": str(e) + } + @mcp.tool() async def get_campfire_lines(project_id: str, campfire_id: str) -> Dict[str, Any]: """Get recent messages from a Basecamp campfire (chat room). diff --git a/mcp_server_cli.py b/mcp_server_cli.py index 7981fc1..5e72fb3 100755 --- a/mcp_server_cli.py +++ b/mcp_server_cli.py @@ -197,6 +197,19 @@ class MCPServer: "required": ["recording_id", "project_id"] } }, + { + "name": "create_comment", + "description": "Create a comment on a Basecamp item", + "inputSchema": { + "type": "object", + "properties": { + "recording_id": {"type": "string", "description": "The item ID"}, + "project_id": {"type": "string", "description": "The project ID"}, + "content": {"type": "string", "description": "The comment content in HTML format"} + }, + "required": ["recording_id", "project_id", "content"] + } + }, { "name": "get_campfire_lines", "description": "Get recent messages from a Basecamp campfire (chat room)", @@ -1015,6 +1028,17 @@ class MCPServer: "count": len(comments) } + elif tool_name == "create_comment": + recording_id = arguments.get("recording_id") + project_id = arguments.get("project_id") + content = arguments.get("content") + comment = client.create_comment(recording_id, project_id, content) + return { + "status": "success", + "comment": comment, + "message": "Comment created successfully" + } + elif tool_name == "get_campfire_lines": project_id = arguments.get("project_id") campfire_id = arguments.get("campfire_id") diff --git a/tests/test_cli_server.py b/tests/test_cli_server.py index c7c6c74..6fc41c8 100644 --- a/tests/test_cli_server.py +++ b/tests/test_cli_server.py @@ -102,7 +102,7 @@ def test_cli_server_tools_list(): # Check that expected tools are present tool_names = [tool["name"] for tool in tools] - expected_tools = ["get_projects", "search_basecamp", "get_todos", "global_search"] + expected_tools = ["get_projects", "search_basecamp", "get_todos", "global_search", "create_comment"] for expected_tool in expected_tools: assert expected_tool in tool_names