creditkarma-mcp 2.0.10 → 2.1.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/README.md +22 -0
- package/dist/bundle.js +374 -83
- package/dist/index.js +2 -1
- package/package.json +9 -9
- package/server.json +2 -2
package/README.md
CHANGED
|
@@ -23,6 +23,28 @@ Ask Claude things like:
|
|
|
23
23
|
- A Credit Karma account
|
|
24
24
|
- For the no-env-var path: the [fetchproxy 0.3.0 Chrome / Safari extension](https://github.com/chrischall/fetchproxy)
|
|
25
25
|
|
|
26
|
+
## Acknowledgement of Terms
|
|
27
|
+
|
|
28
|
+
By using this MCP server, you acknowledge and agree to the following:
|
|
29
|
+
|
|
30
|
+
**1. This server accesses your own Credit Karma account.** Every request is dispatched through your own signed-in browser tab via the fetchproxy extension. **You** are the one logged in. It does not — and cannot — access anyone else's account.
|
|
31
|
+
|
|
32
|
+
**2. [Credit Karma's Terms](https://www.creditkarma.com/about/terms) govern your use of this server**, just as they govern your direct use of creditkarma.com. The clauses most relevant here:
|
|
33
|
+
|
|
34
|
+
> You must not sell, transfer, or assign your account to anyone else… you may not allow anyone else to log into our Services as you.
|
|
35
|
+
|
|
36
|
+
CK does contemplate third-party data retrieval at the user's direction (Section 3.7). There is no explicit anti-scraping clause in the membership agreement; Section 4.1 restricts copying or distributing CK content without express prior written consent.
|
|
37
|
+
|
|
38
|
+
You are agreeing to those terms — read by the maintainer 2026-05-23 — every time you invoke a tool in this server. Critically: this server runs **as you**, not as a third party logging in on your behalf. You direct the tool.
|
|
39
|
+
|
|
40
|
+
**3. Personal, non-commercial use only.** This project is not affiliated with, endorsed by, sponsored by, or in partnership with Intuit, Credit Karma, or any financial institution. It is a personal automation tool that reads your transaction history, spending categories, and account snapshots — the same data Credit Karma already shows you in their app. Do not use it on someone else's account, do not redistribute their content, and do not use it to make trading or lending decisions on behalf of others.
|
|
41
|
+
|
|
42
|
+
**4. This server may break.** Credit Karma rotates its internal endpoints; what works today may 404 tomorrow. This is the nature of unofficial integrations.
|
|
43
|
+
|
|
44
|
+
**5. You accept full responsibility** for any consequences of using this server in connection with your Credit Karma account — rate limiting, account warnings, suspension, or any enforcement action Intuit takes. If Credit Karma objects to your use, stop using this server. **Do not commit your `.env` to git** — your CK session/auth artifacts are credentials, and the Membership Agreement holds you responsible for their confidentiality.
|
|
45
|
+
|
|
46
|
+
This section is the maintainer's good-faith summary of the terms — it is not legal advice and does not modify or supersede Credit Karma's actual Membership Agreement.
|
|
47
|
+
|
|
26
48
|
## Installation
|
|
27
49
|
|
|
28
50
|
### 1. Clone and build
|
package/dist/bundle.js
CHANGED
|
@@ -7658,6 +7658,10 @@ var require_receiver = __commonJS({
|
|
|
7658
7658
|
* extensions
|
|
7659
7659
|
* @param {Boolean} [options.isServer=false] Specifies whether to operate in
|
|
7660
7660
|
* client or server mode
|
|
7661
|
+
* @param {Number} [options.maxBufferedChunks=0] The maximum number of
|
|
7662
|
+
* buffered data chunks
|
|
7663
|
+
* @param {Number} [options.maxFragments=0] The maximum number of message
|
|
7664
|
+
* fragments
|
|
7661
7665
|
* @param {Number} [options.maxPayload=0] The maximum allowed message length
|
|
7662
7666
|
* @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
|
|
7663
7667
|
* not to skip UTF-8 validation for text and close messages
|
|
@@ -7668,6 +7672,8 @@ var require_receiver = __commonJS({
|
|
|
7668
7672
|
this._binaryType = options.binaryType || BINARY_TYPES[0];
|
|
7669
7673
|
this._extensions = options.extensions || {};
|
|
7670
7674
|
this._isServer = !!options.isServer;
|
|
7675
|
+
this._maxBufferedChunks = options.maxBufferedChunks | 0;
|
|
7676
|
+
this._maxFragments = options.maxFragments | 0;
|
|
7671
7677
|
this._maxPayload = options.maxPayload | 0;
|
|
7672
7678
|
this._skipUTF8Validation = !!options.skipUTF8Validation;
|
|
7673
7679
|
this[kWebSocket] = void 0;
|
|
@@ -7697,6 +7703,18 @@ var require_receiver = __commonJS({
|
|
|
7697
7703
|
*/
|
|
7698
7704
|
_write(chunk, encoding, cb) {
|
|
7699
7705
|
if (this._opcode === 8 && this._state == GET_INFO) return cb();
|
|
7706
|
+
if (this._maxBufferedChunks > 0 && this._buffers.length >= this._maxBufferedChunks) {
|
|
7707
|
+
cb(
|
|
7708
|
+
this.createError(
|
|
7709
|
+
RangeError,
|
|
7710
|
+
"Too many buffered chunks",
|
|
7711
|
+
false,
|
|
7712
|
+
1008,
|
|
7713
|
+
"WS_ERR_TOO_MANY_BUFFERED_PARTS"
|
|
7714
|
+
)
|
|
7715
|
+
);
|
|
7716
|
+
return;
|
|
7717
|
+
}
|
|
7700
7718
|
this._bufferedBytes += chunk.length;
|
|
7701
7719
|
this._buffers.push(chunk);
|
|
7702
7720
|
this.startLoop(cb);
|
|
@@ -8026,6 +8044,17 @@ var require_receiver = __commonJS({
|
|
|
8026
8044
|
return;
|
|
8027
8045
|
}
|
|
8028
8046
|
if (data.length) {
|
|
8047
|
+
if (this._maxFragments > 0 && this._fragments.length >= this._maxFragments) {
|
|
8048
|
+
const error51 = this.createError(
|
|
8049
|
+
RangeError,
|
|
8050
|
+
"Too many message fragments",
|
|
8051
|
+
false,
|
|
8052
|
+
1008,
|
|
8053
|
+
"WS_ERR_TOO_MANY_BUFFERED_PARTS"
|
|
8054
|
+
);
|
|
8055
|
+
cb(error51);
|
|
8056
|
+
return;
|
|
8057
|
+
}
|
|
8029
8058
|
this._messageLength = this._totalPayloadLength;
|
|
8030
8059
|
this._fragments.push(data);
|
|
8031
8060
|
}
|
|
@@ -8055,6 +8084,17 @@ var require_receiver = __commonJS({
|
|
|
8055
8084
|
cb(error51);
|
|
8056
8085
|
return;
|
|
8057
8086
|
}
|
|
8087
|
+
if (this._maxFragments > 0 && this._fragments.length >= this._maxFragments) {
|
|
8088
|
+
const error51 = this.createError(
|
|
8089
|
+
RangeError,
|
|
8090
|
+
"Too many message fragments",
|
|
8091
|
+
false,
|
|
8092
|
+
1008,
|
|
8093
|
+
"WS_ERR_TOO_MANY_BUFFERED_PARTS"
|
|
8094
|
+
);
|
|
8095
|
+
cb(error51);
|
|
8096
|
+
return;
|
|
8097
|
+
}
|
|
8058
8098
|
this._fragments.push(buf);
|
|
8059
8099
|
}
|
|
8060
8100
|
this.dataMessage(cb);
|
|
@@ -9261,6 +9301,10 @@ var require_websocket = __commonJS({
|
|
|
9261
9301
|
* multiple times in the same tick
|
|
9262
9302
|
* @param {Function} [options.generateMask] The function used to generate the
|
|
9263
9303
|
* masking key
|
|
9304
|
+
* @param {Number} [options.maxBufferedChunks=0] The maximum number of
|
|
9305
|
+
* buffered data chunks
|
|
9306
|
+
* @param {Number} [options.maxFragments=0] The maximum number of message
|
|
9307
|
+
* fragments
|
|
9264
9308
|
* @param {Number} [options.maxPayload=0] The maximum allowed message size
|
|
9265
9309
|
* @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
|
|
9266
9310
|
* not to skip UTF-8 validation for text and close messages
|
|
@@ -9272,6 +9316,8 @@ var require_websocket = __commonJS({
|
|
|
9272
9316
|
binaryType: this.binaryType,
|
|
9273
9317
|
extensions: this._extensions,
|
|
9274
9318
|
isServer: this._isServer,
|
|
9319
|
+
maxBufferedChunks: options.maxBufferedChunks,
|
|
9320
|
+
maxFragments: options.maxFragments,
|
|
9275
9321
|
maxPayload: options.maxPayload,
|
|
9276
9322
|
skipUTF8Validation: options.skipUTF8Validation
|
|
9277
9323
|
});
|
|
@@ -9571,6 +9617,8 @@ var require_websocket = __commonJS({
|
|
|
9571
9617
|
autoPong: true,
|
|
9572
9618
|
closeTimeout: CLOSE_TIMEOUT,
|
|
9573
9619
|
protocolVersion: protocolVersions[1],
|
|
9620
|
+
maxBufferedChunks: 1024 * 1024,
|
|
9621
|
+
maxFragments: 128 * 1024,
|
|
9574
9622
|
maxPayload: 100 * 1024 * 1024,
|
|
9575
9623
|
skipUTF8Validation: false,
|
|
9576
9624
|
perMessageDeflate: true,
|
|
@@ -9813,6 +9861,8 @@ var require_websocket = __commonJS({
|
|
|
9813
9861
|
websocket.setSocket(socket, head, {
|
|
9814
9862
|
allowSynchronousEvents: opts.allowSynchronousEvents,
|
|
9815
9863
|
generateMask: opts.generateMask,
|
|
9864
|
+
maxBufferedChunks: opts.maxBufferedChunks,
|
|
9865
|
+
maxFragments: opts.maxFragments,
|
|
9816
9866
|
maxPayload: opts.maxPayload,
|
|
9817
9867
|
skipUTF8Validation: opts.skipUTF8Validation
|
|
9818
9868
|
});
|
|
@@ -10155,6 +10205,10 @@ var require_websocket_server = __commonJS({
|
|
|
10155
10205
|
* called
|
|
10156
10206
|
* @param {Function} [options.handleProtocols] A hook to handle protocols
|
|
10157
10207
|
* @param {String} [options.host] The hostname where to bind the server
|
|
10208
|
+
* @param {Number} [options.maxBufferedChunks=1048576] The maximum number of
|
|
10209
|
+
* buffered data chunks
|
|
10210
|
+
* @param {Number} [options.maxFragments=131072] The maximum number of message
|
|
10211
|
+
* fragments
|
|
10158
10212
|
* @param {Number} [options.maxPayload=104857600] The maximum allowed message
|
|
10159
10213
|
* size
|
|
10160
10214
|
* @param {Boolean} [options.noServer=false] Enable no server mode
|
|
@@ -10176,6 +10230,8 @@ var require_websocket_server = __commonJS({
|
|
|
10176
10230
|
options = {
|
|
10177
10231
|
allowSynchronousEvents: true,
|
|
10178
10232
|
autoPong: true,
|
|
10233
|
+
maxBufferedChunks: 1024 * 1024,
|
|
10234
|
+
maxFragments: 128 * 1024,
|
|
10179
10235
|
maxPayload: 100 * 1024 * 1024,
|
|
10180
10236
|
skipUTF8Validation: false,
|
|
10181
10237
|
perMessageDeflate: false,
|
|
@@ -10455,6 +10511,8 @@ var require_websocket_server = __commonJS({
|
|
|
10455
10511
|
socket.removeListener("error", socketOnError);
|
|
10456
10512
|
ws.setSocket(socket, head, {
|
|
10457
10513
|
allowSynchronousEvents: this.options.allowSynchronousEvents,
|
|
10514
|
+
maxBufferedChunks: this.options.maxBufferedChunks,
|
|
10515
|
+
maxFragments: this.options.maxFragments,
|
|
10458
10516
|
maxPayload: this.options.maxPayload,
|
|
10459
10517
|
skipUTF8Validation: this.options.skipUTF8Validation
|
|
10460
10518
|
});
|
|
@@ -34974,6 +35032,7 @@ function registerAuthTools(server, ctx) {
|
|
|
34974
35032
|
|
|
34975
35033
|
// node_modules/@fetchproxy/protocol/dist/frames.js
|
|
34976
35034
|
var PROTOCOL_VERSION = 2;
|
|
35035
|
+
var HKDF_SESSION_INFO = "fetchproxy/1.0.0/session";
|
|
34977
35036
|
var KNOWN_CAPABILITIES = /* @__PURE__ */ new Set([
|
|
34978
35037
|
"fetch",
|
|
34979
35038
|
"read_cookies",
|
|
@@ -35048,7 +35107,7 @@ var ProtocolError = class extends Error {
|
|
|
35048
35107
|
var FORBIDDEN_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
35049
35108
|
var BASE64_RE = /^[A-Za-z0-9+/]*={0,2}$/;
|
|
35050
35109
|
var SCOPE_KEY_RE = /^[A-Za-z0-9_.\-]{1,256}$/;
|
|
35051
|
-
var SCOPE_KEY_GLOB_RE = /^[A-Za-z0-9_.\-]{1,
|
|
35110
|
+
var SCOPE_KEY_GLOB_RE = /^[A-Za-z0-9_.\-]{1,256}\*?$/;
|
|
35052
35111
|
var HEADER_NAME_RE = /^[A-Za-z0-9_\-]{1,128}$/;
|
|
35053
35112
|
var HOSTNAME_RE = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)+$/i;
|
|
35054
35113
|
function assertObject(x, label) {
|
|
@@ -35177,7 +35236,7 @@ function assertStoragePointersArray(value, label, declaredKeys) {
|
|
|
35177
35236
|
if (entry.jsonPointer === void 0) {
|
|
35178
35237
|
throw new ProtocolError(`${label}[${i}].jsonPointer: missing`);
|
|
35179
35238
|
}
|
|
35180
|
-
if (typeof entry.key !== "string" || !
|
|
35239
|
+
if (typeof entry.key !== "string" || !SCOPE_KEY_RE.test(entry.key)) {
|
|
35181
35240
|
throw new ProtocolError(`${label}[${i}].key: invalid key ${JSON.stringify(entry.key)}`);
|
|
35182
35241
|
}
|
|
35183
35242
|
if (typeof entry.jsonPointer !== "string" || !isValidJsonPointer(entry.jsonPointer)) {
|
|
@@ -35276,6 +35335,8 @@ function validateFrame(raw) {
|
|
|
35276
35335
|
return validateReady(raw);
|
|
35277
35336
|
if (t === "frame")
|
|
35278
35337
|
return validateEncrypted(raw);
|
|
35338
|
+
if (t === "pair-pending")
|
|
35339
|
+
return validatePairPending(raw);
|
|
35279
35340
|
throw new ProtocolError(`unknown frame type: ${String(t)}`);
|
|
35280
35341
|
}
|
|
35281
35342
|
function validateHello(raw) {
|
|
@@ -35377,6 +35438,17 @@ function validateEncrypted(raw) {
|
|
|
35377
35438
|
assertBase64(raw.ciphertext, "frame.ciphertext");
|
|
35378
35439
|
return raw;
|
|
35379
35440
|
}
|
|
35441
|
+
var PAIR_CODE_RE = /^\d{3}-\d{3}$/;
|
|
35442
|
+
function validatePairPending(raw) {
|
|
35443
|
+
assertString(raw.mcpId, "pair-pending.mcpId");
|
|
35444
|
+
if (!isValidMcpId(raw.mcpId))
|
|
35445
|
+
throw new ProtocolError("pair-pending.mcpId: invalid format");
|
|
35446
|
+
assertString(raw.pairCode, "pair-pending.pairCode");
|
|
35447
|
+
if (!PAIR_CODE_RE.test(raw.pairCode)) {
|
|
35448
|
+
throw new ProtocolError(`pair-pending.pairCode: must match XXX-XXX, got ${String(raw.pairCode)}`);
|
|
35449
|
+
}
|
|
35450
|
+
return { type: "pair-pending", mcpId: raw.mcpId, pairCode: raw.pairCode };
|
|
35451
|
+
}
|
|
35380
35452
|
function validateInnerFrame(raw) {
|
|
35381
35453
|
assertObject(raw, "inner");
|
|
35382
35454
|
const t = raw.type;
|
|
@@ -35959,15 +36031,22 @@ async function startHost(opts) {
|
|
|
35959
36031
|
let extensionWs = null;
|
|
35960
36032
|
const peers = /* @__PURE__ */ new Map();
|
|
35961
36033
|
const ownInnerListeners = [];
|
|
36034
|
+
const disconnectListeners = [];
|
|
36035
|
+
const pendingPairListeners = [];
|
|
35962
36036
|
let ownSession = null;
|
|
36037
|
+
let ownPendingPairCode = null;
|
|
35963
36038
|
let resolveOwnSession;
|
|
35964
36039
|
let rejectOwnSession;
|
|
35965
|
-
|
|
35966
|
-
|
|
35967
|
-
|
|
35968
|
-
|
|
35969
|
-
|
|
35970
|
-
|
|
36040
|
+
let ownSessionReady;
|
|
36041
|
+
function resetSessionPromise() {
|
|
36042
|
+
ownSessionReady = new Promise((resolve, reject) => {
|
|
36043
|
+
resolveOwnSession = resolve;
|
|
36044
|
+
rejectOwnSession = reject;
|
|
36045
|
+
});
|
|
36046
|
+
ownSessionReady.catch(() => {
|
|
36047
|
+
});
|
|
36048
|
+
}
|
|
36049
|
+
resetSessionPromise();
|
|
35971
36050
|
let extensionHello = null;
|
|
35972
36051
|
wss.on("connection", (ws) => {
|
|
35973
36052
|
let identified = null;
|
|
@@ -36036,11 +36115,12 @@ async function startHost(opts) {
|
|
|
36036
36115
|
}
|
|
36037
36116
|
const extPub = fromB64(frame.extensionSessionPub);
|
|
36038
36117
|
const shared = await ecdhX25519(opts.ownIdentity.x25519Priv, extPub);
|
|
36039
|
-
const key = await hkdfSha256(shared, ownSessionNonce, enc2.encode(
|
|
36040
|
-
if (
|
|
36041
|
-
|
|
36042
|
-
|
|
36043
|
-
|
|
36118
|
+
const key = await hkdfSha256(shared, ownSessionNonce, enc2.encode(HKDF_SESSION_INFO), 32);
|
|
36119
|
+
if (extensionWs !== ws)
|
|
36120
|
+
return;
|
|
36121
|
+
ownSession = new SessionState(key);
|
|
36122
|
+
ownPendingPairCode = null;
|
|
36123
|
+
resolveOwnSession(ownSession);
|
|
36044
36124
|
} else {
|
|
36045
36125
|
const slot = peers.get(frame.mcpId);
|
|
36046
36126
|
if (slot)
|
|
@@ -36067,6 +36147,16 @@ async function startHost(opts) {
|
|
|
36067
36147
|
extensionWs.send(JSON.stringify(frame));
|
|
36068
36148
|
}
|
|
36069
36149
|
}
|
|
36150
|
+
if (frame.type === "pair-pending" && identified === "extension") {
|
|
36151
|
+
if (frame.mcpId === opts.ownMcpId) {
|
|
36152
|
+
ownPendingPairCode = frame.pairCode;
|
|
36153
|
+
pendingPairListeners.forEach((cb) => cb(frame.pairCode));
|
|
36154
|
+
} else {
|
|
36155
|
+
const slot = peers.get(frame.mcpId);
|
|
36156
|
+
if (slot)
|
|
36157
|
+
slot.ws.send(JSON.stringify(frame));
|
|
36158
|
+
}
|
|
36159
|
+
}
|
|
36070
36160
|
} catch (e) {
|
|
36071
36161
|
console.error("[fetchproxy] host: message handler error:", e);
|
|
36072
36162
|
try {
|
|
@@ -36082,6 +36172,9 @@ async function startHost(opts) {
|
|
|
36082
36172
|
if (!ownSession) {
|
|
36083
36173
|
rejectOwnSession(new Error("extension disconnected before ready"));
|
|
36084
36174
|
}
|
|
36175
|
+
ownSession = null;
|
|
36176
|
+
resetSessionPromise();
|
|
36177
|
+
disconnectListeners.forEach((cb) => cb());
|
|
36085
36178
|
}
|
|
36086
36179
|
if (identified === "peer" && peerMcpId)
|
|
36087
36180
|
peers.delete(peerMcpId);
|
|
@@ -36106,7 +36199,14 @@ async function startHost(opts) {
|
|
|
36106
36199
|
},
|
|
36107
36200
|
onOwnInner: (cb) => {
|
|
36108
36201
|
ownInnerListeners.push(cb);
|
|
36109
|
-
}
|
|
36202
|
+
},
|
|
36203
|
+
onExtensionDisconnect: (cb) => {
|
|
36204
|
+
disconnectListeners.push(cb);
|
|
36205
|
+
},
|
|
36206
|
+
onPendingPair: (cb) => {
|
|
36207
|
+
pendingPairListeners.push(cb);
|
|
36208
|
+
},
|
|
36209
|
+
pendingPairCode: () => ownPendingPairCode
|
|
36110
36210
|
};
|
|
36111
36211
|
}
|
|
36112
36212
|
|
|
@@ -36136,36 +36236,57 @@ async function startPeer(opts) {
|
|
|
36136
36236
|
const sessionNonce = fromB64(hello.sessionNonce);
|
|
36137
36237
|
ws.send(JSON.stringify(hello));
|
|
36138
36238
|
const innerListeners = [];
|
|
36239
|
+
const renegotiateListeners = [];
|
|
36240
|
+
const pendingPairListeners = [];
|
|
36139
36241
|
let session = null;
|
|
36242
|
+
let pendingPairCode = null;
|
|
36243
|
+
let resolveFirstReady;
|
|
36244
|
+
let rejectFirstReady;
|
|
36140
36245
|
const sessionPromise = new Promise((resolve, reject) => {
|
|
36141
|
-
|
|
36142
|
-
|
|
36143
|
-
|
|
36144
|
-
|
|
36145
|
-
|
|
36146
|
-
|
|
36147
|
-
|
|
36148
|
-
|
|
36149
|
-
|
|
36150
|
-
|
|
36151
|
-
|
|
36246
|
+
resolveFirstReady = resolve;
|
|
36247
|
+
rejectFirstReady = reject;
|
|
36248
|
+
});
|
|
36249
|
+
const onMessage = async (data) => {
|
|
36250
|
+
try {
|
|
36251
|
+
const raw = JSON.parse(data.toString());
|
|
36252
|
+
const frame = validateFrame(raw);
|
|
36253
|
+
if (frame.type === "ready" && frame.mcpId === opts.mcpId) {
|
|
36254
|
+
const extPub = fromB64(frame.extensionSessionPub);
|
|
36255
|
+
const shared = await ecdhX25519(opts.identity.x25519Priv, extPub);
|
|
36256
|
+
const sessionKey = await hkdfSha256(shared, sessionNonce, enc3.encode(HKDF_SESSION_INFO), 32);
|
|
36257
|
+
const isRenegotiation = session !== null;
|
|
36258
|
+
session = new SessionState(sessionKey);
|
|
36259
|
+
pendingPairCode = null;
|
|
36260
|
+
if (isRenegotiation) {
|
|
36261
|
+
renegotiateListeners.forEach((cb) => cb());
|
|
36262
|
+
} else {
|
|
36263
|
+
resolveFirstReady(session);
|
|
36152
36264
|
}
|
|
36153
|
-
|
|
36154
|
-
|
|
36155
|
-
|
|
36156
|
-
|
|
36157
|
-
|
|
36265
|
+
return;
|
|
36266
|
+
}
|
|
36267
|
+
if (frame.type === "pair-pending" && frame.mcpId === opts.mcpId) {
|
|
36268
|
+
pendingPairCode = frame.pairCode;
|
|
36269
|
+
pendingPairListeners.forEach((cb) => cb(frame.pairCode));
|
|
36270
|
+
return;
|
|
36271
|
+
}
|
|
36272
|
+
if (frame.type === "frame" && frame.mcpId === opts.mcpId) {
|
|
36273
|
+
if (!session)
|
|
36274
|
+
return;
|
|
36275
|
+
if (!session.acceptInboundSeq(frame.seq))
|
|
36276
|
+
return;
|
|
36277
|
+
try {
|
|
36158
36278
|
const inner = await openEncryptedFrame(session.sessionKey, frame);
|
|
36159
36279
|
innerListeners.forEach((cb) => cb(inner));
|
|
36280
|
+
} catch {
|
|
36160
36281
|
}
|
|
36161
|
-
} catch (e) {
|
|
36162
|
-
reject(e instanceof Error ? e : new Error(String(e)));
|
|
36163
36282
|
}
|
|
36164
|
-
}
|
|
36165
|
-
|
|
36166
|
-
|
|
36167
|
-
|
|
36168
|
-
|
|
36283
|
+
} catch (e) {
|
|
36284
|
+
rejectFirstReady(e instanceof Error ? e : new Error(String(e)));
|
|
36285
|
+
}
|
|
36286
|
+
};
|
|
36287
|
+
ws.on("message", onMessage);
|
|
36288
|
+
ws.once("close", () => {
|
|
36289
|
+
rejectFirstReady(new Error("peer WS closed before ready"));
|
|
36169
36290
|
});
|
|
36170
36291
|
sessionPromise.catch(() => {
|
|
36171
36292
|
});
|
|
@@ -36173,13 +36294,21 @@ async function startPeer(opts) {
|
|
|
36173
36294
|
ws,
|
|
36174
36295
|
session: sessionPromise,
|
|
36175
36296
|
sendInner: async (inner) => {
|
|
36176
|
-
|
|
36297
|
+
await sessionPromise;
|
|
36298
|
+
const s = session;
|
|
36177
36299
|
const sealed = await sealInnerFrame(s.sessionKey, opts.mcpId, s.nextOutboundSeq(), inner);
|
|
36178
36300
|
ws.send(JSON.stringify(sealed));
|
|
36179
36301
|
},
|
|
36180
36302
|
onInner: (cb) => {
|
|
36181
36303
|
innerListeners.push(cb);
|
|
36182
36304
|
},
|
|
36305
|
+
onRenegotiate: (cb) => {
|
|
36306
|
+
renegotiateListeners.push(cb);
|
|
36307
|
+
},
|
|
36308
|
+
onPendingPair: (cb) => {
|
|
36309
|
+
pendingPairListeners.push(cb);
|
|
36310
|
+
},
|
|
36311
|
+
pendingPairCode: () => pendingPairCode,
|
|
36183
36312
|
close: () => ws.close()
|
|
36184
36313
|
};
|
|
36185
36314
|
return handle;
|
|
@@ -36236,6 +36365,32 @@ async function loadOrCreateIdentity(serverName, dir = defaultIdentityDir()) {
|
|
|
36236
36365
|
return id;
|
|
36237
36366
|
}
|
|
36238
36367
|
|
|
36368
|
+
// node_modules/@fetchproxy/server/dist/error-kind.js
|
|
36369
|
+
function classifyFetchError(error51) {
|
|
36370
|
+
if (/Could not establish connection/i.test(error51) || /Receiving end does not exist/i.test(error51)) {
|
|
36371
|
+
return "content_script_unreachable";
|
|
36372
|
+
}
|
|
36373
|
+
if (/^tab fetch failed:/.test(error51)) {
|
|
36374
|
+
return "tab_fetch_failed";
|
|
36375
|
+
}
|
|
36376
|
+
if (/^fetch threw:/.test(error51)) {
|
|
36377
|
+
return "tab_fetch_failed";
|
|
36378
|
+
}
|
|
36379
|
+
if (/^no tab matching /.test(error51)) {
|
|
36380
|
+
return "no_tab";
|
|
36381
|
+
}
|
|
36382
|
+
if (/not in domains \[/.test(error51)) {
|
|
36383
|
+
return "domain_denied";
|
|
36384
|
+
}
|
|
36385
|
+
if (/^capability .+ not granted/.test(error51)) {
|
|
36386
|
+
return "capability_denied";
|
|
36387
|
+
}
|
|
36388
|
+
if (/^(request|response) body too large:/.test(error51)) {
|
|
36389
|
+
return "body_too_large";
|
|
36390
|
+
}
|
|
36391
|
+
return "other";
|
|
36392
|
+
}
|
|
36393
|
+
|
|
36239
36394
|
// node_modules/@fetchproxy/server/dist/ws-server.js
|
|
36240
36395
|
var FetchproxyProtocolError = class extends Error {
|
|
36241
36396
|
constructor(message) {
|
|
@@ -36278,7 +36433,11 @@ function assertUrlInDomains(field, url2, domains) {
|
|
|
36278
36433
|
}
|
|
36279
36434
|
var DEFAULT_JSON_OK_STATUSES = [200, 201, 202, 204];
|
|
36280
36435
|
var FetchproxyServer = class {
|
|
36281
|
-
/**
|
|
36436
|
+
/**
|
|
36437
|
+
* Bridge role. `null` until the first verb call (or an explicit
|
|
36438
|
+
* `connect()`) — `listen()` no longer triggers the role election
|
|
36439
|
+
* as of 0.5.3+. Reset to `null` on `close()`.
|
|
36440
|
+
*/
|
|
36282
36441
|
role = null;
|
|
36283
36442
|
opts;
|
|
36284
36443
|
hostHandle = null;
|
|
@@ -36300,6 +36459,12 @@ var FetchproxyServer = class {
|
|
|
36300
36459
|
pendingIdb = /* @__PURE__ */ new Map();
|
|
36301
36460
|
mcpId = null;
|
|
36302
36461
|
identity = null;
|
|
36462
|
+
// 0.5.3+: in-flight role-election / handle-start promise. Set the
|
|
36463
|
+
// first time a verb call runs `ensureConnected`, awaited by concurrent
|
|
36464
|
+
// callers, cleared once the connection is up. Single source of truth
|
|
36465
|
+
// for "we're connecting right now" so two parallel first-calls don't
|
|
36466
|
+
// race the port bind.
|
|
36467
|
+
connectingPromise = null;
|
|
36303
36468
|
constructor(opts) {
|
|
36304
36469
|
if (!Array.isArray(opts.domains) || opts.domains.length === 0) {
|
|
36305
36470
|
throw new Error("FetchproxyServer: opts.domains must be a non-empty array of hostnames");
|
|
@@ -36351,23 +36516,87 @@ var FetchproxyServer = class {
|
|
|
36351
36516
|
};
|
|
36352
36517
|
}
|
|
36353
36518
|
/**
|
|
36354
|
-
*
|
|
36355
|
-
* from disk (creating it on first call)
|
|
36356
|
-
*
|
|
36357
|
-
*
|
|
36358
|
-
* `
|
|
36359
|
-
*
|
|
36519
|
+
* Prepare the bridge for use. Loads the long-term identity keypair
|
|
36520
|
+
* from disk (creating it on first call) and computes this instance's
|
|
36521
|
+
* `mcpId`. Does NOT bind the bridge port or dial any WebSocket — the
|
|
36522
|
+
* connection is established lazily on the first verb call (see
|
|
36523
|
+
* `ensureConnected` / `getOrConnect`).
|
|
36524
|
+
*
|
|
36525
|
+
* Pre-0.5.3 behavior: `listen()` also did role election and started
|
|
36526
|
+
* the host/peer immediately, which meant every configured-but-unused
|
|
36527
|
+
* MCP claimed bridge resources at MCP-client boot. Several MCPs
|
|
36528
|
+
* starting in parallel under Claude Desktop also produced noisy
|
|
36529
|
+
* `ERR_CONNECTION_REFUSED` errors in the extension if it raced ahead
|
|
36530
|
+
* of the first MCP's port bind. Deferring keeps boot quiet and
|
|
36531
|
+
* leaves the port unowned until something actually needs it.
|
|
36532
|
+
*
|
|
36533
|
+
* Calling `listen()` twice without an intervening `close()` is a
|
|
36534
|
+
* no-op (the second call's identity load is idempotent).
|
|
36360
36535
|
*/
|
|
36361
36536
|
async listen() {
|
|
36362
|
-
|
|
36363
|
-
|
|
36537
|
+
if (!this.identity) {
|
|
36538
|
+
this.identity = await loadOrCreateIdentity(this.opts.serverName, this.opts.identityDir);
|
|
36539
|
+
}
|
|
36540
|
+
if (!this.mcpId) {
|
|
36541
|
+
this.mcpId = generateMcpId(this.opts.serverName, this.opts.version);
|
|
36542
|
+
}
|
|
36543
|
+
}
|
|
36544
|
+
/**
|
|
36545
|
+
* Force an eager bridge connection (role-election + host/peer handle
|
|
36546
|
+
* start + listener wiring) without waiting for the first verb call.
|
|
36547
|
+
* Useful for callers that want to surface the role / connection
|
|
36548
|
+
* outcome at boot, or for tests whose harness dials a mock extension
|
|
36549
|
+
* immediately after server construction. Production MCPs that just
|
|
36550
|
+
* answer tool calls should NOT call this — the lazy connect via
|
|
36551
|
+
* `ensureConnected` will do the right thing on first use, keeping
|
|
36552
|
+
* boot cheap and avoiding port-bind contention for MCPs that never
|
|
36553
|
+
* actually get invoked.
|
|
36554
|
+
*
|
|
36555
|
+
* Idempotent: a second call after the first has resolved is a no-op
|
|
36556
|
+
* (the existing handle is reused). Throws if `listen()` was never
|
|
36557
|
+
* called.
|
|
36558
|
+
*/
|
|
36559
|
+
async connect() {
|
|
36560
|
+
await this.ensureConnected();
|
|
36561
|
+
}
|
|
36562
|
+
/**
|
|
36563
|
+
* Establish the bridge connection (role-election + host/peer handle
|
|
36564
|
+
* start + listener wiring) the first time a verb is invoked.
|
|
36565
|
+
* Idempotent after the connection is up; concurrent first-callers
|
|
36566
|
+
* share the same in-flight promise so only one election happens.
|
|
36567
|
+
*
|
|
36568
|
+
* Throws if `listen()` was never called — the contract is that the
|
|
36569
|
+
* MCP author still must wire `transport.start()` at boot to load
|
|
36570
|
+
* identity / set mcpId, even though the WS doesn't open until a
|
|
36571
|
+
* verb runs.
|
|
36572
|
+
*/
|
|
36573
|
+
async ensureConnected() {
|
|
36574
|
+
if (this.hostHandle || this.peerHandle)
|
|
36575
|
+
return;
|
|
36576
|
+
if (this.connectingPromise) {
|
|
36577
|
+
await this.connectingPromise;
|
|
36578
|
+
return;
|
|
36579
|
+
}
|
|
36580
|
+
if (!this.identity || !this.mcpId) {
|
|
36581
|
+
throw new Error("FetchproxyServer: ensureConnected called before listen() \u2014 call listen() at MCP boot to load identity");
|
|
36582
|
+
}
|
|
36583
|
+
this.connectingPromise = this.doConnect();
|
|
36584
|
+
try {
|
|
36585
|
+
await this.connectingPromise;
|
|
36586
|
+
} finally {
|
|
36587
|
+
this.connectingPromise = null;
|
|
36588
|
+
}
|
|
36589
|
+
}
|
|
36590
|
+
async doConnect() {
|
|
36591
|
+
const identity = this.identity;
|
|
36592
|
+
const mcpId = this.mcpId;
|
|
36364
36593
|
const el = await electRole({ host: this.opts.host, port: this.opts.port });
|
|
36365
36594
|
if (el.role === "host") {
|
|
36366
36595
|
this.role = "host";
|
|
36367
36596
|
this.hostHandle = await startHost({
|
|
36368
36597
|
httpServer: el.server,
|
|
36369
|
-
ownIdentity:
|
|
36370
|
-
ownMcpId:
|
|
36598
|
+
ownIdentity: identity,
|
|
36599
|
+
ownMcpId: mcpId,
|
|
36371
36600
|
ownServerName: this.opts.serverName,
|
|
36372
36601
|
ownVersion: this.opts.version,
|
|
36373
36602
|
ownDomains: this.opts.domains,
|
|
@@ -36382,13 +36611,17 @@ var FetchproxyServer = class {
|
|
|
36382
36611
|
onPairCode: this.opts.onPairCode
|
|
36383
36612
|
});
|
|
36384
36613
|
this.hostHandle.onOwnInner((inner) => this.onInner(inner));
|
|
36614
|
+
this.hostHandle.onExtensionDisconnect(() => this.rejectAllPending());
|
|
36615
|
+
this.hostHandle.onPendingPair((code) => {
|
|
36616
|
+
this.rejectAllPending(this.pairingErrorMessage(code));
|
|
36617
|
+
});
|
|
36385
36618
|
} else {
|
|
36386
36619
|
this.role = "peer";
|
|
36387
36620
|
this.peerHandle = await startPeer({
|
|
36388
36621
|
host: this.opts.host,
|
|
36389
36622
|
port: this.opts.port,
|
|
36390
|
-
identity
|
|
36391
|
-
mcpId
|
|
36623
|
+
identity,
|
|
36624
|
+
mcpId,
|
|
36392
36625
|
serverName: this.opts.serverName,
|
|
36393
36626
|
version: this.opts.version,
|
|
36394
36627
|
domains: this.opts.domains,
|
|
@@ -36402,8 +36635,19 @@ var FetchproxyServer = class {
|
|
|
36402
36635
|
sessionStoragePointers: this.opts.sessionStoragePointers
|
|
36403
36636
|
});
|
|
36404
36637
|
this.peerHandle.onInner((inner) => this.onInner(inner));
|
|
36638
|
+
this.peerHandle.onRenegotiate(() => this.rejectAllPending());
|
|
36639
|
+
this.peerHandle.onPendingPair((code) => {
|
|
36640
|
+
this.rejectAllPending(this.pairingErrorMessage(code));
|
|
36641
|
+
});
|
|
36642
|
+
if (this.opts.onPairCode) {
|
|
36643
|
+
const cb = this.opts.onPairCode;
|
|
36644
|
+
this.peerHandle.onPendingPair((code) => cb(code));
|
|
36645
|
+
}
|
|
36405
36646
|
}
|
|
36406
36647
|
}
|
|
36648
|
+
pairingErrorMessage(code) {
|
|
36649
|
+
return `fetchproxy: pairing required for ${this.opts.serverName} \u2014 open the fetchproxy extension popup in Chrome and approve the pair request. Verify the pair code matches: ${code}`;
|
|
36650
|
+
}
|
|
36407
36651
|
/**
|
|
36408
36652
|
* Raw single-shot fetch through the bridge. Most callers should prefer
|
|
36409
36653
|
* the verb shortcuts (`get` / `post` / `getJson` / `postJson` / `getHtml`)
|
|
@@ -36419,8 +36663,11 @@ var FetchproxyServer = class {
|
|
|
36419
36663
|
* offline, etc.).
|
|
36420
36664
|
*/
|
|
36421
36665
|
async fetch(init) {
|
|
36422
|
-
|
|
36423
|
-
|
|
36666
|
+
await this.ensureConnected();
|
|
36667
|
+
const pendingCode = this.currentPendingPairCode();
|
|
36668
|
+
if (pendingCode !== null) {
|
|
36669
|
+
const error51 = this.pairingErrorMessage(pendingCode);
|
|
36670
|
+
return { ok: false, error: error51, kind: classifyFetchError(error51) };
|
|
36424
36671
|
}
|
|
36425
36672
|
const id = this.nextRequestId++;
|
|
36426
36673
|
const inner = { type: "request", id, op: "fetch", init };
|
|
@@ -36561,9 +36808,8 @@ var FetchproxyServer = class {
|
|
|
36561
36808
|
if (!this.opts.capabilities.includes("read_cookies")) {
|
|
36562
36809
|
throw new Error('FetchproxyServer.readCookies(): MCP did not declare "read_cookies" in capabilities \u2014 add it to FetchproxyServerOpts.capabilities to enable this verb');
|
|
36563
36810
|
}
|
|
36564
|
-
|
|
36565
|
-
|
|
36566
|
-
}
|
|
36811
|
+
await this.ensureConnected();
|
|
36812
|
+
this.throwIfPendingPair();
|
|
36567
36813
|
if (opts.subdomain !== void 0)
|
|
36568
36814
|
assertSubdomainLabel(opts.subdomain);
|
|
36569
36815
|
const baseDomain = this.resolveBaseDomain(opts.domain);
|
|
@@ -36620,9 +36866,8 @@ var FetchproxyServer = class {
|
|
|
36620
36866
|
if (!this.opts.capabilities.includes(op)) {
|
|
36621
36867
|
throw new Error(`FetchproxyServer.${op === "read_local_storage" ? "readLocalStorage" : "readSessionStorage"}(): MCP did not declare ${JSON.stringify(op)} in capabilities`);
|
|
36622
36868
|
}
|
|
36623
|
-
|
|
36624
|
-
|
|
36625
|
-
}
|
|
36869
|
+
await this.ensureConnected();
|
|
36870
|
+
this.throwIfPendingPair();
|
|
36626
36871
|
if (!Array.isArray(opts.keys) || opts.keys.length === 0) {
|
|
36627
36872
|
throw new Error(`FetchproxyServer.${op}: opts.keys must be a non-empty array`);
|
|
36628
36873
|
}
|
|
@@ -36677,9 +36922,8 @@ var FetchproxyServer = class {
|
|
|
36677
36922
|
if (!this.opts.capabilities.includes("capture_request_header")) {
|
|
36678
36923
|
throw new Error('FetchproxyServer.captureRequestHeader(): MCP did not declare "capture_request_header" in capabilities');
|
|
36679
36924
|
}
|
|
36680
|
-
|
|
36681
|
-
|
|
36682
|
-
}
|
|
36925
|
+
await this.ensureConnected();
|
|
36926
|
+
this.throwIfPendingPair();
|
|
36683
36927
|
const declared = this.opts.captureHeaders.find((d) => d.urlPattern === opts.urlPattern && d.headerName === opts.headerName);
|
|
36684
36928
|
if (!declared) {
|
|
36685
36929
|
throw new Error(`FetchproxyServer.captureRequestHeader: (urlPattern=${JSON.stringify(opts.urlPattern)}, headerName=${JSON.stringify(opts.headerName)}) not declared in captureHeaders`);
|
|
@@ -36720,9 +36964,8 @@ var FetchproxyServer = class {
|
|
|
36720
36964
|
if (!this.opts.capabilities.includes("read_indexed_db")) {
|
|
36721
36965
|
throw new Error('FetchproxyServer.readIndexedDb(): MCP did not declare "read_indexed_db" in capabilities');
|
|
36722
36966
|
}
|
|
36723
|
-
|
|
36724
|
-
|
|
36725
|
-
}
|
|
36967
|
+
await this.ensureConnected();
|
|
36968
|
+
this.throwIfPendingPair();
|
|
36726
36969
|
if (!Array.isArray(opts.keys) || opts.keys.length === 0) {
|
|
36727
36970
|
throw new Error("FetchproxyServer.readIndexedDb: opts.keys must be a non-empty array");
|
|
36728
36971
|
}
|
|
@@ -36800,10 +37043,11 @@ var FetchproxyServer = class {
|
|
|
36800
37043
|
if (inner.op === void 0 || inner.op === "fetch") {
|
|
36801
37044
|
fetchCb({ ok: true, status: inner.status, url: inner.url, body: inner.body });
|
|
36802
37045
|
} else {
|
|
36803
|
-
|
|
37046
|
+
const error51 = `unexpected ${inner.op} response on fetch awaiter`;
|
|
37047
|
+
fetchCb({ ok: false, error: error51, kind: classifyFetchError(error51) });
|
|
36804
37048
|
}
|
|
36805
37049
|
} else {
|
|
36806
|
-
fetchCb({ ok: false, error: inner.error });
|
|
37050
|
+
fetchCb({ ok: false, error: inner.error, kind: classifyFetchError(inner.error) });
|
|
36807
37051
|
}
|
|
36808
37052
|
return;
|
|
36809
37053
|
}
|
|
@@ -36870,6 +37114,52 @@ var FetchproxyServer = class {
|
|
|
36870
37114
|
}
|
|
36871
37115
|
}
|
|
36872
37116
|
}
|
|
37117
|
+
rejectAllPending(reason = "extension disconnected") {
|
|
37118
|
+
const err = new FetchproxyProtocolError(reason);
|
|
37119
|
+
for (const cb of this.pending.values()) {
|
|
37120
|
+
cb({ ok: false, error: err.message, kind: classifyFetchError(err.message) });
|
|
37121
|
+
}
|
|
37122
|
+
this.pending.clear();
|
|
37123
|
+
for (const cb of this.pendingReadCookies.values()) {
|
|
37124
|
+
cb({ ok: false, error: err.message });
|
|
37125
|
+
}
|
|
37126
|
+
this.pendingReadCookies.clear();
|
|
37127
|
+
for (const { reject } of this.pendingStorage.values())
|
|
37128
|
+
reject(err);
|
|
37129
|
+
this.pendingStorage.clear();
|
|
37130
|
+
for (const { reject } of this.pendingCapture.values())
|
|
37131
|
+
reject(err);
|
|
37132
|
+
this.pendingCapture.clear();
|
|
37133
|
+
for (const { reject } of this.pendingIdb.values())
|
|
37134
|
+
reject(err);
|
|
37135
|
+
this.pendingIdb.clear();
|
|
37136
|
+
}
|
|
37137
|
+
/**
|
|
37138
|
+
* 0.5.2+: read the current pair-pending pair code from whichever handle
|
|
37139
|
+
* is active, returning null when none is pending. Public verbs call this
|
|
37140
|
+
* at the top so that a tool invoked while the bridge is waiting on user
|
|
37141
|
+
* approval fails fast with the actionable error rather than hanging on a
|
|
37142
|
+
* sealed frame the extension will never process.
|
|
37143
|
+
*/
|
|
37144
|
+
currentPendingPairCode() {
|
|
37145
|
+
if (this.hostHandle)
|
|
37146
|
+
return this.hostHandle.pendingPairCode();
|
|
37147
|
+
if (this.peerHandle)
|
|
37148
|
+
return this.peerHandle.pendingPairCode();
|
|
37149
|
+
return null;
|
|
37150
|
+
}
|
|
37151
|
+
/**
|
|
37152
|
+
* 0.5.2+: throw `FetchproxyProtocolError` with the actionable pair-code
|
|
37153
|
+
* message if the bridge is waiting on user approval. Used by the verb
|
|
37154
|
+
* methods (readCookies, readLocalStorage, etc.) that surface errors via
|
|
37155
|
+
* thrown exceptions rather than `ok:false` discriminated unions.
|
|
37156
|
+
*/
|
|
37157
|
+
throwIfPendingPair() {
|
|
37158
|
+
const code = this.currentPendingPairCode();
|
|
37159
|
+
if (code !== null) {
|
|
37160
|
+
throw new FetchproxyProtocolError(this.pairingErrorMessage(code));
|
|
37161
|
+
}
|
|
37162
|
+
}
|
|
36873
37163
|
/**
|
|
36874
37164
|
* Shut down the bridge. Host: terminates the WebSocket server and any
|
|
36875
37165
|
* still-attached extension/peer clients. Peer: closes the upstream
|
|
@@ -36877,6 +37167,10 @@ var FetchproxyServer = class {
|
|
|
36877
37167
|
* twice in a row.
|
|
36878
37168
|
*/
|
|
36879
37169
|
async close() {
|
|
37170
|
+
this.rejectAllPending();
|
|
37171
|
+
if (this.connectingPromise) {
|
|
37172
|
+
await this.connectingPromise.catch(() => void 0);
|
|
37173
|
+
}
|
|
36880
37174
|
if (this.hostHandle)
|
|
36881
37175
|
await this.hostHandle.close();
|
|
36882
37176
|
if (this.peerHandle)
|
|
@@ -36884,6 +37178,7 @@ var FetchproxyServer = class {
|
|
|
36884
37178
|
this.hostHandle = null;
|
|
36885
37179
|
this.peerHandle = null;
|
|
36886
37180
|
this.role = null;
|
|
37181
|
+
this.connectingPromise = null;
|
|
36887
37182
|
}
|
|
36888
37183
|
};
|
|
36889
37184
|
|
|
@@ -36972,8 +37267,7 @@ async function bootstrap(opts) {
|
|
|
36972
37267
|
for (const p of localStoragePointers) {
|
|
36973
37268
|
pointers[p.outputKey] = { storageKey: p.storageKey, jsonPointer: p.jsonPointer };
|
|
36974
37269
|
}
|
|
36975
|
-
|
|
36976
|
-
localStorage = await stub.readLocalStorage({
|
|
37270
|
+
localStorage = await server.readLocalStorage({
|
|
36977
37271
|
keys: allKeys,
|
|
36978
37272
|
...storageDomainOpts,
|
|
36979
37273
|
...localStoragePointers.length > 0 ? { pointers } : {}
|
|
@@ -36986,8 +37280,7 @@ async function bootstrap(opts) {
|
|
|
36986
37280
|
for (const p of sessionStoragePointers) {
|
|
36987
37281
|
pointers[p.outputKey] = { storageKey: p.storageKey, jsonPointer: p.jsonPointer };
|
|
36988
37282
|
}
|
|
36989
|
-
|
|
36990
|
-
sessionStorage = await stub.readSessionStorage({
|
|
37283
|
+
sessionStorage = await server.readSessionStorage({
|
|
36991
37284
|
keys: allKeys,
|
|
36992
37285
|
...storageDomainOpts,
|
|
36993
37286
|
...sessionStoragePointers.length > 0 ? { pointers } : {}
|
|
@@ -37010,9 +37303,6 @@ async function bootstrap(opts) {
|
|
|
37010
37303
|
}
|
|
37011
37304
|
const indexedDbBucket = {};
|
|
37012
37305
|
for (const d of indexedDb) {
|
|
37013
|
-
if (!server.readIndexedDb) {
|
|
37014
|
-
throw new Error("bootstrap: server factory does not implement readIndexedDb (declared indexedDb but server stub omits it)");
|
|
37015
|
-
}
|
|
37016
37306
|
const values = await server.readIndexedDb({
|
|
37017
37307
|
database: d.database,
|
|
37018
37308
|
store: d.store,
|
|
@@ -37049,7 +37339,7 @@ var BootstrapDisabledError = class extends Error {
|
|
|
37049
37339
|
// package.json
|
|
37050
37340
|
var package_default = {
|
|
37051
37341
|
name: "creditkarma-mcp",
|
|
37052
|
-
version: "2.
|
|
37342
|
+
version: "2.1.4",
|
|
37053
37343
|
mcpName: "io.github.chrischall/creditkarma-mcp",
|
|
37054
37344
|
description: "MCP server for Credit Karma \u2014 natural-language access to your transactions, spending, and accounts",
|
|
37055
37345
|
author: "Claude Code (AI) <https://www.anthropic.com/claude>",
|
|
@@ -37093,17 +37383,17 @@ var package_default = {
|
|
|
37093
37383
|
"test:coverage": "vitest run --coverage"
|
|
37094
37384
|
},
|
|
37095
37385
|
dependencies: {
|
|
37096
|
-
"@fetchproxy/bootstrap": "^0.
|
|
37386
|
+
"@fetchproxy/bootstrap": "^0.6.0",
|
|
37097
37387
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
37098
|
-
dotenv: "^17.4.
|
|
37099
|
-
zod: "^4.3
|
|
37388
|
+
dotenv: "^17.4.2",
|
|
37389
|
+
zod: "^4.4.3"
|
|
37100
37390
|
},
|
|
37101
37391
|
devDependencies: {
|
|
37102
|
-
"@types/node": "^25.
|
|
37103
|
-
"@vitest/coverage-v8": "^4.1.
|
|
37392
|
+
"@types/node": "^25.9.1",
|
|
37393
|
+
"@vitest/coverage-v8": "^4.1.7",
|
|
37104
37394
|
esbuild: "^0.28.0",
|
|
37105
|
-
typescript: "^6.0.
|
|
37106
|
-
vitest: "^4.1.
|
|
37395
|
+
typescript: "^6.0.3",
|
|
37396
|
+
vitest: "^4.1.7"
|
|
37107
37397
|
}
|
|
37108
37398
|
};
|
|
37109
37399
|
|
|
@@ -37623,7 +37913,8 @@ async function main() {
|
|
|
37623
37913
|
mcpJsonPath
|
|
37624
37914
|
};
|
|
37625
37915
|
const server = new McpServer(
|
|
37626
|
-
{ name: "creditkarma-mcp", version: "2.
|
|
37916
|
+
{ name: "creditkarma-mcp", version: "2.1.4" }
|
|
37917
|
+
// x-release-please-version
|
|
37627
37918
|
);
|
|
37628
37919
|
registerAuthTools(server, ctx);
|
|
37629
37920
|
registerSyncTools(server, ctx);
|
package/dist/index.js
CHANGED
|
@@ -63,7 +63,8 @@ async function main() {
|
|
|
63
63
|
db,
|
|
64
64
|
mcpJsonPath
|
|
65
65
|
};
|
|
66
|
-
const server = new McpServer({ name: 'creditkarma-mcp', version: '2.
|
|
66
|
+
const server = new McpServer({ name: 'creditkarma-mcp', version: '2.1.4' } // x-release-please-version
|
|
67
|
+
);
|
|
67
68
|
registerAuthTools(server, ctx);
|
|
68
69
|
registerSyncTools(server, ctx);
|
|
69
70
|
registerQueryTools(server, ctx);
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "creditkarma-mcp",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.4",
|
|
4
4
|
"mcpName": "io.github.chrischall/creditkarma-mcp",
|
|
5
|
-
"description": "MCP server for Credit Karma
|
|
5
|
+
"description": "MCP server for Credit Karma — natural-language access to your transactions, spending, and accounts",
|
|
6
6
|
"author": "Claude Code (AI) <https://www.anthropic.com/claude>",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
@@ -44,16 +44,16 @@
|
|
|
44
44
|
"test:coverage": "vitest run --coverage"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@fetchproxy/bootstrap": "^0.
|
|
47
|
+
"@fetchproxy/bootstrap": "^0.6.0",
|
|
48
48
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
49
|
-
"dotenv": "^17.4.
|
|
50
|
-
"zod": "^4.3
|
|
49
|
+
"dotenv": "^17.4.2",
|
|
50
|
+
"zod": "^4.4.3"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
|
-
"@types/node": "^25.
|
|
54
|
-
"@vitest/coverage-v8": "^4.1.
|
|
53
|
+
"@types/node": "^25.9.1",
|
|
54
|
+
"@vitest/coverage-v8": "^4.1.7",
|
|
55
55
|
"esbuild": "^0.28.0",
|
|
56
|
-
"typescript": "^6.0.
|
|
57
|
-
"vitest": "^4.1.
|
|
56
|
+
"typescript": "^6.0.3",
|
|
57
|
+
"vitest": "^4.1.7"
|
|
58
58
|
}
|
|
59
59
|
}
|
package/server.json
CHANGED
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
"url": "https://github.com/chrischall/creditkarma-mcp",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "2.
|
|
9
|
+
"version": "2.1.4",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "creditkarma-mcp",
|
|
14
|
-
"version": "2.
|
|
14
|
+
"version": "2.1.4",
|
|
15
15
|
"transport": {
|
|
16
16
|
"type": "stdio"
|
|
17
17
|
},
|