infinitecampus-mcp 2.8.2 → 2.8.4

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/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { dirname, join } from 'path';
3
3
  import { fileURLToPath } from 'url';
4
- import { loadDotenvSafely, runMcp } from '@chrischall/mcp-utils';
4
+ import { loadDotenvSafely, McpToolError, runMcp } from '@chrischall/mcp-utils';
5
5
  // quiet load of the local .env (no-throw, silent when the file is absent —
6
6
  // e.g. inside an mcpb bundle). Path is resolved next to dist/ so the same
7
7
  // `..`/.env that the previous inline dotenv call used still applies.
@@ -35,7 +35,7 @@ const AI_NOTICE = '[infinitecampus-mcp] Developed and maintained by AI (Claude).
35
35
  // Defer config errors so the server can still start cleanly when env vars
36
36
  // aren't set (e.g. during the host's install-time smoke test, before the
37
37
  // user has filled in user_config OR the user hasn't yet signed into their
38
- // IC portal in the browser). When not configured we register no tools and
38
+ // IC portal in the browser). When not configured every tool still registers and
39
39
  // log a clear stderr message — far better than the previous crash loop.
40
40
  let account = null;
41
41
  let refreshSession;
@@ -51,21 +51,61 @@ catch (e) {
51
51
  configError = e;
52
52
  }
53
53
  // Hand off to mcp-utils' runMcp for the connect + stdio transport + graceful
54
- // shutdown. We keep the deferred-config-error pattern by deciding the registrar
55
- // list and banner up here: when configured we wire every
56
- // register<Domain>Tools(server, client) call; when not, we register no tools so
57
- // the host's install-time tools/list still succeeds and the user gets an
58
- // actionable stderr message (banner) instead of a crash loop.
54
+ // shutdown. The registrar list is the SAME either way; only the client and the
55
+ // banner differ. A tool surface that follows the environment is what this
56
+ // avoids — see `unconfiguredClient`.
59
57
  const COMMON = {
60
58
  name: 'infinitecampus',
61
- version: '2.8.2', // x-release-please-version
59
+ version: '2.8.4', // x-release-please-version
62
60
  };
63
61
  // Shared with the healthcheck so it can report the SAME state the tools see,
64
62
  // including on the unconfigured path where there are no tools at all.
65
63
  const healthState = { account, source, configError, client: null };
66
- if (account) {
67
- const client = new ICClient(account, { refreshSession });
68
- healthState.client = client;
64
+ /**
65
+ * Stands in for the client when configuration is missing, so that every tool
66
+ * registers either way and the failure surfaces WHERE IT IS ACTIONABLE — in the
67
+ * answer to the tool somebody called, not in a tool list they never saw.
68
+ *
69
+ * The surface must not follow the environment. This server is hosted with
70
+ * `perUserChild` + `authFields`, so mcp-host spawns a principal-less child with
71
+ * none of the per-user config for prewarm, auto-update, restart and every
72
+ * admin-portal tool listing — and THAT child's list is what the portal
73
+ * publishes as the connector's surface. It advertised one tool for a connector
74
+ * that has twenty.
75
+ *
76
+ * This continues the reasoning that put the healthcheck on this path rather
77
+ * than reversing it: a server that can explain itself beats one that cannot,
78
+ * and nineteen tools that each name the missing variables explain it better
79
+ * than nineteen absences do. The healthcheck still reports the same state, and
80
+ * `healthState.client` deliberately stays null so it reports "unconfigured"
81
+ * rather than mistaking this stand-in for a live client.
82
+ */
83
+ function unconfiguredClient(reason) {
84
+ const fail = () => {
85
+ throw new McpToolError(`infinitecampus-mcp is not configured: ${reason?.message ?? 'unknown error'}`, {
86
+ hint: 'Set IC_BASE_URL (your portal URL) and IC_DISTRICT (the app-name path segment), ' +
87
+ 'plus IC_USERNAME and IC_PASSWORD together if you are not signing in through the ' +
88
+ 'browser. Call ic_healthcheck for the resolved state.',
89
+ });
90
+ };
91
+ return new Proxy({}, {
92
+ get(_target, prop) {
93
+ // `then` must answer undefined or this object is thenable, and any
94
+ // `await client` would resolve to something other than the client.
95
+ // Symbols (toStringTag, node's inspect) are asked for by the runtime
96
+ // rather than by tool code, so they get the same treatment.
97
+ if (typeof prop === 'symbol' || prop === 'then')
98
+ return undefined;
99
+ return fail;
100
+ },
101
+ });
102
+ }
103
+ {
104
+ const client = account
105
+ ? new ICClient(account, { refreshSession })
106
+ : unconfiguredClient(configError);
107
+ if (account)
108
+ healthState.client = client;
69
109
  const tools = [
70
110
  (server) => registerHealthcheckTools(server, healthState),
71
111
  registerDistrictTools,
@@ -87,19 +127,10 @@ if (account) {
87
127
  registerFeaturesTools,
88
128
  ];
89
129
  const suffix = source === 'fetchproxy' ? ' [via fetchproxy]' : '';
90
- const districtLine = `[infinitecampus-mcp] District: ${account.name} (${account.baseUrl})${suffix}`;
91
- await runMcp({ ...COMMON, deps: client, tools, banner: `${districtLine}\n${AI_NOTICE}` });
92
- }
93
- else {
94
- const notConfigured = `[infinitecampus-mcp] Not configured: ${configError?.message ?? 'unknown error'}\n` +
95
- '[infinitecampus-mcp] Server is running with no tools registered. Set the required env vars and reinstall.';
96
- // NOT `tools: []` any more. A server that registers nothing explains itself
97
- // only on stderr, which a hosted connector never sees — so a misconfigured
98
- // server was indistinguishable from a broken one. The healthcheck is the one
99
- // tool that must survive this path, because this is the path that needs it.
100
- await runMcp({
101
- ...COMMON,
102
- tools: [(server) => registerHealthcheckTools(server, healthState)],
103
- banner: `${notConfigured}\n${AI_NOTICE}`,
104
- });
130
+ const banner = account
131
+ ? `[infinitecampus-mcp] District: ${account.name} (${account.baseUrl})${suffix}`
132
+ : `[infinitecampus-mcp] Not configured: ${configError?.message ?? 'unknown error'}\n` +
133
+ '[infinitecampus-mcp] Every tool is registered and will report this until the ' +
134
+ 'required env vars are set. Call ic_healthcheck for the resolved state.';
135
+ await runMcp({ ...COMMON, deps: client, tools, banner: `${banner}\n${AI_NOTICE}` });
105
136
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "infinitecampus-mcp",
3
- "version": "2.8.2",
3
+ "version": "2.8.4",
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>",
@@ -45,13 +45,13 @@
45
45
  "typecheck": "tsc -p tsconfig.json --noEmit"
46
46
  },
47
47
  "dependencies": {
48
- "@chrischall/mcp-utils": "^0.23.2",
49
- "@fetchproxy/bootstrap": "^2.2.0",
50
- "@fetchproxy/server": "^2.2.0",
51
- "@modelcontextprotocol/sdk": "^1.29.0",
48
+ "@chrischall/mcp-utils": "^0.26.1",
49
+ "@fetchproxy/bootstrap": "^3.0.0",
50
+ "@fetchproxy/server": "^3.0.0",
51
+ "@modelcontextprotocol/sdk": "^1.30.0",
52
52
  "dotenv": "^17.4.2",
53
53
  "node-html-parser": "^9.0.0",
54
- "zod": "^4.4.3"
54
+ "zod": "^4.5.4"
55
55
  },
56
56
  "devDependencies": {
57
57
  "@types/node": "^26.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.8.2",
9
+ "version": "2.8.4",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "infinitecampus-mcp",
14
- "version": "2.8.2",
14
+ "version": "2.8.4",
15
15
  "transport": {
16
16
  "type": "stdio"
17
17
  },