@massteck/aloc-mcp-server 1.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,17 +1,22 @@
1
1
  # ALOC Model Context Protocol (MCP) Server
2
2
 
3
- Official Model Context Protocol (MCP) server for **ALOC** — providing African educational examination content, curriculum structures, and AI-enriched explanations to AI agents and LLM clients (Claude Desktop, Cursor, LangChain, AI Tutors).
3
+ Official Model Context Protocol (MCP) server for **ALOC** (`@massteck/aloc-mcp-server`) — providing African educational examination content, curriculum structures, AI-enriched explanations, and autonomous discovery tools to AI agents and LLM clients (Claude Desktop, Cursor, LangChain, AI Tutors).
4
4
 
5
5
  ---
6
6
 
7
7
  ## Features & Tools
8
8
 
9
- | Tool | Layer | Credits | Description |
9
+ | Tool | Category | Credits | Description |
10
10
  | :--- | :---: | :---: | :--- |
11
- | `aloc_search_questions` | L1 / L2 | 1–3 credits | Search curriculum-aligned examination questions (WAEC, JAMB, NECO) with options, topics, and difficulty. |
12
- | `aloc_get_question_explanation` | L3 | 10 credits | Retrieve step-by-step solutions, misconceptions, and explanations. |
13
- | `aloc_get_curriculum_taxonomy` | L2 | Free/3 credits | Explore the topic taxonomy and syllabus hierarchy. |
14
- | `aloc_find_similar_questions` | L4 | 10 credits | Find vector-similar exam questions. |
11
+ | `aloc_get_platform_overview` | Discovery | **0 credits** | Discover supported African exam bodies (WAEC, JAMB, NECO), subjects, year coverage, and API layer rules. |
12
+ | `aloc_get_sitemap` | Discovery | **0 credits** | Direct URLs to developer portal, documentation, and error guides. |
13
+ | `aloc_get_subject_years` | Discovery | **0 credits** | Available examination years for a specific subject & body. |
14
+ | `aloc_check_credit_balance` | Account | **0 credits** | Check remaining API credit balance & authentication status. |
15
+ | `aloc_search_questions` | Content | **1–3 credits** | Search curriculum-aligned examination questions with options, topics, and difficulty. |
16
+ | `aloc_get_question_explanation` | Learning | **10 credits** | Retrieve step-by-step solutions, misconceptions, and remedial hints. |
17
+ | `aloc_get_curriculum_taxonomy` | Curriculum | **Free/3 credits** | Explore the topic taxonomy and syllabus hierarchy. |
18
+ | `aloc_find_similar_questions` | Intelligence | **10 credits** | Find vector-similar exam questions. |
19
+ | `aloc_report_question_issue` | Quality | **0 credits** | Report a typo or disputed answer to ALOC moderation. |
15
20
 
16
21
  ---
17
22
 
@@ -42,11 +47,24 @@ Add the following to your `claude_desktop_config.json`:
42
47
  }
43
48
  ```
44
49
 
45
- ### 3. Configuration (Cursor / Agent Frameworks)
50
+ ### 3. Configuration (Cursor IDE)
46
51
 
47
- Set the environment variables:
48
- * `ALOC_API_KEY`: Your ALOC Developer API Key (`aloc_...`)
49
- * `ALOC_AGENT_FRAMEWORK`: Optional identifier (e.g. `cursor`, `custom-ai-tutor`)
52
+ Add the following to your `.cursor/mcp.json`:
53
+
54
+ ```json
55
+ {
56
+ "mcpServers": {
57
+ "aloc": {
58
+ "command": "npx",
59
+ "args": ["-y", "@massteck/aloc-mcp-server"],
60
+ "env": {
61
+ "ALOC_API_KEY": "your_aloc_api_key_here",
62
+ "ALOC_AGENT_FRAMEWORK": "cursor"
63
+ }
64
+ }
65
+ }
66
+ }
67
+ ```
50
68
 
51
69
  ---
52
70
 
@@ -56,4 +74,14 @@ Every MCP tool call:
56
74
  1. Passes an authenticated API key via `X-API-Key`.
57
75
  2. Sends client telemetry headers (`X-Client-Type: mcp`, `X-Is-Agent: true`).
58
76
  3. Deducts metered credits matching the ALOC pricing tiers.
59
- 4. Enforces request limits (maximum 20 items per search) to prevent bulk dumping.
77
+ 4. Enforces request limits (maximum 20 items per search) to prevent bulk harvesting.
78
+
79
+ ---
80
+
81
+ ## Documentation & Support
82
+
83
+ For comprehensive integration guides, interactive API playground, and developer credentials, visit the official ALOC Developer Portal:
84
+
85
+ * 📖 **Documentation**: [https://aloc.com.ng/docs](https://aloc.com.ng/docs)
86
+ * 🎮 **API Playground**: [https://aloc.com.ng/playground](https://aloc.com.ng/playground)
87
+ * 📧 **Support**: [aloc.mass@gmail.com](mailto:aloc.mass@gmail.com)
package/dist/client.d.ts CHANGED
@@ -6,7 +6,72 @@ export interface AlocClientConfig {
6
6
  export declare class AlocClient {
7
7
  private http;
8
8
  private apiKey;
9
+ private baseUrl;
9
10
  constructor(config?: AlocClientConfig);
11
+ /**
12
+ * Return high-level self-describing overview of the ALOC platform and coverage.
13
+ */
14
+ getPlatformOverview(): {
15
+ platform: string;
16
+ description: string;
17
+ supportedExaminationBodies: {
18
+ code: string;
19
+ name: string;
20
+ }[];
21
+ yearRange: string;
22
+ supportedSubjects: string[];
23
+ apiLayers: {
24
+ L1: {
25
+ name: string;
26
+ cost: string;
27
+ description: string;
28
+ };
29
+ L2: {
30
+ name: string;
31
+ cost: string;
32
+ description: string;
33
+ };
34
+ L3: {
35
+ name: string;
36
+ cost: string;
37
+ description: string;
38
+ };
39
+ L4: {
40
+ name: string;
41
+ cost: string;
42
+ description: string;
43
+ };
44
+ };
45
+ guidelines: {
46
+ pagination: string;
47
+ filtering: string;
48
+ };
49
+ };
50
+ /**
51
+ * Return sitemap links for developer documentation and portal.
52
+ */
53
+ getSitemap(): {
54
+ portal: {
55
+ home: string;
56
+ dashboard: string;
57
+ apiKeys: string;
58
+ usage: string;
59
+ billing: string;
60
+ };
61
+ documentation: {
62
+ introduction: string;
63
+ authentication: string;
64
+ questions: string;
65
+ metadata: string;
66
+ explanations: string;
67
+ mcp: string;
68
+ errors: string;
69
+ openapiSpec: string;
70
+ };
71
+ };
72
+ /**
73
+ * Search examination questions with filters.
74
+ */
10
75
  getQuestions(params: {
11
76
  subject: string;
12
77
  examType?: string;
@@ -15,11 +80,40 @@ export declare class AlocClient {
15
80
  difficulty?: string;
16
81
  limit?: number;
17
82
  }): Promise<any>;
83
+ /**
84
+ * Retrieve step-by-step AI explanation.
85
+ */
18
86
  getQuestionExplanation(questionId: string, depth?: string): Promise<any>;
87
+ /**
88
+ * Explore curriculum topics and taxonomy hierarchy.
89
+ */
19
90
  getCurriculumTaxonomy(params?: {
20
91
  subject?: string;
21
92
  examType?: string;
22
93
  }): Promise<any>;
94
+ /**
95
+ * Find vector-similar exam questions.
96
+ */
23
97
  getSimilarQuestions(questionId: string, limit?: number): Promise<any>;
98
+ /**
99
+ * List available subjects on ALOC.
100
+ */
24
101
  getSubjects(): Promise<any>;
102
+ /**
103
+ * List examination years available for a specific subject.
104
+ */
105
+ getSubjectYears(subject: string, examType?: string): Promise<any>;
106
+ /**
107
+ * Check credit balance and subscription status.
108
+ */
109
+ checkCreditBalance(): Promise<{
110
+ creditsRemaining: string | number;
111
+ creditsUsedLastRequest: number;
112
+ authenticated: boolean;
113
+ apiKeyPrefix: string;
114
+ }>;
115
+ /**
116
+ * Report an error, typo, or disputed answer on a question.
117
+ */
118
+ reportQuestionIssue(questionId: string, type: "question" | "option_a" | "option_b" | "option_c" | "option_d" | "correct_answer" | "explanation" | "other", message: string, reporterName?: string): Promise<any>;
25
119
  }
package/dist/client.js CHANGED
@@ -8,14 +8,15 @@ const axios_1 = __importDefault(require("axios"));
8
8
  class AlocClient {
9
9
  http;
10
10
  apiKey;
11
+ baseUrl;
11
12
  constructor(config = {}) {
12
13
  this.apiKey = config.apiKey || process.env.ALOC_API_KEY || "";
13
- const baseUrl = config.baseUrl || process.env.ALOC_BASE_URL || "https://dev.aloc.com.ng/api/v1";
14
+ this.baseUrl = config.baseUrl || process.env.ALOC_BASE_URL || "https://dev.aloc.com.ng/api/v1";
14
15
  if (!this.apiKey) {
15
16
  console.error("[WARN] ALOC_API_KEY is not set. Authenticated MCP requests will fail until an API key is provided.");
16
17
  }
17
18
  this.http = axios_1.default.create({
18
- baseURL: baseUrl,
19
+ baseURL: this.baseUrl,
19
20
  headers: {
20
21
  "Content-Type": "application/json",
21
22
  Accept: "application/json",
@@ -28,29 +29,147 @@ class AlocClient {
28
29
  timeout: 15000,
29
30
  });
30
31
  }
32
+ /**
33
+ * Return high-level self-describing overview of the ALOC platform and coverage.
34
+ */
35
+ getPlatformOverview() {
36
+ return {
37
+ platform: "ALOC Educational Assessment Infrastructure",
38
+ description: "Assessment content infrastructure platform for African EdTech. Provides structured, enriched, curriculum-aligned examination content and deep metadata through versioned APIs.",
39
+ supportedExaminationBodies: [
40
+ { code: "waec", name: "West African Examinations Council (Senior Secondary)" },
41
+ { code: "jamb", name: "Joint Admissions and Matriculation Board (UTME)" },
42
+ { code: "neco", name: "National Examinations Council" },
43
+ { code: "post-utme", name: "Institution-specific Post-UTME" },
44
+ ],
45
+ yearRange: "2010 – 2024",
46
+ supportedSubjects: [
47
+ "mathematics",
48
+ "english",
49
+ "biology",
50
+ "physics",
51
+ "chemistry",
52
+ "economics",
53
+ "commerce",
54
+ "accounting",
55
+ "government",
56
+ "literature",
57
+ "crk",
58
+ "irk",
59
+ "civic-education",
60
+ "geography",
61
+ "agricultural-science",
62
+ "further-mathematics",
63
+ "history",
64
+ "insurance",
65
+ ],
66
+ apiLayers: {
67
+ L1: { name: "Raw Questions", cost: "1 credit", description: "Direct question fetch and session retrieval" },
68
+ L2: { name: "Metadata & Taxonomy", cost: "3 credits", description: "Topic taxonomy, difficulty score, and curriculum mapping" },
69
+ L3: { name: "Explanations", cost: "10 credits", description: "Step-by-step solutions, misconceptions, and remedial hints" },
70
+ L4: { name: "Intelligence", cost: "10 credits", description: "Semantic vector similarity, question generation, and trend analytics" },
71
+ },
72
+ guidelines: {
73
+ pagination: "Cursor-based pagination with max 20 items per request (50 items max via direct REST)",
74
+ filtering: "At least one filter (subject, examType, or year) is required",
75
+ },
76
+ };
77
+ }
78
+ /**
79
+ * Return sitemap links for developer documentation and portal.
80
+ */
81
+ getSitemap() {
82
+ return {
83
+ portal: {
84
+ home: "https://aloc.com.ng",
85
+ dashboard: "https://aloc.com.ng/dashboard",
86
+ apiKeys: "https://aloc.com.ng/dashboard/api-keys",
87
+ usage: "https://aloc.com.ng/dashboard/usage",
88
+ billing: "https://aloc.com.ng/dashboard/billing",
89
+ },
90
+ documentation: {
91
+ introduction: "https://aloc.com.ng/docs/getting-started",
92
+ authentication: "https://aloc.com.ng/docs/authentication",
93
+ questions: "https://aloc.com.ng/docs/questions",
94
+ metadata: "https://aloc.com.ng/docs/metadata",
95
+ explanations: "https://aloc.com.ng/docs/explanations",
96
+ mcp: "https://aloc.com.ng/docs/mcp",
97
+ errors: "https://aloc.com.ng/docs/errors",
98
+ openapiSpec: `${this.baseUrl}/docs`,
99
+ },
100
+ };
101
+ }
102
+ /**
103
+ * Search examination questions with filters.
104
+ */
31
105
  async getQuestions(params) {
32
106
  const response = await this.http.get("/questions", { params });
33
107
  return response.data;
34
108
  }
109
+ /**
110
+ * Retrieve step-by-step AI explanation.
111
+ */
35
112
  async getQuestionExplanation(questionId, depth) {
36
113
  const response = await this.http.post(`/questions/${questionId}/explain`, {
37
114
  depth: depth || "step_by_step",
38
115
  });
39
116
  return response.data;
40
117
  }
118
+ /**
119
+ * Explore curriculum topics and taxonomy hierarchy.
120
+ */
41
121
  async getCurriculumTaxonomy(params) {
42
122
  const response = await this.http.get("/curriculum", { params });
43
123
  return response.data;
44
124
  }
125
+ /**
126
+ * Find vector-similar exam questions.
127
+ */
45
128
  async getSimilarQuestions(questionId, limit) {
46
129
  const response = await this.http.get(`/questions/${questionId}/similar`, {
47
130
  params: { limit: limit || 3 },
48
131
  });
49
132
  return response.data;
50
133
  }
134
+ /**
135
+ * List available subjects on ALOC.
136
+ */
51
137
  async getSubjects() {
52
138
  const response = await this.http.get("/subjects");
53
139
  return response.data;
54
140
  }
141
+ /**
142
+ * List examination years available for a specific subject.
143
+ */
144
+ async getSubjectYears(subject, examType) {
145
+ const response = await this.http.get(`/subjects/${encodeURIComponent(subject)}/years`, {
146
+ params: examType ? { examType } : undefined,
147
+ });
148
+ return response.data;
149
+ }
150
+ /**
151
+ * Check credit balance and subscription status.
152
+ */
153
+ async checkCreditBalance() {
154
+ const response = await this.http.get("/subjects");
155
+ const headers = response.headers;
156
+ return {
157
+ creditsRemaining: headers["x-credits-remaining"] ? parseInt(headers["x-credits-remaining"], 10) : "active",
158
+ creditsUsedLastRequest: headers["x-credits-used"] ? parseInt(headers["x-credits-used"], 10) : 0,
159
+ authenticated: true,
160
+ apiKeyPrefix: this.apiKey.substring(0, 10) + "••••••••",
161
+ };
162
+ }
163
+ /**
164
+ * Report an error, typo, or disputed answer on a question.
165
+ */
166
+ async reportQuestionIssue(questionId, type, message, reporterName) {
167
+ const response = await this.http.post(`/questions/${questionId}/report`, {
168
+ type,
169
+ message,
170
+ reporterName: reporterName || "ALOC AI Agent",
171
+ });
172
+ return response.data;
173
+ }
55
174
  }
56
175
  exports.AlocClient = AlocClient;
package/dist/index.js CHANGED
@@ -18,6 +18,49 @@ const server = new index_js_1.Server({
18
18
  server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => {
19
19
  return {
20
20
  tools: [
21
+ {
22
+ name: "aloc_get_platform_overview",
23
+ description: "Discover ALOC assessment infrastructure overview, supported African exam bodies (WAEC, JAMB, NECO), subject list, year coverage (2010–2024), and API layers. Free (0 credits).",
24
+ inputSchema: {
25
+ type: "object",
26
+ properties: {},
27
+ },
28
+ },
29
+ {
30
+ name: "aloc_get_sitemap",
31
+ description: "Get complete navigation sitemap and direct links to ALOC developer portal, documentation, and error code guides. Free (0 credits).",
32
+ inputSchema: {
33
+ type: "object",
34
+ properties: {},
35
+ },
36
+ },
37
+ {
38
+ name: "aloc_get_subject_years",
39
+ description: "List all available examination years for a specific subject and exam body. Free (0 credits).",
40
+ inputSchema: {
41
+ type: "object",
42
+ properties: {
43
+ subject: {
44
+ type: "string",
45
+ description: "Subject name (e.g. 'mathematics', 'physics', 'english')",
46
+ },
47
+ examType: {
48
+ type: "string",
49
+ enum: ["waec", "jamb", "neco", "post-utme"],
50
+ description: "Optional examination body filter",
51
+ },
52
+ },
53
+ required: ["subject"],
54
+ },
55
+ },
56
+ {
57
+ name: "aloc_check_credit_balance",
58
+ description: "Check the developer's remaining ALOC API credit balance and authentication status. Free (0 credits).",
59
+ inputSchema: {
60
+ type: "object",
61
+ properties: {},
62
+ },
63
+ },
21
64
  {
22
65
  name: "aloc_search_questions",
23
66
  description: "Search and retrieve curriculum-aligned examination questions (WAEC, JAMB, NECO) with options, metadata, and difficulty levels. Consumes 1-3 ALOC credits.",
@@ -112,6 +155,42 @@ server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => {
112
155
  required: ["questionId"],
113
156
  },
114
157
  },
158
+ {
159
+ name: "aloc_report_question_issue",
160
+ description: "Report a typo, incorrect answer key, or ambiguity on a specific question to ALOC quality moderation. Free (0 credits).",
161
+ inputSchema: {
162
+ type: "object",
163
+ properties: {
164
+ questionId: {
165
+ type: "string",
166
+ description: "UUID of the question with an issue",
167
+ },
168
+ type: {
169
+ type: "string",
170
+ enum: [
171
+ "question",
172
+ "option_a",
173
+ "option_b",
174
+ "option_c",
175
+ "option_d",
176
+ "correct_answer",
177
+ "explanation",
178
+ "other",
179
+ ],
180
+ description: "Category of issue identified",
181
+ },
182
+ message: {
183
+ type: "string",
184
+ description: "Detailed description of the issue or mathematical discrepancy",
185
+ },
186
+ reporterName: {
187
+ type: "string",
188
+ description: "Optional name of reporting agent or developer",
189
+ },
190
+ },
191
+ required: ["questionId", "type", "message"],
192
+ },
193
+ },
115
194
  ],
116
195
  };
117
196
  });
@@ -120,6 +199,51 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
120
199
  const { name, arguments: args } = request.params;
121
200
  try {
122
201
  switch (name) {
202
+ case "aloc_get_platform_overview": {
203
+ const result = client.getPlatformOverview();
204
+ return {
205
+ content: [
206
+ {
207
+ type: "text",
208
+ text: JSON.stringify(result, null, 2),
209
+ },
210
+ ],
211
+ };
212
+ }
213
+ case "aloc_get_sitemap": {
214
+ const result = client.getSitemap();
215
+ return {
216
+ content: [
217
+ {
218
+ type: "text",
219
+ text: JSON.stringify(result, null, 2),
220
+ },
221
+ ],
222
+ };
223
+ }
224
+ case "aloc_get_subject_years": {
225
+ const { subject, examType } = (args || {});
226
+ const result = await client.getSubjectYears(subject, examType);
227
+ return {
228
+ content: [
229
+ {
230
+ type: "text",
231
+ text: JSON.stringify(result, null, 2),
232
+ },
233
+ ],
234
+ };
235
+ }
236
+ case "aloc_check_credit_balance": {
237
+ const result = await client.checkCreditBalance();
238
+ return {
239
+ content: [
240
+ {
241
+ type: "text",
242
+ text: JSON.stringify(result, null, 2),
243
+ },
244
+ ],
245
+ };
246
+ }
123
247
  case "aloc_search_questions": {
124
248
  const { subject, examType, year, topic, difficulty, limit } = (args || {});
125
249
  const result = await client.getQuestions({
@@ -175,6 +299,18 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
175
299
  ],
176
300
  };
177
301
  }
302
+ case "aloc_report_question_issue": {
303
+ const { questionId, type, message, reporterName } = (args || {});
304
+ const result = await client.reportQuestionIssue(questionId, type, message, reporterName);
305
+ return {
306
+ content: [
307
+ {
308
+ type: "text",
309
+ text: JSON.stringify(result, null, 2),
310
+ },
311
+ ],
312
+ };
313
+ }
178
314
  default:
179
315
  throw new Error(`Unknown ALOC tool: ${name}`);
180
316
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@massteck/aloc-mcp-server",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Official Model Context Protocol (MCP) Server for ALOC assessment content infrastructure",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/client.ts CHANGED
@@ -9,10 +9,11 @@ export interface AlocClientConfig {
9
9
  export class AlocClient {
10
10
  private http: AxiosInstance;
11
11
  private apiKey: string;
12
+ private baseUrl: string;
12
13
 
13
14
  constructor(config: AlocClientConfig = {}) {
14
15
  this.apiKey = config.apiKey || process.env.ALOC_API_KEY || "";
15
- const baseUrl = config.baseUrl || process.env.ALOC_BASE_URL || "https://dev.aloc.com.ng/api/v1";
16
+ this.baseUrl = config.baseUrl || process.env.ALOC_BASE_URL || "https://dev.aloc.com.ng/api/v1";
16
17
 
17
18
  if (!this.apiKey) {
18
19
  console.error(
@@ -21,7 +22,7 @@ export class AlocClient {
21
22
  }
22
23
 
23
24
  this.http = axios.create({
24
- baseURL: baseUrl,
25
+ baseURL: this.baseUrl,
25
26
  headers: {
26
27
  "Content-Type": "application/json",
27
28
  Accept: "application/json",
@@ -35,6 +36,82 @@ export class AlocClient {
35
36
  });
36
37
  }
37
38
 
39
+ /**
40
+ * Return high-level self-describing overview of the ALOC platform and coverage.
41
+ */
42
+ public getPlatformOverview() {
43
+ return {
44
+ platform: "ALOC Educational Assessment Infrastructure",
45
+ description:
46
+ "Assessment content infrastructure platform for African EdTech. Provides structured, enriched, curriculum-aligned examination content and deep metadata through versioned APIs.",
47
+ supportedExaminationBodies: [
48
+ { code: "waec", name: "West African Examinations Council (Senior Secondary)" },
49
+ { code: "jamb", name: "Joint Admissions and Matriculation Board (UTME)" },
50
+ { code: "neco", name: "National Examinations Council" },
51
+ { code: "post-utme", name: "Institution-specific Post-UTME" },
52
+ ],
53
+ yearRange: "2010 – 2024",
54
+ supportedSubjects: [
55
+ "mathematics",
56
+ "english",
57
+ "biology",
58
+ "physics",
59
+ "chemistry",
60
+ "economics",
61
+ "commerce",
62
+ "accounting",
63
+ "government",
64
+ "literature",
65
+ "crk",
66
+ "irk",
67
+ "civic-education",
68
+ "geography",
69
+ "agricultural-science",
70
+ "further-mathematics",
71
+ "history",
72
+ "insurance",
73
+ ],
74
+ apiLayers: {
75
+ L1: { name: "Raw Questions", cost: "1 credit", description: "Direct question fetch and session retrieval" },
76
+ L2: { name: "Metadata & Taxonomy", cost: "3 credits", description: "Topic taxonomy, difficulty score, and curriculum mapping" },
77
+ L3: { name: "Explanations", cost: "10 credits", description: "Step-by-step solutions, misconceptions, and remedial hints" },
78
+ L4: { name: "Intelligence", cost: "10 credits", description: "Semantic vector similarity, question generation, and trend analytics" },
79
+ },
80
+ guidelines: {
81
+ pagination: "Cursor-based pagination with max 20 items per request (50 items max via direct REST)",
82
+ filtering: "At least one filter (subject, examType, or year) is required",
83
+ },
84
+ };
85
+ }
86
+
87
+ /**
88
+ * Return sitemap links for developer documentation and portal.
89
+ */
90
+ public getSitemap() {
91
+ return {
92
+ portal: {
93
+ home: "https://aloc.com.ng",
94
+ dashboard: "https://aloc.com.ng/dashboard",
95
+ apiKeys: "https://aloc.com.ng/dashboard/api-keys",
96
+ usage: "https://aloc.com.ng/dashboard/usage",
97
+ billing: "https://aloc.com.ng/dashboard/billing",
98
+ },
99
+ documentation: {
100
+ introduction: "https://aloc.com.ng/docs/getting-started",
101
+ authentication: "https://aloc.com.ng/docs/authentication",
102
+ questions: "https://aloc.com.ng/docs/questions",
103
+ metadata: "https://aloc.com.ng/docs/metadata",
104
+ explanations: "https://aloc.com.ng/docs/explanations",
105
+ mcp: "https://aloc.com.ng/docs/mcp",
106
+ errors: "https://aloc.com.ng/docs/errors",
107
+ openapiSpec: `${this.baseUrl}/docs`,
108
+ },
109
+ };
110
+ }
111
+
112
+ /**
113
+ * Search examination questions with filters.
114
+ */
38
115
  public async getQuestions(params: {
39
116
  subject: string;
40
117
  examType?: string;
@@ -47,6 +124,9 @@ export class AlocClient {
47
124
  return response.data;
48
125
  }
49
126
 
127
+ /**
128
+ * Retrieve step-by-step AI explanation.
129
+ */
50
130
  public async getQuestionExplanation(questionId: string, depth?: string) {
51
131
  const response = await this.http.post(`/questions/${questionId}/explain`, {
52
132
  depth: depth || "step_by_step",
@@ -54,11 +134,17 @@ export class AlocClient {
54
134
  return response.data;
55
135
  }
56
136
 
137
+ /**
138
+ * Explore curriculum topics and taxonomy hierarchy.
139
+ */
57
140
  public async getCurriculumTaxonomy(params?: { subject?: string; examType?: string }) {
58
141
  const response = await this.http.get("/curriculum", { params });
59
142
  return response.data;
60
143
  }
61
144
 
145
+ /**
146
+ * Find vector-similar exam questions.
147
+ */
62
148
  public async getSimilarQuestions(questionId: string, limit?: number) {
63
149
  const response = await this.http.get(`/questions/${questionId}/similar`, {
64
150
  params: { limit: limit || 3 },
@@ -66,8 +152,52 @@ export class AlocClient {
66
152
  return response.data;
67
153
  }
68
154
 
155
+ /**
156
+ * List available subjects on ALOC.
157
+ */
69
158
  public async getSubjects() {
70
159
  const response = await this.http.get("/subjects");
71
160
  return response.data;
72
161
  }
162
+
163
+ /**
164
+ * List examination years available for a specific subject.
165
+ */
166
+ public async getSubjectYears(subject: string, examType?: string) {
167
+ const response = await this.http.get(`/subjects/${encodeURIComponent(subject)}/years`, {
168
+ params: examType ? { examType } : undefined,
169
+ });
170
+ return response.data;
171
+ }
172
+
173
+ /**
174
+ * Check credit balance and subscription status.
175
+ */
176
+ public async checkCreditBalance() {
177
+ const response = await this.http.get("/subjects");
178
+ const headers = response.headers;
179
+ return {
180
+ creditsRemaining: headers["x-credits-remaining"] ? parseInt(headers["x-credits-remaining"], 10) : "active",
181
+ creditsUsedLastRequest: headers["x-credits-used"] ? parseInt(headers["x-credits-used"], 10) : 0,
182
+ authenticated: true,
183
+ apiKeyPrefix: this.apiKey.substring(0, 10) + "••••••••",
184
+ };
185
+ }
186
+
187
+ /**
188
+ * Report an error, typo, or disputed answer on a question.
189
+ */
190
+ public async reportQuestionIssue(
191
+ questionId: string,
192
+ type: "question" | "option_a" | "option_b" | "option_c" | "option_d" | "correct_answer" | "explanation" | "other",
193
+ message: string,
194
+ reporterName?: string
195
+ ) {
196
+ const response = await this.http.post(`/questions/${questionId}/report`, {
197
+ type,
198
+ message,
199
+ reporterName: reporterName || "ALOC AI Agent",
200
+ });
201
+ return response.data;
202
+ }
73
203
  }
package/src/index.ts CHANGED
@@ -26,6 +26,53 @@ const server = new Server(
26
26
  server.setRequestHandler(ListToolsRequestSchema, async () => {
27
27
  return {
28
28
  tools: [
29
+ {
30
+ name: "aloc_get_platform_overview",
31
+ description:
32
+ "Discover ALOC assessment infrastructure overview, supported African exam bodies (WAEC, JAMB, NECO), subject list, year coverage (2010–2024), and API layers. Free (0 credits).",
33
+ inputSchema: {
34
+ type: "object",
35
+ properties: {},
36
+ },
37
+ },
38
+ {
39
+ name: "aloc_get_sitemap",
40
+ description:
41
+ "Get complete navigation sitemap and direct links to ALOC developer portal, documentation, and error code guides. Free (0 credits).",
42
+ inputSchema: {
43
+ type: "object",
44
+ properties: {},
45
+ },
46
+ },
47
+ {
48
+ name: "aloc_get_subject_years",
49
+ description:
50
+ "List all available examination years for a specific subject and exam body. Free (0 credits).",
51
+ inputSchema: {
52
+ type: "object",
53
+ properties: {
54
+ subject: {
55
+ type: "string",
56
+ description: "Subject name (e.g. 'mathematics', 'physics', 'english')",
57
+ },
58
+ examType: {
59
+ type: "string",
60
+ enum: ["waec", "jamb", "neco", "post-utme"],
61
+ description: "Optional examination body filter",
62
+ },
63
+ },
64
+ required: ["subject"],
65
+ },
66
+ },
67
+ {
68
+ name: "aloc_check_credit_balance",
69
+ description:
70
+ "Check the developer's remaining ALOC API credit balance and authentication status. Free (0 credits).",
71
+ inputSchema: {
72
+ type: "object",
73
+ properties: {},
74
+ },
75
+ },
29
76
  {
30
77
  name: "aloc_search_questions",
31
78
  description:
@@ -124,6 +171,43 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
124
171
  required: ["questionId"],
125
172
  },
126
173
  },
174
+ {
175
+ name: "aloc_report_question_issue",
176
+ description:
177
+ "Report a typo, incorrect answer key, or ambiguity on a specific question to ALOC quality moderation. Free (0 credits).",
178
+ inputSchema: {
179
+ type: "object",
180
+ properties: {
181
+ questionId: {
182
+ type: "string",
183
+ description: "UUID of the question with an issue",
184
+ },
185
+ type: {
186
+ type: "string",
187
+ enum: [
188
+ "question",
189
+ "option_a",
190
+ "option_b",
191
+ "option_c",
192
+ "option_d",
193
+ "correct_answer",
194
+ "explanation",
195
+ "other",
196
+ ],
197
+ description: "Category of issue identified",
198
+ },
199
+ message: {
200
+ type: "string",
201
+ description: "Detailed description of the issue or mathematical discrepancy",
202
+ },
203
+ reporterName: {
204
+ type: "string",
205
+ description: "Optional name of reporting agent or developer",
206
+ },
207
+ },
208
+ required: ["questionId", "type", "message"],
209
+ },
210
+ },
127
211
  ],
128
212
  };
129
213
  });
@@ -134,6 +218,55 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
134
218
 
135
219
  try {
136
220
  switch (name) {
221
+ case "aloc_get_platform_overview": {
222
+ const result = client.getPlatformOverview();
223
+ return {
224
+ content: [
225
+ {
226
+ type: "text",
227
+ text: JSON.stringify(result, null, 2),
228
+ },
229
+ ],
230
+ };
231
+ }
232
+
233
+ case "aloc_get_sitemap": {
234
+ const result = client.getSitemap();
235
+ return {
236
+ content: [
237
+ {
238
+ type: "text",
239
+ text: JSON.stringify(result, null, 2),
240
+ },
241
+ ],
242
+ };
243
+ }
244
+
245
+ case "aloc_get_subject_years": {
246
+ const { subject, examType } = (args || {}) as any;
247
+ const result = await client.getSubjectYears(subject, examType);
248
+ return {
249
+ content: [
250
+ {
251
+ type: "text",
252
+ text: JSON.stringify(result, null, 2),
253
+ },
254
+ ],
255
+ };
256
+ }
257
+
258
+ case "aloc_check_credit_balance": {
259
+ const result = await client.checkCreditBalance();
260
+ return {
261
+ content: [
262
+ {
263
+ type: "text",
264
+ text: JSON.stringify(result, null, 2),
265
+ },
266
+ ],
267
+ };
268
+ }
269
+
137
270
  case "aloc_search_questions": {
138
271
  const { subject, examType, year, topic, difficulty, limit } = (args || {}) as any;
139
272
  const result = await client.getQuestions({
@@ -193,6 +326,19 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
193
326
  };
194
327
  }
195
328
 
329
+ case "aloc_report_question_issue": {
330
+ const { questionId, type, message, reporterName } = (args || {}) as any;
331
+ const result = await client.reportQuestionIssue(questionId, type, message, reporterName);
332
+ return {
333
+ content: [
334
+ {
335
+ type: "text",
336
+ text: JSON.stringify(result, null, 2),
337
+ },
338
+ ],
339
+ };
340
+ }
341
+
196
342
  default:
197
343
  throw new Error(`Unknown ALOC tool: ${name}`);
198
344
  }