Skip to main content
Glama
Connectry-io

Connectry Architect Cert

Official

get_exam_history

Retrieve all completed practice exam attempts with scores, pass/fail status, and per-domain breakdowns. Compare your progress across attempts.

Instructions

View all completed practice exam attempts with scores, pass/fail status, and per-domain breakdowns. Compare your progress across attempts.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • The main tool handler 'get_exam_history'. It registers an MCP tool that retrieves all completed exam attempts from the database, formats them with scores, pass/fail status, domain breakdowns, improvement deltas, and an overall trend summary.
    export function registerGetExamHistory(server: McpServer, db: Database.Database, userConfig: UserConfig): void {
      server.tool(
        'get_exam_history',
        'View all completed practice exam attempts with scores, pass/fail status, and per-domain breakdowns. Compare your progress across attempts.',
        {},
        async () => {
          const userId = userConfig.userId;
          ensureUser(db, userId);
          const history = getExamHistory(db, userId);
    
          if (history.length === 0) {
            return {
              content: [{
                type: 'text' as const,
                text: [
                  '═══ EXAM HISTORY ═══',
                  '',
                  'No completed practice exams yet.',
                  '',
                  'Use start_practice_exam to take your first 60-question practice exam.',
                  'Questions are weighted by domain — just like the real exam.',
                ].join('\n'),
              }],
            };
          }
    
          const lines: string[] = [
            '═══ EXAM HISTORY ═══',
            '',
            `Total Attempts: ${history.length}`,
            `Best Score: ${Math.max(...history.map(h => h.score))}/1000`,
            `Latest Score: ${history[0].score}/1000`,
            '',
          ];
    
          for (const [i, attempt] of history.entries()) {
            const label = i === 0 ? ' (latest)' : '';
            lines.push(`─── Attempt #${history.length - i}${label} ───`);
            lines.push(`  Date: ${attempt.completedAt ?? attempt.startedAt}`);
            lines.push(`  Score: ${attempt.score}/1000 ${attempt.passed ? '✅ PASSED' : '❌ FAILED'}`);
            lines.push(`  Correct: ${attempt.correctAnswers}/${attempt.totalQuestions} (${Math.round((attempt.correctAnswers / attempt.totalQuestions) * 100)}%)`);
            lines.push('');
            lines.push('  Domain Scores:');
    
            const scores = attempt.domainScores;
            for (const key of Object.keys(scores).sort()) {
              const ds = scores[key];
              lines.push(`    D${ds.domainId}: ${ds.domainTitle} — ${ds.correctAnswers}/${ds.totalQuestions} (${ds.accuracyPercent}%)`);
            }
    
            // Show improvement from previous attempt
            if (i < history.length - 1) {
              const previous = history[i + 1];
              const diff = attempt.score - previous.score;
              const arrow = diff > 0 ? '↑' : diff < 0 ? '↓' : '→';
              lines.push('');
              lines.push(`  Change from previous: ${arrow} ${diff > 0 ? '+' : ''}${diff} points`);
            }
    
            lines.push('');
          }
    
          // Trend summary
          if (history.length >= 2) {
            const latest = history[0].score;
            const first = history[history.length - 1].score;
            const totalImprovement = latest - first;
            lines.push('─── Overall Trend ───');
            lines.push(`  First attempt: ${first}/1000`);
            lines.push(`  Latest attempt: ${latest}/1000`);
            lines.push(`  Total improvement: ${totalImprovement > 0 ? '+' : ''}${totalImprovement} points`);
          }
    
          return { content: [{ type: 'text' as const, text: lines.join('\n') }] };
        }
      );
    }
  • Database query function that retrieves all completed exam attempts for a user, ordered by completion date descending.
    export function getExamHistory(db: Database.Database, userId: string): readonly ExamAttempt[] {
      const rows = db.prepare(
        'SELECT * FROM exam_attempts WHERE userId = ? AND completedAt IS NOT NULL ORDER BY completedAt DESC'
      ).all(userId) as Record<string, unknown>[];
      return rows.map(rowToExamAttempt);
    }
  • The ExamAttempt interface defining the shape of exam history data (id, userId, startedAt, completedAt, totalQuestions, correctAnswers, score, passed, questionIds, answeredQuestionIds, domainScores).
    export interface ExamAttempt {
      readonly id: number;
      readonly userId: string;
      readonly startedAt: string;
      readonly completedAt: string | null;
      readonly totalQuestions: number;
      readonly correctAnswers: number;
      readonly score: number;
      readonly passed: boolean;
      readonly questionIds: readonly string[];
      readonly answeredQuestionIds: readonly string[];
      readonly domainScores: Readonly<Record<string, DomainExamScore>>;
    }
  • The DomainExamScore interface used within ExamAttempt for per-domain breakdowns (domainId, domainTitle, totalQuestions, correctAnswers, accuracyPercent, weight).
    export interface DomainExamScore {
      readonly domainId: number;
      readonly domainTitle: string;
      readonly totalQuestions: number;
      readonly correctAnswers: number;
      readonly accuracyPercent: number;
      readonly weight: number;
    }
  • The registerTools function imports and calls registerGetExamHistory as part of the central tool registration.
    export function registerTools(server: McpServer, db: Database.Database, userConfig: UserConfig): void {
      registerSubmitAnswer(server, db, userConfig);
      registerGetProgress(server, db, userConfig);
      registerGetCurriculum(server, db, userConfig);
      registerGetSectionDetails(server, db, userConfig);
      registerGetPracticeQuestion(server, db, userConfig);
      registerStartAssessment(server, db, userConfig);
      registerGetWeakAreas(server, db, userConfig);
      registerGetStudyPlan(server, db, userConfig);
      registerScaffoldProject(server, db, userConfig);
      registerResetProgress(server, db, userConfig);
      registerStartPracticeExam(server, db, userConfig);
      registerSubmitExamAnswer(server, db, userConfig);
      registerGetExamHistory(server, db, userConfig);
      registerFollowUp(server, db, userConfig);
      registerStartCapstoneBuild(server, db, userConfig);
      registerCapstoneBuildStep(server, db, userConfig);
      registerCapstoneBuildStatus(server, db, userConfig);
      registerDashboard(server, db, userConfig);
    }
Behavior3/5

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

The description implies a read-only operation ('view'), and no annotations are present. It does not disclose potential limitations like data volume or authentication requirements, but for a simple tool with no parameters, the description adequately covers the main behavior.

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, 20 words. Front-loaded with action and outputs, second sentence adds value. No redundancy or unnecessary text.

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?

Given no output schema, the description explains return values (scores, pass/fail, per-domain breakdowns) and adds comparison feature. It lacks mention of any limits or prerequisites, but for a no-parameter tool, it is largely sufficient.

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

Parameters4/5

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

Input schema has 0 parameters, and schema description coverage is 100%. Per guidelines, baseline is 4, which is appropriate as the description adds no param-specific details.

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?

Description clearly states what the tool does: view completed practice exam attempts with specific details like scores, pass/fail, and per-domain breakdowns. It also adds a secondary feature of comparing progress. No competing sibling tool serves the same purpose.

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

Usage Guidelines2/5

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

No explicit guidance on when to use this tool versus alternatives. While the purpose is clear, the description does not mention scenarios where other tools might be more appropriate, nor does it provide criteria for selection.

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/Connectry-io/connectrylab-architect-cert-mcp'

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