zola-mcp 1.4.2 → 1.4.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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/dist/bundle.js +265 -73
- package/dist/client.js +40 -26
- package/dist/index.js +1 -1
- package/package.json +3 -3
- package/server.json +2 -2
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
},
|
|
8
8
|
"metadata": {
|
|
9
9
|
"description": "Zola wedding planning tools for Claude Code",
|
|
10
|
-
"version": "1.4.
|
|
10
|
+
"version": "1.4.4"
|
|
11
11
|
},
|
|
12
12
|
"plugins": [
|
|
13
13
|
{
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"displayName": "Zola",
|
|
16
16
|
"source": "./",
|
|
17
17
|
"description": "Zola wedding planning tools for Claude — vendors, budget, guests, seating, events, registry, inquiries, and more via MCP",
|
|
18
|
-
"version": "1.4.
|
|
18
|
+
"version": "1.4.4",
|
|
19
19
|
"author": {
|
|
20
20
|
"name": "Chris Chall"
|
|
21
21
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zola",
|
|
3
3
|
"displayName": "Zola",
|
|
4
|
-
"version": "1.4.
|
|
4
|
+
"version": "1.4.4",
|
|
5
5
|
"description": "Zola wedding planning tools for Claude — vendors, budget, guests, seating, events, registry, inquiries, and more via MCP",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Chris Chall",
|
package/dist/bundle.js
CHANGED
|
@@ -12,7 +12,11 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
12
12
|
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
13
13
|
});
|
|
14
14
|
var __commonJS = (cb, mod) => function __require2() {
|
|
15
|
-
|
|
15
|
+
try {
|
|
16
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
17
|
+
} catch (e) {
|
|
18
|
+
throw mod = 0, e;
|
|
19
|
+
}
|
|
16
20
|
};
|
|
17
21
|
var __export = (target, all) => {
|
|
18
22
|
for (var name in all)
|
|
@@ -34724,8 +34728,12 @@ var API_KEY_RE = new RegExp([
|
|
|
34724
34728
|
// webhook signing secret (Stripe-style)
|
|
34725
34729
|
].map((p) => `\\b${p}`).join("|"), "g");
|
|
34726
34730
|
var QUERY_SECRET_RE = /([?&](?:access_token|refresh_token|client_secret|api_?key|signature|token|key|sig)=)[^&#\s"'<>`]+/gi;
|
|
34731
|
+
var AWS_SIGV4_RE = /([?&]X-Amz-(?:Signature|Security-Token|Credential)=)[^&#\s"'<>`]+/gi;
|
|
34732
|
+
var JSON_SECRET_KEYS = "access_token|refresh_token|client_secret|api_?key|password|passwd|secret|token";
|
|
34733
|
+
var JSON_SECRET_DQ_RE = new RegExp(`("(?:${JSON_SECRET_KEYS})"\\s*:\\s*")[^"]*(")`, "gi");
|
|
34734
|
+
var JSON_SECRET_SQ_RE = new RegExp(`('(?:${JSON_SECRET_KEYS})'\\s*:\\s*')[^']*(')`, "gi");
|
|
34727
34735
|
function redactSecrets(text) {
|
|
34728
|
-
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(JWT_RE, "[REDACTED]");
|
|
34736
|
+
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]");
|
|
34729
34737
|
}
|
|
34730
34738
|
function truncateErrorMessage(text, max = DEFAULT_ERROR_MESSAGE_MAX) {
|
|
34731
34739
|
const str = text === null || text === void 0 ? "" : String(text);
|
|
@@ -34848,6 +34856,8 @@ var NonNegInt = external_exports.number().int().nonnegative();
|
|
|
34848
34856
|
var NonEmptyString = external_exports.string().min(1);
|
|
34849
34857
|
var IsoDate = external_exports.iso.date();
|
|
34850
34858
|
var IsoTime = external_exports.string().regex(/^([01]?\d|2[0-3]):[0-5]\d$/, "must be HH:MM (24h), e.g. 19:30");
|
|
34859
|
+
var NumericIdString = external_exports.string().regex(/^\d+$/, "must be a numeric id (digits only)").describe("A numeric id string (digits only), safe to interpolate into a URL path.");
|
|
34860
|
+
var SafePathSegment = external_exports.string().min(1).regex(/^[^/?#\s]+$/, 'must not contain "/", "?", "#", or whitespace').refine((v) => !v.includes(".."), 'must not contain ".."').describe('A single URL path segment, safe to interpolate: no "/", "..", "?", "#", or whitespace.');
|
|
34851
34861
|
var schemaOrigin = external_exports.string().optional().describe("Portal origin (e.g. https://<vendor>.example.co) selecting which active session to use. Optional when only one session is active.");
|
|
34852
34862
|
var schemaConfirm = external_exports.boolean().optional().describe("Must be true to proceed. Without this, the tool returns a preview.");
|
|
34853
34863
|
var paginationSchema = {
|
|
@@ -34859,6 +34869,41 @@ var pageSchema = {
|
|
|
34859
34869
|
page_size: external_exports.number().int().min(1).max(200).default(50).describe("Number of items per page (1-200).")
|
|
34860
34870
|
};
|
|
34861
34871
|
|
|
34872
|
+
// node_modules/@chrischall/mcp-utils/dist/auth/cached-token.js
|
|
34873
|
+
function createCachedTokenSource(opts) {
|
|
34874
|
+
const now = opts.now ?? Date.now;
|
|
34875
|
+
const bufferMs = opts.bufferMs ?? 6e4;
|
|
34876
|
+
const defaultTtlMs = opts.defaultTtlMs ?? 36e5;
|
|
34877
|
+
let cached2 = null;
|
|
34878
|
+
let inFlight = null;
|
|
34879
|
+
const expiryOf = (minted) => {
|
|
34880
|
+
if (minted.expiresAt !== void 0) {
|
|
34881
|
+
return minted.expiresAt instanceof Date ? minted.expiresAt.getTime() : minted.expiresAt;
|
|
34882
|
+
}
|
|
34883
|
+
return now() + (minted.ttlMs ?? defaultTtlMs);
|
|
34884
|
+
};
|
|
34885
|
+
return {
|
|
34886
|
+
getToken() {
|
|
34887
|
+
if (cached2 && cached2.expiresAt - bufferMs > now())
|
|
34888
|
+
return Promise.resolve(cached2.token);
|
|
34889
|
+
if (inFlight)
|
|
34890
|
+
return inFlight;
|
|
34891
|
+
const p = opts.mint().then((minted) => {
|
|
34892
|
+
cached2 = { token: minted.token, expiresAt: expiryOf(minted) };
|
|
34893
|
+
return minted.token;
|
|
34894
|
+
}).finally(() => {
|
|
34895
|
+
if (inFlight === p)
|
|
34896
|
+
inFlight = null;
|
|
34897
|
+
});
|
|
34898
|
+
inFlight = p;
|
|
34899
|
+
return p;
|
|
34900
|
+
},
|
|
34901
|
+
invalidate() {
|
|
34902
|
+
cached2 = null;
|
|
34903
|
+
}
|
|
34904
|
+
};
|
|
34905
|
+
}
|
|
34906
|
+
|
|
34862
34907
|
// node_modules/@chrischall/mcp-utils/dist/auth/index.js
|
|
34863
34908
|
function bridgeDownHintOf(err) {
|
|
34864
34909
|
if (err instanceof Error && err.name === "FetchproxyBridgeDownError") {
|
|
@@ -35860,10 +35905,20 @@ async function openEncryptedFrame(sessionKey, frame) {
|
|
|
35860
35905
|
|
|
35861
35906
|
// node_modules/@fetchproxy/server/dist/election.js
|
|
35862
35907
|
import { createServer, Server as HttpServer } from "node:http";
|
|
35908
|
+
var DEFAULT_BIND_TIMEOUT_MS = 5e3;
|
|
35863
35909
|
async function electRole(opts) {
|
|
35864
35910
|
const server = createServer();
|
|
35911
|
+
const bindTimeoutMs = opts.bindTimeoutMs ?? DEFAULT_BIND_TIMEOUT_MS;
|
|
35865
35912
|
return new Promise((resolve, reject) => {
|
|
35913
|
+
let timer;
|
|
35914
|
+
const clearBindTimer = () => {
|
|
35915
|
+
if (timer) {
|
|
35916
|
+
clearTimeout(timer);
|
|
35917
|
+
timer = void 0;
|
|
35918
|
+
}
|
|
35919
|
+
};
|
|
35866
35920
|
const onError = (e) => {
|
|
35921
|
+
clearBindTimer();
|
|
35867
35922
|
server.removeListener("listening", onListening);
|
|
35868
35923
|
if (e.code === "EADDRINUSE") {
|
|
35869
35924
|
try {
|
|
@@ -35876,11 +35931,25 @@ async function electRole(opts) {
|
|
|
35876
35931
|
}
|
|
35877
35932
|
};
|
|
35878
35933
|
const onListening = () => {
|
|
35934
|
+
clearBindTimer();
|
|
35879
35935
|
server.removeListener("error", onError);
|
|
35880
35936
|
resolve({ role: "host", server });
|
|
35881
35937
|
};
|
|
35882
35938
|
server.once("error", onError);
|
|
35883
35939
|
server.once("listening", onListening);
|
|
35940
|
+
if (bindTimeoutMs > 0) {
|
|
35941
|
+
timer = setTimeout(() => {
|
|
35942
|
+
server.removeListener("error", onError);
|
|
35943
|
+
server.removeListener("listening", onListening);
|
|
35944
|
+
try {
|
|
35945
|
+
server.close();
|
|
35946
|
+
} catch {
|
|
35947
|
+
}
|
|
35948
|
+
reject(new Error(`fetchproxy: bind to ${opts.host}:${opts.port} timed out after ${bindTimeoutMs}ms (port may be in a bad state)`));
|
|
35949
|
+
}, bindTimeoutMs);
|
|
35950
|
+
if (typeof timer.unref === "function")
|
|
35951
|
+
timer.unref();
|
|
35952
|
+
}
|
|
35884
35953
|
server.listen(opts.port, opts.host);
|
|
35885
35954
|
});
|
|
35886
35955
|
}
|
|
@@ -35973,6 +36042,44 @@ var SessionState = class {
|
|
|
35973
36042
|
}
|
|
35974
36043
|
};
|
|
35975
36044
|
|
|
36045
|
+
// node_modules/@fetchproxy/server/dist/session-ready.js
|
|
36046
|
+
var SESSION_READY_TIMEOUT_MS = 3e4;
|
|
36047
|
+
var FetchproxySessionNotReadyError = class extends Error {
|
|
36048
|
+
reason;
|
|
36049
|
+
pairCode;
|
|
36050
|
+
mcpId;
|
|
36051
|
+
hint;
|
|
36052
|
+
constructor(info) {
|
|
36053
|
+
const pairing = info.pairCode !== null && info.pairCode !== "";
|
|
36054
|
+
const hint = pairing ? `Open the Transporter extension popup and approve pair code ${info.pairCode} for "${info.mcpId}", then retry.` : `The extension is connected but hasn't confirmed a session for "${info.mcpId}" \u2014 sign in to the target site in that browser (and approve the requested scope if it changed), then retry.`;
|
|
36055
|
+
super(`fetchproxy: ${pairing ? "pairing not yet approved" : "no confirmed browser session"} for "${info.mcpId}". ${hint}`);
|
|
36056
|
+
this.name = "FetchproxySessionNotReadyError";
|
|
36057
|
+
this.reason = pairing ? "pair-required" : "not-ready";
|
|
36058
|
+
this.pairCode = pairing ? info.pairCode : null;
|
|
36059
|
+
this.mcpId = info.mcpId;
|
|
36060
|
+
this.hint = hint;
|
|
36061
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
36062
|
+
}
|
|
36063
|
+
};
|
|
36064
|
+
async function awaitSessionReady(ready, opts) {
|
|
36065
|
+
const ms = opts.timeoutMs ?? SESSION_READY_TIMEOUT_MS;
|
|
36066
|
+
if (ms <= 0)
|
|
36067
|
+
return ready;
|
|
36068
|
+
let timer;
|
|
36069
|
+
const timeout = new Promise((_, reject) => {
|
|
36070
|
+
timer = setTimeout(() => {
|
|
36071
|
+
reject(new FetchproxySessionNotReadyError({ mcpId: opts.mcpId, pairCode: opts.pendingPairCode() }));
|
|
36072
|
+
}, ms);
|
|
36073
|
+
timer.unref?.();
|
|
36074
|
+
});
|
|
36075
|
+
try {
|
|
36076
|
+
return await Promise.race([ready, timeout]);
|
|
36077
|
+
} finally {
|
|
36078
|
+
if (timer)
|
|
36079
|
+
clearTimeout(timer);
|
|
36080
|
+
}
|
|
36081
|
+
}
|
|
36082
|
+
|
|
35976
36083
|
// node_modules/@fetchproxy/server/dist/host.js
|
|
35977
36084
|
var PUBLIC_ORIGIN_RE = /^https?:\/\/(?!(127\.0\.0\.1|localhost)(:|$))/i;
|
|
35978
36085
|
var enc2 = new TextEncoder();
|
|
@@ -36060,6 +36167,29 @@ async function startHost(opts) {
|
|
|
36060
36167
|
return;
|
|
36061
36168
|
}
|
|
36062
36169
|
if (frame.type === "hello" && frame.role === "server") {
|
|
36170
|
+
const peerEdPub = fromB64(frame.identityEd25519Pub);
|
|
36171
|
+
const peerSigMsg = concatBytes(enc2.encode(frame.mcpId), fromB64(frame.sessionNonce));
|
|
36172
|
+
const peerSig = fromB64(frame.sessionSig);
|
|
36173
|
+
let peerSigOk = false;
|
|
36174
|
+
try {
|
|
36175
|
+
peerSigOk = await ed25519Verify(peerEdPub, peerSigMsg, peerSig);
|
|
36176
|
+
} catch {
|
|
36177
|
+
peerSigOk = false;
|
|
36178
|
+
}
|
|
36179
|
+
if (!peerSigOk) {
|
|
36180
|
+
console.warn("[fetchproxy] peer hello signature invalid \u2014 refusing registration (possible squatter)");
|
|
36181
|
+
ws.close(1008, "peer hello signature invalid");
|
|
36182
|
+
return;
|
|
36183
|
+
}
|
|
36184
|
+
const existing = peers.get(frame.mcpId);
|
|
36185
|
+
if (existing && existing.ws !== ws) {
|
|
36186
|
+
const existingEdPub = existing.helloFrame.identityEd25519Pub;
|
|
36187
|
+
if (existingEdPub !== frame.identityEd25519Pub) {
|
|
36188
|
+
console.warn("[fetchproxy] peer mcpId already mapped to a different identity \u2014 refusing (mcpId squatting)");
|
|
36189
|
+
ws.close(1008, "mcpId already registered to another identity");
|
|
36190
|
+
return;
|
|
36191
|
+
}
|
|
36192
|
+
}
|
|
36063
36193
|
identified = "peer";
|
|
36064
36194
|
peerMcpId = frame.mcpId;
|
|
36065
36195
|
peers.set(frame.mcpId, { ws, helloFrame: frame });
|
|
@@ -36152,8 +36282,10 @@ async function startHost(opts) {
|
|
|
36152
36282
|
resetSessionPromise();
|
|
36153
36283
|
disconnectListeners.forEach((cb) => cb());
|
|
36154
36284
|
}
|
|
36155
|
-
if (identified === "peer" && peerMcpId)
|
|
36156
|
-
peers.
|
|
36285
|
+
if (identified === "peer" && peerMcpId) {
|
|
36286
|
+
if (peers.get(peerMcpId)?.ws === ws)
|
|
36287
|
+
peers.delete(peerMcpId);
|
|
36288
|
+
}
|
|
36157
36289
|
});
|
|
36158
36290
|
});
|
|
36159
36291
|
return {
|
|
@@ -36169,7 +36301,10 @@ async function startHost(opts) {
|
|
|
36169
36301
|
});
|
|
36170
36302
|
}),
|
|
36171
36303
|
sendOwnInner: async (inner) => {
|
|
36172
|
-
const session = await ownSessionReady
|
|
36304
|
+
const session = await awaitSessionReady(ownSessionReady, {
|
|
36305
|
+
mcpId: opts.ownMcpId,
|
|
36306
|
+
pendingPairCode: () => ownPendingPairCode
|
|
36307
|
+
});
|
|
36173
36308
|
if (!extensionWs)
|
|
36174
36309
|
throw new Error("host: no extension connected");
|
|
36175
36310
|
const sealed = await sealInnerFrame(session.sessionKey, opts.ownMcpId, session.nextOutboundSeq(), inner);
|
|
@@ -36274,7 +36409,10 @@ async function startPeer(opts) {
|
|
|
36274
36409
|
ws,
|
|
36275
36410
|
session: sessionPromise,
|
|
36276
36411
|
sendInner: async (inner) => {
|
|
36277
|
-
await sessionPromise
|
|
36412
|
+
await awaitSessionReady(sessionPromise, {
|
|
36413
|
+
mcpId: opts.mcpId,
|
|
36414
|
+
pendingPairCode: () => pendingPairCode
|
|
36415
|
+
});
|
|
36278
36416
|
const s = session;
|
|
36279
36417
|
const sealed = await sealInnerFrame(s.sessionKey, opts.mcpId, s.nextOutboundSeq(), inner);
|
|
36280
36418
|
ws.send(JSON.stringify(sealed));
|
|
@@ -36940,6 +37078,35 @@ var FetchproxyServer = class {
|
|
|
36940
37078
|
this.keepAliveTimer = null;
|
|
36941
37079
|
}
|
|
36942
37080
|
}
|
|
37081
|
+
/**
|
|
37082
|
+
* Send an inner request frame via whichever bridge handle is active. If the
|
|
37083
|
+
* send throws (e.g. `FetchproxySessionNotReadyError` — the session never
|
|
37084
|
+
* confirmed), the frame never reached the bridge, so no reply will arrive:
|
|
37085
|
+
* drop the just-registered pending resolver for this id (it lives in exactly
|
|
37086
|
+
* one of the op maps — request ids are unique) so it doesn't leak until the
|
|
37087
|
+
* server closes, then rethrow.
|
|
37088
|
+
*/
|
|
37089
|
+
async sendInnerFrame(inner) {
|
|
37090
|
+
try {
|
|
37091
|
+
if (this.hostHandle) {
|
|
37092
|
+
await this.hostHandle.sendOwnInner(inner);
|
|
37093
|
+
} else if (this.peerHandle) {
|
|
37094
|
+
await this.peerHandle.sendInner(inner);
|
|
37095
|
+
}
|
|
37096
|
+
} catch (err) {
|
|
37097
|
+
if ("id" in inner && typeof inner.id === "number") {
|
|
37098
|
+
const { id } = inner;
|
|
37099
|
+
this.pending.delete(id);
|
|
37100
|
+
this.pendingReadCookies.delete(id);
|
|
37101
|
+
this.pendingStorage.delete(id);
|
|
37102
|
+
this.pendingCapture.delete(id);
|
|
37103
|
+
this.pendingRedirect.delete(id);
|
|
37104
|
+
this.pendingDownload.delete(id);
|
|
37105
|
+
this.pendingIdb.delete(id);
|
|
37106
|
+
}
|
|
37107
|
+
throw err;
|
|
37108
|
+
}
|
|
37109
|
+
}
|
|
36943
37110
|
/**
|
|
36944
37111
|
* Single bridge round-trip, wrapped by `fetchTimeoutMs` when set.
|
|
36945
37112
|
* On timeout returns the `{ok:false, kind:'timeout'}` envelope —
|
|
@@ -36951,11 +37118,7 @@ var FetchproxyServer = class {
|
|
|
36951
37118
|
const pending = new Promise((resolve) => {
|
|
36952
37119
|
this.pending.set(id, resolve);
|
|
36953
37120
|
});
|
|
36954
|
-
|
|
36955
|
-
await this.hostHandle.sendOwnInner(inner);
|
|
36956
|
-
} else if (this.peerHandle) {
|
|
36957
|
-
await this.peerHandle.sendInner(inner);
|
|
36958
|
-
}
|
|
37121
|
+
await this.sendInnerFrame(inner);
|
|
36959
37122
|
const timeoutMs = this.opts.fetchTimeoutMs;
|
|
36960
37123
|
if (timeoutMs === void 0 || timeoutMs <= 0)
|
|
36961
37124
|
return pending;
|
|
@@ -36984,6 +37147,51 @@ var FetchproxyServer = class {
|
|
|
36984
37147
|
clearTimeout(timer);
|
|
36985
37148
|
}
|
|
36986
37149
|
}
|
|
37150
|
+
/**
|
|
37151
|
+
* FP-B2: bound a non-`fetch` verb's reply wait by `fetchTimeoutMs`.
|
|
37152
|
+
*
|
|
37153
|
+
* Before this, only `fetch()` raced its `pending` reply against a timer
|
|
37154
|
+
* (`_fetchOnceWithTimeout`); `readCookies`, the storage reads,
|
|
37155
|
+
* `capture_request_header`, `capture_redirect`, `download`, and
|
|
37156
|
+
* `read_indexed_db` awaited their `pending` promise with no race, so a
|
|
37157
|
+
* wedged extension hung the tool call indefinitely.
|
|
37158
|
+
*
|
|
37159
|
+
* `pending` is the already-registered reply promise. `pendingMap`/`id`
|
|
37160
|
+
* point at the op-specific map entry so we can drop it on expiry exactly
|
|
37161
|
+
* as `_fetchOnceWithTimeout` does — otherwise a late bridge reply would
|
|
37162
|
+
* resolve into a stale resolver / leak. On timeout we reject with a
|
|
37163
|
+
* `FetchproxyTimeoutError` (the same throwable the convenience methods
|
|
37164
|
+
* already surface for fetch timeouts). `0`/unset opts out (unbounded),
|
|
37165
|
+
* matching the fetch path.
|
|
37166
|
+
*/
|
|
37167
|
+
async _withVerbTimeout(pending, pendingMap, id, url2) {
|
|
37168
|
+
const timeoutMs = this.opts.fetchTimeoutMs;
|
|
37169
|
+
if (timeoutMs === void 0 || timeoutMs <= 0)
|
|
37170
|
+
return pending;
|
|
37171
|
+
let timer;
|
|
37172
|
+
const start = Date.now();
|
|
37173
|
+
try {
|
|
37174
|
+
return await Promise.race([
|
|
37175
|
+
pending,
|
|
37176
|
+
new Promise((_resolve, reject) => {
|
|
37177
|
+
timer = setTimeout(() => {
|
|
37178
|
+
pendingMap.delete(id);
|
|
37179
|
+
reject(new FetchproxyTimeoutError({
|
|
37180
|
+
url: url2,
|
|
37181
|
+
timeoutMs,
|
|
37182
|
+
role: this.role,
|
|
37183
|
+
port: this.opts.port,
|
|
37184
|
+
elapsedMs: Date.now() - start,
|
|
37185
|
+
retryAttempted: false
|
|
37186
|
+
}));
|
|
37187
|
+
}, timeoutMs);
|
|
37188
|
+
})
|
|
37189
|
+
]);
|
|
37190
|
+
} finally {
|
|
37191
|
+
if (timer)
|
|
37192
|
+
clearTimeout(timer);
|
|
37193
|
+
}
|
|
37194
|
+
}
|
|
36987
37195
|
/**
|
|
36988
37196
|
* Map an `ok:false` fetch result to its typed throwable. Centralizes
|
|
36989
37197
|
* the kind-to-error-class switch so `request()` and (via the same
|
|
@@ -37274,12 +37482,8 @@ var FetchproxyServer = class {
|
|
|
37274
37482
|
const pending = new Promise((resolve) => {
|
|
37275
37483
|
this.pendingReadCookies.set(id, resolve);
|
|
37276
37484
|
});
|
|
37277
|
-
|
|
37278
|
-
|
|
37279
|
-
} else if (this.peerHandle) {
|
|
37280
|
-
await this.peerHandle.sendInner(inner);
|
|
37281
|
-
}
|
|
37282
|
-
const result = await pending;
|
|
37485
|
+
await this.sendInnerFrame(inner);
|
|
37486
|
+
const result = await this._withVerbTimeout(pending, this.pendingReadCookies, id, `https://${host}`);
|
|
37283
37487
|
if (!result.ok) {
|
|
37284
37488
|
throw new FetchproxyProtocolError(result.error);
|
|
37285
37489
|
}
|
|
@@ -37345,12 +37549,8 @@ var FetchproxyServer = class {
|
|
|
37345
37549
|
const pending = new Promise((resolve, reject) => {
|
|
37346
37550
|
this.pendingStorage.set(id, { resolve, reject });
|
|
37347
37551
|
});
|
|
37348
|
-
|
|
37349
|
-
|
|
37350
|
-
} else if (this.peerHandle) {
|
|
37351
|
-
await this.peerHandle.sendInner(inner);
|
|
37352
|
-
}
|
|
37353
|
-
return pending;
|
|
37552
|
+
await this.sendInnerFrame(inner);
|
|
37553
|
+
return this._withVerbTimeout(pending, this.pendingStorage, id, `https://${host}`);
|
|
37354
37554
|
}
|
|
37355
37555
|
/**
|
|
37356
37556
|
* 0.3.0+: snapshot the next outgoing request's named header. Single-
|
|
@@ -37454,12 +37654,8 @@ var FetchproxyServer = class {
|
|
|
37454
37654
|
const pending = new Promise((resolve, reject) => {
|
|
37455
37655
|
this.pendingCapture.set(id, { resolve, reject });
|
|
37456
37656
|
});
|
|
37457
|
-
|
|
37458
|
-
|
|
37459
|
-
} else if (this.peerHandle) {
|
|
37460
|
-
await this.peerHandle.sendInner(inner);
|
|
37461
|
-
}
|
|
37462
|
-
return pending;
|
|
37657
|
+
await this.sendInnerFrame(inner);
|
|
37658
|
+
return this._withVerbTimeout(pending, this.pendingCapture, id, `https://${opts.host}${opts.path ?? "/*"}`);
|
|
37463
37659
|
}
|
|
37464
37660
|
/**
|
|
37465
37661
|
* Snapshot the redirect target URL of the next request the browser
|
|
@@ -37543,12 +37739,8 @@ var FetchproxyServer = class {
|
|
|
37543
37739
|
const pending = new Promise((resolve, reject) => {
|
|
37544
37740
|
this.pendingRedirect.set(id, { resolve, reject });
|
|
37545
37741
|
});
|
|
37546
|
-
|
|
37547
|
-
|
|
37548
|
-
} else if (this.peerHandle) {
|
|
37549
|
-
await this.peerHandle.sendInner(inner);
|
|
37550
|
-
}
|
|
37551
|
-
return pending;
|
|
37742
|
+
await this.sendInnerFrame(inner);
|
|
37743
|
+
return this._withVerbTimeout(pending, this.pendingRedirect, id, `https://${opts.host}${opts.path ?? "/*"}`);
|
|
37552
37744
|
}
|
|
37553
37745
|
/**
|
|
37554
37746
|
* Download `url` through the BROWSER's own network stack via
|
|
@@ -37629,12 +37821,8 @@ var FetchproxyServer = class {
|
|
|
37629
37821
|
const pending = new Promise((resolve, reject) => {
|
|
37630
37822
|
this.pendingDownload.set(id, { resolve, reject });
|
|
37631
37823
|
});
|
|
37632
|
-
|
|
37633
|
-
|
|
37634
|
-
} else if (this.peerHandle) {
|
|
37635
|
-
await this.peerHandle.sendInner(inner);
|
|
37636
|
-
}
|
|
37637
|
-
return pending;
|
|
37824
|
+
await this.sendInnerFrame(inner);
|
|
37825
|
+
return this._withVerbTimeout(pending, this.pendingDownload, id, opts.url);
|
|
37638
37826
|
}
|
|
37639
37827
|
/**
|
|
37640
37828
|
* 0.4.0+: read declared IndexedDB keys from the user's signed-in
|
|
@@ -37681,12 +37869,8 @@ var FetchproxyServer = class {
|
|
|
37681
37869
|
const pending = new Promise((resolve, reject) => {
|
|
37682
37870
|
this.pendingIdb.set(id, { resolve, reject });
|
|
37683
37871
|
});
|
|
37684
|
-
|
|
37685
|
-
|
|
37686
|
-
} else if (this.peerHandle) {
|
|
37687
|
-
await this.peerHandle.sendInner(inner);
|
|
37688
|
-
}
|
|
37689
|
-
return pending;
|
|
37872
|
+
await this.sendInnerFrame(inner);
|
|
37873
|
+
return this._withVerbTimeout(pending, this.pendingIdb, id, origin);
|
|
37690
37874
|
}
|
|
37691
37875
|
assertScopeSubset(requested, declared, label) {
|
|
37692
37876
|
const undeclared = undeclaredKeys(requested, declared);
|
|
@@ -38084,7 +38268,7 @@ var BootstrapDisabledError = class extends Error {
|
|
|
38084
38268
|
// package.json
|
|
38085
38269
|
var package_default = {
|
|
38086
38270
|
name: "zola-mcp",
|
|
38087
|
-
version: "1.4.
|
|
38271
|
+
version: "1.4.4",
|
|
38088
38272
|
mcpName: "io.github.chrischall/zola-mcp",
|
|
38089
38273
|
description: "Zola wedding MCP server for Claude",
|
|
38090
38274
|
author: "Claude Code (AI) <https://www.anthropic.com/claude>",
|
|
@@ -38130,7 +38314,7 @@ var package_default = {
|
|
|
38130
38314
|
"test:watch": "vitest"
|
|
38131
38315
|
},
|
|
38132
38316
|
dependencies: {
|
|
38133
|
-
"@chrischall/mcp-utils": "^0.
|
|
38317
|
+
"@chrischall/mcp-utils": "^0.12.0",
|
|
38134
38318
|
"@fetchproxy/bootstrap": "^1.3.0",
|
|
38135
38319
|
"@fetchproxy/server": "^1.3.0",
|
|
38136
38320
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
@@ -38138,7 +38322,7 @@ var package_default = {
|
|
|
38138
38322
|
zod: "^4.4.3"
|
|
38139
38323
|
},
|
|
38140
38324
|
devDependencies: {
|
|
38141
|
-
"@types/node": "^
|
|
38325
|
+
"@types/node": "^26.0.0",
|
|
38142
38326
|
"@vitest/coverage-v8": "^4.1.8",
|
|
38143
38327
|
esbuild: "^0.28.0",
|
|
38144
38328
|
typescript: "^6.0.3",
|
|
@@ -38183,18 +38367,26 @@ async function resolveRefreshToken() {
|
|
|
38183
38367
|
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
38184
38368
|
await loadDotenvSafely({ path: join2(__dirname, "..", ".env"), override: false });
|
|
38185
38369
|
var MOBILE_BASE_URL = "https://mobile-api.zola.com";
|
|
38370
|
+
var SESSION_REFRESH_BUFFER_MS = 5 * 60 * 1e3;
|
|
38186
38371
|
var ZolaClient = class {
|
|
38187
|
-
sessionToken = null;
|
|
38188
|
-
sessionExpiry = null;
|
|
38189
38372
|
cachedContext = null;
|
|
38190
38373
|
// WAF requires x-zola-session-id on all mobile-api.zola.com requests
|
|
38191
38374
|
deviceSessionId = crypto.randomUUID().toUpperCase();
|
|
38375
|
+
// The `ZOLA_SESSION_TOKEN` seed is a cold-start-only shortcut, exactly as the
|
|
38376
|
+
// old `ensureSession` gated it on `sessionToken === null`; a 401 re-mint goes
|
|
38377
|
+
// straight to `refresh()`, never back to the env token.
|
|
38378
|
+
triedEnvSeed = false;
|
|
38379
|
+
// Single-flight cached mint: caches the 30-min session JWT until 5 min before
|
|
38380
|
+
// its `exp`, coalesces concurrent mints, and re-mints on demand after a 401.
|
|
38381
|
+
session = createCachedTokenSource({
|
|
38382
|
+
mint: () => this.mintSession(),
|
|
38383
|
+
bufferMs: SESSION_REFRESH_BUFFER_MS
|
|
38384
|
+
});
|
|
38192
38385
|
/**
|
|
38193
38386
|
* Make a request to the Zola mobile API (mobile-api.zola.com).
|
|
38194
38387
|
* Uses Bearer JWT auth with x-zola-session-id header.
|
|
38195
38388
|
*/
|
|
38196
38389
|
async requestMobile(method, path, body) {
|
|
38197
|
-
await this.ensureSession();
|
|
38198
38390
|
return this.doRequest(method, path, body);
|
|
38199
38391
|
}
|
|
38200
38392
|
/**
|
|
@@ -38204,7 +38396,6 @@ var ZolaClient = class {
|
|
|
38204
38396
|
* raw bytes and the server's content-type so callers can pass it through.
|
|
38205
38397
|
*/
|
|
38206
38398
|
async requestMobileBinary(method, path, body) {
|
|
38207
|
-
await this.ensureSession();
|
|
38208
38399
|
const response = await this.sendWithRetry(method, path, body, false, false, "*/*");
|
|
38209
38400
|
const contentType = response.headers.get("content-type") ?? "application/octet-stream";
|
|
38210
38401
|
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
@@ -38248,10 +38439,11 @@ var ZolaClient = class {
|
|
|
38248
38439
|
return text ? JSON.parse(text) : null;
|
|
38249
38440
|
}
|
|
38250
38441
|
async sendWithRetry(method, path, body, isAuthRetry = false, isRateRetry = false, accept = "application/json") {
|
|
38251
|
-
const
|
|
38442
|
+
const token = await this.session.getToken();
|
|
38443
|
+
const sessionId = decodeJwtSessionId(token);
|
|
38252
38444
|
const headers = {
|
|
38253
38445
|
accept,
|
|
38254
|
-
authorization: `Bearer ${
|
|
38446
|
+
authorization: `Bearer ${token}`,
|
|
38255
38447
|
"x-zola-platform-type": "iphone_app",
|
|
38256
38448
|
"x-zola-session-id": this.deviceSessionId,
|
|
38257
38449
|
"user-agent": "Zola/42.5.0 (iPad; iOS 26.4; Scale/2.0)",
|
|
@@ -38264,9 +38456,7 @@ var ZolaClient = class {
|
|
|
38264
38456
|
...body !== void 0 ? { body: JSON.stringify(body) } : {}
|
|
38265
38457
|
});
|
|
38266
38458
|
if (response.status === 401 && !isAuthRetry) {
|
|
38267
|
-
this.
|
|
38268
|
-
this.sessionExpiry = null;
|
|
38269
|
-
await this.refresh();
|
|
38459
|
+
this.session.invalidate();
|
|
38270
38460
|
return this.sendWithRetry(method, path, body, true, isRateRetry, accept);
|
|
38271
38461
|
}
|
|
38272
38462
|
if (response.status === 429) {
|
|
@@ -38284,30 +38474,33 @@ var ZolaClient = class {
|
|
|
38284
38474
|
}
|
|
38285
38475
|
return response;
|
|
38286
38476
|
}
|
|
38287
|
-
|
|
38288
|
-
|
|
38289
|
-
|
|
38290
|
-
|
|
38291
|
-
|
|
38477
|
+
/**
|
|
38478
|
+
* Mint a session token for the cache. On the very first mint, use a valid
|
|
38479
|
+
* `ZOLA_SESSION_TOKEN` if present (a cold-start shortcut that skips the
|
|
38480
|
+
* initial refresh); otherwise, and on every later mint, refresh via the
|
|
38481
|
+
* mobile API. Returns `{ token, expiresAt }` so the cache serves it until
|
|
38482
|
+
* `SESSION_REFRESH_BUFFER_MS` before the JWT's `exp`.
|
|
38483
|
+
*/
|
|
38484
|
+
async mintSession() {
|
|
38485
|
+
if (!this.triedEnvSeed) {
|
|
38486
|
+
this.triedEnvSeed = true;
|
|
38292
38487
|
const envSession = readEnvVar("ZOLA_SESSION_TOKEN");
|
|
38293
38488
|
if (envSession) {
|
|
38294
38489
|
try {
|
|
38295
38490
|
const exp = decodeJwtExp(envSession);
|
|
38296
|
-
if (exp * 1e3 - Date.now() >
|
|
38297
|
-
|
|
38298
|
-
this.sessionExpiry = new Date(exp * 1e3);
|
|
38299
|
-
return;
|
|
38491
|
+
if (exp * 1e3 - Date.now() > SESSION_REFRESH_BUFFER_MS) {
|
|
38492
|
+
return { token: envSession, expiresAt: exp * 1e3 };
|
|
38300
38493
|
}
|
|
38301
38494
|
} catch {
|
|
38302
38495
|
}
|
|
38303
38496
|
}
|
|
38304
38497
|
}
|
|
38305
|
-
|
|
38498
|
+
return this.refresh();
|
|
38306
38499
|
}
|
|
38307
38500
|
/**
|
|
38308
38501
|
* Refresh the session using the mobile API endpoint.
|
|
38309
38502
|
* POST /v3/sessions/refresh with the refresh token JWT.
|
|
38310
|
-
* Returns a new session_token (30-min)
|
|
38503
|
+
* Returns a new session_token (30-min) as a {@link MintedToken}.
|
|
38311
38504
|
*
|
|
38312
38505
|
* The refresh token comes from `resolveRefreshToken()`, which tries
|
|
38313
38506
|
* (1) ZOLA_REFRESH_TOKEN env var, then (2) the fetchproxy 0.3.0 extension's
|
|
@@ -38336,8 +38529,7 @@ To fix: set ZOLA_REFRESH_TOKEN, or install the fetchproxy extension and sign int
|
|
|
38336
38529
|
const result = await response.json();
|
|
38337
38530
|
const { session_token } = result.data;
|
|
38338
38531
|
const exp = decodeJwtExp(session_token);
|
|
38339
|
-
|
|
38340
|
-
this.sessionExpiry = new Date(exp * 1e3);
|
|
38532
|
+
return { token: session_token, expiresAt: exp * 1e3 };
|
|
38341
38533
|
}
|
|
38342
38534
|
};
|
|
38343
38535
|
var client = new ZolaClient();
|
|
@@ -40303,7 +40495,7 @@ function registerEventInvitationTools(server) {
|
|
|
40303
40495
|
}
|
|
40304
40496
|
|
|
40305
40497
|
// src/index.ts
|
|
40306
|
-
var VERSION = "1.4.
|
|
40498
|
+
var VERSION = "1.4.4";
|
|
40307
40499
|
await runMcp({
|
|
40308
40500
|
name: "zola-mcp",
|
|
40309
40501
|
version: VERSION,
|
package/dist/client.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { dirname, join } from 'path';
|
|
2
2
|
import { fileURLToPath } from 'url';
|
|
3
|
-
import { readEnvVar, loadDotenvSafely, decodeJwtExp, decodeJwtSessionId, formatApiError, truncateErrorMessage, } from '@chrischall/mcp-utils';
|
|
3
|
+
import { readEnvVar, loadDotenvSafely, decodeJwtExp, decodeJwtSessionId, formatApiError, truncateErrorMessage, createCachedTokenSource, } from '@chrischall/mcp-utils';
|
|
4
4
|
import { resolveRefreshToken } from './auth.js';
|
|
5
5
|
// Load `.env` next to the compiled entry point. `loadDotenvSafely` is a
|
|
6
6
|
// no-throw loader: in bundled mode (no resolvable `dotenv`) it returns false
|
|
@@ -8,18 +8,29 @@ import { resolveRefreshToken } from './auth.js';
|
|
|
8
8
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
9
|
await loadDotenvSafely({ path: join(__dirname, '..', '.env'), override: false });
|
|
10
10
|
const MOBILE_BASE_URL = 'https://mobile-api.zola.com';
|
|
11
|
+
// Refresh the session token this long before its JWT `exp` — the same 5-minute
|
|
12
|
+
// comfort margin the hand-rolled cache used for both the "still valid" check and
|
|
13
|
+
// the `ZOLA_SESSION_TOKEN` seed's freshness gate.
|
|
14
|
+
const SESSION_REFRESH_BUFFER_MS = 5 * 60 * 1000;
|
|
11
15
|
export class ZolaClient {
|
|
12
|
-
sessionToken = null;
|
|
13
|
-
sessionExpiry = null;
|
|
14
16
|
cachedContext = null;
|
|
15
17
|
// WAF requires x-zola-session-id on all mobile-api.zola.com requests
|
|
16
18
|
deviceSessionId = crypto.randomUUID().toUpperCase();
|
|
19
|
+
// The `ZOLA_SESSION_TOKEN` seed is a cold-start-only shortcut, exactly as the
|
|
20
|
+
// old `ensureSession` gated it on `sessionToken === null`; a 401 re-mint goes
|
|
21
|
+
// straight to `refresh()`, never back to the env token.
|
|
22
|
+
triedEnvSeed = false;
|
|
23
|
+
// Single-flight cached mint: caches the 30-min session JWT until 5 min before
|
|
24
|
+
// its `exp`, coalesces concurrent mints, and re-mints on demand after a 401.
|
|
25
|
+
session = createCachedTokenSource({
|
|
26
|
+
mint: () => this.mintSession(),
|
|
27
|
+
bufferMs: SESSION_REFRESH_BUFFER_MS,
|
|
28
|
+
});
|
|
17
29
|
/**
|
|
18
30
|
* Make a request to the Zola mobile API (mobile-api.zola.com).
|
|
19
31
|
* Uses Bearer JWT auth with x-zola-session-id header.
|
|
20
32
|
*/
|
|
21
33
|
async requestMobile(method, path, body) {
|
|
22
|
-
await this.ensureSession();
|
|
23
34
|
return this.doRequest(method, path, body);
|
|
24
35
|
}
|
|
25
36
|
/**
|
|
@@ -29,7 +40,6 @@ export class ZolaClient {
|
|
|
29
40
|
* raw bytes and the server's content-type so callers can pass it through.
|
|
30
41
|
*/
|
|
31
42
|
async requestMobileBinary(method, path, body) {
|
|
32
|
-
await this.ensureSession();
|
|
33
43
|
const response = await this.sendWithRetry(method, path, body, false, false, '*/*');
|
|
34
44
|
const contentType = response.headers.get('content-type') ?? 'application/octet-stream';
|
|
35
45
|
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
@@ -75,10 +85,14 @@ export class ZolaClient {
|
|
|
75
85
|
return (text ? JSON.parse(text) : null);
|
|
76
86
|
}
|
|
77
87
|
async sendWithRetry(method, path, body, isAuthRetry = false, isRateRetry = false, accept = 'application/json') {
|
|
78
|
-
|
|
88
|
+
// Current session token from the cache (mints/refreshes as needed). The
|
|
89
|
+
// per-request `x-zola-user-session-id` is derived from it every call, so a
|
|
90
|
+
// re-minted token below carries its own session id on the replay.
|
|
91
|
+
const token = await this.session.getToken();
|
|
92
|
+
const sessionId = decodeJwtSessionId(token);
|
|
79
93
|
const headers = {
|
|
80
94
|
accept,
|
|
81
|
-
authorization: `Bearer ${
|
|
95
|
+
authorization: `Bearer ${token}`,
|
|
82
96
|
'x-zola-platform-type': 'iphone_app',
|
|
83
97
|
'x-zola-session-id': this.deviceSessionId,
|
|
84
98
|
'user-agent': 'Zola/42.5.0 (iPad; iOS 26.4; Scale/2.0)',
|
|
@@ -92,9 +106,8 @@ export class ZolaClient {
|
|
|
92
106
|
...(body !== undefined ? { body: JSON.stringify(body) } : {}),
|
|
93
107
|
});
|
|
94
108
|
if (response.status === 401 && !isAuthRetry) {
|
|
95
|
-
|
|
96
|
-
this.
|
|
97
|
-
await this.refresh();
|
|
109
|
+
// Drop the cached token so the replay's getToken re-mints via refresh().
|
|
110
|
+
this.session.invalidate();
|
|
98
111
|
return this.sendWithRetry(method, path, body, true, isRateRetry, accept);
|
|
99
112
|
}
|
|
100
113
|
if (response.status === 429) {
|
|
@@ -110,22 +123,24 @@ export class ZolaClient {
|
|
|
110
123
|
}
|
|
111
124
|
return response;
|
|
112
125
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
126
|
+
/**
|
|
127
|
+
* Mint a session token for the cache. On the very first mint, use a valid
|
|
128
|
+
* `ZOLA_SESSION_TOKEN` if present (a cold-start shortcut that skips the
|
|
129
|
+
* initial refresh); otherwise, and on every later mint, refresh via the
|
|
130
|
+
* mobile API. Returns `{ token, expiresAt }` so the cache serves it until
|
|
131
|
+
* `SESSION_REFRESH_BUFFER_MS` before the JWT's `exp`.
|
|
132
|
+
*/
|
|
133
|
+
async mintSession() {
|
|
134
|
+
// Cold-start seed: use ZOLA_SESSION_TOKEN if present and comfortably
|
|
135
|
+
// unexpired. Only attempted once — a 401 re-mint always goes to refresh().
|
|
136
|
+
if (!this.triedEnvSeed) {
|
|
137
|
+
this.triedEnvSeed = true;
|
|
121
138
|
const envSession = readEnvVar('ZOLA_SESSION_TOKEN');
|
|
122
139
|
if (envSession) {
|
|
123
140
|
try {
|
|
124
141
|
const exp = decodeJwtExp(envSession);
|
|
125
|
-
if (exp * 1000 - Date.now() >
|
|
126
|
-
|
|
127
|
-
this.sessionExpiry = new Date(exp * 1000);
|
|
128
|
-
return;
|
|
142
|
+
if (exp * 1000 - Date.now() > SESSION_REFRESH_BUFFER_MS) {
|
|
143
|
+
return { token: envSession, expiresAt: exp * 1000 };
|
|
129
144
|
}
|
|
130
145
|
}
|
|
131
146
|
catch {
|
|
@@ -133,12 +148,12 @@ export class ZolaClient {
|
|
|
133
148
|
}
|
|
134
149
|
}
|
|
135
150
|
}
|
|
136
|
-
|
|
151
|
+
return this.refresh();
|
|
137
152
|
}
|
|
138
153
|
/**
|
|
139
154
|
* Refresh the session using the mobile API endpoint.
|
|
140
155
|
* POST /v3/sessions/refresh with the refresh token JWT.
|
|
141
|
-
* Returns a new session_token (30-min)
|
|
156
|
+
* Returns a new session_token (30-min) as a {@link MintedToken}.
|
|
142
157
|
*
|
|
143
158
|
* The refresh token comes from `resolveRefreshToken()`, which tries
|
|
144
159
|
* (1) ZOLA_REFRESH_TOKEN env var, then (2) the fetchproxy 0.3.0 extension's
|
|
@@ -168,8 +183,7 @@ export class ZolaClient {
|
|
|
168
183
|
const result = (await response.json());
|
|
169
184
|
const { session_token } = result.data;
|
|
170
185
|
const exp = decodeJwtExp(session_token);
|
|
171
|
-
|
|
172
|
-
this.sessionExpiry = new Date(exp * 1000);
|
|
186
|
+
return { token: session_token, expiresAt: exp * 1000 };
|
|
173
187
|
}
|
|
174
188
|
}
|
|
175
189
|
export const client = new ZolaClient();
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,7 @@ import { registerWebsiteThemeTools } from './tools/website-theme.js';
|
|
|
12
12
|
import { registerRegistryItemTools } from './tools/registry-items.js';
|
|
13
13
|
import { registerInvitationTools } from './tools/invitations.js';
|
|
14
14
|
import { registerEventInvitationTools } from './tools/event-invitations.js';
|
|
15
|
-
const VERSION = '1.4.
|
|
15
|
+
const VERSION = '1.4.4'; // x-release-please-version
|
|
16
16
|
await runMcp({
|
|
17
17
|
name: 'zola-mcp',
|
|
18
18
|
version: VERSION,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zola-mcp",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.4",
|
|
4
4
|
"mcpName": "io.github.chrischall/zola-mcp",
|
|
5
5
|
"description": "Zola wedding MCP server for Claude",
|
|
6
6
|
"author": "Claude Code (AI) <https://www.anthropic.com/claude>",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"test:watch": "vitest"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@chrischall/mcp-utils": "^0.
|
|
49
|
+
"@chrischall/mcp-utils": "^0.12.0",
|
|
50
50
|
"@fetchproxy/bootstrap": "^1.3.0",
|
|
51
51
|
"@fetchproxy/server": "^1.3.0",
|
|
52
52
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"zod": "^4.4.3"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
|
-
"@types/node": "^
|
|
57
|
+
"@types/node": "^26.0.0",
|
|
58
58
|
"@vitest/coverage-v8": "^4.1.8",
|
|
59
59
|
"esbuild": "^0.28.0",
|
|
60
60
|
"typescript": "^6.0.3",
|
package/server.json
CHANGED
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
"url": "https://github.com/chrischall/zola-mcp",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "1.4.
|
|
9
|
+
"version": "1.4.4",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "zola-mcp",
|
|
14
|
-
"version": "1.4.
|
|
14
|
+
"version": "1.4.4",
|
|
15
15
|
"transport": {
|
|
16
16
|
"type": "stdio"
|
|
17
17
|
},
|