ofw-mcp 2.2.0 → 2.3.0
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 +279 -46
- package/dist/index.js +1 -1
- package/package.json +3 -3
- package/server.json +2 -2
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
},
|
|
7
7
|
"metadata": {
|
|
8
8
|
"description": "OurFamilyWizard tools for Claude Code",
|
|
9
|
-
"version": "2.
|
|
9
|
+
"version": "2.3.0"
|
|
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.
|
|
17
|
+
"version": "2.3.0",
|
|
18
18
|
"author": {
|
|
19
19
|
"name": "Chris Chall"
|
|
20
20
|
},
|
package/dist/bundle.js
CHANGED
|
@@ -7395,14 +7395,14 @@ var require_permessage_deflate = __commonJS({
|
|
|
7395
7395
|
}
|
|
7396
7396
|
};
|
|
7397
7397
|
module.exports = PerMessageDeflate2;
|
|
7398
|
-
function deflateOnData(
|
|
7399
|
-
this[kBuffers].push(
|
|
7400
|
-
this[kTotalLength] +=
|
|
7398
|
+
function deflateOnData(chunk2) {
|
|
7399
|
+
this[kBuffers].push(chunk2);
|
|
7400
|
+
this[kTotalLength] += chunk2.length;
|
|
7401
7401
|
}
|
|
7402
|
-
function inflateOnData(
|
|
7403
|
-
this[kTotalLength] +=
|
|
7402
|
+
function inflateOnData(chunk2) {
|
|
7403
|
+
this[kTotalLength] += chunk2.length;
|
|
7404
7404
|
if (this[kPerMessageDeflate]._maxPayload < 1 || this[kTotalLength] <= this[kPerMessageDeflate]._maxPayload) {
|
|
7405
|
-
this[kBuffers].push(
|
|
7405
|
+
this[kBuffers].push(chunk2);
|
|
7406
7406
|
return;
|
|
7407
7407
|
}
|
|
7408
7408
|
this[kError] = new RangeError("Max payload size exceeded");
|
|
@@ -7702,7 +7702,7 @@ var require_receiver = __commonJS({
|
|
|
7702
7702
|
* @param {Function} cb Callback
|
|
7703
7703
|
* @private
|
|
7704
7704
|
*/
|
|
7705
|
-
_write(
|
|
7705
|
+
_write(chunk2, encoding, cb) {
|
|
7706
7706
|
if (this._opcode === 8 && this._state == GET_INFO) return cb();
|
|
7707
7707
|
if (this._maxBufferedChunks > 0 && this._buffers.length >= this._maxBufferedChunks) {
|
|
7708
7708
|
cb(
|
|
@@ -7716,8 +7716,8 @@ var require_receiver = __commonJS({
|
|
|
7716
7716
|
);
|
|
7717
7717
|
return;
|
|
7718
7718
|
}
|
|
7719
|
-
this._bufferedBytes +=
|
|
7720
|
-
this._buffers.push(
|
|
7719
|
+
this._bufferedBytes += chunk2.length;
|
|
7720
|
+
this._buffers.push(chunk2);
|
|
7721
7721
|
this.startLoop(cb);
|
|
7722
7722
|
}
|
|
7723
7723
|
/**
|
|
@@ -9991,8 +9991,8 @@ var require_websocket = __commonJS({
|
|
|
9991
9991
|
this.removeListener("end", socketOnEnd);
|
|
9992
9992
|
websocket._readyState = WebSocket2.CLOSING;
|
|
9993
9993
|
if (!this._readableState.endEmitted && !websocket._closeFrameReceived && !websocket._receiver._writableState.errorEmitted && this._readableState.length !== 0) {
|
|
9994
|
-
const
|
|
9995
|
-
websocket._receiver.write(
|
|
9994
|
+
const chunk2 = this.read(this._readableState.length);
|
|
9995
|
+
websocket._receiver.write(chunk2);
|
|
9996
9996
|
}
|
|
9997
9997
|
websocket._receiver.end();
|
|
9998
9998
|
this[kWebSocket] = void 0;
|
|
@@ -10004,8 +10004,8 @@ var require_websocket = __commonJS({
|
|
|
10004
10004
|
websocket._receiver.on("finish", receiverOnFinish);
|
|
10005
10005
|
}
|
|
10006
10006
|
}
|
|
10007
|
-
function socketOnData(
|
|
10008
|
-
if (!this[kWebSocket]._receiver.write(
|
|
10007
|
+
function socketOnData(chunk2) {
|
|
10008
|
+
if (!this[kWebSocket]._receiver.write(chunk2)) {
|
|
10009
10009
|
this.pause();
|
|
10010
10010
|
}
|
|
10011
10011
|
}
|
|
@@ -10108,14 +10108,14 @@ var require_stream = __commonJS({
|
|
|
10108
10108
|
duplex._read = function() {
|
|
10109
10109
|
if (ws.isPaused) ws.resume();
|
|
10110
10110
|
};
|
|
10111
|
-
duplex._write = function(
|
|
10111
|
+
duplex._write = function(chunk2, encoding, callback) {
|
|
10112
10112
|
if (ws.readyState === ws.CONNECTING) {
|
|
10113
10113
|
ws.once("open", function open() {
|
|
10114
|
-
duplex._write(
|
|
10114
|
+
duplex._write(chunk2, encoding, callback);
|
|
10115
10115
|
});
|
|
10116
10116
|
return;
|
|
10117
10117
|
}
|
|
10118
|
-
ws.send(
|
|
10118
|
+
ws.send(chunk2, callback);
|
|
10119
10119
|
};
|
|
10120
10120
|
duplex.on("end", duplexOnEnd);
|
|
10121
10121
|
duplex.on("error", duplexOnError);
|
|
@@ -34547,8 +34547,8 @@ import process3 from "node:process";
|
|
|
34547
34547
|
|
|
34548
34548
|
// node_modules/@modelcontextprotocol/sdk/dist/esm/shared/stdio.js
|
|
34549
34549
|
var ReadBuffer = class {
|
|
34550
|
-
append(
|
|
34551
|
-
this._buffer = this._buffer ? Buffer.concat([this._buffer,
|
|
34550
|
+
append(chunk2) {
|
|
34551
|
+
this._buffer = this._buffer ? Buffer.concat([this._buffer, chunk2]) : chunk2;
|
|
34552
34552
|
}
|
|
34553
34553
|
readMessage() {
|
|
34554
34554
|
if (!this._buffer) {
|
|
@@ -34580,8 +34580,8 @@ var StdioServerTransport = class {
|
|
|
34580
34580
|
this._stdout = _stdout;
|
|
34581
34581
|
this._readBuffer = new ReadBuffer();
|
|
34582
34582
|
this._started = false;
|
|
34583
|
-
this._ondata = (
|
|
34584
|
-
this._readBuffer.append(
|
|
34583
|
+
this._ondata = (chunk2) => {
|
|
34584
|
+
this._readBuffer.append(chunk2);
|
|
34585
34585
|
this.processReadBuffer();
|
|
34586
34586
|
};
|
|
34587
34587
|
this._onerror = (error51) => {
|
|
@@ -35999,6 +35999,19 @@ function classifyFetchError(error51) {
|
|
|
35999
35999
|
return "other";
|
|
36000
36000
|
}
|
|
36001
36001
|
|
|
36002
|
+
// node_modules/@fetchproxy/server/dist/classify-bridge-error.js
|
|
36003
|
+
function classifyBridgeError(err) {
|
|
36004
|
+
if (err instanceof FetchproxyTimeoutError)
|
|
36005
|
+
return "timeout";
|
|
36006
|
+
if (err instanceof FetchproxyBridgeDownError)
|
|
36007
|
+
return "bridge_down";
|
|
36008
|
+
if (err instanceof FetchproxyHttpError)
|
|
36009
|
+
return "http";
|
|
36010
|
+
if (err instanceof FetchproxyProtocolError)
|
|
36011
|
+
return "protocol";
|
|
36012
|
+
return "other";
|
|
36013
|
+
}
|
|
36014
|
+
|
|
36002
36015
|
// node_modules/@fetchproxy/server/dist/ws-server.js
|
|
36003
36016
|
var FetchproxyProtocolError = class extends Error {
|
|
36004
36017
|
constructor(message) {
|
|
@@ -36028,7 +36041,7 @@ var FetchproxyBridgeDownError = class extends FetchproxyProtocolError {
|
|
|
36028
36041
|
const retryAttempted = args.retryAttempted ?? false;
|
|
36029
36042
|
const op = args.op ?? "fetch";
|
|
36030
36043
|
const retryClause = retryAttempted ? `Server already burned a one-shot lazy-revive retry; SW is still down. ` : `Server lazy-revive retry was disabled (bridgeReviveDelayMs unset/0). `;
|
|
36031
|
-
const hint = `the fetchproxy extension's service worker is not responding ("${args.originalError}"). Chrome evicts extension service workers after ~30s idle by default. ${retryClause}
|
|
36044
|
+
const hint = `the fetchproxy extension's service worker is not responding ("${args.originalError}"). Chrome evicts extension service workers after ~30s idle by default. ${retryClause}Make sure a tab for this domain is open, fully loaded, and signed in (the bridge fetches through that tab) \u2014 then retry. If it keeps happening, reload the extension from chrome://extensions and reload the tab.`;
|
|
36032
36045
|
super(`fetchproxy bridge down during ${op}${args.url ? ` (${args.url})` : ""}. ${hint}`);
|
|
36033
36046
|
this.name = "FetchproxyBridgeDownError";
|
|
36034
36047
|
this.originalError = args.originalError;
|
|
@@ -36050,6 +36063,14 @@ var FetchproxyTimeoutError = class extends FetchproxyProtocolError {
|
|
|
36050
36063
|
port;
|
|
36051
36064
|
/** 0.8.0+: actual elapsed milliseconds when the timer won the race. */
|
|
36052
36065
|
elapsedMs;
|
|
36066
|
+
/**
|
|
36067
|
+
* 0.11.0+ (#90/#91): true when the server's lazy-revive retry path
|
|
36068
|
+
* fired for this timeout (a cold-start `timeout` symptom followed by
|
|
36069
|
+
* a warm-and-retry that also timed out). False when the retry was
|
|
36070
|
+
* disabled (`bridgeReviveDelayMs` unset/0) so the timeout surfaced on
|
|
36071
|
+
* the first attempt.
|
|
36072
|
+
*/
|
|
36073
|
+
retryAttempted;
|
|
36053
36074
|
constructor(args) {
|
|
36054
36075
|
super(`fetchproxy: ${args.url} did not respond within ${args.timeoutMs}ms`);
|
|
36055
36076
|
this.name = "FetchproxyTimeoutError";
|
|
@@ -36058,6 +36079,7 @@ var FetchproxyTimeoutError = class extends FetchproxyProtocolError {
|
|
|
36058
36079
|
this.role = args.role ?? null;
|
|
36059
36080
|
this.port = args.port ?? 0;
|
|
36060
36081
|
this.elapsedMs = args.elapsedMs ?? args.timeoutMs;
|
|
36082
|
+
this.retryAttempted = args.retryAttempted ?? false;
|
|
36061
36083
|
}
|
|
36062
36084
|
};
|
|
36063
36085
|
var SUBDOMAIN_LABEL_RE = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;
|
|
@@ -36131,6 +36153,25 @@ var FetchproxyServer = class {
|
|
|
36131
36153
|
// for "we're connecting right now" so two parallel first-calls don't
|
|
36132
36154
|
// race the port bind.
|
|
36133
36155
|
connectingPromise = null;
|
|
36156
|
+
// 0.8.1+ (#67): server-initiated keep-alive ping. Active when
|
|
36157
|
+
// `keepAliveIntervalMs` is set AND we've seen recent activity
|
|
36158
|
+
// (fetch/capture success or failure, or markActive()) within
|
|
36159
|
+
// `keepAliveMaxIdleMs`. The interval handle is created lazily on
|
|
36160
|
+
// first activity and torn down on close() / extension disconnect.
|
|
36161
|
+
keepAliveTimer = null;
|
|
36162
|
+
lastActiveAt = null;
|
|
36163
|
+
// 0.10.0+ (#73): observability counters surfaced via
|
|
36164
|
+
// bridgeHealth().keepAlive / .swEviction. Monotonic across the process
|
|
36165
|
+
// lifetime so a downstream healthcheck tool can verify the keep-alive
|
|
36166
|
+
// is actually preventing SW eviction. `lastPingAt` and `totalPings`
|
|
36167
|
+
// are stamped from `startKeepaliveIfIdle`'s tick. `lazyRevive*` and
|
|
36168
|
+
// `lastEvictionDetectedAt` are stamped from the lazy-revive code path
|
|
36169
|
+
// in fetch() / captureRequestHeader().
|
|
36170
|
+
lastPingAt = null;
|
|
36171
|
+
totalPings = 0;
|
|
36172
|
+
lazyReviveAttempts = 0;
|
|
36173
|
+
lazyReviveSuccesses = 0;
|
|
36174
|
+
lastEvictionDetectedAt = null;
|
|
36134
36175
|
constructor(opts) {
|
|
36135
36176
|
if (!Array.isArray(opts.domains) || opts.domains.length === 0) {
|
|
36136
36177
|
throw new Error("FetchproxyServer: opts.domains must be a non-empty array of hostnames");
|
|
@@ -36183,6 +36224,22 @@ var FetchproxyServer = class {
|
|
|
36183
36224
|
// the legacy hang-forever / fail-once-on-SW-eviction behavior.
|
|
36184
36225
|
fetchTimeoutMs: opts.fetchTimeoutMs ?? 3e4,
|
|
36185
36226
|
bridgeReviveDelayMs: opts.bridgeReviveDelayMs ?? 2e3,
|
|
36227
|
+
// 0.10.0+ (#72): keep-alive defaults to 25s — round-3 #71 cohort
|
|
36228
|
+
// wave showed every Pattern A consumer was opting into this same
|
|
36229
|
+
// value. Pass `0` to disable; the existing `<= 0` guards in
|
|
36230
|
+
// `startKeepaliveIfIdle` / `noteActivityForKeepalive` honour that.
|
|
36231
|
+
//
|
|
36232
|
+
// #90 (P1-1): tightened to 20s. 25s left only ~5s of slack under
|
|
36233
|
+
// Chrome's ~30s SW-eviction window — slack that timer drift, a
|
|
36234
|
+
// busy host event loop (CPU-bound response parsing between calls),
|
|
36235
|
+
// and the ping's own round-trip latency routinely ate, so the SW
|
|
36236
|
+
// evicted before the next ping landed and the next call cold-
|
|
36237
|
+
// started. 20s restores real margin. (The extension
|
|
36238
|
+
// `chrome.alarms` backstop is clamped by Chrome to a 30s minimum
|
|
36239
|
+
// period, firing *at* the edge — it can't rescue a sub-30s race;
|
|
36240
|
+
// the server ping is the real defense.)
|
|
36241
|
+
keepAliveIntervalMs: opts.keepAliveIntervalMs ?? 2e4,
|
|
36242
|
+
keepAliveMaxIdleMs: opts.keepAliveMaxIdleMs ?? 5 * 60 * 1e3,
|
|
36186
36243
|
identityDir: opts.identityDir,
|
|
36187
36244
|
onPairCode: opts.onPairCode
|
|
36188
36245
|
};
|
|
@@ -36283,7 +36340,10 @@ var FetchproxyServer = class {
|
|
|
36283
36340
|
onPairCode: this.opts.onPairCode
|
|
36284
36341
|
});
|
|
36285
36342
|
this.hostHandle.onOwnInner((inner) => this.onInner(inner));
|
|
36286
|
-
this.hostHandle.onExtensionDisconnect(() =>
|
|
36343
|
+
this.hostHandle.onExtensionDisconnect(() => {
|
|
36344
|
+
this.stopKeepalive();
|
|
36345
|
+
this.rejectAllPending();
|
|
36346
|
+
});
|
|
36287
36347
|
this.hostHandle.onPendingPair((code) => {
|
|
36288
36348
|
this.rejectAllPending(this.pairingErrorMessage(code));
|
|
36289
36349
|
});
|
|
@@ -36307,7 +36367,10 @@ var FetchproxyServer = class {
|
|
|
36307
36367
|
sessionStoragePointers: this.opts.sessionStoragePointers
|
|
36308
36368
|
});
|
|
36309
36369
|
this.peerHandle.onInner((inner) => this.onInner(inner));
|
|
36310
|
-
this.peerHandle.onRenegotiate(() =>
|
|
36370
|
+
this.peerHandle.onRenegotiate(() => {
|
|
36371
|
+
this.stopKeepalive();
|
|
36372
|
+
this.rejectAllPending();
|
|
36373
|
+
});
|
|
36311
36374
|
this.peerHandle.onPendingPair((code) => {
|
|
36312
36375
|
this.rejectAllPending(this.pairingErrorMessage(code));
|
|
36313
36376
|
});
|
|
@@ -36348,13 +36411,18 @@ var FetchproxyServer = class {
|
|
|
36348
36411
|
}
|
|
36349
36412
|
const first = await this._fetchOnceWithTimeout(init);
|
|
36350
36413
|
const reviveMs = this.opts.bridgeReviveDelayMs;
|
|
36351
|
-
|
|
36352
|
-
if (
|
|
36414
|
+
const isColdStartSymptom = !first.ok && (first.kind === "content_script_unreachable" || first.kind === "timeout");
|
|
36415
|
+
if (isColdStartSymptom) {
|
|
36416
|
+
this.lastEvictionDetectedAt = Date.now();
|
|
36417
|
+
}
|
|
36418
|
+
if (isColdStartSymptom && reviveMs !== void 0 && reviveMs > 0) {
|
|
36419
|
+
this.lazyReviveAttempts += 1;
|
|
36353
36420
|
await new Promise((r) => setTimeout(r, reviveMs));
|
|
36354
36421
|
const second = await this._fetchOnceWithTimeout(init);
|
|
36355
|
-
if (second.ok)
|
|
36422
|
+
if (second.ok) {
|
|
36423
|
+
this.lazyReviveSuccesses += 1;
|
|
36356
36424
|
this.recordSuccess();
|
|
36357
|
-
else
|
|
36425
|
+
} else
|
|
36358
36426
|
this.recordFailure(`${second.kind ?? "other"}: ${second.error}`);
|
|
36359
36427
|
return { ...second, retryAttempted: true };
|
|
36360
36428
|
}
|
|
@@ -36376,6 +36444,9 @@ var FetchproxyServer = class {
|
|
|
36376
36444
|
* call (addresses #23 ask 4).
|
|
36377
36445
|
*/
|
|
36378
36446
|
bridgeHealth() {
|
|
36447
|
+
const intervalMs = this.opts.keepAliveIntervalMs;
|
|
36448
|
+
const maxIdleMs = this.opts.keepAliveMaxIdleMs;
|
|
36449
|
+
const idleSinceMs = this.lastActiveAt === null ? null : Date.now() - this.lastActiveAt;
|
|
36379
36450
|
return {
|
|
36380
36451
|
role: this.role,
|
|
36381
36452
|
port: this.opts.port,
|
|
@@ -36386,17 +36457,85 @@ var FetchproxyServer = class {
|
|
|
36386
36457
|
lastFailureAt: this.lastFailureAt,
|
|
36387
36458
|
lastFailureReason: this.lastFailureReason,
|
|
36388
36459
|
consecutiveFailures: this.consecutiveFailures,
|
|
36389
|
-
lastExtensionMessageAt: this.lastExtensionMessageAt
|
|
36460
|
+
lastExtensionMessageAt: this.lastExtensionMessageAt,
|
|
36461
|
+
keepAlive: {
|
|
36462
|
+
enabled: intervalMs > 0,
|
|
36463
|
+
intervalMs,
|
|
36464
|
+
maxIdleMs,
|
|
36465
|
+
lastPingAt: this.lastPingAt,
|
|
36466
|
+
totalPings: this.totalPings,
|
|
36467
|
+
idleSinceMs
|
|
36468
|
+
},
|
|
36469
|
+
swEviction: {
|
|
36470
|
+
lazyReviveAttempts: this.lazyReviveAttempts,
|
|
36471
|
+
lazyReviveSuccesses: this.lazyReviveSuccesses,
|
|
36472
|
+
lastEvictionDetectedAt: this.lastEvictionDetectedAt
|
|
36473
|
+
}
|
|
36390
36474
|
};
|
|
36391
36475
|
}
|
|
36392
36476
|
recordSuccess() {
|
|
36393
36477
|
this.lastSuccessAt = Date.now();
|
|
36394
36478
|
this.consecutiveFailures = 0;
|
|
36479
|
+
this.noteActivityForKeepalive();
|
|
36395
36480
|
}
|
|
36396
36481
|
recordFailure(reason) {
|
|
36397
36482
|
this.lastFailureAt = Date.now();
|
|
36398
36483
|
this.lastFailureReason = reason;
|
|
36399
36484
|
this.consecutiveFailures += 1;
|
|
36485
|
+
this.noteActivityForKeepalive();
|
|
36486
|
+
}
|
|
36487
|
+
/**
|
|
36488
|
+
* 0.8.1+ (#67): caller-side hint that work is happening or about to
|
|
36489
|
+
* happen — bumps the keep-alive idle gate so the server keeps pinging
|
|
36490
|
+
* the extension. Useful for MCPs that do a chain of side-effectful
|
|
36491
|
+
* work between bridge calls and don't want the SW to evict in the
|
|
36492
|
+
* gap (e.g. server-side parsing of a previous response that takes
|
|
36493
|
+
* tens of seconds). No-op when `keepAliveIntervalMs` is `0`.
|
|
36494
|
+
*/
|
|
36495
|
+
markActive() {
|
|
36496
|
+
this.noteActivityForKeepalive();
|
|
36497
|
+
}
|
|
36498
|
+
noteActivityForKeepalive() {
|
|
36499
|
+
const intervalMs = this.opts.keepAliveIntervalMs;
|
|
36500
|
+
if (intervalMs <= 0)
|
|
36501
|
+
return;
|
|
36502
|
+
this.lastActiveAt = Date.now();
|
|
36503
|
+
this.startKeepaliveIfIdle();
|
|
36504
|
+
}
|
|
36505
|
+
startKeepaliveIfIdle() {
|
|
36506
|
+
if (this.keepAliveTimer !== null)
|
|
36507
|
+
return;
|
|
36508
|
+
const intervalMs = this.opts.keepAliveIntervalMs;
|
|
36509
|
+
if (intervalMs <= 0)
|
|
36510
|
+
return;
|
|
36511
|
+
this.keepAliveTimer = setInterval(() => {
|
|
36512
|
+
const now = Date.now();
|
|
36513
|
+
if (this.lastActiveAt === null || now - this.lastActiveAt > this.opts.keepAliveMaxIdleMs) {
|
|
36514
|
+
this.stopKeepalive();
|
|
36515
|
+
return;
|
|
36516
|
+
}
|
|
36517
|
+
this.totalPings += 1;
|
|
36518
|
+
this.lastPingAt = now;
|
|
36519
|
+
void this.sendKeepalivePing();
|
|
36520
|
+
}, intervalMs);
|
|
36521
|
+
}
|
|
36522
|
+
async sendKeepalivePing() {
|
|
36523
|
+
try {
|
|
36524
|
+
const inner = { type: "ping" };
|
|
36525
|
+
if (this.hostHandle) {
|
|
36526
|
+
await this.hostHandle.sendOwnInner(inner);
|
|
36527
|
+
} else if (this.peerHandle) {
|
|
36528
|
+
await this.peerHandle.sendInner(inner);
|
|
36529
|
+
}
|
|
36530
|
+
} catch (e) {
|
|
36531
|
+
console.error("[fetchproxy] keepalive ping send failed:", e);
|
|
36532
|
+
}
|
|
36533
|
+
}
|
|
36534
|
+
stopKeepalive() {
|
|
36535
|
+
if (this.keepAliveTimer !== null) {
|
|
36536
|
+
clearInterval(this.keepAliveTimer);
|
|
36537
|
+
this.keepAliveTimer = null;
|
|
36538
|
+
}
|
|
36400
36539
|
}
|
|
36401
36540
|
/**
|
|
36402
36541
|
* Single bridge round-trip, wrapped by `fetchTimeoutMs` when set.
|
|
@@ -36455,7 +36594,8 @@ var FetchproxyServer = class {
|
|
|
36455
36594
|
timeoutMs: this.opts.fetchTimeoutMs ?? 0,
|
|
36456
36595
|
role: this.role,
|
|
36457
36596
|
port: this.opts.port,
|
|
36458
|
-
elapsedMs: result.elapsedMs
|
|
36597
|
+
elapsedMs: result.elapsedMs,
|
|
36598
|
+
retryAttempted
|
|
36459
36599
|
});
|
|
36460
36600
|
}
|
|
36461
36601
|
if (result.kind === "content_script_unreachable") {
|
|
@@ -36586,6 +36726,108 @@ var FetchproxyServer = class {
|
|
|
36586
36726
|
const response = await this.get(path, this.applyJsonDefaults(opts));
|
|
36587
36727
|
return response.body;
|
|
36588
36728
|
}
|
|
36729
|
+
/**
|
|
36730
|
+
* 0.11.0+: method-generic JSON convenience helper. Generalizes the
|
|
36731
|
+
* `fetchJson<T>(path, { method, headers, body })` that
|
|
36732
|
+
* zillow/redfin/compass/homes hand-rolled char-for-char in their
|
|
36733
|
+
* `src/client.ts`:
|
|
36734
|
+
*
|
|
36735
|
+
* - sets `Accept: application/json`;
|
|
36736
|
+
* - adds `Content-Type: application/json` only for a non-GET request
|
|
36737
|
+
* that carries a `body` (and only if the caller didn't set one);
|
|
36738
|
+
* - `JSON.stringify`s the body (GET / no-body sends nothing);
|
|
36739
|
+
* - treats a `204` or an empty body as `data: null` (no parse);
|
|
36740
|
+
* - otherwise `JSON.parse`s the body.
|
|
36741
|
+
*
|
|
36742
|
+
* Scope is serialization + header defaults + 204-handling +
|
|
36743
|
+
* JSON.parse ONLY. It deliberately does NOT assert on the HTTP status
|
|
36744
|
+
* or look for a sign-in interstitial — those guards differ per site
|
|
36745
|
+
* (Zillow's `captcha-delivery`, Redfin's AWS-WAF challenge, …), so it
|
|
36746
|
+
* returns BOTH the parsed `data` and the raw `FetchResult` and leaves
|
|
36747
|
+
* the consumer to run its own `throwIfNotOk` / `throwIfSignInPage`
|
|
36748
|
+
* over `result`.
|
|
36749
|
+
*
|
|
36750
|
+
* Bridge-level failures (no signed-in tab, SW down, timeout) still
|
|
36751
|
+
* throw the typed errors via `request()`, exactly like the verb
|
|
36752
|
+
* helpers — only successful round-trips (any HTTP status) return.
|
|
36753
|
+
*/
|
|
36754
|
+
async requestJson(method, path, opts = {}) {
|
|
36755
|
+
const isGet = method.toUpperCase() === "GET";
|
|
36756
|
+
const sendBody = !isGet && opts.body !== void 0;
|
|
36757
|
+
const headers = {
|
|
36758
|
+
Accept: "application/json",
|
|
36759
|
+
...sendBody && !this.hasContentType(opts.headers ?? {}) ? { "Content-Type": "application/json" } : {},
|
|
36760
|
+
...opts.headers ?? {}
|
|
36761
|
+
};
|
|
36762
|
+
const response = await this.request(method, path, {
|
|
36763
|
+
headers,
|
|
36764
|
+
body: sendBody ? JSON.stringify(opts.body) : void 0,
|
|
36765
|
+
...opts.subdomain !== void 0 ? { subdomain: opts.subdomain } : {},
|
|
36766
|
+
...opts.domain !== void 0 ? { domain: opts.domain } : {}
|
|
36767
|
+
});
|
|
36768
|
+
const result = {
|
|
36769
|
+
ok: true,
|
|
36770
|
+
status: response.status,
|
|
36771
|
+
url: response.url,
|
|
36772
|
+
body: response.body
|
|
36773
|
+
};
|
|
36774
|
+
if (response.status === 204 || response.body === "") {
|
|
36775
|
+
return { data: null, result };
|
|
36776
|
+
}
|
|
36777
|
+
let data;
|
|
36778
|
+
try {
|
|
36779
|
+
data = JSON.parse(response.body);
|
|
36780
|
+
} catch (e) {
|
|
36781
|
+
throw new Error(`fetchproxy ${method} ${path} \u2014 response was not JSON: ${e instanceof Error ? e.message : String(e)}`);
|
|
36782
|
+
}
|
|
36783
|
+
return { data, result };
|
|
36784
|
+
}
|
|
36785
|
+
/**
|
|
36786
|
+
* 0.11.0+: run a single healthcheck probe through `fetchFn`, measure
|
|
36787
|
+
* the elapsed round-trip, classify any thrown error, and project the
|
|
36788
|
+
* post-probe `bridgeHealth()` into a snake-cased `bridge` sub-object.
|
|
36789
|
+
*
|
|
36790
|
+
* This is the transport half of the probe loop zillow/redfin/homes
|
|
36791
|
+
* had duplicated verbatim in `src/tools/healthcheck.ts`. The MCP
|
|
36792
|
+
* supplies its own probe call (`(path) => client.fetchHtml(path)`)
|
|
36793
|
+
* and probe path (e.g. `'/robots.txt'`); the tool registration and
|
|
36794
|
+
* the site-specific plain-English hint text STAY in the consumer.
|
|
36795
|
+
*
|
|
36796
|
+
* `bridgeHealth()` is read AFTER the probe so its freshness counters
|
|
36797
|
+
* (`lastSuccessAt` / `consecutiveFailures` / …) reflect this very
|
|
36798
|
+
* round-trip rather than a stale pre-probe snapshot.
|
|
36799
|
+
*/
|
|
36800
|
+
async runProbe(fetchFn, probePath) {
|
|
36801
|
+
const start = Date.now();
|
|
36802
|
+
let ok = false;
|
|
36803
|
+
let error51;
|
|
36804
|
+
try {
|
|
36805
|
+
await fetchFn(probePath);
|
|
36806
|
+
ok = true;
|
|
36807
|
+
} catch (e) {
|
|
36808
|
+
error51 = {
|
|
36809
|
+
kind: classifyBridgeError(e),
|
|
36810
|
+
message: e instanceof Error ? e.message : String(e)
|
|
36811
|
+
};
|
|
36812
|
+
}
|
|
36813
|
+
const elapsed_ms = Date.now() - start;
|
|
36814
|
+
const health = this.bridgeHealth();
|
|
36815
|
+
return {
|
|
36816
|
+
ok,
|
|
36817
|
+
elapsed_ms,
|
|
36818
|
+
bridge: {
|
|
36819
|
+
role: health.role,
|
|
36820
|
+
port: health.port,
|
|
36821
|
+
server_version: health.serverVersion,
|
|
36822
|
+
fetch_timeout_ms: health.fetchTimeoutMs,
|
|
36823
|
+
last_success_at: health.lastSuccessAt,
|
|
36824
|
+
last_failure_at: health.lastFailureAt,
|
|
36825
|
+
last_failure_reason: health.lastFailureReason,
|
|
36826
|
+
consecutive_failures: health.consecutiveFailures
|
|
36827
|
+
},
|
|
36828
|
+
...error51 ? { error: error51 } : {}
|
|
36829
|
+
};
|
|
36830
|
+
}
|
|
36589
36831
|
/**
|
|
36590
36832
|
* Snapshot the user's non-HttpOnly cookies for the chosen domain.
|
|
36591
36833
|
*
|
|
@@ -36754,11 +36996,14 @@ var FetchproxyServer = class {
|
|
|
36754
36996
|
this.recordFailure(`capture_request_header: ${err.message ?? String(err)}`);
|
|
36755
36997
|
throw err;
|
|
36756
36998
|
}
|
|
36999
|
+
this.lastEvictionDetectedAt = Date.now();
|
|
36757
37000
|
const reviveMs = this.opts.bridgeReviveDelayMs ?? 0;
|
|
36758
37001
|
if (reviveMs > 0) {
|
|
37002
|
+
this.lazyReviveAttempts += 1;
|
|
36759
37003
|
await new Promise((r) => setTimeout(r, reviveMs));
|
|
36760
37004
|
try {
|
|
36761
37005
|
const result = await this._captureRequestHeaderOnce(callOpts);
|
|
37006
|
+
this.lazyReviveSuccesses += 1;
|
|
36762
37007
|
this.recordSuccess();
|
|
36763
37008
|
return result;
|
|
36764
37009
|
} catch (retryErr) {
|
|
@@ -37051,6 +37296,7 @@ var FetchproxyServer = class {
|
|
|
37051
37296
|
* twice in a row.
|
|
37052
37297
|
*/
|
|
37053
37298
|
async close() {
|
|
37299
|
+
this.stopKeepalive();
|
|
37054
37300
|
this.rejectAllPending();
|
|
37055
37301
|
if (this.connectingPromise) {
|
|
37056
37302
|
await this.connectingPromise.catch(() => void 0);
|
|
@@ -37066,19 +37312,6 @@ var FetchproxyServer = class {
|
|
|
37066
37312
|
}
|
|
37067
37313
|
};
|
|
37068
37314
|
|
|
37069
|
-
// node_modules/@fetchproxy/server/dist/classify-bridge-error.js
|
|
37070
|
-
function classifyBridgeError(err) {
|
|
37071
|
-
if (err instanceof FetchproxyTimeoutError)
|
|
37072
|
-
return "timeout";
|
|
37073
|
-
if (err instanceof FetchproxyBridgeDownError)
|
|
37074
|
-
return "bridge_down";
|
|
37075
|
-
if (err instanceof FetchproxyHttpError)
|
|
37076
|
-
return "http";
|
|
37077
|
-
if (err instanceof FetchproxyProtocolError)
|
|
37078
|
-
return "protocol";
|
|
37079
|
-
return "other";
|
|
37080
|
-
}
|
|
37081
|
-
|
|
37082
37315
|
// node_modules/@fetchproxy/bootstrap/dist/index.js
|
|
37083
37316
|
var defaultFactory = (opts) => new FetchproxyServer(opts);
|
|
37084
37317
|
async function bootstrap(opts) {
|
|
@@ -37322,7 +37555,7 @@ function getDefaultInlineAttachments() {
|
|
|
37322
37555
|
// package.json
|
|
37323
37556
|
var package_default = {
|
|
37324
37557
|
name: "ofw-mcp",
|
|
37325
|
-
version: "2.
|
|
37558
|
+
version: "2.3.0",
|
|
37326
37559
|
mcpName: "io.github.chrischall/ofw-mcp",
|
|
37327
37560
|
description: "OurFamilyWizard MCP server for Claude \u2014 developed and maintained by AI (Claude Code)",
|
|
37328
37561
|
author: "Claude Code (AI) <https://www.anthropic.com/claude>",
|
|
@@ -37349,8 +37582,8 @@ var package_default = {
|
|
|
37349
37582
|
"test:watch": "vitest"
|
|
37350
37583
|
},
|
|
37351
37584
|
dependencies: {
|
|
37352
|
-
"@fetchproxy/bootstrap": "^0.
|
|
37353
|
-
"@fetchproxy/server": "^0.
|
|
37585
|
+
"@fetchproxy/bootstrap": "^0.11.0",
|
|
37586
|
+
"@fetchproxy/server": "^0.11.0",
|
|
37354
37587
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
37355
37588
|
dotenv: "^17.4.2",
|
|
37356
37589
|
zod: "^4.4.3"
|
|
@@ -38784,7 +39017,7 @@ process.emit = function(event, ...args) {
|
|
|
38784
39017
|
}
|
|
38785
39018
|
return originalEmit(event, ...args);
|
|
38786
39019
|
};
|
|
38787
|
-
var server = new McpServer({ name: "ofw", version: "2.
|
|
39020
|
+
var server = new McpServer({ name: "ofw", version: "2.3.0" });
|
|
38788
39021
|
registerUserTools(server, client);
|
|
38789
39022
|
registerMessageTools(server, client);
|
|
38790
39023
|
registerCalendarTools(server, client);
|
package/dist/index.js
CHANGED
|
@@ -17,7 +17,7 @@ import { registerMessageTools } from './tools/messages.js';
|
|
|
17
17
|
import { registerCalendarTools } from './tools/calendar.js';
|
|
18
18
|
import { registerExpenseTools } from './tools/expenses.js';
|
|
19
19
|
import { registerJournalTools } from './tools/journal.js';
|
|
20
|
-
const server = new McpServer({ name: 'ofw', version: '2.
|
|
20
|
+
const server = new McpServer({ name: 'ofw', version: '2.3.0' }); // x-release-please-version
|
|
21
21
|
registerUserTools(server, client);
|
|
22
22
|
registerMessageTools(server, client);
|
|
23
23
|
registerCalendarTools(server, client);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ofw-mcp",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"mcpName": "io.github.chrischall/ofw-mcp",
|
|
5
5
|
"description": "OurFamilyWizard MCP server for Claude — developed and maintained by AI (Claude Code)",
|
|
6
6
|
"author": "Claude Code (AI) <https://www.anthropic.com/claude>",
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
"test:watch": "vitest"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@fetchproxy/bootstrap": "^0.
|
|
31
|
-
"@fetchproxy/server": "^0.
|
|
30
|
+
"@fetchproxy/bootstrap": "^0.11.0",
|
|
31
|
+
"@fetchproxy/server": "^0.11.0",
|
|
32
32
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
33
33
|
"dotenv": "^17.4.2",
|
|
34
34
|
"zod": "^4.4.3"
|
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.
|
|
9
|
+
"version": "2.3.0",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "ofw-mcp",
|
|
14
|
-
"version": "2.
|
|
14
|
+
"version": "2.3.0",
|
|
15
15
|
"transport": {
|
|
16
16
|
"type": "stdio"
|
|
17
17
|
},
|