delete_announcements_by_criteria
Remove Canvas course announcements based on specific criteria such as title content, posting date ranges, or regular expression patterns to manage announcement content efficiently.
Instructions
Delete announcements matching specific criteria.
Args:
course_identifier: The Canvas course code or ID
criteria: Dict with search criteria:
- "title_contains": str - Delete if title contains this text
- "older_than": str - Delete if posted before this date (ISO format)
- "newer_than": str - Delete if posted after this date (ISO format)
- "title_regex": str - Delete if title matches regex pattern
limit: Maximum number of announcements to delete (safety limit)
dry_run: If True, show what would be deleted without actually deleting
Returns:
String with operation results showing matched and deleted announcements
Example usage:
# Delete all announcements older than 30 days
from datetime import datetime, timedelta
results = delete_announcements_by_criteria(
"60366",
criteria={
"older_than": (datetime.now() - timedelta(days=30)).isoformat(),
"title_contains": "reminder"
},
limit=10,
dry_run=False
)
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| course_identifier | Yes | ||
| criteria | Yes | ||
| dry_run | No | ||
| limit | No |
Implementation Reference
- The core handler function implementing the 'delete_announcements_by_criteria' MCP tool. It fetches announcements, applies flexible criteria (title contains/regex, date ranges), optionally limits results, supports dry-run mode, and performs bulk deletion with detailed reporting.@validate_params async def delete_announcements_by_criteria( course_identifier: str | int, criteria: dict, limit: int | None = None, dry_run: bool = True ) -> str: """ Delete announcements matching specific criteria. Args: course_identifier: The Canvas course code or ID criteria: Dict with search criteria: - "title_contains": str - Delete if title contains this text - "older_than": str - Delete if posted before this date (ISO format) - "newer_than": str - Delete if posted after this date (ISO format) - "title_regex": str - Delete if title matches regex pattern limit: Maximum number of announcements to delete (safety limit) dry_run: If True, show what would be deleted without actually deleting Returns: String with operation results showing matched and deleted announcements Example usage: # Delete all announcements older than 30 days from datetime import datetime, timedelta results = delete_announcements_by_criteria( "60366", criteria={ "older_than": (datetime.now() - timedelta(days=30)).isoformat(), "title_contains": "reminder" }, limit=10, dry_run=False ) """ course_id = await get_course_id(course_identifier) # First list all announcements params = { "include[]": ["announcement"], "only_announcements": True, "per_page": 100 } announcements = await fetch_all_paginated_results(f"/courses/{course_id}/discussion_topics", params) if isinstance(announcements, dict) and "error" in announcements: return f"Error fetching announcements: {announcements['error']}" if not announcements: return f"No announcements found for course {course_identifier}." # Filter based on criteria matched = [] for announcement in announcements: match = True announcement_title = announcement.get("title", "") posted_at_str = announcement.get("posted_at") # Check title_contains if "title_contains" in criteria: if criteria["title_contains"].lower() not in announcement_title.lower(): match = False # Check title_regex if "title_regex" in criteria and match: try: if not re.search(criteria["title_regex"], announcement_title, re.IGNORECASE): match = False except re.error: return f"Invalid regex pattern: {criteria['title_regex']}" # Check date criteria if posted_at_str and match: try: posted_at = datetime.fromisoformat(posted_at_str.replace('Z', '+00:00')) if "older_than" in criteria: older_than = datetime.fromisoformat(criteria["older_than"].replace('Z', '+00:00')) if posted_at >= older_than: match = False if "newer_than" in criteria and match: newer_than = datetime.fromisoformat(criteria["newer_than"].replace('Z', '+00:00')) if posted_at <= newer_than: match = False except ValueError as e: return f"Error parsing date: {e}" if match: matched.append(announcement) # Apply limit if specified limit_reached = False if limit and len(matched) > limit: matched = matched[:limit] limit_reached = True course_display = await get_course_code(course_id) or course_identifier result = f"Criteria-based deletion results for course {course_display}:\n\n" result += f"Search criteria: {json.dumps(criteria, indent=2)}\n\n" result += f"Matched {len(matched)} announcements" if limit_reached: result += f" (limited to {limit})" result += "\n\n" if not matched: result += "No announcements matched the specified criteria." return result # Show what was matched result += "Matched announcements:\n" for announcement in matched: result += f" - ID: {announcement.get('id')}, Title: {announcement.get('title', 'Untitled')}, Posted: {format_date(announcement.get('posted_at'))}\n" result += "\n" if dry_run: result += "DRY RUN: No announcements were actually deleted.\n" result += "Set dry_run=False to perform actual deletions." return result # Perform actual deletions deleted = [] failed = [] for announcement in matched: announcement_id = announcement.get("id") try: response = await make_canvas_request( "delete", f"/courses/{course_id}/discussion_topics/{announcement_id}" ) if "error" in response: failed.append({ "id": str(announcement_id), "title": announcement.get("title", "Unknown Title"), "error": response["error"] }) else: deleted.append({ "id": str(announcement_id), "title": announcement.get("title", "Unknown Title") }) except Exception as e: failed.append({ "id": str(announcement_id), "title": announcement.get("title", "Unknown Title"), "error": str(e) }) result += f"Deletion completed: {len(deleted)} successful, {len(failed)} failed\n\n" if deleted: result += "Successfully deleted:\n" for item in deleted: result += f" - ID: {item['id']}, Title: {item['title']}\n" result += "\n" if failed: result += "Failed to delete:\n" for item in failed: result += f" - ID: {item['id']}, Title: {item['title']}, Error: {item['error']}\n" return result
- src/canvas_mcp/server.py:49-49 (registration)Registration call within register_all_tools(mcp) that invokes register_discussion_tools, which defines and registers the delete_announcements_by_criteria handler using the @mcp.tool() decorator.register_discussion_tools(mcp)
- Supporting single-announcement deletion function used within the criteria-based deletion logic.@mcp.tool() @validate_params async def delete_announcement( course_identifier: str | int, announcement_id: str | int ) -> str: """ Delete an announcement from a Canvas course. Announcements are technically discussion topics in Canvas, so this uses the discussion_topics endpoint to delete them. Args: course_identifier: The Canvas course code (e.g., badm_554_120251_246794) or ID announcement_id: The Canvas announcement/discussion topic ID to delete Returns: String describing the deletion result with status and title Raises: HTTPError: - 401: User doesn't have permission to delete the announcement - 404: Announcement not found in the specified course - 403: Editing is restricted for this announcement Example usage: result = delete_announcement("60366", "925355") print(f"Result: {result}") """ course_id = await get_course_id(course_identifier) # First, get the announcement details to return meaningful information announcement = await make_canvas_request( "get", f"/courses/{course_id}/discussion_topics/{announcement_id}" ) if "error" in announcement: return f"Error fetching announcement details: {announcement['error']}" announcement_title = announcement.get("title", "Unknown Title") # Proceed with deletion response = await make_canvas_request( "delete", f"/courses/{course_id}/discussion_topics/{announcement_id}" ) if "error" in response: return f"Error deleting announcement '{announcement_title}': {response['error']}" course_display = await get_course_code(course_id) or course_identifier return f"Announcement deleted successfully from course {course_display}:\n\n" + \ f"ID: {announcement_id}\n" + \ f"Title: {announcement_title}\n" + \ "Status: deleted\n" + \ "Message: Announcement deleted successfully"