@geometra/mcp 1.44.0 → 1.45.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/session.js +29 -2
  2. package/package.json +1 -1
package/dist/session.js CHANGED
@@ -645,19 +645,46 @@ export function connect(url, opts) {
645
645
  let resolved = false;
646
646
  let lastMessageAt = Date.now();
647
647
  let heartbeatInterval = null;
648
+ let pendingPongBy = null;
649
+ // Heartbeat: send a real WS-level ping every 15s and only tear the socket
650
+ // down if the peer fails to respond to two consecutive pings (i.e. ~45s of
651
+ // true unresponsiveness). Previous versions used a dumb idle timer that
652
+ // closed the socket after 30s of no inbound frames — which killed sessions
653
+ // during normal form-submission flows where the DOM is legitimately idle
654
+ // for 20-30+ seconds while the backend processes (Greenhouse submit →
655
+ // security-code dialog is the canonical repro). A real ping/pong cycle
656
+ // distinguishes a silent-but-healthy session from a dead one.
648
657
  function startHeartbeat() {
649
658
  if (heartbeatInterval)
650
659
  return;
651
660
  heartbeatInterval = setInterval(() => {
652
- if (Date.now() - lastMessageAt > 30_000) {
661
+ // If we're waiting on a pong and it's overdue, the peer is dead.
662
+ if (pendingPongBy !== null && Date.now() > pendingPongBy) {
653
663
  try {
654
664
  ws.close();
655
665
  }
656
666
  catch { /* ignore */ }
667
+ return;
668
+ }
669
+ // Only send a new ping if we haven't heard anything for a while,
670
+ // to avoid spamming a chatty session.
671
+ if (Date.now() - lastMessageAt > 10_000) {
672
+ try {
673
+ ws.ping();
674
+ // Allow 30s for the pong before declaring the peer dead.
675
+ pendingPongBy = Date.now() + 30_000;
676
+ }
677
+ catch {
678
+ /* if ping throws, the socket is already gone — let 'close' handle */
679
+ }
657
680
  }
658
- }, 30_000);
681
+ }, 15_000);
659
682
  heartbeatInterval.unref();
660
683
  }
684
+ ws.on('pong', () => {
685
+ lastMessageAt = Date.now();
686
+ pendingPongBy = null;
687
+ });
661
688
  const timeout = setTimeout(() => {
662
689
  if (!resolved) {
663
690
  resolved = true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geometra/mcp",
3
- "version": "1.44.0",
3
+ "version": "1.45.0",
4
4
  "description": "MCP server for Geometra — interact with running Geometra apps via the geometry protocol, no browser needed",
5
5
  "license": "MIT",
6
6
  "type": "module",