infinitecampus-mcp 2.4.0 → 2.4.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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/dist/bundle.js +229 -49
- package/dist/index.js +1 -1
- package/package.json +4 -4
- package/server.json +2 -2
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
},
|
|
8
8
|
"metadata": {
|
|
9
9
|
"description": "MCP server for Infinite Campus (Campus Parent) — grades, attendance, assignments, messages, documents",
|
|
10
|
-
"version": "2.4.
|
|
10
|
+
"version": "2.4.2"
|
|
11
11
|
},
|
|
12
12
|
"plugins": [
|
|
13
13
|
{
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"displayName": "Infinite Campus",
|
|
16
16
|
"source": "./",
|
|
17
17
|
"description": "Infinite Campus (Campus Parent) MCP server for Claude — grades, attendance, assignments, messages, and documents via natural language",
|
|
18
|
-
"version": "2.4.
|
|
18
|
+
"version": "2.4.2",
|
|
19
19
|
"author": {
|
|
20
20
|
"name": "Chris Hall"
|
|
21
21
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "infinitecampus-mcp",
|
|
3
3
|
"displayName": "Infinite Campus",
|
|
4
|
-
"version": "2.4.
|
|
4
|
+
"version": "2.4.2",
|
|
5
5
|
"description": "Infinite Campus (Campus Parent) MCP server for Claude — grades, attendance, assignments, messages, and documents",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Chris Hall",
|
package/dist/bundle.js
CHANGED
|
@@ -3776,6 +3776,7 @@ var require_fast_uri = __commonJS({
|
|
|
3776
3776
|
return uriTokens.join("");
|
|
3777
3777
|
}
|
|
3778
3778
|
var URI_PARSE = /^(?:([^#/:?]+):)?(?:\/\/((?:([^#/?@]*)@)?(\[[^#/?\]]+\]|[^#/:?]*)(?::(\d*))?))?([^#?]*)(?:\?([^#]*))?(?:#((?:.|[\n\r])*))?/u;
|
|
3779
|
+
var AUTHORITY_PREFIX = /^(?:[^#/:?]+:)?\/\/([^/?#]*)/;
|
|
3779
3780
|
function getParseError(parsed, matches) {
|
|
3780
3781
|
if (matches[2] !== void 0 && parsed.path && parsed.path[0] !== "/") {
|
|
3781
3782
|
return 'URI path must start with "/" when authority is present.';
|
|
@@ -3805,6 +3806,11 @@ var require_fast_uri = __commonJS({
|
|
|
3805
3806
|
uri = "//" + uri;
|
|
3806
3807
|
}
|
|
3807
3808
|
}
|
|
3809
|
+
const authorityMatch = uri.match(AUTHORITY_PREFIX);
|
|
3810
|
+
if (authorityMatch !== null && authorityMatch[1].indexOf("\\") !== -1) {
|
|
3811
|
+
parsed.error = "URI authority must not contain a literal backslash.";
|
|
3812
|
+
malformedAuthorityOrPort = true;
|
|
3813
|
+
}
|
|
3808
3814
|
const matches = uri.match(URI_PARSE);
|
|
3809
3815
|
if (matches) {
|
|
3810
3816
|
parsed.scheme = matches[1];
|
|
@@ -3848,7 +3854,7 @@ var require_fast_uri = __commonJS({
|
|
|
3848
3854
|
if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport)) {
|
|
3849
3855
|
if (parsed.host && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP === false && nonSimpleDomain(parsed.host)) {
|
|
3850
3856
|
try {
|
|
3851
|
-
parsed.host = URL
|
|
3857
|
+
parsed.host = new URL("http://" + parsed.host).hostname;
|
|
3852
3858
|
} catch (e) {
|
|
3853
3859
|
parsed.error = parsed.error || "Host's domain name can not be converted to ASCII: " + e;
|
|
3854
3860
|
}
|
|
@@ -7693,6 +7699,7 @@ var require_receiver = __commonJS({
|
|
|
7693
7699
|
this._opcode = 0;
|
|
7694
7700
|
this._totalPayloadLength = 0;
|
|
7695
7701
|
this._messageLength = 0;
|
|
7702
|
+
this._numFragments = 0;
|
|
7696
7703
|
this._fragments = [];
|
|
7697
7704
|
this._errored = false;
|
|
7698
7705
|
this._loop = false;
|
|
@@ -8043,23 +8050,23 @@ var require_receiver = __commonJS({
|
|
|
8043
8050
|
this.controlMessage(data, cb);
|
|
8044
8051
|
return;
|
|
8045
8052
|
}
|
|
8053
|
+
if (this._maxFragments > 0 && ++this._numFragments > this._maxFragments) {
|
|
8054
|
+
const error51 = this.createError(
|
|
8055
|
+
RangeError,
|
|
8056
|
+
"Too many message fragments",
|
|
8057
|
+
false,
|
|
8058
|
+
1008,
|
|
8059
|
+
"WS_ERR_TOO_MANY_BUFFERED_PARTS"
|
|
8060
|
+
);
|
|
8061
|
+
cb(error51);
|
|
8062
|
+
return;
|
|
8063
|
+
}
|
|
8046
8064
|
if (this._compressed) {
|
|
8047
8065
|
this._state = INFLATING;
|
|
8048
8066
|
this.decompress(data, cb);
|
|
8049
8067
|
return;
|
|
8050
8068
|
}
|
|
8051
8069
|
if (data.length) {
|
|
8052
|
-
if (this._maxFragments > 0 && this._fragments.length >= this._maxFragments) {
|
|
8053
|
-
const error51 = this.createError(
|
|
8054
|
-
RangeError,
|
|
8055
|
-
"Too many message fragments",
|
|
8056
|
-
false,
|
|
8057
|
-
1008,
|
|
8058
|
-
"WS_ERR_TOO_MANY_BUFFERED_PARTS"
|
|
8059
|
-
);
|
|
8060
|
-
cb(error51);
|
|
8061
|
-
return;
|
|
8062
|
-
}
|
|
8063
8070
|
this._messageLength = this._totalPayloadLength;
|
|
8064
8071
|
this._fragments.push(data);
|
|
8065
8072
|
}
|
|
@@ -8089,17 +8096,6 @@ var require_receiver = __commonJS({
|
|
|
8089
8096
|
cb(error51);
|
|
8090
8097
|
return;
|
|
8091
8098
|
}
|
|
8092
|
-
if (this._maxFragments > 0 && this._fragments.length >= this._maxFragments) {
|
|
8093
|
-
const error51 = this.createError(
|
|
8094
|
-
RangeError,
|
|
8095
|
-
"Too many message fragments",
|
|
8096
|
-
false,
|
|
8097
|
-
1008,
|
|
8098
|
-
"WS_ERR_TOO_MANY_BUFFERED_PARTS"
|
|
8099
|
-
);
|
|
8100
|
-
cb(error51);
|
|
8101
|
-
return;
|
|
8102
|
-
}
|
|
8103
8099
|
this._fragments.push(buf);
|
|
8104
8100
|
}
|
|
8105
8101
|
this.dataMessage(cb);
|
|
@@ -8122,6 +8118,7 @@ var require_receiver = __commonJS({
|
|
|
8122
8118
|
this._totalPayloadLength = 0;
|
|
8123
8119
|
this._messageLength = 0;
|
|
8124
8120
|
this._fragmented = 0;
|
|
8121
|
+
this._numFragments = 0;
|
|
8125
8122
|
this._fragments = [];
|
|
8126
8123
|
if (this._opcode === 2) {
|
|
8127
8124
|
let data;
|
|
@@ -9622,8 +9619,8 @@ var require_websocket = __commonJS({
|
|
|
9622
9619
|
autoPong: true,
|
|
9623
9620
|
closeTimeout: CLOSE_TIMEOUT,
|
|
9624
9621
|
protocolVersion: protocolVersions[1],
|
|
9625
|
-
maxBufferedChunks:
|
|
9626
|
-
maxFragments:
|
|
9622
|
+
maxBufferedChunks: 256 * 1024,
|
|
9623
|
+
maxFragments: 16 * 1024,
|
|
9627
9624
|
maxPayload: 100 * 1024 * 1024,
|
|
9628
9625
|
skipUTF8Validation: false,
|
|
9629
9626
|
perMessageDeflate: true,
|
|
@@ -10210,9 +10207,9 @@ var require_websocket_server = __commonJS({
|
|
|
10210
10207
|
* called
|
|
10211
10208
|
* @param {Function} [options.handleProtocols] A hook to handle protocols
|
|
10212
10209
|
* @param {String} [options.host] The hostname where to bind the server
|
|
10213
|
-
* @param {Number} [options.maxBufferedChunks=
|
|
10210
|
+
* @param {Number} [options.maxBufferedChunks=262144] The maximum number of
|
|
10214
10211
|
* buffered data chunks
|
|
10215
|
-
* @param {Number} [options.maxFragments=
|
|
10212
|
+
* @param {Number} [options.maxFragments=16384] The maximum number of message
|
|
10216
10213
|
* fragments
|
|
10217
10214
|
* @param {Number} [options.maxPayload=104857600] The maximum allowed message
|
|
10218
10215
|
* size
|
|
@@ -10235,8 +10232,8 @@ var require_websocket_server = __commonJS({
|
|
|
10235
10232
|
options = {
|
|
10236
10233
|
allowSynchronousEvents: true,
|
|
10237
10234
|
autoPong: true,
|
|
10238
|
-
maxBufferedChunks:
|
|
10239
|
-
maxFragments:
|
|
10235
|
+
maxBufferedChunks: 256 * 1024,
|
|
10236
|
+
maxFragments: 16 * 1024,
|
|
10240
10237
|
maxPayload: 100 * 1024 * 1024,
|
|
10241
10238
|
skipUTF8Validation: false,
|
|
10242
10239
|
perMessageDeflate: false,
|
|
@@ -34846,7 +34843,8 @@ var KNOWN_CAPABILITIES = /* @__PURE__ */ new Set([
|
|
|
34846
34843
|
"capture_redirect",
|
|
34847
34844
|
"read_indexed_db",
|
|
34848
34845
|
"read_dom",
|
|
34849
|
-
"download"
|
|
34846
|
+
"download",
|
|
34847
|
+
"graphql"
|
|
34850
34848
|
]);
|
|
34851
34849
|
|
|
34852
34850
|
// node_modules/@fetchproxy/protocol/dist/mcp-id.js
|
|
@@ -35149,6 +35147,7 @@ function assertIndexedDbScopesArray(value, label) {
|
|
|
35149
35147
|
}
|
|
35150
35148
|
var DOM_SELECTOR_RE = /^[^-]{1,512}$/;
|
|
35151
35149
|
var DOM_ATTRIBUTE_RE = /^[A-Za-z_:][A-Za-z0-9_:.\-]{0,127}$/;
|
|
35150
|
+
var GRAPHQL_OP_NAME_RE = /^[_A-Za-z][_0-9A-Za-z]{0,127}$/;
|
|
35152
35151
|
function assertDomSelectorsArray(value, label) {
|
|
35153
35152
|
if (!Array.isArray(value)) {
|
|
35154
35153
|
throw new ProtocolError(`${label}: expected array, got ${typeof value}`);
|
|
@@ -35185,6 +35184,37 @@ function assertDomSelectorsArray(value, label) {
|
|
|
35185
35184
|
}
|
|
35186
35185
|
}
|
|
35187
35186
|
}
|
|
35187
|
+
function assertGraphqlOpsArray(value, label) {
|
|
35188
|
+
if (!Array.isArray(value)) {
|
|
35189
|
+
throw new ProtocolError(`${label}: expected array, got ${typeof value}`);
|
|
35190
|
+
}
|
|
35191
|
+
const seen = /* @__PURE__ */ new Set();
|
|
35192
|
+
for (let i = 0; i < value.length; i++) {
|
|
35193
|
+
const entry = value[i];
|
|
35194
|
+
assertObject(entry, `${label}[${i}]`);
|
|
35195
|
+
if (entry.name === void 0) {
|
|
35196
|
+
throw new ProtocolError(`${label}[${i}].name: missing`);
|
|
35197
|
+
}
|
|
35198
|
+
if (entry.operationName === void 0) {
|
|
35199
|
+
throw new ProtocolError(`${label}[${i}].operationName: missing`);
|
|
35200
|
+
}
|
|
35201
|
+
if (typeof entry.name !== "string" || !SCOPE_KEY_RE.test(entry.name)) {
|
|
35202
|
+
throw new ProtocolError(`${label}[${i}].name: invalid ${JSON.stringify(entry.name)}`);
|
|
35203
|
+
}
|
|
35204
|
+
if (typeof entry.operationName !== "string" || !GRAPHQL_OP_NAME_RE.test(entry.operationName)) {
|
|
35205
|
+
throw new ProtocolError(`${label}[${i}].operationName: invalid ${JSON.stringify(entry.operationName)}`);
|
|
35206
|
+
}
|
|
35207
|
+
if (seen.has(entry.name)) {
|
|
35208
|
+
throw new ProtocolError(`${label}: duplicate name ${JSON.stringify(entry.name)}`);
|
|
35209
|
+
}
|
|
35210
|
+
seen.add(entry.name);
|
|
35211
|
+
for (const k of Object.keys(entry)) {
|
|
35212
|
+
if (k !== "name" && k !== "operationName") {
|
|
35213
|
+
throw new ProtocolError(`${label}[${i}]: unexpected field ${JSON.stringify(k)}`);
|
|
35214
|
+
}
|
|
35215
|
+
}
|
|
35216
|
+
}
|
|
35217
|
+
}
|
|
35188
35218
|
function validateFrame(raw) {
|
|
35189
35219
|
assertObject(raw, "frame");
|
|
35190
35220
|
const t = raw.type;
|
|
@@ -35263,6 +35293,9 @@ function validateHello(raw) {
|
|
|
35263
35293
|
if (raw.domSelectors !== void 0) {
|
|
35264
35294
|
assertDomSelectorsArray(raw.domSelectors, "hello.domSelectors");
|
|
35265
35295
|
}
|
|
35296
|
+
if (raw.graphqlOps !== void 0) {
|
|
35297
|
+
assertGraphqlOpsArray(raw.graphqlOps, "hello.graphqlOps");
|
|
35298
|
+
}
|
|
35266
35299
|
assertBase64(raw.identityX25519Pub, "hello.identityX25519Pub");
|
|
35267
35300
|
assertBase64(raw.identityEd25519Pub, "hello.identityEd25519Pub");
|
|
35268
35301
|
assertBase64(raw.sessionNonce, "hello.sessionNonce");
|
|
@@ -35512,6 +35545,28 @@ function validateInnerRequest(raw) {
|
|
|
35512
35545
|
}
|
|
35513
35546
|
return raw;
|
|
35514
35547
|
}
|
|
35548
|
+
if (raw.op === "graphql_query") {
|
|
35549
|
+
assertObject(raw.init, "inner.init");
|
|
35550
|
+
if (raw.init.name === void 0)
|
|
35551
|
+
throw new ProtocolError("inner.init.name: missing");
|
|
35552
|
+
if (raw.init.variables === void 0) {
|
|
35553
|
+
throw new ProtocolError("inner.init.variables: missing");
|
|
35554
|
+
}
|
|
35555
|
+
assertString(raw.init.name, "inner.init.name");
|
|
35556
|
+
if (raw.init.name.length === 0) {
|
|
35557
|
+
throw new ProtocolError("inner.init.name: must be non-empty");
|
|
35558
|
+
}
|
|
35559
|
+
assertObject(raw.init.variables, "inner.init.variables");
|
|
35560
|
+
if (raw.init.tabUrl !== void 0) {
|
|
35561
|
+
assertString(raw.init.tabUrl, "inner.init.tabUrl");
|
|
35562
|
+
}
|
|
35563
|
+
for (const k of Object.keys(raw.init)) {
|
|
35564
|
+
if (k !== "name" && k !== "variables" && k !== "tabUrl") {
|
|
35565
|
+
throw new ProtocolError(`inner.init: unexpected field ${JSON.stringify(k)} on graphql_query`);
|
|
35566
|
+
}
|
|
35567
|
+
}
|
|
35568
|
+
return raw;
|
|
35569
|
+
}
|
|
35515
35570
|
if (raw.op === "download") {
|
|
35516
35571
|
assertObject(raw.init, "inner.init");
|
|
35517
35572
|
if (raw.init.url === void 0) {
|
|
@@ -35537,7 +35592,7 @@ function validateInnerRequest(raw) {
|
|
|
35537
35592
|
}
|
|
35538
35593
|
return raw;
|
|
35539
35594
|
}
|
|
35540
|
-
throw new ProtocolError(`inner.op: must be one of "fetch", "read_cookies", "read_local_storage", "read_session_storage", "capture_request_header", "capture_redirect", "read_indexed_db", "read_dom", "download"; got ${JSON.stringify(raw.op)}`);
|
|
35595
|
+
throw new ProtocolError(`inner.op: must be one of "fetch", "read_cookies", "read_local_storage", "read_session_storage", "capture_request_header", "capture_redirect", "read_indexed_db", "read_dom", "download", "graphql_query"; got ${JSON.stringify(raw.op)}`);
|
|
35541
35596
|
}
|
|
35542
35597
|
function assertNonEmptyKeyArray(value, label) {
|
|
35543
35598
|
if (!Array.isArray(value)) {
|
|
@@ -35568,6 +35623,10 @@ function assertStringMap(value, label) {
|
|
|
35568
35623
|
}
|
|
35569
35624
|
}
|
|
35570
35625
|
}
|
|
35626
|
+
var KNOWN_RESPONSE_OPS = /* @__PURE__ */ new Set([
|
|
35627
|
+
...KNOWN_CAPABILITIES,
|
|
35628
|
+
"graphql_query"
|
|
35629
|
+
]);
|
|
35571
35630
|
function validateInnerResponse(raw) {
|
|
35572
35631
|
assertPositiveInt(raw.id, "inner.id");
|
|
35573
35632
|
if (raw.ok === true) {
|
|
@@ -35629,6 +35688,13 @@ function validateInnerResponse(raw) {
|
|
|
35629
35688
|
assertStringMap(raw.values, "inner.values");
|
|
35630
35689
|
return raw;
|
|
35631
35690
|
}
|
|
35691
|
+
if (op === "graphql_query") {
|
|
35692
|
+
if (raw.data === void 0) {
|
|
35693
|
+
throw new ProtocolError("inner.data: missing on graphql_query response");
|
|
35694
|
+
}
|
|
35695
|
+
assertObject(raw.data, "inner.data");
|
|
35696
|
+
return raw;
|
|
35697
|
+
}
|
|
35632
35698
|
if (op === "download") {
|
|
35633
35699
|
assertObject(raw.value, "inner.value");
|
|
35634
35700
|
assertString(raw.value.path, "inner.value.path");
|
|
@@ -35653,7 +35719,7 @@ function validateInnerResponse(raw) {
|
|
|
35653
35719
|
if (raw.ok === false) {
|
|
35654
35720
|
assertString(raw.error, "inner.error");
|
|
35655
35721
|
if (raw.op !== void 0) {
|
|
35656
|
-
if (typeof raw.op !== "string" || !
|
|
35722
|
+
if (typeof raw.op !== "string" || !KNOWN_RESPONSE_OPS.has(raw.op)) {
|
|
35657
35723
|
throw new ProtocolError(`inner.op: unknown response op ${JSON.stringify(raw.op)}`);
|
|
35658
35724
|
}
|
|
35659
35725
|
}
|
|
@@ -35845,11 +35911,33 @@ async function sealInnerFrame(sessionKey, mcpId, seq, inner) {
|
|
|
35845
35911
|
};
|
|
35846
35912
|
}
|
|
35847
35913
|
async function openEncryptedFrame(sessionKey, frame) {
|
|
35848
|
-
const
|
|
35849
|
-
|
|
35850
|
-
|
|
35851
|
-
|
|
35852
|
-
|
|
35914
|
+
const result = await openEncryptedFrameDetailed(sessionKey, frame);
|
|
35915
|
+
if (result.stage === "ok")
|
|
35916
|
+
return result.inner;
|
|
35917
|
+
throw result.error instanceof Error ? result.error : new Error(String(result.error));
|
|
35918
|
+
}
|
|
35919
|
+
async function openEncryptedFrameDetailed(sessionKey, frame) {
|
|
35920
|
+
let pt;
|
|
35921
|
+
try {
|
|
35922
|
+
const iv = fromB64(frame.iv);
|
|
35923
|
+
const ct = fromB64(frame.ciphertext);
|
|
35924
|
+
pt = await aesGcmOpen(sessionKey, iv, ct);
|
|
35925
|
+
} catch (error51) {
|
|
35926
|
+
return { stage: "decrypt-failed", error: error51 };
|
|
35927
|
+
}
|
|
35928
|
+
let parsed;
|
|
35929
|
+
try {
|
|
35930
|
+
parsed = JSON.parse(dec.decode(pt));
|
|
35931
|
+
} catch (error51) {
|
|
35932
|
+
return { stage: "validation-failed", error: error51, recoveredId: void 0 };
|
|
35933
|
+
}
|
|
35934
|
+
try {
|
|
35935
|
+
const inner = validateInnerFrame(parsed);
|
|
35936
|
+
return { stage: "ok", inner };
|
|
35937
|
+
} catch (error51) {
|
|
35938
|
+
const recoveredId = parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) && typeof parsed.id === "number" && Number.isInteger(parsed.id) && parsed.id > 0 ? parsed.id : void 0;
|
|
35939
|
+
return { stage: "validation-failed", error: error51, recoveredId };
|
|
35940
|
+
}
|
|
35853
35941
|
}
|
|
35854
35942
|
|
|
35855
35943
|
// node_modules/@fetchproxy/server/dist/election.js
|
|
@@ -35975,6 +36063,12 @@ async function buildServerHello(opts) {
|
|
|
35975
36063
|
...d.attribute !== void 0 ? { attribute: d.attribute } : {}
|
|
35976
36064
|
}));
|
|
35977
36065
|
}
|
|
36066
|
+
if (opts.graphqlOps && opts.graphqlOps.length > 0) {
|
|
36067
|
+
hello.graphqlOps = opts.graphqlOps.map((d) => ({
|
|
36068
|
+
name: d.name,
|
|
36069
|
+
operationName: d.operationName
|
|
36070
|
+
}));
|
|
36071
|
+
}
|
|
35978
36072
|
return hello;
|
|
35979
36073
|
}
|
|
35980
36074
|
|
|
@@ -36065,7 +36159,8 @@ async function startHost(opts) {
|
|
|
36065
36159
|
indexedDbScopes: opts.ownIndexedDbScopes,
|
|
36066
36160
|
localStoragePointers: opts.ownLocalStoragePointers,
|
|
36067
36161
|
sessionStoragePointers: opts.ownSessionStoragePointers,
|
|
36068
|
-
domSelectors: opts.ownDomSelectors
|
|
36162
|
+
domSelectors: opts.ownDomSelectors,
|
|
36163
|
+
graphqlOps: opts.ownGraphqlOps
|
|
36069
36164
|
});
|
|
36070
36165
|
const ownSessionNonce = fromB64(ownHello.sessionNonce);
|
|
36071
36166
|
let extensionWs = null;
|
|
@@ -36302,7 +36397,8 @@ async function startPeer(opts) {
|
|
|
36302
36397
|
indexedDbScopes: opts.indexedDbScopes,
|
|
36303
36398
|
domSelectors: opts.domSelectors,
|
|
36304
36399
|
localStoragePointers: opts.localStoragePointers,
|
|
36305
|
-
sessionStoragePointers: opts.sessionStoragePointers
|
|
36400
|
+
sessionStoragePointers: opts.sessionStoragePointers,
|
|
36401
|
+
graphqlOps: opts.graphqlOps
|
|
36306
36402
|
});
|
|
36307
36403
|
const sessionNonce = fromB64(hello.sessionNonce);
|
|
36308
36404
|
ws.send(JSON.stringify(hello));
|
|
@@ -36346,10 +36442,20 @@ async function startPeer(opts) {
|
|
|
36346
36442
|
return;
|
|
36347
36443
|
if (!session.acceptInboundSeq(frame.seq))
|
|
36348
36444
|
return;
|
|
36349
|
-
|
|
36350
|
-
|
|
36351
|
-
innerListeners.forEach((cb) => cb(inner));
|
|
36352
|
-
}
|
|
36445
|
+
const result = await openEncryptedFrameDetailed(session.sessionKey, frame);
|
|
36446
|
+
if (result.stage === "ok") {
|
|
36447
|
+
innerListeners.forEach((cb) => cb(result.inner));
|
|
36448
|
+
} else if (result.stage === "decrypt-failed") {
|
|
36449
|
+
} else {
|
|
36450
|
+
console.error("[fetchproxy] peer: received a frame that decrypted OK but failed validation:", result.error);
|
|
36451
|
+
if (result.recoveredId !== void 0) {
|
|
36452
|
+
innerListeners.forEach((cb) => cb({
|
|
36453
|
+
type: "response",
|
|
36454
|
+
id: result.recoveredId,
|
|
36455
|
+
ok: false,
|
|
36456
|
+
error: `malformed response failed protocol validation: ${String(result.error)}`
|
|
36457
|
+
}));
|
|
36458
|
+
}
|
|
36353
36459
|
}
|
|
36354
36460
|
}
|
|
36355
36461
|
} catch (e) {
|
|
@@ -36626,6 +36732,10 @@ var FetchproxyServer = class {
|
|
|
36626
36732
|
pendingIdb = /* @__PURE__ */ new Map();
|
|
36627
36733
|
// download awaiters resolve the saved-file metadata (path + size + mime).
|
|
36628
36734
|
pendingDownload = /* @__PURE__ */ new Map();
|
|
36735
|
+
// 1.x+: graphql_query awaiters resolve the GraphQL `data` object. Its
|
|
36736
|
+
// shape is operation-specific, so the awaiter resolves `unknown` and the
|
|
36737
|
+
// caller narrows.
|
|
36738
|
+
pendingGraphql = /* @__PURE__ */ new Map();
|
|
36629
36739
|
mcpId = null;
|
|
36630
36740
|
identity = null;
|
|
36631
36741
|
// 0.5.3+: in-flight role-election / handle-start promise. Set the
|
|
@@ -36713,6 +36823,10 @@ var FetchproxyServer = class {
|
|
|
36713
36823
|
selector: d.selector,
|
|
36714
36824
|
...d.attribute !== void 0 ? { attribute: d.attribute } : {}
|
|
36715
36825
|
})),
|
|
36826
|
+
graphqlOps: (opts.graphqlOps ?? []).map((d) => ({
|
|
36827
|
+
name: d.name,
|
|
36828
|
+
operationName: d.operationName
|
|
36829
|
+
})),
|
|
36716
36830
|
// 0.8.0+: timer + lazy-revive default to ON. Every realty MCP
|
|
36717
36831
|
// adapter was about to set these to the same numbers anyway; the
|
|
36718
36832
|
// back-door is `0` (explicit opt-out) if a caller genuinely wants
|
|
@@ -36834,6 +36948,7 @@ var FetchproxyServer = class {
|
|
|
36834
36948
|
ownLocalStoragePointers: this.opts.localStoragePointers,
|
|
36835
36949
|
ownSessionStoragePointers: this.opts.sessionStoragePointers,
|
|
36836
36950
|
ownDomSelectors: this.opts.domSelectors,
|
|
36951
|
+
ownGraphqlOps: this.opts.graphqlOps,
|
|
36837
36952
|
onPairCode: this.opts.onPairCode
|
|
36838
36953
|
});
|
|
36839
36954
|
this.hostHandle.onOwnInner((inner) => this.onInner(inner));
|
|
@@ -36862,7 +36977,8 @@ var FetchproxyServer = class {
|
|
|
36862
36977
|
indexedDbScopes: this.opts.indexedDbScopes,
|
|
36863
36978
|
localStoragePointers: this.opts.localStoragePointers,
|
|
36864
36979
|
sessionStoragePointers: this.opts.sessionStoragePointers,
|
|
36865
|
-
domSelectors: this.opts.domSelectors
|
|
36980
|
+
domSelectors: this.opts.domSelectors,
|
|
36981
|
+
graphqlOps: this.opts.graphqlOps
|
|
36866
36982
|
});
|
|
36867
36983
|
this.peerHandle.onInner((inner) => this.onInner(inner));
|
|
36868
36984
|
this.peerHandle.onRenegotiate(() => {
|
|
@@ -37068,6 +37184,7 @@ var FetchproxyServer = class {
|
|
|
37068
37184
|
this.pendingRedirect.delete(id);
|
|
37069
37185
|
this.pendingDownload.delete(id);
|
|
37070
37186
|
this.pendingIdb.delete(id);
|
|
37187
|
+
this.pendingGraphql.delete(id);
|
|
37071
37188
|
}
|
|
37072
37189
|
throw err;
|
|
37073
37190
|
}
|
|
@@ -37877,6 +37994,52 @@ var FetchproxyServer = class {
|
|
|
37877
37994
|
await this.sendInnerFrame(inner);
|
|
37878
37995
|
return this._withVerbTimeout(pending, this.pendingStorage, id, origin);
|
|
37879
37996
|
}
|
|
37997
|
+
/**
|
|
37998
|
+
* 1.x+: run a declared GraphQL operation through the page's own Apollo
|
|
37999
|
+
* client (`window.__APOLLO_CLIENT__`) in the signed-in tab's MAIN world.
|
|
38000
|
+
* Requires `'graphql'` in capabilities AND `name` to match a declared
|
|
38001
|
+
* `graphqlOps` entry. The extension resolves `name` → `operationName` →
|
|
38002
|
+
* the live DocumentNode the page already observed, then invokes
|
|
38003
|
+
* `client.query({ query, variables })` — the site's own request path, so
|
|
38004
|
+
* per-request bot telemetry (Akamai etc.) runs automatically.
|
|
38005
|
+
*
|
|
38006
|
+
* Returns the GraphQL `data` object on success (shape is
|
|
38007
|
+
* operation-specific; the caller narrows). Throws a plain `Error` on
|
|
38008
|
+
* developer mistakes (undeclared capability, undeclared name) and a
|
|
38009
|
+
* descriptive `Error` on the `ok:false` bridge path — which includes the
|
|
38010
|
+
* typed "operation not yet observed on this tab" case (open the site's
|
|
38011
|
+
* page and retry).
|
|
38012
|
+
*/
|
|
38013
|
+
async graphqlQuery(opts) {
|
|
38014
|
+
if (!this.opts.capabilities.includes("graphql")) {
|
|
38015
|
+
throw new Error('FetchproxyServer.graphqlQuery(): MCP did not declare "graphql" in capabilities');
|
|
38016
|
+
}
|
|
38017
|
+
if (typeof opts.name !== "string" || opts.name.length === 0) {
|
|
38018
|
+
throw new Error("FetchproxyServer.graphqlQuery: opts.name must be a non-empty string");
|
|
38019
|
+
}
|
|
38020
|
+
const declaredNames = this.opts.graphqlOps.map((d) => d.name);
|
|
38021
|
+
if (!declaredNames.includes(opts.name)) {
|
|
38022
|
+
throw new Error(`FetchproxyServer.graphqlQuery: operation ${JSON.stringify(opts.name)} not in declared graphqlOps [${declaredNames.map((n) => JSON.stringify(n)).join(", ")}]`);
|
|
38023
|
+
}
|
|
38024
|
+
await this.ensureConnected();
|
|
38025
|
+
this.throwIfPendingPair();
|
|
38026
|
+
const id = this.nextRequestId++;
|
|
38027
|
+
const inner = {
|
|
38028
|
+
type: "request",
|
|
38029
|
+
id,
|
|
38030
|
+
op: "graphql_query",
|
|
38031
|
+
init: {
|
|
38032
|
+
name: opts.name,
|
|
38033
|
+
variables: opts.variables,
|
|
38034
|
+
...opts.tabUrl !== void 0 ? { tabUrl: opts.tabUrl } : {}
|
|
38035
|
+
}
|
|
38036
|
+
};
|
|
38037
|
+
const pending = new Promise((resolve, reject) => {
|
|
38038
|
+
this.pendingGraphql.set(id, { resolve, reject });
|
|
38039
|
+
});
|
|
38040
|
+
await this.sendInnerFrame(inner);
|
|
38041
|
+
return this._withVerbTimeout(pending, this.pendingGraphql, id, opts.name);
|
|
38042
|
+
}
|
|
37880
38043
|
assertScopeSubset(requested, declared, label) {
|
|
37881
38044
|
const undeclared = undeclaredKeys(requested, declared);
|
|
37882
38045
|
if (undeclared.length > 0) {
|
|
@@ -38014,6 +38177,20 @@ var FetchproxyServer = class {
|
|
|
38014
38177
|
}
|
|
38015
38178
|
return;
|
|
38016
38179
|
}
|
|
38180
|
+
const graphqlCb = this.pendingGraphql.get(inner.id);
|
|
38181
|
+
if (graphqlCb) {
|
|
38182
|
+
this.pendingGraphql.delete(inner.id);
|
|
38183
|
+
if (inner.ok) {
|
|
38184
|
+
if (inner.op === "graphql_query") {
|
|
38185
|
+
graphqlCb.resolve(inner.data);
|
|
38186
|
+
} else {
|
|
38187
|
+
graphqlCb.reject(new FetchproxyProtocolError(`unexpected ${String(inner.op)} response on graphql_query awaiter`));
|
|
38188
|
+
}
|
|
38189
|
+
} else {
|
|
38190
|
+
graphqlCb.reject(new FetchproxyProtocolError(inner.error));
|
|
38191
|
+
}
|
|
38192
|
+
return;
|
|
38193
|
+
}
|
|
38017
38194
|
const cookiesCb = this.pendingReadCookies.get(inner.id);
|
|
38018
38195
|
if (cookiesCb) {
|
|
38019
38196
|
this.pendingReadCookies.delete(inner.id);
|
|
@@ -38065,6 +38242,9 @@ var FetchproxyServer = class {
|
|
|
38065
38242
|
for (const { reject } of this.pendingDownload.values())
|
|
38066
38243
|
reject(err);
|
|
38067
38244
|
this.pendingDownload.clear();
|
|
38245
|
+
for (const { reject } of this.pendingGraphql.values())
|
|
38246
|
+
reject(err);
|
|
38247
|
+
this.pendingGraphql.clear();
|
|
38068
38248
|
}
|
|
38069
38249
|
/**
|
|
38070
38250
|
* 0.5.2+: read the current pair-pending pair code from whichever handle
|
|
@@ -38311,7 +38491,7 @@ function loadAccount(env = process.env) {
|
|
|
38311
38491
|
// package.json
|
|
38312
38492
|
var package_default = {
|
|
38313
38493
|
name: "infinitecampus-mcp",
|
|
38314
|
-
version: "2.4.
|
|
38494
|
+
version: "2.4.2",
|
|
38315
38495
|
mcpName: "io.github.chrischall/infinitecampus-mcp",
|
|
38316
38496
|
description: "Infinite Campus (Campus Parent) MCP server \u2014 multi-district read + message/document write",
|
|
38317
38497
|
author: "Claude Code (AI) <https://www.anthropic.com/claude>",
|
|
@@ -38353,9 +38533,9 @@ var package_default = {
|
|
|
38353
38533
|
"test:watch": "vitest"
|
|
38354
38534
|
},
|
|
38355
38535
|
dependencies: {
|
|
38356
|
-
"@chrischall/mcp-utils": "^0.
|
|
38357
|
-
"@fetchproxy/bootstrap": "^1.
|
|
38358
|
-
"@fetchproxy/server": "^1.
|
|
38536
|
+
"@chrischall/mcp-utils": "^0.14.0",
|
|
38537
|
+
"@fetchproxy/bootstrap": "^1.7.0",
|
|
38538
|
+
"@fetchproxy/server": "^1.7.0",
|
|
38359
38539
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
38360
38540
|
dotenv: "^17.4.2",
|
|
38361
38541
|
"node-html-parser": "^9.0.0",
|
|
@@ -39927,7 +40107,7 @@ try {
|
|
|
39927
40107
|
}
|
|
39928
40108
|
var COMMON = {
|
|
39929
40109
|
name: "infinitecampus",
|
|
39930
|
-
version: "2.4.
|
|
40110
|
+
version: "2.4.2"
|
|
39931
40111
|
// x-release-please-version
|
|
39932
40112
|
};
|
|
39933
40113
|
if (account) {
|
package/dist/index.js
CHANGED
|
@@ -57,7 +57,7 @@ catch (e) {
|
|
|
57
57
|
// actionable stderr message (banner) instead of a crash loop.
|
|
58
58
|
const COMMON = {
|
|
59
59
|
name: 'infinitecampus',
|
|
60
|
-
version: '2.4.
|
|
60
|
+
version: '2.4.2', // x-release-please-version
|
|
61
61
|
};
|
|
62
62
|
if (account) {
|
|
63
63
|
const client = new ICClient(account, { preloaded });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "infinitecampus-mcp",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.2",
|
|
4
4
|
"mcpName": "io.github.chrischall/infinitecampus-mcp",
|
|
5
5
|
"description": "Infinite Campus (Campus Parent) MCP server — multi-district read + message/document write",
|
|
6
6
|
"author": "Claude Code (AI) <https://www.anthropic.com/claude>",
|
|
@@ -42,9 +42,9 @@
|
|
|
42
42
|
"test:watch": "vitest"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@chrischall/mcp-utils": "^0.
|
|
46
|
-
"@fetchproxy/bootstrap": "^1.
|
|
47
|
-
"@fetchproxy/server": "^1.
|
|
45
|
+
"@chrischall/mcp-utils": "^0.14.0",
|
|
46
|
+
"@fetchproxy/bootstrap": "^1.7.0",
|
|
47
|
+
"@fetchproxy/server": "^1.7.0",
|
|
48
48
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
49
49
|
"dotenv": "^17.4.2",
|
|
50
50
|
"node-html-parser": "^9.0.0",
|
package/server.json
CHANGED
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
"url": "https://github.com/chrischall/infinitecampus-mcp",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "2.4.
|
|
9
|
+
"version": "2.4.2",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "infinitecampus-mcp",
|
|
14
|
-
"version": "2.4.
|
|
14
|
+
"version": "2.4.2",
|
|
15
15
|
"transport": {
|
|
16
16
|
"type": "stdio"
|
|
17
17
|
},
|