@chrischall/pickuppatrol-mcp 1.0.0 → 1.0.1

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.
@@ -6,7 +6,7 @@
6
6
  },
7
7
  "metadata": {
8
8
  "description": "PickUp Patrol school-dismissal tools for Claude Code",
9
- "version": "1.0.0"
9
+ "version": "1.0.1"
10
10
  },
11
11
  "plugins": [
12
12
  {
@@ -14,7 +14,7 @@
14
14
  "displayName": "PickUp Patrol",
15
15
  "source": "./",
16
16
  "description": "Read and change your children's school dismissal plans in PickUp Patrol — defaults, day-by-day changes and school cutoff times — via MCP",
17
- "version": "1.0.0",
17
+ "version": "1.0.1",
18
18
  "author": {
19
19
  "name": "Chris Chall"
20
20
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pickuppatrol",
3
3
  "displayName": "PickUp Patrol",
4
- "version": "1.0.0",
4
+ "version": "1.0.1",
5
5
  "description": "Read and change your children's school dismissal plans in PickUp Patrol — defaults, day-by-day changes and school cutoff times — via MCP",
6
6
  "author": {
7
7
  "name": "Chris Chall"
package/dist/bundle.js CHANGED
@@ -23302,6 +23302,20 @@ var require_v4 = __commonJS({
23302
23302
  }
23303
23303
  });
23304
23304
 
23305
+ // node_modules/@chrischall/mcp-utils/dist/cancel/index.js
23306
+ import { AsyncLocalStorage } from "node:async_hooks";
23307
+ var storage = new AsyncLocalStorage();
23308
+ function withCallSignal(signal, fn, request) {
23309
+ return signal ? storage.run({ signal, ...request ? { request } : {} }, fn) : fn();
23310
+ }
23311
+
23312
+ // node_modules/@chrischall/mcp-utils/dist/caller/index.js
23313
+ import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
23314
+ var storage2 = new AsyncLocalStorage2();
23315
+ function withCallerCapabilities(capabilities, fn) {
23316
+ return capabilities ? storage2.run(capabilities, fn) : fn();
23317
+ }
23318
+
23305
23319
  // node_modules/@modelcontextprotocol/server/dist/chunk-Br0eD_fh.mjs
23306
23320
  var __create2 = Object.create;
23307
23321
  var __defProp2 = Object.defineProperty;
@@ -37977,13 +37991,28 @@ var API_KEY_RE = new RegExp([
37977
37991
  "whsec_[A-Za-z0-9]{16,}\\b"
37978
37992
  // webhook signing secret (Stripe-style)
37979
37993
  ].map((p) => `\\b${p}`).join("|"), "g");
37980
- var QUERY_SECRET_RE = /([?&](?:access_token|refresh_token|client_secret|api_?key|signature|token|key|sig)=)[^&#\s"'<>`]+/gi;
37994
+ var QUERY_SECRET_RE = /([?&](?:(?:access|refresh|id|auth|session|csrf|xsrf)[_-]?token|client[_-]?secret|api[_-]?secret|api[_-]?key|password|passwd|signature|token|key|sig)=)[^&#\s"'<>`]+/gi;
37995
+ var OAUTH_CODE_RE = /([?&]code=)(?=[^&#\s"'<>`]{16,})[^&#\s"'<>`]+/gi;
37996
+ var FORM_PASSWORD_RE = /((?:^|[\s,;:"'(\[{])(?:password|passwd)=)[^&#\s"'<>`]+/gim;
37997
+ var HEADER_SECRET_RE = /(\b(?:x[-_][a-z0-9_-]*?(?:api[-_]?key|token|secret|auth)[a-z0-9_-]*|api[-_]?key)\s*:\s*)[^\s,;"'<>`]+/gi;
37981
37998
  var AWS_SIGV4_RE = /([?&]X-Amz-(?:Signature|Security-Token|Credential)=)[^&#\s"'<>`]+/gi;
37982
- var JSON_SECRET_KEYS = "access_token|refresh_token|client_secret|api_?key|password|passwd|secret|token";
37983
- var JSON_SECRET_DQ_RE = new RegExp(`("(?:${JSON_SECRET_KEYS})"\\s*:\\s*")[^"]*(")`, "gi");
37984
- var JSON_SECRET_SQ_RE = new RegExp(`('(?:${JSON_SECRET_KEYS})'\\s*:\\s*')[^']*(')`, "gi");
37999
+ var JSON_SECRET_KEYS = [
38000
+ "(?:access|refresh|id|auth|session|bearer|csrf|xsrf)[_-]?token",
38001
+ "client[_-]?secret",
38002
+ "api[_-]?secret",
38003
+ "(?:x[_-])?api[_-]?key",
38004
+ "private[_-]?key",
38005
+ "x[_-][a-z0-9_-]*?(?:api[_-]?key|token|secret|auth)[a-z0-9_-]*",
38006
+ "(?:proxy-)?authorization",
38007
+ "password",
38008
+ "passwd",
38009
+ "secret",
38010
+ "token"
38011
+ ].join("|");
38012
+ var JSON_SECRET_DQ_RE = new RegExp(`("(?:${JSON_SECRET_KEYS})"\\s*:\\s*")(?:[^"\\\\]|\\\\.)*(")`, "gi");
38013
+ var JSON_SECRET_SQ_RE = new RegExp(`('(?:${JSON_SECRET_KEYS})'\\s*:\\s*')(?:[^'\\\\]|\\\\.)*(')`, "gi");
37985
38014
  function redactSecrets(text) {
37986
- return text.replace(BEARER_RE, "$1[REDACTED]").replace(BASIC_AUTH_RE, "$1[REDACTED]").replace(SET_COOKIE_RE, "$1$2=[REDACTED]").replace(COOKIE_HEADER_RE, (_m, prefix, pairs) => `${prefix}${pairs.replace(/=[^;,\s]*/g, "=[REDACTED]")}`).replace(API_KEY_RE, "[REDACTED]").replace(QUERY_SECRET_RE, "$1[REDACTED]").replace(AWS_SIGV4_RE, "$1[REDACTED]").replace(JSON_SECRET_DQ_RE, "$1[REDACTED]$2").replace(JSON_SECRET_SQ_RE, "$1[REDACTED]$2").replace(JWT_RE, "[REDACTED]");
38015
+ return text.replace(BEARER_RE, "$1[REDACTED]").replace(BASIC_AUTH_RE, "$1[REDACTED]").replace(SET_COOKIE_RE, "$1$2=[REDACTED]").replace(COOKIE_HEADER_RE, (_m, prefix, pairs) => `${prefix}${pairs.replace(/=[^;,\s]*/g, "=[REDACTED]")}`).replace(API_KEY_RE, "[REDACTED]").replace(HEADER_SECRET_RE, "$1[REDACTED]").replace(QUERY_SECRET_RE, "$1[REDACTED]").replace(OAUTH_CODE_RE, "$1[REDACTED]").replace(FORM_PASSWORD_RE, "$1[REDACTED]").replace(AWS_SIGV4_RE, "$1[REDACTED]").replace(JSON_SECRET_DQ_RE, "$1[REDACTED]$2").replace(JSON_SECRET_SQ_RE, "$1[REDACTED]$2").replace(JWT_RE, "[REDACTED]");
37987
38016
  }
37988
38017
  function messageOf(err) {
37989
38018
  if (err instanceof Error)
@@ -57690,17 +57719,73 @@ Hint: ${err.hint}`);
57690
57719
  }
57691
57720
  throw err;
57692
57721
  }
57722
+ function mcpRequestFrom(args) {
57723
+ const ctx = args.at(-1);
57724
+ if (typeof ctx !== "object" || ctx === null)
57725
+ return void 0;
57726
+ const req = ctx.mcpReq;
57727
+ if (typeof req !== "object" || req === null)
57728
+ return void 0;
57729
+ return typeof req.notify === "function" ? req : void 0;
57730
+ }
57731
+ function callSignalFrom(args) {
57732
+ const ctx = args.at(-1);
57733
+ if (typeof ctx !== "object" || ctx === null)
57734
+ return void 0;
57735
+ const req = ctx.mcpReq;
57736
+ if (typeof req !== "object" || req === null)
57737
+ return void 0;
57738
+ const signal = req.signal;
57739
+ return signal instanceof AbortSignal ? signal : void 0;
57740
+ }
57741
+ function declaredCapabilitiesFrom(server) {
57742
+ const inner = server.server;
57743
+ if (typeof inner !== "object" || inner === null)
57744
+ return void 0;
57745
+ const read = inner.getClientCapabilities;
57746
+ if (typeof read !== "function")
57747
+ return void 0;
57748
+ try {
57749
+ const declared = read.call(inner);
57750
+ return typeof declared === "object" && declared !== null ? declared : void 0;
57751
+ } catch {
57752
+ return void 0;
57753
+ }
57754
+ }
57693
57755
  function surfaceToolHints(server) {
57694
57756
  const register2 = server.registerTool.bind(server);
57695
- server.registerTool = (name, config2, cb) => register2(name, config2, (...args) => {
57696
- let result;
57697
- try {
57698
- result = cb(...args);
57699
- } catch (err) {
57700
- return hintResultOrRethrow(err);
57701
- }
57702
- return result instanceof Promise ? result.catch(hintResultOrRethrow) : result;
57703
- });
57757
+ server.registerTool = (name, config2, cb) => register2(name, config2, (...args) => (
57758
+ // THE CALLER'S CANCELLATION, made ambient for the whole handler
57759
+ // (`cancel/index.ts`). The SDK delivers it and the fleet ignored it:
57760
+ // measured against @modelcontextprotocol/server 2.0.0, a cancelled
57761
+ // call aborts `ctx.mcpReq.signal` with the caller's reason and the
57762
+ // handler runs to completion regardless — so the HTTP request stays
57763
+ // in flight, the child keeps burning metered CPU, and the upstream
57764
+ // keeps being hit for somebody who has gone. claude.ai sent 101
57765
+ // cancellations in the week to 2026-09-20.
57766
+ //
57767
+ // Here because this is the one wrapper every tool already passes
57768
+ // through: threading the signal by hand would mean editing several
57769
+ // hundred handlers and missing exactly the ones nobody edits.
57770
+ withCallSignal(callSignalFrom(args), () => (
57771
+ // WHAT THE CALLER CAN DO, made ambient for the same reason and in
57772
+ // the same place (`caller/index.ts`). Only the 2025-era half needs
57773
+ // the wrapper — a 2026-07-28 request answers for itself off its own
57774
+ // envelope — but a guarded tool that cannot tell whether the caller
57775
+ // can be shown a prompt returns one the SDK then refuses to deliver,
57776
+ // and that refusal reaches the caller as an unexplained protocol
57777
+ // error it had no chance to catch.
57778
+ withCallerCapabilities(declaredCapabilitiesFrom(server), () => {
57779
+ let result;
57780
+ try {
57781
+ result = cb(...args);
57782
+ } catch (err) {
57783
+ return hintResultOrRethrow(err);
57784
+ }
57785
+ return result instanceof Promise ? result.catch(hintResultOrRethrow) : result;
57786
+ })
57787
+ ), mcpRequestFrom(args))
57788
+ ));
57704
57789
  }
57705
57790
  async function createMcpServer(opts) {
57706
57791
  const server = new McpServer({ name: opts.name, version: opts.version }, { supportedProtocolVersions: [...SERVER_PROTOCOL_VERSIONS] });
@@ -57715,13 +57800,47 @@ async function createMcpServer(opts) {
57715
57800
  }
57716
57801
  return server;
57717
57802
  }
57803
+ var DEFAULT_SHUTDOWN_TIMEOUT_MS = 5e3;
57804
+ var DEFAULT_REPEAT_SIGNAL_GRACE_MS = 500;
57718
57805
  function withGracefulShutdown(target, opts = {}) {
57719
57806
  const shouldExit = opts.exit ?? true;
57807
+ if (opts.timeoutMs !== void 0 && !(Number.isFinite(opts.timeoutMs) && opts.timeoutMs > 0)) {
57808
+ throw new RangeError("withGracefulShutdown: timeoutMs must be a finite number greater than 0.");
57809
+ }
57810
+ const timeoutMs = opts.timeoutMs ?? DEFAULT_SHUTDOWN_TIMEOUT_MS;
57811
+ if (opts.repeatSignalGraceMs !== void 0 && !(Number.isFinite(opts.repeatSignalGraceMs) && opts.repeatSignalGraceMs >= 0)) {
57812
+ throw new RangeError("withGracefulShutdown: repeatSignalGraceMs must be a finite number >= 0.");
57813
+ }
57814
+ const repeatGraceMs = opts.repeatSignalGraceMs ?? DEFAULT_REPEAT_SIGNAL_GRACE_MS;
57720
57815
  let shuttingDown = false;
57816
+ let firstSignalAt = 0;
57817
+ let exited = false;
57818
+ const exitOnce = () => {
57819
+ if (exited)
57820
+ return;
57821
+ exited = true;
57822
+ process.exit(0);
57823
+ };
57721
57824
  const handler = (signal) => {
57722
- if (shuttingDown)
57825
+ if (shuttingDown) {
57826
+ if (Date.now() - firstSignalAt < repeatGraceMs)
57827
+ return;
57828
+ if (shouldExit) {
57829
+ console.error(`[mcp-utils] second ${signal} during shutdown \u2014 exiting now`);
57830
+ exitOnce();
57831
+ }
57723
57832
  return;
57833
+ }
57724
57834
  shuttingDown = true;
57835
+ firstSignalAt = Date.now();
57836
+ let timer;
57837
+ if (shouldExit) {
57838
+ timer = setTimeout(() => {
57839
+ console.error(`[mcp-utils] graceful shutdown on ${signal} did not finish in ${timeoutMs}ms \u2014 exiting`);
57840
+ exitOnce();
57841
+ }, timeoutMs);
57842
+ timer.unref?.();
57843
+ }
57725
57844
  void (async () => {
57726
57845
  try {
57727
57846
  if (opts.onSignal)
@@ -57730,8 +57849,10 @@ function withGracefulShutdown(target, opts = {}) {
57730
57849
  } catch (err) {
57731
57850
  console.error(`[mcp-utils] error during graceful shutdown on ${signal}: ${err instanceof Error ? err.message : String(err)}`);
57732
57851
  } finally {
57852
+ if (timer !== void 0)
57853
+ clearTimeout(timer);
57733
57854
  if (shouldExit)
57734
- process.exit(0);
57855
+ exitOnce();
57735
57856
  }
57736
57857
  })();
57737
57858
  };
@@ -58158,7 +58279,7 @@ var PickUpPatrolClient = class {
58158
58279
  var client = new PickUpPatrolClient();
58159
58280
 
58160
58281
  // src/version.ts
58161
- var VERSION = "1.0.0";
58282
+ var VERSION = "1.0.1";
58162
58283
 
58163
58284
  // src/dates.ts
58164
58285
  var WEEKDAY_NAMES = [
package/dist/version.d.ts CHANGED
@@ -7,4 +7,4 @@
7
7
  * annotation, so there is exactly one line to keep in sync and
8
8
  * `versionSyncTest` has exactly one line to check.
9
9
  */
10
- export declare const VERSION = "1.0.0";
10
+ export declare const VERSION = "1.0.1";
package/dist/version.js CHANGED
@@ -7,4 +7,4 @@
7
7
  * annotation, so there is exactly one line to keep in sync and
8
8
  * `versionSyncTest` has exactly one line to check.
9
9
  */
10
- export const VERSION = '1.0.0'; // x-release-please-version
10
+ export const VERSION = '1.0.1'; // x-release-please-version
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chrischall/pickuppatrol-mcp",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "license": "MIT",
5
5
  "mcpName": "io.github.chrischall/pickuppatrol-mcp",
6
6
  "description": "PickUp Patrol MCP server for Claude — developed and maintained by AI (Claude Code)",
@@ -57,10 +57,10 @@
57
57
  "test:watch": "vitest"
58
58
  },
59
59
  "dependencies": {
60
- "@chrischall/mcp-utils": "^1.0.0",
60
+ "@chrischall/mcp-utils": "^2.4.0",
61
61
  "@modelcontextprotocol/server": "^2.0.0",
62
62
  "dotenv": "^17.4.0",
63
- "zod": "^4.6.2"
63
+ "zod": "^4.6.5"
64
64
  },
65
65
  "devDependencies": {
66
66
  "@modelcontextprotocol/client": "^2.0.0",
package/server.json CHANGED
@@ -6,12 +6,12 @@
6
6
  "url": "https://github.com/chrischall/pickuppatrol-mcp",
7
7
  "source": "github"
8
8
  },
9
- "version": "1.0.0",
9
+ "version": "1.0.1",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "@chrischall/pickuppatrol-mcp",
14
- "version": "1.0.0",
14
+ "version": "1.0.1",
15
15
  "transport": {
16
16
  "type": "stdio"
17
17
  }