Skip to main content
Glama

Get My Grades

get_my_grades

Retrieve your grade breakdown for a specific course or all enrolled courses, showing points, percentages, and comments for each grade item.

Instructions

Fetch your grade breakdown for a specific course or all enrolled courses. Shows grade items with points, percentages, and comments. Use this when the user asks about grades, scores, marks, GPA, academic performance, or how they're doing in a class.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
courseIdNoCourse ID to get grades for. If omitted, returns grades for all enrolled courses.

Implementation Reference

  • The main handler function `registerGetMyGrades` that registers the 'get_my_grades' tool on the MCP server. It handles two cases: fetching grades for a single course (by courseId) or fetching grades for all enrolled courses. It calls the Brightspace API endpoint /grades/values/myGradeValues and returns grade items with points, percentages, and comments.
    export function registerGetMyGrades(
      server: McpServer,
      apiClient: D2LApiClient,
      config: AppConfig
    ): void {
      server.registerTool(
        "get_my_grades",
        {
          title: "Get My Grades",
          description:
            "Fetch your grade breakdown for a specific course or all enrolled courses. Shows grade items with points, percentages, and comments. Use this when the user asks about grades, scores, marks, GPA, academic performance, or how they're doing in a class.",
          inputSchema: GetMyGradesSchema,
        },
        async (args: any) => {
          try {
            log("DEBUG", "get_my_grades tool called", { args });
    
            // Parse and validate input
            const { courseId } = GetMyGradesSchema.parse(args);
    
            // Single course case
            if (courseId) {
              const path = apiClient.le(courseId, "/grades/values/myGradeValues/");
              const gradeValues = await apiClient.get<GradeValue[]>(path, {
                ttl: DEFAULT_CACHE_TTLS.grades,
              });
    
              // Map to clean objects
              const grades = gradeValues.map((gv) => ({
                name: gv.GradeObjectName,
                displayGrade: gv.DisplayedGrade,
                pointsNumerator: gv.PointsNumerator,
                pointsDenominator: gv.PointsDenominator,
                weightedNumerator: gv.WeightedNumerator,
                weightedDenominator: gv.WeightedDenominator,
                comments: gv.Comments?.Text || null,
                lastModified: gv.LastModified,
              }));
    
              log("INFO", `get_my_grades: Retrieved ${grades.length} grade items for course ${courseId}`);
              return toolResponse({ courseId, grades });
            }
    
            // 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 grades for each course (handle 403s gracefully)
            const gradePromises = filteredEnrollments.map(async (item) => {
              try {
                const path = apiClient.le(
                  item.OrgUnit.Id,
                  "/grades/values/myGradeValues/"
                );
                const gradeValues = await apiClient.get<GradeValue[]>(path, {
                  ttl: DEFAULT_CACHE_TTLS.grades,
                });
    
                const grades = gradeValues.map((gv) => ({
                  name: gv.GradeObjectName,
                  displayGrade: gv.DisplayedGrade,
                  pointsNumerator: gv.PointsNumerator,
                  pointsDenominator: gv.PointsDenominator,
                  weightedNumerator: gv.WeightedNumerator,
                  weightedDenominator: gv.WeightedDenominator,
                  comments: gv.Comments?.Text || null,
                  lastModified: gv.LastModified,
                }));
    
                return {
                  courseId: item.OrgUnit.Id,
                  courseName: item.OrgUnit.Name,
                  grades,
                };
              } catch (error: any) {
                // 403 means no access (past course, etc) - log and skip
                if (error?.status === 403) {
                  log(
                    "DEBUG",
                    `get_my_grades: 403 Forbidden for course ${item.OrgUnit.Id} (${item.OrgUnit.Name}) - skipping`
                  );
                  return null;
                }
                throw error; // Re-throw other errors
              }
            });
    
            const results = await Promise.allSettled(gradePromises);
            const courses = results
              .filter(
                (r): r is PromiseFulfilledResult<any> =>
                  r.status === "fulfilled" && r.value !== null
              )
              .map((r) => r.value);
    
            log(
              "INFO",
              `get_my_grades: Retrieved grades for ${courses.length} courses (out of ${enrollmentResponse.Items.length} enrolled)`
            );
            return toolResponse({ courses });
          } catch (error) {
            return sanitizeError(error);
          }
        }
      );
    }
  • Zod schema `GetMyGradesSchema` defining the input for the tool: an optional `courseId` (coerced positive integer). If omitted, grades are returned for all enrolled courses.
    export const GetMyGradesSchema = z.object({
      courseId: z.coerce.number().int().positive().optional().describe("Course ID to get grades for. If omitted, returns grades for all enrolled courses."),
    });
  • src/index.ts:181-181 (registration)
    Registration call in the main server entry point where `registerGetMyGrades` is invoked with the server, API client, and config.
    registerGetMyGrades(server, apiClient, config);
  • Barrel export re-exporting `registerGetMyGrades` from the get-my-grades module.
    export { registerGetMyGrades } from "./get-my-grades.js";
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, so description must disclose behavioral traits. It only mentions output format (points, percentages, comments) but does not state that it is a read-only operation, any permissions needed, or side effects.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, both front-loaded with key information. No unnecessary words.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with one optional parameter and no output schema, the description provides adequate context: action, scope, output content, and usage hints. Missing explicit read-only declaration, but overall sufficient.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, with a clear description for courseId. The description adds context about the response but no additional parameter semantics. Baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool fetches grade breakdowns for a specific course or all courses, with specific fields (points, percentages, comments). It distinguishes itself from sibling tools like get_assignments by focusing on grades.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly lists when to use: when user asks about grades, scores, marks, GPA, academic performance. Does not mention when not to use or alternatives, but the guidance is clear and sufficient.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/RohanMuppa/brightspace-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server