infinitecampus-mcp 0.1.1 → 0.1.2
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 +2 -2
- package/dist/bundle.js +31 -9
- package/dist/client.js +6 -0
- package/dist/index.js +1 -1
- package/dist/tools/districts.js +1 -0
- package/dist/tools/documents.js +26 -8
- package/package.json +1 -1
- package/skills/ic/SKILL.md +6 -14
package/README.md
CHANGED
|
@@ -15,9 +15,9 @@ MCP server for Infinite Campus (Campus Parent portal). Single-account config —
|
|
|
15
15
|
| Behavior | `ic_list_behavior` |
|
|
16
16
|
| Food service | `ic_list_food_service` |
|
|
17
17
|
| Documents | `ic_list_documents`, `ic_download_document` |
|
|
18
|
-
|
|
|
18
|
+
| Notifications | `ic_list_messages` (prism notifications), `ic_get_message` (unread count) |
|
|
19
19
|
|
|
20
|
-
Tools that the harness will gate as write/IO operations: `
|
|
20
|
+
Tools that the harness will gate as write/IO operations: `ic_download_document`.
|
|
21
21
|
|
|
22
22
|
## Configuration
|
|
23
23
|
|
package/dist/bundle.js
CHANGED
|
@@ -30155,8 +30155,13 @@ var ICClient = class {
|
|
|
30155
30155
|
sessions = /* @__PURE__ */ new Map();
|
|
30156
30156
|
linkedTo = /* @__PURE__ */ new Map();
|
|
30157
30157
|
// linkedDistrictName → primaryDistrictName
|
|
30158
|
+
primaryName;
|
|
30158
30159
|
constructor(account2) {
|
|
30159
30160
|
this.accounts.set(account2.name, account2);
|
|
30161
|
+
this.primaryName = account2.name;
|
|
30162
|
+
}
|
|
30163
|
+
async ensureDiscovery() {
|
|
30164
|
+
await this.ensureSession(this.accounts.get(this.primaryName));
|
|
30160
30165
|
}
|
|
30161
30166
|
listDistricts() {
|
|
30162
30167
|
return [...this.accounts.values()].map((a) => ({
|
|
@@ -30454,6 +30459,7 @@ function registerDistrictTools(server2, client2) {
|
|
|
30454
30459
|
description: "List Infinite Campus districts configured for this MCP server. Returns names + base URLs (no credentials).",
|
|
30455
30460
|
annotations: { readOnlyHint: true }
|
|
30456
30461
|
}, async () => {
|
|
30462
|
+
await client2.ensureDiscovery();
|
|
30457
30463
|
const data = client2.listDistricts();
|
|
30458
30464
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
30459
30465
|
});
|
|
@@ -30687,24 +30693,40 @@ var downloadArgs = external_exports3.object({
|
|
|
30687
30693
|
});
|
|
30688
30694
|
function registerDocumentTools(server2, client2) {
|
|
30689
30695
|
server2.registerTool("ic_list_documents", {
|
|
30690
|
-
description: "List a student's available documents (report cards, transcripts, etc.). Returns metadata only \u2014 use ic_download_document to fetch the file.",
|
|
30696
|
+
description: "List a student's available documents (report cards, transcripts, etc.). Returns metadata only \u2014 use ic_download_document to fetch the file. Returns FeatureDisabled if the district has the module turned off.",
|
|
30691
30697
|
annotations: { readOnlyHint: true },
|
|
30692
30698
|
inputSchema: listArgs2.shape
|
|
30693
30699
|
}, async (rawArgs) => {
|
|
30694
30700
|
const args = listArgs2.parse(rawArgs);
|
|
30695
|
-
|
|
30696
|
-
|
|
30701
|
+
try {
|
|
30702
|
+
const data = await client2.request(args.district, `/campus/resources/portal/documents?personID=${encodeURIComponent(args.studentId)}`);
|
|
30703
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
30704
|
+
} catch (e) {
|
|
30705
|
+
if (e instanceof Error && e.message.startsWith("IC 404 ")) {
|
|
30706
|
+
const warn = { warning: "FeatureDisabled", feature: "documents", district: args.district, data: [] };
|
|
30707
|
+
return { content: [{ type: "text", text: JSON.stringify(warn, null, 2) }] };
|
|
30708
|
+
}
|
|
30709
|
+
throw e;
|
|
30710
|
+
}
|
|
30697
30711
|
});
|
|
30698
30712
|
server2.registerTool("ic_download_document", {
|
|
30699
|
-
description: "Download a student's document (PDF) to disk. documentId is the downloadUrl returned by ic_list_documents.",
|
|
30713
|
+
description: "Download a student's document (PDF) to disk. documentId is the downloadUrl returned by ic_list_documents. Returns FeatureDisabled if the district has the module turned off.",
|
|
30700
30714
|
annotations: { destructiveHint: true },
|
|
30701
30715
|
inputSchema: downloadArgs.shape
|
|
30702
30716
|
}, async (rawArgs) => {
|
|
30703
30717
|
const args = downloadArgs.parse(rawArgs);
|
|
30704
|
-
|
|
30705
|
-
|
|
30706
|
-
|
|
30707
|
-
|
|
30718
|
+
try {
|
|
30719
|
+
const meta3 = await client2.download(args.district, args.documentId, args.destinationPath, {
|
|
30720
|
+
overwrite: args.overwrite ?? false
|
|
30721
|
+
});
|
|
30722
|
+
return { content: [{ type: "text", text: JSON.stringify(meta3, null, 2) }] };
|
|
30723
|
+
} catch (e) {
|
|
30724
|
+
if (e instanceof Error && e.message.startsWith("IC download 404")) {
|
|
30725
|
+
const warn = { warning: "FeatureDisabled", feature: "documents", district: args.district };
|
|
30726
|
+
return { content: [{ type: "text", text: JSON.stringify(warn, null, 2) }] };
|
|
30727
|
+
}
|
|
30728
|
+
throw e;
|
|
30729
|
+
}
|
|
30708
30730
|
});
|
|
30709
30731
|
}
|
|
30710
30732
|
|
|
@@ -30717,7 +30739,7 @@ try {
|
|
|
30717
30739
|
}
|
|
30718
30740
|
var account = loadAccount();
|
|
30719
30741
|
var client = new ICClient(account);
|
|
30720
|
-
var server = new McpServer({ name: "infinitecampus", version: "0.1.
|
|
30742
|
+
var server = new McpServer({ name: "infinitecampus", version: "0.1.2" });
|
|
30721
30743
|
registerDistrictTools(server, client);
|
|
30722
30744
|
registerStudentTools(server, client);
|
|
30723
30745
|
registerScheduleTools(server, client);
|
package/dist/client.js
CHANGED
|
@@ -5,8 +5,14 @@ export class ICClient {
|
|
|
5
5
|
accounts = new Map();
|
|
6
6
|
sessions = new Map();
|
|
7
7
|
linkedTo = new Map(); // linkedDistrictName → primaryDistrictName
|
|
8
|
+
primaryName;
|
|
8
9
|
constructor(account) {
|
|
9
10
|
this.accounts.set(account.name, account);
|
|
11
|
+
this.primaryName = account.name;
|
|
12
|
+
}
|
|
13
|
+
async ensureDiscovery() {
|
|
14
|
+
// Ensure primary account is logged in, which triggers CUPS linked-district discovery
|
|
15
|
+
await this.ensureSession(this.accounts.get(this.primaryName));
|
|
10
16
|
}
|
|
11
17
|
listDistricts() {
|
|
12
18
|
return [...this.accounts.values()].map((a) => ({
|
package/dist/index.js
CHANGED
|
@@ -27,7 +27,7 @@ import { registerMessageTools } from './tools/messages.js';
|
|
|
27
27
|
import { registerDocumentTools } from './tools/documents.js';
|
|
28
28
|
const account = loadAccount();
|
|
29
29
|
const client = new ICClient(account);
|
|
30
|
-
const server = new McpServer({ name: 'infinitecampus', version: '0.1.
|
|
30
|
+
const server = new McpServer({ name: 'infinitecampus', version: '0.1.2' });
|
|
31
31
|
registerDistrictTools(server, client);
|
|
32
32
|
registerStudentTools(server, client);
|
|
33
33
|
registerScheduleTools(server, client);
|
package/dist/tools/districts.js
CHANGED
|
@@ -3,6 +3,7 @@ export function registerDistrictTools(server, client) {
|
|
|
3
3
|
description: 'List Infinite Campus districts configured for this MCP server. Returns names + base URLs (no credentials).',
|
|
4
4
|
annotations: { readOnlyHint: true },
|
|
5
5
|
}, async () => {
|
|
6
|
+
await client.ensureDiscovery();
|
|
6
7
|
const data = client.listDistricts();
|
|
7
8
|
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
8
9
|
});
|
package/dist/tools/documents.js
CHANGED
|
@@ -11,23 +11,41 @@ const downloadArgs = z.object({
|
|
|
11
11
|
});
|
|
12
12
|
export function registerDocumentTools(server, client) {
|
|
13
13
|
server.registerTool('ic_list_documents', {
|
|
14
|
-
description: "List a student's available documents (report cards, transcripts, etc.). Returns metadata only — use ic_download_document to fetch the file.",
|
|
14
|
+
description: "List a student's available documents (report cards, transcripts, etc.). Returns metadata only — use ic_download_document to fetch the file. Returns FeatureDisabled if the district has the module turned off.",
|
|
15
15
|
annotations: { readOnlyHint: true },
|
|
16
16
|
inputSchema: listArgs.shape,
|
|
17
17
|
}, async (rawArgs) => {
|
|
18
18
|
const args = listArgs.parse(rawArgs);
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
try {
|
|
20
|
+
const data = await client.request(args.district, `/campus/resources/portal/documents?personID=${encodeURIComponent(args.studentId)}`);
|
|
21
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
22
|
+
}
|
|
23
|
+
catch (e) {
|
|
24
|
+
if (e instanceof Error && e.message.startsWith('IC 404 ')) {
|
|
25
|
+
const warn = { warning: 'FeatureDisabled', feature: 'documents', district: args.district, data: [] };
|
|
26
|
+
return { content: [{ type: 'text', text: JSON.stringify(warn, null, 2) }] };
|
|
27
|
+
}
|
|
28
|
+
throw e;
|
|
29
|
+
}
|
|
21
30
|
});
|
|
22
31
|
server.registerTool('ic_download_document', {
|
|
23
|
-
description: "Download a student's document (PDF) to disk. documentId is the downloadUrl returned by ic_list_documents.",
|
|
32
|
+
description: "Download a student's document (PDF) to disk. documentId is the downloadUrl returned by ic_list_documents. Returns FeatureDisabled if the district has the module turned off.",
|
|
24
33
|
annotations: { destructiveHint: true },
|
|
25
34
|
inputSchema: downloadArgs.shape,
|
|
26
35
|
}, async (rawArgs) => {
|
|
27
36
|
const args = downloadArgs.parse(rawArgs);
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
37
|
+
try {
|
|
38
|
+
const meta = await client.download(args.district, args.documentId, args.destinationPath, {
|
|
39
|
+
overwrite: args.overwrite ?? false,
|
|
40
|
+
});
|
|
41
|
+
return { content: [{ type: 'text', text: JSON.stringify(meta, null, 2) }] };
|
|
42
|
+
}
|
|
43
|
+
catch (e) {
|
|
44
|
+
if (e instanceof Error && e.message.startsWith('IC download 404')) {
|
|
45
|
+
const warn = { warning: 'FeatureDisabled', feature: 'documents', district: args.district };
|
|
46
|
+
return { content: [{ type: 'text', text: JSON.stringify(warn, null, 2) }] };
|
|
47
|
+
}
|
|
48
|
+
throw e;
|
|
49
|
+
}
|
|
32
50
|
});
|
|
33
51
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "infinitecampus-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Infinite Campus (Campus Parent) MCP server — multi-district read + message/document write",
|
|
5
5
|
"author": "Claude Code (AI) <https://www.anthropic.com/claude>",
|
|
6
6
|
"repository": {
|
package/skills/ic/SKILL.md
CHANGED
|
@@ -114,16 +114,14 @@ Every tool except `ic_list_districts` takes `district` as its first arg (the dis
|
|
|
114
114
|
### Documents
|
|
115
115
|
| Tool | Notes |
|
|
116
116
|
|------|-------|
|
|
117
|
-
| `ic_list_documents(district, studentId)` | Metadata only (report cards, transcripts, etc.). |
|
|
118
|
-
| `ic_download_document(district,
|
|
117
|
+
| `ic_list_documents(district, studentId)` | Metadata only (report cards, transcripts, etc.). Returns `FeatureDisabled` if the district has the documents module turned off. |
|
|
118
|
+
| `ic_download_document(district, documentId, destinationPath)` | Writes the PDF to `destinationPath` on disk. **`destinationPath` is required** — confirm the path with the user before calling. Returns `FeatureDisabled` if unavailable. |
|
|
119
119
|
|
|
120
|
-
###
|
|
120
|
+
### Notifications
|
|
121
121
|
| Tool | Notes |
|
|
122
122
|
|------|-------|
|
|
123
|
-
| `ic_list_messages(district,
|
|
124
|
-
| `ic_get_message(district
|
|
125
|
-
| `ic_list_message_recipients(district)` | Valid recipient IDs (teachers, staff) for this district. Call before `ic_send_message` to validate recipients. |
|
|
126
|
-
| `ic_send_message(district, subject, body, recipientIds[])` | **Sends a real message through the portal.** Recipients must come from `ic_list_message_recipients` — made-up IDs will fail. |
|
|
123
|
+
| `ic_list_messages(district, limit?)` | Portal notifications (district announcements, teacher messages, system alerts) via prism notification system. |
|
|
124
|
+
| `ic_get_message(district)` | Unread notification/message count. |
|
|
127
125
|
|
|
128
126
|
## Workflows
|
|
129
127
|
|
|
@@ -139,11 +137,6 @@ Every tool except `ic_list_districts` takes `district` as its first arg (the dis
|
|
|
139
137
|
**Today's schedule:**
|
|
140
138
|
- `ic_get_schedule(district, studentId)` — returns today's classes by default
|
|
141
139
|
|
|
142
|
-
**Email a teacher:**
|
|
143
|
-
1. `ic_list_message_recipients(district)` → find the teacher's recipient ID
|
|
144
|
-
2. Draft subject/body with the user and confirm
|
|
145
|
-
3. `ic_send_message(district, subject, body, [teacherRecipientId])`
|
|
146
|
-
|
|
147
140
|
**Get the report card:**
|
|
148
141
|
1. `ic_list_documents(district, studentId)` → find the report card's `documentId`
|
|
149
142
|
2. Confirm destination path with the user
|
|
@@ -151,6 +144,5 @@ Every tool except `ic_list_districts` takes `district` as its first arg (the dis
|
|
|
151
144
|
|
|
152
145
|
## Caution
|
|
153
146
|
|
|
154
|
-
- `ic_send_message` actually sends a portal message to teachers/staff — always confirm subject, body, and recipients with the user before calling.
|
|
155
147
|
- `ic_download_document` writes a PDF to disk at `destinationPath` — confirm the path with the user; overwrites silently.
|
|
156
|
-
- Endpoint behavior varies by district. If `ic_list_behavior` or `
|
|
148
|
+
- Endpoint behavior varies by district. If `ic_list_behavior`, `ic_list_food_service`, `ic_list_documents`, or `ic_download_document` returns a `FeatureDisabled` warning, that module is simply turned off for the district — it's not an error.
|