delete_announcements_by_criteria
Remove Canvas course announcements based on criteria like date ranges, title content, or regex patterns, with options for dry runs and safety limits.
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 | ||
| limit | No | ||
| dry_run | No |
Implementation Reference
- The core handler function implementing the delete_announcements_by_criteria tool. It fetches announcements, filters them based on criteria (title contains/regex, date ranges), optionally limits the number, shows a dry-run preview, and deletes matching ones using Canvas API.@mcp.tool() @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)Call to register_discussion_tools(mcp) which registers all discussion tools including delete_announcements_by_criteria via its @mcp.tool() decorator.register_discussion_tools(mcp)
- src/canvas_mcp/tools/__init__.py:5-5 (registration)Import of register_discussion_tools from discussions.py, enabling its use in the tools module.from .discussions import register_discussion_tools
- src/canvas_mcp/server.py:25-25 (registration)Import of register_discussion_tools in server.py.register_discussion_tools,
- Input schema and documentation from the tool's docstring, describing parameters, criteria options, and return format. Type hints provide additional schema via @validate_params decorator.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 ) """