@chrischall/tripadvisor-mcp 0.3.0 → 0.3.2

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 the TripAdvisor Terra API — location search, details, photos, and reviews",
10
- "version": "0.3.0"
10
+ "version": "0.3.2"
11
11
  },
12
12
  "plugins": [
13
13
  {
@@ -15,7 +15,7 @@
15
15
  "displayName": "TripAdvisor",
16
16
  "source": "./",
17
17
  "description": "TripAdvisor travel data via the Terra API — search hotels, restaurants, and attractions, with details, photos, and reviews",
18
- "version": "0.3.0",
18
+ "version": "0.3.2",
19
19
  "author": {
20
20
  "name": "Chris Hall"
21
21
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "tripadvisor-mcp",
3
3
  "displayName": "TripAdvisor",
4
- "version": "0.3.0",
4
+ "version": "0.3.2",
5
5
  "description": "MCP server for the TripAdvisor Terra API — location search, details, photos, and reviews",
6
6
  "author": {
7
7
  "name": "Chris Hall",
package/dist/bundle.js CHANGED
@@ -3776,6 +3776,7 @@ var require_fast_uri = __commonJS({
3776
3776
  return uriTokens.join("");
3777
3777
  }
3778
3778
  var URI_PARSE = /^(?:([^#/:?]+):)?(?:\/\/((?:([^#/?@]*)@)?(\[[^#/?\]]+\]|[^#/:?]*)(?::(\d*))?))?([^#?]*)(?:\?([^#]*))?(?:#((?:.|[\n\r])*))?/u;
3779
+ var AUTHORITY_PREFIX = /^(?:[^#/:?]+:)?\/\/([^/?#]*)/;
3779
3780
  function getParseError(parsed, matches) {
3780
3781
  if (matches[2] !== void 0 && parsed.path && parsed.path[0] !== "/") {
3781
3782
  return 'URI path must start with "/" when authority is present.';
@@ -3805,6 +3806,11 @@ var require_fast_uri = __commonJS({
3805
3806
  uri = "//" + uri;
3806
3807
  }
3807
3808
  }
3809
+ const authorityMatch = uri.match(AUTHORITY_PREFIX);
3810
+ if (authorityMatch !== null && authorityMatch[1].indexOf("\\") !== -1) {
3811
+ parsed.error = "URI authority must not contain a literal backslash.";
3812
+ malformedAuthorityOrPort = true;
3813
+ }
3808
3814
  const matches = uri.match(URI_PARSE);
3809
3815
  if (matches) {
3810
3816
  parsed.scheme = matches[1];
@@ -7693,6 +7699,7 @@ var require_receiver = __commonJS({
7693
7699
  this._opcode = 0;
7694
7700
  this._totalPayloadLength = 0;
7695
7701
  this._messageLength = 0;
7702
+ this._numFragments = 0;
7696
7703
  this._fragments = [];
7697
7704
  this._errored = false;
7698
7705
  this._loop = false;
@@ -8043,23 +8050,23 @@ var require_receiver = __commonJS({
8043
8050
  this.controlMessage(data, cb);
8044
8051
  return;
8045
8052
  }
8053
+ if (this._maxFragments > 0 && ++this._numFragments > this._maxFragments) {
8054
+ const error51 = this.createError(
8055
+ RangeError,
8056
+ "Too many message fragments",
8057
+ false,
8058
+ 1008,
8059
+ "WS_ERR_TOO_MANY_BUFFERED_PARTS"
8060
+ );
8061
+ cb(error51);
8062
+ return;
8063
+ }
8046
8064
  if (this._compressed) {
8047
8065
  this._state = INFLATING;
8048
8066
  this.decompress(data, cb);
8049
8067
  return;
8050
8068
  }
8051
8069
  if (data.length) {
8052
- if (this._maxFragments > 0 && this._fragments.length >= this._maxFragments) {
8053
- const error51 = this.createError(
8054
- RangeError,
8055
- "Too many message fragments",
8056
- false,
8057
- 1008,
8058
- "WS_ERR_TOO_MANY_BUFFERED_PARTS"
8059
- );
8060
- cb(error51);
8061
- return;
8062
- }
8063
8070
  this._messageLength = this._totalPayloadLength;
8064
8071
  this._fragments.push(data);
8065
8072
  }
@@ -8089,17 +8096,6 @@ var require_receiver = __commonJS({
8089
8096
  cb(error51);
8090
8097
  return;
8091
8098
  }
8092
- if (this._maxFragments > 0 && this._fragments.length >= this._maxFragments) {
8093
- const error51 = this.createError(
8094
- RangeError,
8095
- "Too many message fragments",
8096
- false,
8097
- 1008,
8098
- "WS_ERR_TOO_MANY_BUFFERED_PARTS"
8099
- );
8100
- cb(error51);
8101
- return;
8102
- }
8103
8099
  this._fragments.push(buf);
8104
8100
  }
8105
8101
  this.dataMessage(cb);
@@ -8122,6 +8118,7 @@ var require_receiver = __commonJS({
8122
8118
  this._totalPayloadLength = 0;
8123
8119
  this._messageLength = 0;
8124
8120
  this._fragmented = 0;
8121
+ this._numFragments = 0;
8125
8122
  this._fragments = [];
8126
8123
  if (this._opcode === 2) {
8127
8124
  let data;
@@ -9622,8 +9619,8 @@ var require_websocket = __commonJS({
9622
9619
  autoPong: true,
9623
9620
  closeTimeout: CLOSE_TIMEOUT,
9624
9621
  protocolVersion: protocolVersions[1],
9625
- maxBufferedChunks: 1024 * 1024,
9626
- maxFragments: 128 * 1024,
9622
+ maxBufferedChunks: 256 * 1024,
9623
+ maxFragments: 16 * 1024,
9627
9624
  maxPayload: 100 * 1024 * 1024,
9628
9625
  skipUTF8Validation: false,
9629
9626
  perMessageDeflate: true,
@@ -10210,9 +10207,9 @@ var require_websocket_server = __commonJS({
10210
10207
  * called
10211
10208
  * @param {Function} [options.handleProtocols] A hook to handle protocols
10212
10209
  * @param {String} [options.host] The hostname where to bind the server
10213
- * @param {Number} [options.maxBufferedChunks=1048576] The maximum number of
10210
+ * @param {Number} [options.maxBufferedChunks=262144] The maximum number of
10214
10211
  * buffered data chunks
10215
- * @param {Number} [options.maxFragments=131072] The maximum number of message
10212
+ * @param {Number} [options.maxFragments=16384] The maximum number of message
10216
10213
  * fragments
10217
10214
  * @param {Number} [options.maxPayload=104857600] The maximum allowed message
10218
10215
  * size
@@ -10235,8 +10232,8 @@ var require_websocket_server = __commonJS({
10235
10232
  options = {
10236
10233
  allowSynchronousEvents: true,
10237
10234
  autoPong: true,
10238
- maxBufferedChunks: 1024 * 1024,
10239
- maxFragments: 128 * 1024,
10235
+ maxBufferedChunks: 256 * 1024,
10236
+ maxFragments: 16 * 1024,
10240
10237
  maxPayload: 100 * 1024 * 1024,
10241
10238
  skipUTF8Validation: false,
10242
10239
  perMessageDeflate: false,
@@ -34939,7 +34936,7 @@ var pageSchema = {
34939
34936
  };
34940
34937
 
34941
34938
  // src/version.ts
34942
- var VERSION = "0.3.0";
34939
+ var VERSION = "0.3.2";
34943
34940
 
34944
34941
  // src/client.ts
34945
34942
  import { dirname, join } from "node:path";
@@ -35274,7 +35271,8 @@ var KNOWN_CAPABILITIES = /* @__PURE__ */ new Set([
35274
35271
  "capture_redirect",
35275
35272
  "read_indexed_db",
35276
35273
  "read_dom",
35277
- "download"
35274
+ "download",
35275
+ "graphql"
35278
35276
  ]);
35279
35277
 
35280
35278
  // node_modules/@fetchproxy/protocol/dist/mcp-id.js
@@ -35577,6 +35575,7 @@ function assertIndexedDbScopesArray(value, label) {
35577
35575
  }
35578
35576
  var DOM_SELECTOR_RE = /^[^-]{1,512}$/;
35579
35577
  var DOM_ATTRIBUTE_RE = /^[A-Za-z_:][A-Za-z0-9_:.\-]{0,127}$/;
35578
+ var GRAPHQL_OP_NAME_RE = /^[_A-Za-z][_0-9A-Za-z]{0,127}$/;
35580
35579
  function assertDomSelectorsArray(value, label) {
35581
35580
  if (!Array.isArray(value)) {
35582
35581
  throw new ProtocolError(`${label}: expected array, got ${typeof value}`);
@@ -35613,6 +35612,37 @@ function assertDomSelectorsArray(value, label) {
35613
35612
  }
35614
35613
  }
35615
35614
  }
35615
+ function assertGraphqlOpsArray(value, label) {
35616
+ if (!Array.isArray(value)) {
35617
+ throw new ProtocolError(`${label}: expected array, got ${typeof value}`);
35618
+ }
35619
+ const seen = /* @__PURE__ */ new Set();
35620
+ for (let i = 0; i < value.length; i++) {
35621
+ const entry = value[i];
35622
+ assertObject(entry, `${label}[${i}]`);
35623
+ if (entry.name === void 0) {
35624
+ throw new ProtocolError(`${label}[${i}].name: missing`);
35625
+ }
35626
+ if (entry.operationName === void 0) {
35627
+ throw new ProtocolError(`${label}[${i}].operationName: missing`);
35628
+ }
35629
+ if (typeof entry.name !== "string" || !SCOPE_KEY_RE.test(entry.name)) {
35630
+ throw new ProtocolError(`${label}[${i}].name: invalid ${JSON.stringify(entry.name)}`);
35631
+ }
35632
+ if (typeof entry.operationName !== "string" || !GRAPHQL_OP_NAME_RE.test(entry.operationName)) {
35633
+ throw new ProtocolError(`${label}[${i}].operationName: invalid ${JSON.stringify(entry.operationName)}`);
35634
+ }
35635
+ if (seen.has(entry.name)) {
35636
+ throw new ProtocolError(`${label}: duplicate name ${JSON.stringify(entry.name)}`);
35637
+ }
35638
+ seen.add(entry.name);
35639
+ for (const k of Object.keys(entry)) {
35640
+ if (k !== "name" && k !== "operationName") {
35641
+ throw new ProtocolError(`${label}[${i}]: unexpected field ${JSON.stringify(k)}`);
35642
+ }
35643
+ }
35644
+ }
35645
+ }
35616
35646
  function validateFrame(raw) {
35617
35647
  assertObject(raw, "frame");
35618
35648
  const t = raw.type;
@@ -35691,6 +35721,9 @@ function validateHello(raw) {
35691
35721
  if (raw.domSelectors !== void 0) {
35692
35722
  assertDomSelectorsArray(raw.domSelectors, "hello.domSelectors");
35693
35723
  }
35724
+ if (raw.graphqlOps !== void 0) {
35725
+ assertGraphqlOpsArray(raw.graphqlOps, "hello.graphqlOps");
35726
+ }
35694
35727
  assertBase64(raw.identityX25519Pub, "hello.identityX25519Pub");
35695
35728
  assertBase64(raw.identityEd25519Pub, "hello.identityEd25519Pub");
35696
35729
  assertBase64(raw.sessionNonce, "hello.sessionNonce");
@@ -35940,6 +35973,28 @@ function validateInnerRequest(raw) {
35940
35973
  }
35941
35974
  return raw;
35942
35975
  }
35976
+ if (raw.op === "graphql_query") {
35977
+ assertObject(raw.init, "inner.init");
35978
+ if (raw.init.name === void 0)
35979
+ throw new ProtocolError("inner.init.name: missing");
35980
+ if (raw.init.variables === void 0) {
35981
+ throw new ProtocolError("inner.init.variables: missing");
35982
+ }
35983
+ assertString(raw.init.name, "inner.init.name");
35984
+ if (raw.init.name.length === 0) {
35985
+ throw new ProtocolError("inner.init.name: must be non-empty");
35986
+ }
35987
+ assertObject(raw.init.variables, "inner.init.variables");
35988
+ if (raw.init.tabUrl !== void 0) {
35989
+ assertString(raw.init.tabUrl, "inner.init.tabUrl");
35990
+ }
35991
+ for (const k of Object.keys(raw.init)) {
35992
+ if (k !== "name" && k !== "variables" && k !== "tabUrl") {
35993
+ throw new ProtocolError(`inner.init: unexpected field ${JSON.stringify(k)} on graphql_query`);
35994
+ }
35995
+ }
35996
+ return raw;
35997
+ }
35943
35998
  if (raw.op === "download") {
35944
35999
  assertObject(raw.init, "inner.init");
35945
36000
  if (raw.init.url === void 0) {
@@ -35965,7 +36020,7 @@ function validateInnerRequest(raw) {
35965
36020
  }
35966
36021
  return raw;
35967
36022
  }
35968
- throw new ProtocolError(`inner.op: must be one of "fetch", "read_cookies", "read_local_storage", "read_session_storage", "capture_request_header", "capture_redirect", "read_indexed_db", "read_dom", "download"; got ${JSON.stringify(raw.op)}`);
36023
+ throw new ProtocolError(`inner.op: must be one of "fetch", "read_cookies", "read_local_storage", "read_session_storage", "capture_request_header", "capture_redirect", "read_indexed_db", "read_dom", "download", "graphql_query"; got ${JSON.stringify(raw.op)}`);
35969
36024
  }
35970
36025
  function assertNonEmptyKeyArray(value, label) {
35971
36026
  if (!Array.isArray(value)) {
@@ -35996,6 +36051,10 @@ function assertStringMap(value, label) {
35996
36051
  }
35997
36052
  }
35998
36053
  }
36054
+ var KNOWN_RESPONSE_OPS = /* @__PURE__ */ new Set([
36055
+ ...KNOWN_CAPABILITIES,
36056
+ "graphql_query"
36057
+ ]);
35999
36058
  function validateInnerResponse(raw) {
36000
36059
  assertPositiveInt(raw.id, "inner.id");
36001
36060
  if (raw.ok === true) {
@@ -36057,6 +36116,13 @@ function validateInnerResponse(raw) {
36057
36116
  assertStringMap(raw.values, "inner.values");
36058
36117
  return raw;
36059
36118
  }
36119
+ if (op === "graphql_query") {
36120
+ if (raw.data === void 0) {
36121
+ throw new ProtocolError("inner.data: missing on graphql_query response");
36122
+ }
36123
+ assertObject(raw.data, "inner.data");
36124
+ return raw;
36125
+ }
36060
36126
  if (op === "download") {
36061
36127
  assertObject(raw.value, "inner.value");
36062
36128
  assertString(raw.value.path, "inner.value.path");
@@ -36081,7 +36147,7 @@ function validateInnerResponse(raw) {
36081
36147
  if (raw.ok === false) {
36082
36148
  assertString(raw.error, "inner.error");
36083
36149
  if (raw.op !== void 0) {
36084
- if (typeof raw.op !== "string" || !KNOWN_CAPABILITIES.has(raw.op)) {
36150
+ if (typeof raw.op !== "string" || !KNOWN_RESPONSE_OPS.has(raw.op)) {
36085
36151
  throw new ProtocolError(`inner.op: unknown response op ${JSON.stringify(raw.op)}`);
36086
36152
  }
36087
36153
  }
@@ -36273,11 +36339,33 @@ async function sealInnerFrame(sessionKey, mcpId, seq, inner) {
36273
36339
  };
36274
36340
  }
36275
36341
  async function openEncryptedFrame(sessionKey, frame) {
36276
- const iv = fromB64(frame.iv);
36277
- const ct = fromB64(frame.ciphertext);
36278
- const pt = await aesGcmOpen(sessionKey, iv, ct);
36279
- const parsed = JSON.parse(dec.decode(pt));
36280
- return validateInnerFrame(parsed);
36342
+ const result = await openEncryptedFrameDetailed(sessionKey, frame);
36343
+ if (result.stage === "ok")
36344
+ return result.inner;
36345
+ throw result.error instanceof Error ? result.error : new Error(String(result.error));
36346
+ }
36347
+ async function openEncryptedFrameDetailed(sessionKey, frame) {
36348
+ let pt;
36349
+ try {
36350
+ const iv = fromB64(frame.iv);
36351
+ const ct = fromB64(frame.ciphertext);
36352
+ pt = await aesGcmOpen(sessionKey, iv, ct);
36353
+ } catch (error51) {
36354
+ return { stage: "decrypt-failed", error: error51 };
36355
+ }
36356
+ let parsed;
36357
+ try {
36358
+ parsed = JSON.parse(dec.decode(pt));
36359
+ } catch (error51) {
36360
+ return { stage: "validation-failed", error: error51, recoveredId: void 0 };
36361
+ }
36362
+ try {
36363
+ const inner = validateInnerFrame(parsed);
36364
+ return { stage: "ok", inner };
36365
+ } catch (error51) {
36366
+ const recoveredId = parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) && typeof parsed.id === "number" && Number.isInteger(parsed.id) && parsed.id > 0 ? parsed.id : void 0;
36367
+ return { stage: "validation-failed", error: error51, recoveredId };
36368
+ }
36281
36369
  }
36282
36370
 
36283
36371
  // node_modules/@fetchproxy/server/dist/election.js
@@ -36403,6 +36491,12 @@ async function buildServerHello(opts) {
36403
36491
  ...d.attribute !== void 0 ? { attribute: d.attribute } : {}
36404
36492
  }));
36405
36493
  }
36494
+ if (opts.graphqlOps && opts.graphqlOps.length > 0) {
36495
+ hello.graphqlOps = opts.graphqlOps.map((d) => ({
36496
+ name: d.name,
36497
+ operationName: d.operationName
36498
+ }));
36499
+ }
36406
36500
  return hello;
36407
36501
  }
36408
36502
 
@@ -36493,7 +36587,8 @@ async function startHost(opts) {
36493
36587
  indexedDbScopes: opts.ownIndexedDbScopes,
36494
36588
  localStoragePointers: opts.ownLocalStoragePointers,
36495
36589
  sessionStoragePointers: opts.ownSessionStoragePointers,
36496
- domSelectors: opts.ownDomSelectors
36590
+ domSelectors: opts.ownDomSelectors,
36591
+ graphqlOps: opts.ownGraphqlOps
36497
36592
  });
36498
36593
  const ownSessionNonce = fromB64(ownHello.sessionNonce);
36499
36594
  let extensionWs = null;
@@ -36730,7 +36825,8 @@ async function startPeer(opts) {
36730
36825
  indexedDbScopes: opts.indexedDbScopes,
36731
36826
  domSelectors: opts.domSelectors,
36732
36827
  localStoragePointers: opts.localStoragePointers,
36733
- sessionStoragePointers: opts.sessionStoragePointers
36828
+ sessionStoragePointers: opts.sessionStoragePointers,
36829
+ graphqlOps: opts.graphqlOps
36734
36830
  });
36735
36831
  const sessionNonce = fromB64(hello.sessionNonce);
36736
36832
  ws.send(JSON.stringify(hello));
@@ -36774,10 +36870,20 @@ async function startPeer(opts) {
36774
36870
  return;
36775
36871
  if (!session.acceptInboundSeq(frame.seq))
36776
36872
  return;
36777
- try {
36778
- const inner = await openEncryptedFrame(session.sessionKey, frame);
36779
- innerListeners.forEach((cb) => cb(inner));
36780
- } catch {
36873
+ const result = await openEncryptedFrameDetailed(session.sessionKey, frame);
36874
+ if (result.stage === "ok") {
36875
+ innerListeners.forEach((cb) => cb(result.inner));
36876
+ } else if (result.stage === "decrypt-failed") {
36877
+ } else {
36878
+ console.error("[fetchproxy] peer: received a frame that decrypted OK but failed validation:", result.error);
36879
+ if (result.recoveredId !== void 0) {
36880
+ innerListeners.forEach((cb) => cb({
36881
+ type: "response",
36882
+ id: result.recoveredId,
36883
+ ok: false,
36884
+ error: `malformed response failed protocol validation: ${String(result.error)}`
36885
+ }));
36886
+ }
36781
36887
  }
36782
36888
  }
36783
36889
  } catch (e) {
@@ -37054,6 +37160,10 @@ var FetchproxyServer = class {
37054
37160
  pendingIdb = /* @__PURE__ */ new Map();
37055
37161
  // download awaiters resolve the saved-file metadata (path + size + mime).
37056
37162
  pendingDownload = /* @__PURE__ */ new Map();
37163
+ // 1.x+: graphql_query awaiters resolve the GraphQL `data` object. Its
37164
+ // shape is operation-specific, so the awaiter resolves `unknown` and the
37165
+ // caller narrows.
37166
+ pendingGraphql = /* @__PURE__ */ new Map();
37057
37167
  mcpId = null;
37058
37168
  identity = null;
37059
37169
  // 0.5.3+: in-flight role-election / handle-start promise. Set the
@@ -37141,6 +37251,10 @@ var FetchproxyServer = class {
37141
37251
  selector: d.selector,
37142
37252
  ...d.attribute !== void 0 ? { attribute: d.attribute } : {}
37143
37253
  })),
37254
+ graphqlOps: (opts.graphqlOps ?? []).map((d) => ({
37255
+ name: d.name,
37256
+ operationName: d.operationName
37257
+ })),
37144
37258
  // 0.8.0+: timer + lazy-revive default to ON. Every realty MCP
37145
37259
  // adapter was about to set these to the same numbers anyway; the
37146
37260
  // back-door is `0` (explicit opt-out) if a caller genuinely wants
@@ -37262,6 +37376,7 @@ var FetchproxyServer = class {
37262
37376
  ownLocalStoragePointers: this.opts.localStoragePointers,
37263
37377
  ownSessionStoragePointers: this.opts.sessionStoragePointers,
37264
37378
  ownDomSelectors: this.opts.domSelectors,
37379
+ ownGraphqlOps: this.opts.graphqlOps,
37265
37380
  onPairCode: this.opts.onPairCode
37266
37381
  });
37267
37382
  this.hostHandle.onOwnInner((inner) => this.onInner(inner));
@@ -37290,7 +37405,8 @@ var FetchproxyServer = class {
37290
37405
  indexedDbScopes: this.opts.indexedDbScopes,
37291
37406
  localStoragePointers: this.opts.localStoragePointers,
37292
37407
  sessionStoragePointers: this.opts.sessionStoragePointers,
37293
- domSelectors: this.opts.domSelectors
37408
+ domSelectors: this.opts.domSelectors,
37409
+ graphqlOps: this.opts.graphqlOps
37294
37410
  });
37295
37411
  this.peerHandle.onInner((inner) => this.onInner(inner));
37296
37412
  this.peerHandle.onRenegotiate(() => {
@@ -37496,6 +37612,7 @@ var FetchproxyServer = class {
37496
37612
  this.pendingRedirect.delete(id);
37497
37613
  this.pendingDownload.delete(id);
37498
37614
  this.pendingIdb.delete(id);
37615
+ this.pendingGraphql.delete(id);
37499
37616
  }
37500
37617
  throw err;
37501
37618
  }
@@ -38305,6 +38422,52 @@ var FetchproxyServer = class {
38305
38422
  await this.sendInnerFrame(inner);
38306
38423
  return this._withVerbTimeout(pending, this.pendingStorage, id, origin);
38307
38424
  }
38425
+ /**
38426
+ * 1.x+: run a declared GraphQL operation through the page's own Apollo
38427
+ * client (`window.__APOLLO_CLIENT__`) in the signed-in tab's MAIN world.
38428
+ * Requires `'graphql'` in capabilities AND `name` to match a declared
38429
+ * `graphqlOps` entry. The extension resolves `name` → `operationName` →
38430
+ * the live DocumentNode the page already observed, then invokes
38431
+ * `client.query({ query, variables })` — the site's own request path, so
38432
+ * per-request bot telemetry (Akamai etc.) runs automatically.
38433
+ *
38434
+ * Returns the GraphQL `data` object on success (shape is
38435
+ * operation-specific; the caller narrows). Throws a plain `Error` on
38436
+ * developer mistakes (undeclared capability, undeclared name) and a
38437
+ * descriptive `Error` on the `ok:false` bridge path — which includes the
38438
+ * typed "operation not yet observed on this tab" case (open the site's
38439
+ * page and retry).
38440
+ */
38441
+ async graphqlQuery(opts) {
38442
+ if (!this.opts.capabilities.includes("graphql")) {
38443
+ throw new Error('FetchproxyServer.graphqlQuery(): MCP did not declare "graphql" in capabilities');
38444
+ }
38445
+ if (typeof opts.name !== "string" || opts.name.length === 0) {
38446
+ throw new Error("FetchproxyServer.graphqlQuery: opts.name must be a non-empty string");
38447
+ }
38448
+ const declaredNames = this.opts.graphqlOps.map((d) => d.name);
38449
+ if (!declaredNames.includes(opts.name)) {
38450
+ throw new Error(`FetchproxyServer.graphqlQuery: operation ${JSON.stringify(opts.name)} not in declared graphqlOps [${declaredNames.map((n) => JSON.stringify(n)).join(", ")}]`);
38451
+ }
38452
+ await this.ensureConnected();
38453
+ this.throwIfPendingPair();
38454
+ const id = this.nextRequestId++;
38455
+ const inner = {
38456
+ type: "request",
38457
+ id,
38458
+ op: "graphql_query",
38459
+ init: {
38460
+ name: opts.name,
38461
+ variables: opts.variables,
38462
+ ...opts.tabUrl !== void 0 ? { tabUrl: opts.tabUrl } : {}
38463
+ }
38464
+ };
38465
+ const pending = new Promise((resolve, reject) => {
38466
+ this.pendingGraphql.set(id, { resolve, reject });
38467
+ });
38468
+ await this.sendInnerFrame(inner);
38469
+ return this._withVerbTimeout(pending, this.pendingGraphql, id, opts.name);
38470
+ }
38308
38471
  assertScopeSubset(requested, declared, label) {
38309
38472
  const undeclared = undeclaredKeys(requested, declared);
38310
38473
  if (undeclared.length > 0) {
@@ -38442,6 +38605,20 @@ var FetchproxyServer = class {
38442
38605
  }
38443
38606
  return;
38444
38607
  }
38608
+ const graphqlCb = this.pendingGraphql.get(inner.id);
38609
+ if (graphqlCb) {
38610
+ this.pendingGraphql.delete(inner.id);
38611
+ if (inner.ok) {
38612
+ if (inner.op === "graphql_query") {
38613
+ graphqlCb.resolve(inner.data);
38614
+ } else {
38615
+ graphqlCb.reject(new FetchproxyProtocolError(`unexpected ${String(inner.op)} response on graphql_query awaiter`));
38616
+ }
38617
+ } else {
38618
+ graphqlCb.reject(new FetchproxyProtocolError(inner.error));
38619
+ }
38620
+ return;
38621
+ }
38445
38622
  const cookiesCb = this.pendingReadCookies.get(inner.id);
38446
38623
  if (cookiesCb) {
38447
38624
  this.pendingReadCookies.delete(inner.id);
@@ -38493,6 +38670,9 @@ var FetchproxyServer = class {
38493
38670
  for (const { reject } of this.pendingDownload.values())
38494
38671
  reject(err);
38495
38672
  this.pendingDownload.clear();
38673
+ for (const { reject } of this.pendingGraphql.values())
38674
+ reject(err);
38675
+ this.pendingGraphql.clear();
38496
38676
  }
38497
38677
  /**
38498
38678
  * 0.5.2+: read the current pair-pending pair code from whichever handle
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  /** Single source of the server version. release-please bumps the literal below. */
2
- export const VERSION = '0.3.0'; // x-release-please-version
2
+ export const VERSION = '0.3.2'; // x-release-please-version
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chrischall/tripadvisor-mcp",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "mcpName": "io.github.chrischall/tripadvisor-mcp",
5
5
  "description": "TripAdvisor Terra API MCP server for Claude — search locations, details, photos, and reviews. Developed and maintained by AI (Claude Code).",
6
6
  "author": "Claude Code (AI) <https://www.anthropic.com/claude>",
@@ -44,8 +44,8 @@
44
44
  "test:coverage": "vitest run --coverage"
45
45
  },
46
46
  "dependencies": {
47
- "@chrischall/mcp-utils": "^0.13.0",
48
- "@fetchproxy/server": "^1.3.4",
47
+ "@chrischall/mcp-utils": "^0.14.0",
48
+ "@fetchproxy/server": "^1.7.0",
49
49
  "@modelcontextprotocol/sdk": "^1.29.0",
50
50
  "dotenv": "^17.4.0",
51
51
  "zod": "^4.4.2"
package/server.json CHANGED
@@ -6,12 +6,12 @@
6
6
  "url": "https://github.com/chrischall/tripadvisor-mcp",
7
7
  "source": "github"
8
8
  },
9
- "version": "0.3.0",
9
+ "version": "0.3.2",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "@chrischall/tripadvisor-mcp",
14
- "version": "0.3.0",
14
+ "version": "0.3.2",
15
15
  "transport": {
16
16
  "type": "stdio"
17
17
  },