@chrischall/eventbrite-mcp 0.3.2 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,7 +7,7 @@
7
7
  },
8
8
  "metadata": {
9
9
  "description": "MCP server for Eventbrite — your tickets and orders, organizer data, and public event discovery",
10
- "version": "0.3.2"
10
+ "version": "0.3.3"
11
11
  },
12
12
  "plugins": [
13
13
  {
@@ -15,7 +15,7 @@
15
15
  "displayName": "Eventbrite",
16
16
  "source": "./",
17
17
  "description": "MCP server for Eventbrite — tickets, orders, organizer data, and public event search. Account tools use a personal API token; discovery search routes through the user's signed-in eventbrite.com tab via the fetchproxy bridge, reusing their authenticated session.",
18
- "version": "0.3.2",
18
+ "version": "0.3.3",
19
19
  "author": {
20
20
  "name": "Chris Hall"
21
21
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "eventbrite-mcp",
3
3
  "displayName": "Eventbrite",
4
- "version": "0.3.2",
4
+ "version": "0.3.3",
5
5
  "description": "MCP server for Eventbrite — your tickets and orders, organizer data, and public event discovery",
6
6
  "author": {
7
7
  "name": "Chris Hall",
package/dist/bundle.js CHANGED
@@ -11085,7 +11085,7 @@ var require_websocket = __commonJS({
11085
11085
  var http = __require("http");
11086
11086
  var net = __require("net");
11087
11087
  var tls = __require("tls");
11088
- var { randomBytes, createHash } = __require("crypto");
11088
+ var { randomBytes: randomBytes2, createHash } = __require("crypto");
11089
11089
  var { Duplex, Readable } = __require("stream");
11090
11090
  var { URL: URL2 } = __require("url");
11091
11091
  var PerMessageDeflate2 = require_permessage_deflate();
@@ -11623,7 +11623,7 @@ var require_websocket = __commonJS({
11623
11623
  }
11624
11624
  }
11625
11625
  const defaultPort = isSecure ? 443 : 80;
11626
- const key = randomBytes(16).toString("base64");
11626
+ const key = randomBytes2(16).toString("base64");
11627
11627
  const request = isSecure ? https.request : http.request;
11628
11628
  const protocolSet = /* @__PURE__ */ new Set();
11629
11629
  let perMessageDeflate;
@@ -12636,9 +12636,29 @@ var init_session = __esm({
12636
12636
  outboundSeq = 0;
12637
12637
  lastInboundSeq = 0;
12638
12638
  inflightInbound = /* @__PURE__ */ new Set();
12639
+ saturationWarned = false;
12639
12640
  constructor(sessionKey) {
12640
12641
  this.sessionKey = sessionKey;
12641
12642
  }
12643
+ /**
12644
+ * Whether the caller holding this claim verdict should log saturation now.
12645
+ * True only on the TRANSITION into it: a line per dropped frame turned a
12646
+ * flood — or a set that never drains — into a log flood amplifying the very
12647
+ * condition it reported (#376). An `'ok'` claim re-arms the latch, since it
12648
+ * is the one signal every caller sees that the set has drained below the
12649
+ * bound; a replay needs no capacity and leaves it alone. Latched per
12650
+ * session, so a renegotiated one starts armed. A set hovering at the bound
12651
+ * can still alternate ok/saturated, but that logs at the rate frames finish
12652
+ * opening, not the rate they arrive.
12653
+ */
12654
+ saturationWarningDue(claim2) {
12655
+ if (claim2 === "ok")
12656
+ this.saturationWarned = false;
12657
+ if (claim2 !== "saturated" || this.saturationWarned)
12658
+ return false;
12659
+ this.saturationWarned = true;
12660
+ return true;
12661
+ }
12642
12662
  nextOutboundSeq() {
12643
12663
  this.outboundSeq += 1;
12644
12664
  return this.outboundSeq;
@@ -12649,19 +12669,21 @@ var init_session = __esm({
12649
12669
  * the first `await` of the receive path, or a duplicate frame read in the
12650
12670
  * same pass will be judged against a counter neither frame has moved yet.
12651
12671
  *
12652
- * Answering false means "not this frame's to process" replayed, or a
12653
- * duplicate of one still in flight. Every true MUST be answered by exactly
12654
- * one {@link commitInboundSeq} or {@link releaseInboundSeq}.
12672
+ * Anything but `'ok'` means "not this frame's to process": `'replay'` for
12673
+ * a spent seq or a duplicate of one still in flight, `'saturated'` when the
12674
+ * in-flight bound is full. Replay is checked first, since refusing a stale
12675
+ * seq needs no capacity. Every `'ok'` MUST be answered by exactly one
12676
+ * {@link commitInboundSeq} or {@link releaseInboundSeq}.
12655
12677
  */
12656
12678
  claimInboundSeq(seq) {
12657
12679
  if (seq <= this.lastInboundSeq)
12658
- return false;
12680
+ return "replay";
12659
12681
  if (this.inflightInbound.has(seq))
12660
- return false;
12682
+ return "replay";
12661
12683
  if (this.inflightInbound.size >= MAX_INFLIGHT_INBOUND_SEQS)
12662
- return false;
12684
+ return "saturated";
12663
12685
  this.inflightInbound.add(seq);
12664
- return true;
12686
+ return "ok";
12665
12687
  }
12666
12688
  /**
12667
12689
  * Give a claim back without spending the seq — for a frame that did not
@@ -12763,7 +12785,8 @@ var init_session_ready = __esm({
12763
12785
  });
12764
12786
 
12765
12787
  // node_modules/@fetchproxy/server/dist/identity.js
12766
- import { readFile, writeFile, mkdir, chmod } from "node:fs/promises";
12788
+ import { readFile, readdir, writeFile, mkdir, chmod, rename, rm, unlink } from "node:fs/promises";
12789
+ import { randomBytes } from "node:crypto";
12767
12790
  import { isAbsolute, join as join2 } from "node:path";
12768
12791
  import { homedir } from "node:os";
12769
12792
  function envIdentityDir() {
@@ -12817,16 +12840,45 @@ function parseIdentity(text) {
12817
12840
  createdAt: j.createdAt
12818
12841
  };
12819
12842
  }
12843
+ async function openIdentityDir(dir) {
12844
+ await mkdir(dir, { recursive: true, mode: 448 });
12845
+ try {
12846
+ await chmod(dir, 448);
12847
+ } catch (e) {
12848
+ const code = e.code;
12849
+ if (code !== "EROFS" && code !== "EPERM")
12850
+ throw e;
12851
+ }
12852
+ }
12853
+ async function sweepStagingFiles(dir, serverName) {
12854
+ const base = safeIdentityFileBase(serverName).replace(/[.]/g, "\\.");
12855
+ const staging = new RegExp(`^${base}\\.json\\.[0-9a-f]{${STAGING_BYTES * 2}}\\.tmp$`);
12856
+ let names;
12857
+ try {
12858
+ names = await readdir(dir);
12859
+ } catch {
12860
+ return;
12861
+ }
12862
+ await Promise.all(names.filter((name) => staging.test(name)).map((name) => unlink(join2(dir, name)).catch(() => void 0)));
12863
+ }
12820
12864
  async function writeIdentityFile(dir, serverName, id) {
12821
12865
  const path = identityFilePath(dir, serverName);
12822
- await mkdir(dir, { recursive: true, mode: 448 });
12823
- await writeFile(path, serializeIdentity(id), { mode: 384 });
12824
- await chmod(path, 384);
12866
+ await openIdentityDir(dir);
12867
+ await sweepStagingFiles(dir, serverName);
12868
+ const tmp = `${path}.${randomBytes(STAGING_BYTES).toString("hex")}.tmp`;
12869
+ try {
12870
+ await writeFile(tmp, serializeIdentity(id), { mode: 384, flag: "wx" });
12871
+ await chmod(tmp, 384);
12872
+ await rename(tmp, path);
12873
+ } catch (e) {
12874
+ await rm(tmp, { force: true });
12875
+ throw e;
12876
+ }
12825
12877
  return path;
12826
12878
  }
12827
12879
  async function loadOrCreateIdentity(serverName, dir = defaultIdentityDir()) {
12828
12880
  const path = identityFilePath(dir, serverName);
12829
- await mkdir(dir, { recursive: true, mode: 448 });
12881
+ await openIdentityDir(dir);
12830
12882
  try {
12831
12883
  return parseIdentity(await readFile(path, "utf8"));
12832
12884
  } catch (e) {
@@ -12837,17 +12889,18 @@ async function loadOrCreateIdentity(serverName, dir = defaultIdentityDir()) {
12837
12889
  await writeIdentityFile(dir, serverName, id);
12838
12890
  return id;
12839
12891
  }
12840
- var SAFE_PLAIN, SAFE_SCOPED;
12892
+ var SAFE_PLAIN, SAFE_SCOPED, STAGING_BYTES;
12841
12893
  var init_identity = __esm({
12842
12894
  "node_modules/@fetchproxy/server/dist/identity.js"() {
12843
12895
  init_dist();
12844
12896
  SAFE_PLAIN = /^[A-Za-z0-9._-]+$/;
12845
12897
  SAFE_SCOPED = /^@[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/;
12898
+ STAGING_BYTES = 6;
12846
12899
  }
12847
12900
  });
12848
12901
 
12849
12902
  // node_modules/@fetchproxy/server/dist/extension-trust.js
12850
- import { readFile as readFile2, writeFile as writeFile2, rename, unlink, mkdir as mkdir2, chmod as chmod2 } from "node:fs/promises";
12903
+ import { readFile as readFile2, writeFile as writeFile2, rename as rename2, unlink as unlink2, mkdir as mkdir2, chmod as chmod2 } from "node:fs/promises";
12851
12904
  import { isAbsolute as isAbsolute2, join as join3 } from "node:path";
12852
12905
  function envTrustDir(env = process.env) {
12853
12906
  const raw = env[TRUST_DIR_ENV];
@@ -12947,7 +13000,7 @@ async function writeExtensionPin(serverName, pin, dir = defaultTrustDir()) {
12947
13000
  const tmp = `${path}.tmp`;
12948
13001
  await writeFile2(tmp, JSON.stringify(pin, null, 2), { mode: 384 });
12949
13002
  await chmod2(tmp, 384);
12950
- await rename(tmp, path);
13003
+ await rename2(tmp, path);
12951
13004
  }
12952
13005
  var TRUST_DIR_ENV, TRUST_NEW_EXTENSION_ENV;
12953
13006
  var init_extension_trust = __esm({
@@ -13302,7 +13355,11 @@ async function startHost(opts) {
13302
13355
  const session = ownSession;
13303
13356
  if (!session)
13304
13357
  return;
13305
- if (!session.claimInboundSeq(frame.seq))
13358
+ const claim2 = session.claimInboundSeq(frame.seq);
13359
+ if (session.saturationWarningDue(claim2)) {
13360
+ console.warn(`[fetchproxy] ${opts.ownServerName}: dropped an inbound frame (seq ${frame.seq}) unread \u2014 too many frames from the extension are still being opened (inbound claims saturated). Not a replay. Further drops are not logged until the in-flight set drains.`);
13361
+ }
13362
+ if (claim2 !== "ok")
13306
13363
  return;
13307
13364
  let inner;
13308
13365
  try {
@@ -13450,7 +13507,9 @@ var init_host = __esm({
13450
13507
 
13451
13508
  // node_modules/@fetchproxy/server/dist/peer.js
13452
13509
  async function startPeer(opts) {
13453
- const ws = new import_websocket.default(`ws://${opts.host}:${opts.port}`);
13510
+ const ws = new import_websocket.default(`ws://${opts.host}:${opts.port}`, {
13511
+ maxPayload: opts.maxPayloadBytes ?? MAX_PAYLOAD_BYTES
13512
+ });
13454
13513
  await new Promise((resolve, reject) => {
13455
13514
  const onOpen = () => {
13456
13515
  cleanup();
@@ -13688,7 +13747,11 @@ async function startPeer(opts) {
13688
13747
  const inboundSession = session;
13689
13748
  if (!inboundSession)
13690
13749
  return;
13691
- if (!inboundSession.claimInboundSeq(frame.seq))
13750
+ const claim2 = inboundSession.claimInboundSeq(frame.seq);
13751
+ if (inboundSession.saturationWarningDue(claim2)) {
13752
+ console.warn(`[fetchproxy] ${opts.serverName}: dropped an inbound frame (seq ${frame.seq}) unread \u2014 too many frames from the extension are still being opened (inbound claims saturated). Not a replay. Further drops are not logged until the in-flight set drains.`);
13753
+ }
13754
+ if (claim2 !== "ok")
13692
13755
  return;
13693
13756
  let result;
13694
13757
  try {
@@ -13779,6 +13842,7 @@ var init_peer = __esm({
13779
13842
  init_dist();
13780
13843
  init_build_server_hello();
13781
13844
  init_frame_size();
13845
+ init_host();
13782
13846
  init_session();
13783
13847
  init_session_ready();
13784
13848
  init_extension_trust();
@@ -45673,7 +45737,7 @@ var pageSchema = {
45673
45737
  init_errors();
45674
45738
 
45675
45739
  // src/version.ts
45676
- var VERSION = "0.3.2";
45740
+ var VERSION = "0.3.3";
45677
45741
 
45678
45742
  // src/client.ts
45679
45743
  import { dirname, join } from "path";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = '0.3.2'; // x-release-please-version
1
+ export const VERSION = '0.3.3'; // x-release-please-version
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chrischall/eventbrite-mcp",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "mcpName": "io.github.chrischall/eventbrite-mcp",
5
5
  "description": "Eventbrite MCP server for Claude — developed and maintained by AI (Claude Code)",
6
6
  "author": "Claude Code (AI) <https://www.anthropic.com/claude>",
@@ -46,7 +46,7 @@
46
46
  },
47
47
  "dependencies": {
48
48
  "@chrischall/mcp-utils": "^0.26.1",
49
- "@fetchproxy/server": "^3.0.0",
49
+ "@fetchproxy/server": "^3.0.1",
50
50
  "@modelcontextprotocol/sdk": "^1.30.0",
51
51
  "dotenv": "^17.4.0",
52
52
  "zod": "^4.5.4"
package/server.json CHANGED
@@ -6,12 +6,12 @@
6
6
  "url": "https://github.com/chrischall/eventbrite-mcp",
7
7
  "source": "github"
8
8
  },
9
- "version": "0.3.2",
9
+ "version": "0.3.3",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "@chrischall/eventbrite-mcp",
14
- "version": "0.3.2",
14
+ "version": "0.3.3",
15
15
  "transport": {
16
16
  "type": "stdio"
17
17
  },