simplepractice-mcp 1.1.2 → 1.1.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.
@@ -6,14 +6,14 @@
6
6
  },
7
7
  "metadata": {
8
8
  "description": "SimplePractice Client Portal tools for Claude",
9
- "version": "1.1.2"
9
+ "version": "1.1.3"
10
10
  },
11
11
  "plugins": [
12
12
  {
13
13
  "name": "simplepractice",
14
14
  "source": "./",
15
15
  "description": "Read a SimplePractice Client Portal — appointments, invoices and superbills, documents to sign, and practice announcements. Signs in with the portal's own passwordless emailed link; requests go straight to the portal's JSON:API over your own session.",
16
- "version": "1.1.2",
16
+ "version": "1.1.3",
17
17
  "author": {
18
18
  "name": "Chris Chall",
19
19
  "url": "https://github.com/chrischall"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "simplepractice",
3
- "version": "1.1.2",
3
+ "version": "1.1.3",
4
4
  "description": "SimplePractice Client Portal — appointments, billing, documents, and announcements",
5
5
  "author": {
6
6
  "name": "Chris Chall",
package/dist/bundle.js CHANGED
@@ -34362,13 +34362,28 @@ var API_KEY_RE = new RegExp([
34362
34362
  "whsec_[A-Za-z0-9]{16,}\\b"
34363
34363
  // webhook signing secret (Stripe-style)
34364
34364
  ].map((p) => `\\b${p}`).join("|"), "g");
34365
- var QUERY_SECRET_RE = /([?&](?:access_token|refresh_token|client_secret|api_?key|signature|token|key|sig)=)[^&#\s"'<>`]+/gi;
34365
+ 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;
34366
+ var OAUTH_CODE_RE = /([?&]code=)(?=[^&#\s"'<>`]{16,})[^&#\s"'<>`]+/gi;
34367
+ var FORM_PASSWORD_RE = /((?:^|[\s,;:"'(\[{])(?:password|passwd)=)[^&#\s"'<>`]+/gim;
34368
+ var HEADER_SECRET_RE = /(\b(?:x[-_][a-z0-9_-]*?(?:api[-_]?key|token|secret|auth)[a-z0-9_-]*|api[-_]?key)\s*:\s*)[^\s,;"'<>`]+/gi;
34366
34369
  var AWS_SIGV4_RE = /([?&]X-Amz-(?:Signature|Security-Token|Credential)=)[^&#\s"'<>`]+/gi;
34367
- var JSON_SECRET_KEYS = "access_token|refresh_token|client_secret|api_?key|password|passwd|secret|token";
34368
- var JSON_SECRET_DQ_RE = new RegExp(`("(?:${JSON_SECRET_KEYS})"\\s*:\\s*")[^"]*(")`, "gi");
34369
- var JSON_SECRET_SQ_RE = new RegExp(`('(?:${JSON_SECRET_KEYS})'\\s*:\\s*')[^']*(')`, "gi");
34370
+ var JSON_SECRET_KEYS = [
34371
+ "(?:access|refresh|id|auth|session|bearer|csrf|xsrf)[_-]?token",
34372
+ "client[_-]?secret",
34373
+ "api[_-]?secret",
34374
+ "(?:x[_-])?api[_-]?key",
34375
+ "private[_-]?key",
34376
+ "x[_-][a-z0-9_-]*?(?:api[_-]?key|token|secret|auth)[a-z0-9_-]*",
34377
+ "(?:proxy-)?authorization",
34378
+ "password",
34379
+ "passwd",
34380
+ "secret",
34381
+ "token"
34382
+ ].join("|");
34383
+ var JSON_SECRET_DQ_RE = new RegExp(`("(?:${JSON_SECRET_KEYS})"\\s*:\\s*")(?:[^"\\\\]|\\\\.)*(")`, "gi");
34384
+ var JSON_SECRET_SQ_RE = new RegExp(`('(?:${JSON_SECRET_KEYS})'\\s*:\\s*')(?:[^'\\\\]|\\\\.)*(')`, "gi");
34370
34385
  function redactSecrets(text) {
34371
- 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]");
34386
+ 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]");
34372
34387
  }
34373
34388
  function truncateErrorMessage(text, max = DEFAULT_ERROR_MESSAGE_MAX) {
34374
34389
  const str = text === null || text === void 0 ? "" : String(text);
@@ -34560,13 +34575,47 @@ async function createMcpServer(opts) {
34560
34575
  }
34561
34576
  return server;
34562
34577
  }
34578
+ var DEFAULT_SHUTDOWN_TIMEOUT_MS = 5e3;
34579
+ var DEFAULT_REPEAT_SIGNAL_GRACE_MS = 500;
34563
34580
  function withGracefulShutdown(target, opts = {}) {
34564
34581
  const shouldExit = opts.exit ?? true;
34582
+ if (opts.timeoutMs !== void 0 && !(Number.isFinite(opts.timeoutMs) && opts.timeoutMs > 0)) {
34583
+ throw new RangeError("withGracefulShutdown: timeoutMs must be a finite number greater than 0.");
34584
+ }
34585
+ const timeoutMs = opts.timeoutMs ?? DEFAULT_SHUTDOWN_TIMEOUT_MS;
34586
+ if (opts.repeatSignalGraceMs !== void 0 && !(Number.isFinite(opts.repeatSignalGraceMs) && opts.repeatSignalGraceMs >= 0)) {
34587
+ throw new RangeError("withGracefulShutdown: repeatSignalGraceMs must be a finite number >= 0.");
34588
+ }
34589
+ const repeatGraceMs = opts.repeatSignalGraceMs ?? DEFAULT_REPEAT_SIGNAL_GRACE_MS;
34565
34590
  let shuttingDown = false;
34591
+ let firstSignalAt = 0;
34592
+ let exited = false;
34593
+ const exitOnce = () => {
34594
+ if (exited)
34595
+ return;
34596
+ exited = true;
34597
+ process.exit(0);
34598
+ };
34566
34599
  const handler = (signal) => {
34567
- if (shuttingDown)
34600
+ if (shuttingDown) {
34601
+ if (Date.now() - firstSignalAt < repeatGraceMs)
34602
+ return;
34603
+ if (shouldExit) {
34604
+ console.error(`[mcp-utils] second ${signal} during shutdown \u2014 exiting now`);
34605
+ exitOnce();
34606
+ }
34568
34607
  return;
34608
+ }
34569
34609
  shuttingDown = true;
34610
+ firstSignalAt = Date.now();
34611
+ let timer;
34612
+ if (shouldExit) {
34613
+ timer = setTimeout(() => {
34614
+ console.error(`[mcp-utils] graceful shutdown on ${signal} did not finish in ${timeoutMs}ms \u2014 exiting`);
34615
+ exitOnce();
34616
+ }, timeoutMs);
34617
+ timer.unref?.();
34618
+ }
34570
34619
  void (async () => {
34571
34620
  try {
34572
34621
  if (opts.onSignal)
@@ -34575,8 +34624,10 @@ function withGracefulShutdown(target, opts = {}) {
34575
34624
  } catch (err) {
34576
34625
  console.error(`[mcp-utils] error during graceful shutdown on ${signal}: ${err instanceof Error ? err.message : String(err)}`);
34577
34626
  } finally {
34627
+ if (timer !== void 0)
34628
+ clearTimeout(timer);
34578
34629
  if (shouldExit)
34579
- process.exit(0);
34630
+ exitOnce();
34580
34631
  }
34581
34632
  })();
34582
34633
  };
@@ -34718,9 +34769,12 @@ var pageSchema = {
34718
34769
  page_size: external_exports.number().int().min(1).max(200).default(50).describe("Number of items per page (1-200).")
34719
34770
  };
34720
34771
  function toolAnnotations(opts = {}) {
34772
+ if (opts.readOnly === true && opts.destructive === true) {
34773
+ throw new Error("toolAnnotations: readOnly: true contradicts destructive: true \u2014 a destructive tool is not read-only.");
34774
+ }
34721
34775
  return {
34722
34776
  ...opts.title !== void 0 ? { title: opts.title } : {},
34723
- readOnlyHint: opts.readOnly ?? true,
34777
+ readOnlyHint: opts.readOnly ?? opts.destructive === void 0,
34724
34778
  ...opts.idempotent !== void 0 ? { idempotentHint: opts.idempotent } : {},
34725
34779
  ...opts.destructive !== void 0 ? { destructiveHint: opts.destructive } : {},
34726
34780
  ...opts.openWorld !== void 0 ? { openWorldHint: opts.openWorld } : {}
@@ -34728,7 +34782,7 @@ function toolAnnotations(opts = {}) {
34728
34782
  }
34729
34783
 
34730
34784
  // src/version.ts
34731
- var VERSION = "1.1.2";
34785
+ var VERSION = "1.1.3";
34732
34786
 
34733
34787
  // node_modules/@chrischall/mcp-utils/dist/session/index.js
34734
34788
  import { existsSync, readFileSync, writeFileSync, mkdirSync, chmodSync, renameSync, unlinkSync } from "node:fs";
package/dist/version.js CHANGED
@@ -2,4 +2,4 @@
2
2
  * Single source of truth for the server version. release-please rewrites the
3
3
  * literal below; every other file imports VERSION rather than repeating it.
4
4
  */
5
- export const VERSION = '1.1.2'; // x-release-please-version
5
+ export const VERSION = '1.1.3'; // x-release-please-version
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "simplepractice-mcp",
3
- "version": "1.1.2",
3
+ "version": "1.1.3",
4
4
  "license": "MIT",
5
5
  "mcpName": "io.github.chrischall/simplepractice-mcp",
6
6
  "description": "SimplePractice Client Portal MCP server for Claude — developed and maintained by AI (Claude Code)",
@@ -34,10 +34,10 @@
34
34
  "test:watch": "vitest"
35
35
  },
36
36
  "dependencies": {
37
- "@chrischall/mcp-utils": "^2.2.0",
37
+ "@chrischall/mcp-utils": "^2.4.0",
38
38
  "@modelcontextprotocol/server": "^2.0.0",
39
39
  "dotenv": "^18.0.0",
40
- "zod": "^4.6.2"
40
+ "zod": "^4.6.5"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@modelcontextprotocol/client": "^2.0.0",
package/server.json CHANGED
@@ -6,12 +6,12 @@
6
6
  "url": "https://github.com/chrischall/simplepractice-mcp",
7
7
  "source": "github"
8
8
  },
9
- "version": "1.1.2",
9
+ "version": "1.1.3",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "simplepractice-mcp",
14
- "version": "1.1.2",
14
+ "version": "1.1.3",
15
15
  "transport": {
16
16
  "type": "stdio"
17
17
  },