ofw-mcp 2.16.0 → 2.16.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": "OurFamilyWizard tools for Claude Code",
9
- "version": "2.16.0"
9
+ "version": "2.16.1"
10
10
  },
11
11
  "plugins": [
12
12
  {
@@ -14,7 +14,7 @@
14
14
  "displayName": "OurFamilyWizard",
15
15
  "source": "./",
16
16
  "description": "OurFamilyWizard co-parenting tools for Claude — messages, calendar, expenses, and journal via MCP",
17
- "version": "2.16.0",
17
+ "version": "2.16.1",
18
18
  "author": {
19
19
  "name": "Chris Chall"
20
20
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ofw",
3
3
  "displayName": "OurFamilyWizard",
4
- "version": "2.16.0",
4
+ "version": "2.16.1",
5
5
  "description": "OurFamilyWizard co-parenting tools for Claude — messages, calendar, expenses, and journal via MCP",
6
6
  "author": {
7
7
  "name": "Chris Chall"
package/dist/bundle.js CHANGED
@@ -39449,6 +39449,11 @@ function warn(opts, why) {
39449
39449
  console.error(`[${opts.label}] WARNING: could not project ${opts.context} (${why}); returning the unprojected payload. The upstream shape has probably changed \u2014 the projection needs updating.`);
39450
39450
  }
39451
39451
 
39452
+ // node_modules/@chrischall/mcp-utils/dist/response/media.js
39453
+ var MEDIA_NOUN = "(?:avatar|picture|photo|thumbnail|thumb|image|icon|banner|profile_pic(?:ture)?|logo)";
39454
+ var MEDIA_QUALIFIER = "(?:primary|secondary|main|default|cover|hero|profile|master|rendered|small|medium|large|full|original|tall)";
39455
+ var MEDIA_KEY = new RegExp(`^(?:(?:${MEDIA_QUALIFIER}|${MEDIA_NOUN})[_-]?)?${MEDIA_NOUN}s?(?:[_-]?(?:link|uri|url|src)s?)?$`, "i");
39456
+
39452
39457
  // node_modules/@chrischall/mcp-utils/dist/response/index.js
39453
39458
  function rawTextResult(text) {
39454
39459
  return { content: [{ type: "text", text }] };
@@ -42199,11 +42204,20 @@ var FetchproxyNoTabError = class extends FetchproxyHintedError {
42199
42204
  this.name = "FetchproxyNoTabError";
42200
42205
  }
42201
42206
  };
42207
+ var FetchproxyTabOpeningError = class extends FetchproxyHintedError {
42208
+ constructor(originalError) {
42209
+ super(originalError, "the extension is opening that tab now \u2014 retry in a few seconds. You do not need to open one yourself, and this is not a version problem.");
42210
+ this.name = "FetchproxyTabOpeningError";
42211
+ }
42212
+ };
42202
42213
  var SCOPE_REJECTION = /not in declared/;
42203
- var NO_TAB_REJECTION = /no tab matching (?!.*content script loaded)/;
42214
+ var NO_TAB_REJECTION = /no tab matching (?!.*(content script loaded|one is still opening))/;
42215
+ var TAB_OPENING_REJECTION = /no tab matching .*one is still opening/;
42204
42216
  function protocolErrorFrom(error61) {
42205
42217
  if (SCOPE_REJECTION.test(error61))
42206
42218
  return new FetchproxyScopeError(error61);
42219
+ if (TAB_OPENING_REJECTION.test(error61))
42220
+ return new FetchproxyTabOpeningError(error61);
42207
42221
  if (NO_TAB_REJECTION.test(error61))
42208
42222
  return new FetchproxyNoTabError(error61);
42209
42223
  return new FetchproxyProtocolError(error61);
@@ -44235,6 +44249,8 @@ var CREDENTIAL_ARMS = /* @__PURE__ */ new Set([
44235
44249
  "ok",
44236
44250
  "no_credential",
44237
44251
  "credential_rejected",
44252
+ "session_expired",
44253
+ "verification_pending",
44238
44254
  "timeout",
44239
44255
  "http",
44240
44256
  "transport",
@@ -44251,6 +44267,10 @@ function credentialHint(arm, prefix, hostLabel, source) {
44251
44267
  return `No credential resolved. Nothing was available to authenticate with \u2014 sign in and reconnect the connector so ${prefix} receives a token, or set the documented environment variable.`;
44252
44268
  case "credential_rejected":
44253
44269
  return `${hostLabel} rejected the credential from '${source}'. It is present but no longer valid \u2014 most often expired or revoked upstream. Re-authenticate and reconnect; retrying will not fix it.`;
44270
+ case "session_expired":
44271
+ return `The credential from '${source}' is configured, but no session is live \u2014 ${hostLabel} served a sign-in page rather than the data. Sign in again; a cookie-session portal expires these on its own, so this recurs between uses.`;
44272
+ case "verification_pending":
44273
+ return `${hostLabel} is holding the sign-in on a second factor rather than refusing it. Supply the verification code the ACCOUNT HOLDER received \u2014 the credential itself is not the problem, so changing it will not help.`;
44254
44274
  case "timeout":
44255
44275
  return `The credential from '${source}' resolved, but ${hostLabel} did not answer in time. Usually transient \u2014 retry. If it persists, ${hostLabel} is slow or unreachable from here.`;
44256
44276
  case "http":
@@ -44261,12 +44281,102 @@ function credentialHint(arm, prefix, hostLabel, source) {
44261
44281
  return `Unexpected failure \u2014 see error.message.`;
44262
44282
  }
44263
44283
  }
44284
+ function credentialHealthcheckDescription(hostLabel) {
44285
+ return `Resolves the credential the way real tools do, then makes one authenticated request to ${hostLabel}. Reports which source supplied the credential, whether ${hostLabel} accepted it, the round-trip time, and a plain-English hint distinguishing 'no credential' from 'credential rejected' from 'a ${hostLabel}-side problem'. Read-only; never returns the credential itself.`;
44286
+ }
44287
+ async function runCredentialHealthcheck(args) {
44288
+ const { prefix, hostLabel, probePath, resolveCredential, probeFn, classifyThrown, hints } = args;
44289
+ const probeUrl = probePath ? `https://${hostLabel}${probePath.startsWith("/") ? "" : "/"}${probePath}` : void 0;
44290
+ let probeStarted = 0;
44291
+ let state;
44292
+ try {
44293
+ state = await resolveCredential();
44294
+ } catch (e) {
44295
+ const classified = classifyThrown?.(e);
44296
+ const result2 = {
44297
+ ok: false,
44298
+ // Still false, and still no source: a classification explains WHY
44299
+ // nothing resolved, it does not invent a credential that did.
44300
+ credential: { source: null, resolved: false },
44301
+ // No `url`: nothing was probed, and naming one implies it was tried.
44302
+ probe: { elapsed_ms: 0 },
44303
+ error: {
44304
+ kind: classified?.kind ?? "no_credential",
44305
+ message: truncateErrorMessage(messageOf(e)),
44306
+ ...classified?.detail !== void 0 ? { detail: classified.detail } : {}
44307
+ },
44308
+ // The hint must follow the KIND beside it. Falling back to
44309
+ // `no_credential`'s copy under a classified kind would state a cause
44310
+ // the kind contradicts — the same disagreement this path exists to
44311
+ // remove. So: an inline hint wins; else the classified arm's own
44312
+ // copy (consumer override first); else, for a kind this module has
44313
+ // no copy for, the neutral `unknown` text rather than one that
44314
+ // asserts a cause; else the unclassified `no_credential` default.
44315
+ hint: classified?.hint ?? (isArm(classified?.kind) ? hints?.[classified.kind] ?? credentialHint(classified.kind, prefix, hostLabel, null) : classified !== void 0 ? credentialHint("unknown", prefix, hostLabel, null) : hints?.no_credential ?? credentialHint("no_credential", prefix, hostLabel, null))
44316
+ };
44317
+ return { content: [{ type: "text", text: JSON.stringify(result2, null, 2) }] };
44318
+ }
44319
+ const credential = {
44320
+ source: state.source,
44321
+ resolved: state.source !== null,
44322
+ ...state.detail !== void 0 ? { detail: state.detail } : {}
44323
+ };
44324
+ if (!credential.resolved) {
44325
+ const result2 = {
44326
+ ok: false,
44327
+ credential,
44328
+ probe: { elapsed_ms: 0 },
44329
+ error: { kind: "no_credential", message: "no credential source resolved" },
44330
+ hint: hints?.no_credential ?? credentialHint("no_credential", prefix, hostLabel, null)
44331
+ };
44332
+ return { content: [{ type: "text", text: JSON.stringify(result2, null, 2) }] };
44333
+ }
44334
+ let arm = "ok";
44335
+ let error61;
44336
+ let status;
44337
+ let customHint;
44338
+ probeStarted = Date.now();
44339
+ try {
44340
+ await probeFn();
44341
+ } catch (e) {
44342
+ status = statusOf(e);
44343
+ const aborted2 = e instanceof Error && e.name === "AbortError";
44344
+ arm = status === 401 || status === 403 ? "credential_rejected" : status !== void 0 ? "http" : aborted2 || /timeout|timed out|ETIMEDOUT/i.test(messageOf(e)) ? "timeout" : /fetch failed|ENOTFOUND|ECONNREFUSED|ECONNRESET|network/i.test(messageOf(e)) ? "transport" : "unknown";
44345
+ let kind = arm;
44346
+ let detail;
44347
+ const custom2 = classifyThrown?.(e);
44348
+ if (custom2) {
44349
+ kind = custom2.kind;
44350
+ customHint = custom2.hint;
44351
+ detail = custom2.detail;
44352
+ arm = isArm(kind) ? kind : "unknown";
44353
+ }
44354
+ error61 = {
44355
+ kind,
44356
+ // Redacted AND bounded before it reaches the result: an upstream
44357
+ // failure routinely quotes what it was sent, and a healthcheck is
44358
+ // the tool people paste into a chat when something is broken.
44359
+ message: truncateErrorMessage(messageOf(e)),
44360
+ ...detail !== void 0 ? { detail } : {}
44361
+ };
44362
+ }
44363
+ const result = {
44364
+ ok: error61 === void 0,
44365
+ credential,
44366
+ probe: {
44367
+ ...probeUrl ? { url: probeUrl } : {},
44368
+ elapsed_ms: Date.now() - probeStarted,
44369
+ ...status !== void 0 ? { status } : {}
44370
+ },
44371
+ ...error61 ? { error: error61 } : {},
44372
+ hint: customHint ?? hints?.[arm] ?? credentialHint(arm, prefix, hostLabel, state.source)
44373
+ };
44374
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
44375
+ }
44264
44376
  function registerCredentialHealthcheckTool(args) {
44265
- const { server, prefix, hostLabel, probePath, resolveCredential, probeFn, classifyThrown, hints } = args;
44266
- const probeUrl = probePath ? `https://${hostLabel}${probePath}` : void 0;
44267
- server.registerTool(`${prefix}_healthcheck`, {
44377
+ args.server.registerTool(`${args.prefix}_healthcheck`, {
44268
44378
  title: "Verify credentials and upstream reachability",
44269
- description: `Resolves the credential the way real tools do, then makes one authenticated request to ${hostLabel}. Reports which source supplied the credential, whether ${hostLabel} accepted it, the round-trip time, and a plain-English hint distinguishing 'no credential' from 'credential rejected' from 'a ${hostLabel}-side problem'. Call this when a real tool fails and you want to know which hop broke. Read-only; never returns the credential itself.`,
44379
+ description: `${credentialHealthcheckDescription(args.hostLabel)} Call this when a real tool fails and you want to know which hop broke.`,
44270
44380
  annotations: {
44271
44381
  title: "Verify credentials and upstream reachability",
44272
44382
  readOnlyHint: true,
@@ -44274,92 +44384,7 @@ function registerCredentialHealthcheckTool(args) {
44274
44384
  openWorldHint: true
44275
44385
  },
44276
44386
  inputSchema: {}
44277
- }, async () => {
44278
- let probeStarted = 0;
44279
- let state;
44280
- try {
44281
- state = await resolveCredential();
44282
- } catch (e) {
44283
- const classified = classifyThrown?.(e);
44284
- const result2 = {
44285
- ok: false,
44286
- // Still false, and still no source: a classification explains WHY
44287
- // nothing resolved, it does not invent a credential that did.
44288
- credential: { source: null, resolved: false },
44289
- // No `url`: nothing was probed, and naming one implies it was tried.
44290
- probe: { elapsed_ms: 0 },
44291
- error: {
44292
- kind: classified?.kind ?? "no_credential",
44293
- message: truncateErrorMessage(messageOf(e)),
44294
- ...classified?.detail !== void 0 ? { detail: classified.detail } : {}
44295
- },
44296
- // The hint must follow the KIND beside it. Falling back to
44297
- // `no_credential`'s copy under a classified kind would state a cause
44298
- // the kind contradicts — the same disagreement this path exists to
44299
- // remove. So: an inline hint wins; else the classified arm's own
44300
- // copy (consumer override first); else, for a kind this module has
44301
- // no copy for, the neutral `unknown` text rather than one that
44302
- // asserts a cause; else the unclassified `no_credential` default.
44303
- hint: classified?.hint ?? (isArm(classified?.kind) ? hints?.[classified.kind] ?? credentialHint(classified.kind, prefix, hostLabel, null) : classified !== void 0 ? credentialHint("unknown", prefix, hostLabel, null) : hints?.no_credential ?? credentialHint("no_credential", prefix, hostLabel, null))
44304
- };
44305
- return { content: [{ type: "text", text: JSON.stringify(result2, null, 2) }] };
44306
- }
44307
- const credential = {
44308
- source: state.source,
44309
- resolved: state.source !== null,
44310
- ...state.detail !== void 0 ? { detail: state.detail } : {}
44311
- };
44312
- if (!credential.resolved) {
44313
- const result2 = {
44314
- ok: false,
44315
- credential,
44316
- probe: { elapsed_ms: 0 },
44317
- error: { kind: "no_credential", message: "no credential source resolved" },
44318
- hint: hints?.no_credential ?? credentialHint("no_credential", prefix, hostLabel, null)
44319
- };
44320
- return { content: [{ type: "text", text: JSON.stringify(result2, null, 2) }] };
44321
- }
44322
- let arm = "ok";
44323
- let error61;
44324
- let status;
44325
- let customHint;
44326
- probeStarted = Date.now();
44327
- try {
44328
- await probeFn();
44329
- } catch (e) {
44330
- status = statusOf(e);
44331
- const aborted2 = e instanceof Error && e.name === "AbortError";
44332
- arm = status === 401 || status === 403 ? "credential_rejected" : status !== void 0 ? "http" : aborted2 || /timeout|timed out|ETIMEDOUT/i.test(messageOf(e)) ? "timeout" : /fetch failed|ENOTFOUND|ECONNREFUSED|ECONNRESET|network/i.test(messageOf(e)) ? "transport" : "unknown";
44333
- let kind = arm;
44334
- let detail;
44335
- const custom2 = classifyThrown?.(e);
44336
- if (custom2) {
44337
- kind = custom2.kind;
44338
- customHint = custom2.hint;
44339
- detail = custom2.detail;
44340
- }
44341
- error61 = {
44342
- kind,
44343
- // Redacted AND bounded before it reaches the result: an upstream
44344
- // failure routinely quotes what it was sent, and a healthcheck is
44345
- // the tool people paste into a chat when something is broken.
44346
- message: truncateErrorMessage(messageOf(e)),
44347
- ...detail !== void 0 ? { detail } : {}
44348
- };
44349
- }
44350
- const result = {
44351
- ok: error61 === void 0,
44352
- credential,
44353
- probe: {
44354
- ...probeUrl ? { url: probeUrl } : {},
44355
- elapsed_ms: Date.now() - probeStarted,
44356
- ...status !== void 0 ? { status } : {}
44357
- },
44358
- ...error61 ? { error: error61 } : {},
44359
- hint: customHint ?? hints?.[arm] ?? credentialHint(arm, prefix, hostLabel, state.source)
44360
- };
44361
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
44362
- });
44387
+ }, async () => runCredentialHealthcheck(args));
44363
44388
  }
44364
44389
 
44365
44390
  // src/protocol.ts
@@ -44434,7 +44459,7 @@ async function loginWithPassword(username, password) {
44434
44459
  // package.json
44435
44460
  var package_default = {
44436
44461
  name: "ofw-mcp",
44437
- version: "2.16.0",
44462
+ version: "2.16.1",
44438
44463
  license: "MIT",
44439
44464
  mcpName: "io.github.chrischall/ofw-mcp",
44440
44465
  description: "OurFamilyWizard MCP server for Claude \u2014 developed and maintained by AI (Claude Code)",
@@ -44468,11 +44493,11 @@ var package_default = {
44468
44493
  typecheck: "tsc -p tsconfig.json --noEmit"
44469
44494
  },
44470
44495
  dependencies: {
44471
- "@chrischall/mcp-utils": "^0.21.0",
44496
+ "@chrischall/mcp-utils": "^0.26.1",
44472
44497
  "@fetchproxy/bootstrap": "^2.2.0",
44473
- "@modelcontextprotocol/sdk": "^1.29.0",
44498
+ "@modelcontextprotocol/sdk": "^1.30.0",
44474
44499
  dotenv: "^17.4.2",
44475
- zod: "^4.4.3"
44500
+ zod: "^4.5.4"
44476
44501
  },
44477
44502
  devDependencies: {
44478
44503
  "@types/node": "^26.0.0",
@@ -49420,7 +49445,7 @@ var nodeCacheProvider = () => nodeCache ??= OFWCache.open(getCacheDbPath());
49420
49445
  var nodeAttachmentIO = new NodeAttachmentIO();
49421
49446
  await runMcp({
49422
49447
  name: "ofw",
49423
- version: "2.16.0",
49448
+ version: "2.16.1",
49424
49449
  // x-release-please-version
49425
49450
  deps: client,
49426
49451
  tools: [
package/dist/index.js CHANGED
@@ -36,7 +36,7 @@ const nodeAttachmentIO = new NodeAttachmentIO();
36
36
  // always succeeds before any credential check runs.
37
37
  await runMcp({
38
38
  name: 'ofw',
39
- version: '2.16.0', // x-release-please-version
39
+ version: '2.16.1', // x-release-please-version
40
40
  deps: client,
41
41
  tools: [
42
42
  registerHealthcheckTools,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ofw-mcp",
3
- "version": "2.16.0",
3
+ "version": "2.16.1",
4
4
  "license": "MIT",
5
5
  "mcpName": "io.github.chrischall/ofw-mcp",
6
6
  "description": "OurFamilyWizard MCP server for Claude — developed and maintained by AI (Claude Code)",
@@ -34,11 +34,11 @@
34
34
  "typecheck": "tsc -p tsconfig.json --noEmit"
35
35
  },
36
36
  "dependencies": {
37
- "@chrischall/mcp-utils": "^0.21.0",
37
+ "@chrischall/mcp-utils": "^0.26.1",
38
38
  "@fetchproxy/bootstrap": "^2.2.0",
39
- "@modelcontextprotocol/sdk": "^1.29.0",
39
+ "@modelcontextprotocol/sdk": "^1.30.0",
40
40
  "dotenv": "^17.4.2",
41
- "zod": "^4.4.3"
41
+ "zod": "^4.5.4"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@types/node": "^26.0.0",
package/server.json CHANGED
@@ -6,12 +6,12 @@
6
6
  "url": "https://github.com/chrischall/ofw-mcp",
7
7
  "source": "github"
8
8
  },
9
- "version": "2.16.0",
9
+ "version": "2.16.1",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "ofw-mcp",
14
- "version": "2.16.0",
14
+ "version": "2.16.1",
15
15
  "transport": {
16
16
  "type": "stdio"
17
17
  },