Get Announcements
get_announcementsRetrieve recent announcements from your Brightspace courses. Filter by course ID for a specific class or get updates across all courses.
Instructions
Fetch recent announcements from your courses. Can filter to a specific course or get announcements across all courses. Use this when the user asks about announcements, news, updates from instructors, recent posts, or what professors said.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| courseId | No | Course ID to get announcements for. If omitted, returns recent announcements across all courses. | |
| count | No | Maximum number of announcements to return |
Implementation Reference
- src/tools/get-announcements.ts:57-191 (handler)The registerGetAnnouncements function registers the 'get_announcements' MCP tool using server.registerTool(). The handler fetches announcements from Brightspace API. For a single course (courseId provided), it calls apiClient.le(courseId, '/news/') and maps results. For all courses, it fetches enrollments, applies course filtering, then fetches news for each course (handling 403 errors gracefully), and returns sorted + sliced announcements.
export function registerGetAnnouncements( server: McpServer, apiClient: D2LApiClient, config: AppConfig ): void { server.registerTool( "get_announcements", { title: "Get Announcements", description: "Fetch recent announcements from your courses. Can filter to a specific course or get announcements across all courses. Use this when the user asks about announcements, news, updates from instructors, recent posts, or what professors said.", inputSchema: GetAnnouncementsSchema, }, async (args: any) => { try { log("DEBUG", "get_announcements tool called", { args }); // Parse and validate input const { courseId, count } = GetAnnouncementsSchema.parse(args); // Single course case if (courseId) { const path = apiClient.le(courseId, "/news/"); const newsItems = await apiClient.get<NewsItem[]>(path, { ttl: DEFAULT_CACHE_TTLS.announcements, }); // Map to clean objects const announcements = newsItems .map((item) => ({ id: item.Id, title: item.Title, body: item.Body.Text, createdBy: item.CreatedBy.DisplayName, createdDate: item.CreatedDate, startDate: item.StartDate, isPinned: item.IsPinned, })) .sort( (a, b) => new Date(b.createdDate).getTime() - new Date(a.createdDate).getTime() ) .slice(0, count); log( "INFO", `get_announcements: Retrieved ${announcements.length} announcements for course ${courseId}` ); return toolResponse(announcements); } // All courses case // First, fetch enrolled courses const enrollmentPath = apiClient.lp( "/enrollments/myenrollments/?orgUnitTypeId=3&isActive=true" ); const enrollmentResponse = await apiClient.get<EnrollmentResponse>( enrollmentPath, { ttl: DEFAULT_CACHE_TTLS.enrollments } ); // Apply course filter const filteredEnrollments = applyCourseFilter( enrollmentResponse.Items.map(item => ({ id: item.OrgUnit.Id, name: item.OrgUnit.Name, code: item.OrgUnit.Code, isActive: item.Access.IsActive, ...item, })), config.courseFilter ); // Fetch announcements for each course (handle 403s gracefully) const announcementPromises = filteredEnrollments.map( async (item) => { try { const path = apiClient.le(item.OrgUnit.Id, "/news/"); const newsItems = await apiClient.get<NewsItem[]>(path, { ttl: DEFAULT_CACHE_TTLS.announcements, }); return newsItems.map((newsItem) => ({ id: newsItem.Id, title: newsItem.Title, body: newsItem.Body.Text, createdBy: newsItem.CreatedBy.DisplayName, createdDate: newsItem.CreatedDate, startDate: newsItem.StartDate, isPinned: newsItem.IsPinned, courseId: item.OrgUnit.Id, courseName: item.OrgUnit.Name, })); } catch (error: any) { // 403 means no access (past course, etc) - log and skip if (error?.status === 403) { log( "DEBUG", `get_announcements: 403 Forbidden for course ${item.OrgUnit.Id} (${item.OrgUnit.Name}) - skipping` ); return []; } throw error; // Re-throw other errors } } ); const results = await Promise.allSettled(announcementPromises); const allAnnouncements = results .filter( (r): r is PromiseFulfilledResult<any> => r.status === "fulfilled" ) .flatMap((r) => r.value); // Sort by created date and slice to count const announcements = allAnnouncements .sort( (a, b) => new Date(b.createdDate).getTime() - new Date(a.createdDate).getTime() ) .slice(0, count); log( "INFO", `get_announcements: Retrieved ${announcements.length} announcements (out of ${allAnnouncements.length} total across ${enrollmentResponse.Items.length} courses)` ); return toolResponse(announcements); } catch (error) { return sanitizeError(error); } } ); } - src/tools/schemas.ts:28-31 (schema)GetAnnouncementsSchema is a Zod schema with two fields: courseId (optional positive integer) and count (integer 1-50, default 10). Used for input validation of the 'get_announcements' tool.
export const GetAnnouncementsSchema = z.object({ courseId: z.coerce.number().int().positive().optional().describe("Course ID to get announcements for. If omitted, returns recent announcements across all courses."), count: z.coerce.number().int().min(1).max(50).default(10).describe("Maximum number of announcements to return"), }); - src/index.ts:182-182 (registration)The tool is registered at server startup by calling registerGetAnnouncements(server, apiClient, config) in the main server initialization.
registerGetAnnouncements(server, apiClient, config); - src/tools/index.ts:11-11 (registration)Barrel export of registerGetAnnouncements from get-announcements.js, making it available via the tools index.
export { registerGetAnnouncements } from "./get-announcements.js";